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.util.Arrays;
22 import org.bouncycastle.util.Memoable;
23
24
25
26
27 @SuppressWarnings("checkstyle:MagicNumber")
28 public abstract class GordianBlake2Base
29 implements ExtendedDigest, Memoable {
30
31
32
33 static final int NUMWORDS = 8;
34
35
36
37
38 private static final int MAXBYTE = 0xFF;
39
40
41
42
43 private static final byte[][] SIGMA = {
44 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
45 {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
46 {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
47 {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
48 {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
49 {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
50 {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
51 {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
52 {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
53 {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
54 };
55
56
57
58
59 private final int theRounds;
60
61
62
63
64 private final long theMaxXofLen;
65
66
67
68
69 private final byte[] theBuffer;
70
71
72
73
74 private int thePos;
75
76
77
78
79 private short theDigestLen;
80
81
82
83
84 private byte[] theKey;
85
86
87
88
89 private byte[] theSalt;
90
91
92
93
94 private byte[] thePersonal;
95
96
97
98
99 private short theFanOut;
100
101
102
103
104 private short theMaxDepth;
105
106
107
108
109 private int theLeafLen;
110
111
112
113
114 private int theNodeOffset;
115
116
117
118
119 private short theNodeDepth;
120
121
122
123
124 private int theXofLen;
125
126
127
128
129 private short theInnerLen;
130
131
132
133
134 private boolean isLastBlock;
135
136
137
138
139 private boolean isLastNode;
140
141
142
143
144
145
146
147 GordianBlake2Base(final int pRounds,
148 final int pBlockLen) {
149
150 theRounds = pRounds;
151 theBuffer = new byte[pBlockLen];
152 theFanOut = 1;
153 theMaxDepth = 1;
154
155
156 theMaxXofLen = this instanceof GordianBlake2bDigest ? 0xFFFFFFFEL : 0xFFFEL;
157 }
158
159
160
161
162
163
164 GordianBlake2Base(final GordianBlake2Base pSource) {
165
166 theRounds = pSource.theRounds;
167 theBuffer = new byte[pSource.theBuffer.length];
168
169
170 theMaxXofLen = pSource.theMaxXofLen;
171 }
172
173
174
175
176
177
178 void setDigestLength(final int pLength) {
179 if (pLength < 0 || pLength > theBuffer.length << 2) {
180 throw new IllegalArgumentException("DigestLength out of range");
181 }
182 theDigestLen = (short) pLength;
183 }
184
185 @Override
186 public int getDigestSize() {
187 return theDigestLen;
188 }
189
190
191
192
193
194
195 public void init(final GordianBlake2Parameters pParams) {
196
197 setKey(pParams.getKey());
198 setSalt(pParams.getSalt());
199 setPersonalisation(pParams.getPersonalisation());
200 setXofLen(pParams.getMaxOutputLength());
201 setTreeConfig(pParams.getTreeFanOut(), pParams.getTreeMaxDepth(), pParams.getTreeLeafLen());
202
203
204 reset();
205 }
206
207
208
209
210
211
212 void setKey(final byte[] pKey) {
213 if (pKey == null || pKey.length == 0) {
214 clearKey();
215 theKey = null;
216 } else {
217 if (pKey.length > theBuffer.length >> 1) {
218 throw new IllegalArgumentException("Key too long");
219 }
220 clearKey();
221 theKey = Arrays.copyOf(pKey, pKey.length);
222 }
223 }
224
225
226
227
228 private void clearKey() {
229 if (theKey != null) {
230 Arrays.fill(theKey, (byte) 0);
231 }
232 }
233
234
235
236
237
238
239 int getKeyLen() {
240 return theKey == null ? 0 : theKey.length;
241 }
242
243
244
245
246
247
248 void setSalt(final byte[] pSalt) {
249 if (pSalt == null || pSalt.length == 0) {
250 theSalt = null;
251 } else {
252 if (pSalt.length != theBuffer.length >> 3) {
253 throw new IllegalArgumentException("Salt incorrect length");
254 }
255 theSalt = Arrays.copyOf(pSalt, pSalt.length);
256 }
257 }
258
259
260
261
262
263
264 byte[] getSalt() {
265 return theSalt;
266 }
267
268
269
270
271
272
273 void setPersonalisation(final byte[] pPersonal) {
274 if (pPersonal == null || pPersonal.length == 0) {
275 thePersonal = null;
276 } else {
277 if (pPersonal.length != theBuffer.length >> 3) {
278 throw new IllegalArgumentException("Personalisation incorrect length");
279 }
280 thePersonal = Arrays.copyOf(pPersonal, pPersonal.length);
281 }
282 }
283
284
285
286
287
288
289 byte[] getPersonal() {
290 return thePersonal;
291 }
292
293
294
295
296
297
298 void setXofLen(final long pXofLen) {
299 if (pXofLen < -1 || pXofLen > theMaxXofLen) {
300 throw new IllegalArgumentException("XofLength out of range");
301 }
302 theXofLen = (int) pXofLen;
303 }
304
305
306
307
308
309
310 int getXofLen() {
311 return theXofLen;
312 }
313
314
315
316
317
318
319
320
321 void setTreeConfig(final int pFanOut,
322 final int pMaxDepth,
323 final int pLeafLen) {
324
325 if (pFanOut < 0 || pFanOut > MAXBYTE) {
326 throw new IllegalArgumentException("FanOut out of range");
327 }
328 final boolean seqMode = pFanOut == 1;
329
330
331 final boolean xofMode = pMaxDepth == 0;
332 if (pMaxDepth < 0 || pMaxDepth > MAXBYTE) {
333 throw new IllegalArgumentException("MaxDepth out of range");
334 }
335 if (seqMode != (pMaxDepth == 1)) {
336 throw new IllegalArgumentException("Inconsistent treeConfig for sequentialMode");
337 }
338
339
340 if (pLeafLen < 0) {
341 throw new IllegalArgumentException("LeafLength out of range");
342 }
343 if (seqMode != (pLeafLen == 0)) {
344 throw new IllegalArgumentException("Inconsistent treeConfig for LeafLen and fanOut");
345 }
346 if (xofMode && pFanOut != 0) {
347 throw new IllegalArgumentException("Inconsistent treeConfig for xofMode");
348 }
349
350
351 theFanOut = (short) pFanOut;
352 theMaxDepth = (short) pMaxDepth;
353 theLeafLen = pLeafLen;
354 }
355
356
357
358
359
360
361 short getFanOut() {
362 return theFanOut;
363 }
364
365
366
367
368
369
370 short getMaxDepth() {
371 return theMaxDepth;
372 }
373
374
375
376
377
378
379 int getLeafLen() {
380 return theLeafLen;
381 }
382
383
384
385
386
387
388
389 void setNodePosition(final int pOffset,
390 final int pDepth) {
391 if (pOffset < 0) {
392 throw new IllegalArgumentException("NodeOffset out of range");
393 }
394 theNodeOffset = pOffset;
395 if (pDepth < 0 || pDepth > MAXBYTE) {
396 throw new IllegalArgumentException("NodeDepth out of range");
397 }
398 theNodeDepth = (byte) pDepth;
399 reset();
400 }
401
402
403
404
405
406
407 int getNodeOffset() {
408 return theNodeOffset;
409 }
410
411
412
413
414
415
416 short getNodeDepth() {
417 return theNodeDepth;
418 }
419
420
421
422
423
424
425 boolean isLastBlock() {
426 return isLastBlock;
427 }
428
429
430
431
432 void setLastNode() {
433 isLastNode = true;
434 }
435
436
437
438
439
440
441 boolean isLastNode() {
442 return isLastNode;
443 }
444
445
446
447
448
449
450 void setInnerLength(final int pInnerLen) {
451 if (pInnerLen < 0 || pInnerLen > MAXBYTE) {
452 throw new IllegalArgumentException("InnerLength out of range");
453 }
454 theInnerLen = (short) pInnerLen;
455 }
456
457
458
459
460
461
462 short getInnerLen() {
463 return theInnerLen;
464 }
465
466 @Override
467 public void update(final byte b) {
468
469 final int blockLen = theBuffer.length;
470 final int remainingLength = blockLen - thePos;
471 if (remainingLength == 0) {
472
473 adjustCounter(blockLen);
474 compressF(theBuffer, 0);
475
476
477 Arrays.fill(theBuffer, (byte) 0);
478 thePos = 0;
479 }
480
481
482 theBuffer[thePos] = b;
483 thePos++;
484 }
485
486 @Override
487 public void update(final byte[] pMessage,
488 final int pOffset,
489 final int pLen) {
490
491 if (pMessage == null || pLen == 0) {
492 return;
493 }
494
495
496 final int blockLen = theBuffer.length;
497 int remainingLen = 0;
498 if (thePos != 0) {
499
500 remainingLen = blockLen - thePos;
501
502
503 if (remainingLen >= pLen) {
504
505 System.arraycopy(pMessage, pOffset, theBuffer, thePos, pLen);
506 thePos += pLen;
507 return;
508 }
509
510
511 System.arraycopy(pMessage, pOffset, theBuffer, thePos, remainingLen);
512
513
514 adjustCounter(blockLen);
515
516
517 compressF(theBuffer, 0);
518
519
520 thePos = 0;
521 Arrays.fill(theBuffer, (byte) 0);
522 }
523
524
525 int messagePos;
526 final int blockWiseLastPos = pOffset + pLen - blockLen;
527 for (messagePos = pOffset + remainingLen; messagePos < blockWiseLastPos; messagePos += blockLen) {
528
529 adjustCounter(blockLen);
530
531
532 compressF(pMessage, messagePos);
533 }
534
535
536 final int len = pLen - messagePos;
537 System.arraycopy(pMessage, messagePos, theBuffer, 0, pOffset + len);
538 thePos += pOffset + len;
539 }
540
541 @Override
542 public int doFinal(final byte[] pOut,
543 final int pOutOffset) {
544
545 isLastBlock = true;
546 completeCounter(thePos);
547
548
549 compressF(theBuffer, 0);
550 Arrays.fill(theBuffer, (byte) 0);
551
552
553 outputDigest(pOut, pOutOffset);
554
555
556 reset();
557
558
559 return theDigestLen;
560 }
561
562 @Override
563 public void reset() {
564
565 isLastBlock = false;
566 isLastNode = false;
567
568
569 thePos = 0;
570 Arrays.fill(theBuffer, (byte) 0);
571
572
573 activateH();
574 }
575
576
577
578
579
580
581 void reset(final GordianBlake2Base pSource) {
582
583 theDigestLen = pSource.theDigestLen;
584 theInnerLen = pSource.theInnerLen;
585 theLeafLen = pSource.theLeafLen;
586 theXofLen = pSource.theXofLen;
587 theFanOut = pSource.theFanOut;
588 theMaxDepth = pSource.theMaxDepth;
589 theNodeDepth = pSource.theNodeDepth;
590 theNodeOffset = pSource.theNodeOffset;
591
592
593 isLastNode = pSource.isLastNode;
594
595
596 theKey = Arrays.clone(pSource.theKey);
597 theSalt = Arrays.clone(pSource.theSalt);
598 thePersonal = Arrays.clone(pSource.thePersonal);
599
600
601 System.arraycopy(pSource.theBuffer, 0, theBuffer, 0, theBuffer.length);
602 thePos = pSource.thePos;
603 }
604
605
606
607
608
609
610 abstract void adjustCounter(int pCount);
611
612
613
614
615
616
617 abstract void completeCounter(int pCount);
618
619
620
621
622
623
624
625 abstract void outputDigest(byte[] pOut,
626 int pOutOffset);
627
628
629
630
631 void initKeyBlock() {
632
633 if (theKey != null) {
634
635 System.arraycopy(theKey, 0, theBuffer, 0, theKey.length);
636 thePos = theBuffer.length;
637 }
638 }
639
640
641
642
643 abstract void activateH();
644
645
646
647
648
649
650
651 private static byte[] getSigmaForRound(final int pRound) {
652 return SIGMA[pRound % SIGMA.length];
653 }
654
655
656
657
658
659
660
661 private void compressF(final byte[] pMessage,
662 final int pMsgPos) {
663
664 initV();
665 initM(pMessage, pMsgPos);
666
667
668 for (int round = 0; round < theRounds; round++) {
669
670 final byte[] sigma = getSigmaForRound(round);
671
672
673 mixG(sigma[0], sigma[1], 0, 4, 8, 12);
674 mixG(sigma[2], sigma[3], 1, 5, 9, 13);
675 mixG(sigma[4], sigma[5], 2, 6, 10, 14);
676 mixG(sigma[6], sigma[7], 3, 7, 11, 15);
677
678
679 mixG(sigma[8], sigma[9], 0, 5, 10, 15);
680 mixG(sigma[10], sigma[11], 1, 6, 11, 12);
681 mixG(sigma[12], sigma[13], 2, 7, 8, 13);
682 mixG(sigma[14], sigma[15], 3, 4, 9, 14);
683 }
684
685
686 adjustH();
687 }
688
689
690
691
692 abstract void initV();
693
694
695
696
697
698
699
700 abstract void initM(byte[] pMessage,
701 int pMsgPos);
702
703
704
705
706
707
708
709
710
711
712
713 abstract void mixG(int msgIdx1,
714 int msgIdx2,
715 int posA,
716 int posB,
717 int posC,
718 int posD);
719
720
721
722
723 abstract void adjustH();
724 }