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.digests;
18
19 import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinBase.Configuration;
20 import io.github.tonywasher.joceanus.gordianknot.impl.ext.params.GordianSkeinParameters;
21 import org.bouncycastle.crypto.ExtendedDigest;
22 import org.bouncycastle.crypto.Xof;
23 import org.bouncycastle.util.Memoable;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class GordianSkeinXof
39 implements ExtendedDigest, Memoable, Xof {
40
41
42
43 private static final int MAX_XOFLEN = -2;
44
45
46
47
48 private final GordianSkeinBase theUnderlying;
49
50
51
52
53 private final byte[] singleByte = new byte[1];
54
55
56
57
58 private byte[] outputCache;
59
60
61
62
63 private long theXofLen;
64
65
66
67
68 private long theXofRemaining;
69
70
71
72
73 private long nextOutputIdx;
74
75
76
77
78 private int bytesInCache;
79
80
81
82
83
84
85 public GordianSkeinXof(final GordianSkeinBase pDigest) {
86
87 theUnderlying = pDigest;
88 outputCache = new byte[theUnderlying.getBlockSize()];
89
90
91 theXofRemaining = -1L;
92 theXofLen = -1;
93 }
94
95
96
97
98
99
100 public GordianSkeinXof(final GordianSkeinXof pSource) {
101
102 theUnderlying = (GordianSkeinBase) pSource.theUnderlying.copy();
103 outputCache = new byte[theUnderlying.getBlockSize()];
104
105
106 reset(pSource);
107 }
108
109
110
111
112
113
114 public GordianSkeinXof(final GordianSkeinDigest pDigest) {
115 this(pDigest.getBase());
116 }
117
118
119
120
121
122
123 public void init(final GordianSkeinParameters pParams) {
124
125 final long myXofLen = pParams.getMaxOutputLength();
126 if (myXofLen < -1) {
127 throw new IllegalArgumentException("Invalid output length");
128 }
129 theXofLen = myXofLen;
130
131
132 declareConfig();
133
134
135 theUnderlying.init(pParams);
136 theXofRemaining = -1L;
137 }
138
139
140
141
142 private void declareConfig() {
143
144 long myLen = theXofLen == 0 ? theUnderlying.getOutputSize() : theXofLen;
145 myLen = myLen == -1 ? -1L : myLen * Byte.SIZE;
146 final Configuration myConfig = new Configuration(myLen);
147 theUnderlying.setConfiguration(myConfig);
148 }
149
150 @Override
151 public String getAlgorithmName() {
152 final String myBase = "SkeinXof";
153 if (theXofLen == -1) {
154 return myBase;
155 }
156 final long myLen = theXofLen == 0 ? theUnderlying.getOutputSize() : theXofLen;
157 return myBase + "-" + (theUnderlying.getBlockSize() * Byte.SIZE) + "-" + (myLen * Byte.SIZE);
158 }
159
160 @Override
161 public int getDigestSize() {
162 return (theXofLen == 0 || theXofLen == -1) ? theUnderlying.getOutputSize() : (int) theXofLen;
163 }
164
165 @Override
166 public int getByteLength() {
167 return theUnderlying.getBlockSize();
168 }
169
170 @Override
171 public void update(final byte b) {
172 singleByte[0] = b;
173 update(singleByte, 0, 1);
174 }
175
176 @Override
177 public void update(final byte[] pMessage,
178 final int pOffset,
179 final int pLen) {
180 if (theXofRemaining != -1) {
181 throw new IllegalStateException("Already outputting");
182 }
183 theUnderlying.update(pMessage, pOffset, pLen);
184 }
185
186 @Override
187 public int doFinal(final byte[] pOut,
188 final int pOutOffset) {
189 return doFinal(pOut, pOutOffset, getDigestSize());
190 }
191
192 @Override
193 public int doFinal(final byte[] pOut,
194 final int pOutOffset,
195 final int pOutLen) {
196
197 final int length = doOutput(pOut, pOutOffset, pOutLen);
198
199
200 reset();
201 return length;
202 }
203
204 @Override
205 public int doOutput(final byte[] pOut,
206 final int pOutOffset,
207 final int pOutLen) {
208
209 if (theXofRemaining == -1) {
210
211 nextOutputIdx = 0;
212 bytesInCache = 0;
213
214
215 if (theXofLen == 0) {
216
217 theXofRemaining = theUnderlying.getOutputSize();
218
219
220 } else {
221
222 theXofRemaining = theXofLen == -1
223 ? MAX_XOFLEN
224 : theXofLen;
225 }
226
227
228 theUnderlying.initiateOutput();
229 }
230
231
232 if (pOutLen < 0
233 || (theXofRemaining > 0 && pOutLen > theXofRemaining)) {
234 throw new IllegalArgumentException("Insufficient bytes remaining");
235 }
236
237
238 int dataLeft = pOutLen;
239 int outPos = pOutOffset;
240 if (bytesInCache > 0) {
241
242 final int dataToCopy = Math.min(dataLeft, bytesInCache);
243 System.arraycopy(outputCache, outputCache.length - bytesInCache, pOut, outPos, dataToCopy);
244
245
246 theXofRemaining -= dataToCopy;
247 bytesInCache -= dataToCopy;
248 outPos += dataToCopy;
249 dataLeft -= dataToCopy;
250 }
251
252
253 while (dataLeft > 0) {
254
255 theUnderlying.output(nextOutputIdx++, outputCache, 0, outputCache.length);
256 bytesInCache = outputCache.length;
257
258
259 final int dataToCopy = Math.min(dataLeft, outputCache.length);
260 System.arraycopy(outputCache, 0, pOut, outPos, dataToCopy);
261
262
263 theXofRemaining -= dataToCopy;
264 bytesInCache -= dataToCopy;
265 outPos += dataToCopy;
266 dataLeft -= dataToCopy;
267 }
268
269
270 return pOutLen;
271 }
272
273 @Override
274 public void reset() {
275 theUnderlying.reset();
276 theXofRemaining = -1L;
277 }
278
279 @Override
280 public void reset(final Memoable pSource) {
281
282 final GordianSkeinXof mySource = (GordianSkeinXof) pSource;
283
284
285 theUnderlying.reset(mySource.theUnderlying);
286
287
288 theXofLen = mySource.theXofLen;
289 theXofRemaining = mySource.theXofRemaining;
290 bytesInCache = mySource.bytesInCache;
291 nextOutputIdx = mySource.nextOutputIdx;
292
293
294 System.arraycopy(mySource.outputCache, 0, outputCache, 0, outputCache.length);
295
296
297 declareConfig();
298 }
299
300 @Override
301 public GordianSkeinXof copy() {
302 return new GordianSkeinXof(this);
303 }
304 }