1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.impl.ext.engines;
18
19 import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinBase;
20 import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinXof;
21 import io.github.tonywasher.joceanus.gordianknot.impl.ext.params.GordianSkeinParameters.GordianSkeinParametersBuilder;
22 import org.bouncycastle.crypto.CipherParameters;
23 import org.bouncycastle.crypto.DataLengthException;
24 import org.bouncycastle.crypto.OutputLengthException;
25 import org.bouncycastle.crypto.StreamCipher;
26 import org.bouncycastle.crypto.params.KeyParameter;
27 import org.bouncycastle.crypto.params.ParametersWithIV;
28 import org.bouncycastle.util.Memoable;
29
30
31
32
33 public class GordianSkeinXofEngine
34 implements StreamCipher, Memoable {
35
36
37
38 private int theIndex;
39
40
41
42
43 private final byte[] theKeyStream;
44
45
46
47
48 private final GordianSkeinXof theSkeinXof;
49
50
51
52
53 private GordianSkeinXof theResetState;
54
55
56
57
58
59
60 public GordianSkeinXofEngine(final int pLength) {
61 final GordianSkeinBase myBase = new GordianSkeinBase(pLength, pLength);
62 theSkeinXof = new GordianSkeinXof(myBase);
63 theKeyStream = new byte[myBase.getBlockSize()];
64 }
65
66
67
68
69
70
71 private GordianSkeinXofEngine(final GordianSkeinXofEngine pSource) {
72 theSkeinXof = new GordianSkeinXof(pSource.theSkeinXof);
73 theKeyStream = new byte[theSkeinXof.getByteLength()];
74 reset(pSource);
75 }
76
77
78
79
80
81
82
83
84 public void init(final boolean forEncryption,
85 final CipherParameters params) {
86
87
88
89
90
91
92 CipherParameters myParams = params;
93 byte[] newKey = null;
94 byte[] newIV = null;
95 if ((myParams instanceof ParametersWithIV ivParams)) {
96 newIV = ivParams.getIV();
97 myParams = ivParams.getParameters();
98 }
99 if (myParams instanceof KeyParameter keyParam) {
100 newKey = keyParam.getKey();
101 }
102 if (newKey == null || newIV == null) {
103 throw new IllegalArgumentException("A key and IV must be provided");
104 }
105
106
107 final GordianSkeinParametersBuilder myBuilder = new GordianSkeinParametersBuilder()
108 .setKey(newKey)
109 .setNonce(newIV)
110 .setMaxOutputLen(-1);
111 theSkeinXof.init(myBuilder.build());
112
113
114 theResetState = theSkeinXof.copy();
115
116
117 theIndex = 0;
118 makeStreamBlock();
119 }
120
121 @Override
122 public String getAlgorithmName() {
123 return theSkeinXof.getAlgorithmName();
124 }
125
126 @Override
127 public int processBytes(final byte[] in,
128 final int inOff,
129 final int len,
130 final byte[] out,
131 final int outOff) {
132
133 if (theResetState == null) {
134 throw new IllegalStateException(getAlgorithmName() + " not initialised");
135 }
136 if ((inOff + len) > in.length) {
137 throw new DataLengthException("input buffer too short");
138 }
139 if ((outOff + len) > out.length) {
140 throw new OutputLengthException("output buffer too short");
141 }
142
143
144 for (int i = 0; i < len; i++) {
145 out[i + outOff] = returnByte(in[i + inOff]);
146 }
147 return len;
148 }
149
150 @Override
151 public void reset() {
152 if (theResetState != null) {
153 theSkeinXof.reset(theResetState);
154 theIndex = 0;
155 makeStreamBlock();
156 }
157 }
158
159 @Override
160 public byte returnByte(final byte in) {
161 final byte out = (byte) (theKeyStream[theIndex] ^ in);
162 theIndex = (theIndex + 1) % theKeyStream.length;
163
164 if (theIndex == 0) {
165 makeStreamBlock();
166 }
167 return out;
168 }
169
170
171
172
173 private void makeStreamBlock() {
174
175 theSkeinXof.doOutput(theKeyStream, 0, theKeyStream.length);
176 }
177
178 @Override
179 public GordianSkeinXofEngine copy() {
180 return new GordianSkeinXofEngine(this);
181 }
182
183 @Override
184 public void reset(final Memoable pState) {
185 final GordianSkeinXofEngine e = (GordianSkeinXofEngine) pState;
186 if (theKeyStream.length != e.theKeyStream.length) {
187 throw new IllegalArgumentException();
188 }
189 theSkeinXof.reset(e.theSkeinXof);
190 System.arraycopy(e.theKeyStream, 0, theKeyStream, 0, theKeyStream.length);
191 theIndex = e.theIndex;
192 theResetState = e.theResetState;
193 }
194 }
195