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