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.GordianBlake3Parameters;
20 import org.bouncycastle.crypto.ExtendedDigest;
21 import org.bouncycastle.crypto.Xof;
22 import org.bouncycastle.util.Memoable;
23 import org.bouncycastle.util.Pack;
24
25 import java.util.ArrayDeque;
26 import java.util.Arrays;
27 import java.util.Deque;
28
29
30
31
32 public class GordianBlake3Digest
33 implements ExtendedDigest, Memoable, Xof {
34
35
36
37 private static final String ERR_OUTPUTTING = "Already outputting";
38
39
40
41
42 private static final int NUMWORDS = 8;
43
44
45
46
47 private static final int ROUNDS = 7;
48
49
50
51
52 private static final int BLOCKLEN = NUMWORDS * Integer.BYTES * 2;
53
54
55
56
57 private static final int CHUNKLEN = 1024;
58
59
60
61
62 private static final int CHUNKSTART = 0b00000001;
63
64
65
66
67 private static final int CHUNKEND = 0b00000010;
68
69
70
71
72 private static final int PARENT = 0b00000100;
73
74
75
76
77 private static final int ROOT = 0b00001000;
78
79
80
81
82 private static final int KEYEDHASH = 0b00010000;
83
84
85
86
87 private static final int DERIVECONTEXT = 0b00100000;
88
89
90
91
92 private static final int DERIVEKEY = 0b01000000;
93
94
95
96
97 private static final int CHAINING0 = 0;
98
99
100
101
102 private static final int CHAINING1 = 1;
103
104
105
106
107 private static final int CHAINING2 = 2;
108
109
110
111
112 private static final int CHAINING3 = 3;
113
114
115
116
117 private static final int CHAINING4 = 4;
118
119
120
121
122 private static final int CHAINING5 = 5;
123
124
125
126
127 private static final int CHAINING6 = 6;
128
129
130
131
132 private static final int CHAINING7 = 7;
133
134
135
136
137 private static final int IV0 = 8;
138
139
140
141
142 private static final int IV1 = 9;
143
144
145
146
147 private static final int IV2 = 10;
148
149
150
151
152 private static final int IV3 = 11;
153
154
155
156
157 private static final int COUNT0 = 12;
158
159
160
161
162 private static final int COUNT1 = 13;
163
164
165
166
167 private static final int DATALEN = 14;
168
169
170
171
172 private static final int FLAGS = 15;
173
174
175
176
177 private static final byte[] SIGMA = {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8};
178
179
180
181
182 private static final byte[] ROTATE = {16, 12, 8, 7};
183
184
185
186
187 private static final int[] IV = {
188 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
189 };
190
191
192
193
194 private final byte[] theBuffer = new byte[BLOCKLEN];
195
196
197
198
199 private final int[] theK = new int[NUMWORDS];
200
201
202
203
204 private final int[] theChaining = new int[NUMWORDS];
205
206
207
208
209 private final int[] theV = new int[NUMWORDS << 1];
210
211
212
213
214 private final int[] theM = new int[NUMWORDS << 1];
215
216
217
218
219 private final byte[] theIndices = new byte[NUMWORDS << 1];
220
221
222
223
224 private final Deque<int[]> theStack = new ArrayDeque<>();
225
226
227
228
229 private final int theDigestLen;
230
231
232
233
234 private boolean outputting;
235
236
237
238
239 private long outputAvailable;
240
241
242
243
244 private int theMode;
245
246
247
248
249 private int theOutputMode;
250
251
252
253
254 private int theOutputDataLen;
255
256
257
258
259 private long theCounter;
260
261
262
263
264 private int theCurrBytes;
265
266
267
268
269 private int thePos;
270
271
272
273
274 public GordianBlake3Digest() {
275 this(BLOCKLEN >> 1);
276 }
277
278
279
280
281
282
283 public GordianBlake3Digest(final int pDigestLen) {
284 theDigestLen = pDigestLen;
285 init(null);
286 }
287
288
289
290
291
292
293 private GordianBlake3Digest(final GordianBlake3Digest pSource) {
294
295 theDigestLen = pSource.theDigestLen;
296
297
298 reset(pSource);
299 }
300
301 @Override
302 public int getByteLength() {
303 return BLOCKLEN;
304 }
305
306 @Override
307 public String getAlgorithmName() {
308 return "BLAKE3";
309 }
310
311 @Override
312 public int getDigestSize() {
313 return theDigestLen;
314 }
315
316
317
318
319
320
321 public void init(final GordianBlake3Parameters pParams) {
322
323 final byte[] myKey = pParams == null ? null : pParams.getKey();
324 final byte[] myContext = pParams == null ? null : pParams.getContext();
325
326
327 reset();
328
329
330 if (myKey != null) {
331
332 initKey(myKey);
333 Arrays.fill(myKey, (byte) 0);
334
335
336 } else if (myContext != null) {
337
338 initNullKey();
339 theMode = DERIVECONTEXT;
340
341
342 update(myContext, 0, myContext.length);
343 doFinal(theBuffer, 0);
344 initKeyFromContext();
345 reset();
346
347
348 } else {
349 initNullKey();
350 theMode = 0;
351 }
352 }
353
354 @Override
355 public void update(final byte b) {
356
357 if (outputting) {
358 throw new IllegalStateException(ERR_OUTPUTTING);
359 }
360
361
362 final int blockLen = theBuffer.length;
363 final int remainingLength = blockLen - thePos;
364 if (remainingLength == 0) {
365
366 compressBlock(theBuffer, 0);
367
368
369 Arrays.fill(theBuffer, (byte) 0);
370 thePos = 0;
371 }
372
373
374 theBuffer[thePos] = b;
375 thePos++;
376 }
377
378 @Override
379 public void update(final byte[] pMessage,
380 final int pOffset,
381 final int pLen) {
382
383 if (pMessage == null || pLen == 0) {
384 return;
385 }
386
387
388 if (outputting) {
389 throw new IllegalStateException(ERR_OUTPUTTING);
390 }
391
392
393 int remainingLen = 0;
394 if (thePos != 0) {
395
396 remainingLen = BLOCKLEN - thePos;
397
398
399 if (remainingLen >= pLen) {
400
401 System.arraycopy(pMessage, pOffset, theBuffer, thePos, pLen);
402 thePos += pLen;
403 return;
404 }
405
406
407 System.arraycopy(pMessage, pOffset, theBuffer, thePos, remainingLen);
408
409
410 compressBlock(theBuffer, 0);
411
412
413 thePos = 0;
414 Arrays.fill(theBuffer, (byte) 0);
415 }
416
417
418 int messagePos;
419 final int blockWiseLastPos = pOffset + pLen - BLOCKLEN;
420 for (messagePos = pOffset + remainingLen; messagePos < blockWiseLastPos; messagePos += BLOCKLEN) {
421
422 compressBlock(pMessage, messagePos);
423 }
424
425
426 final int len = pLen - messagePos;
427 System.arraycopy(pMessage, messagePos, theBuffer, 0, pOffset + len);
428 thePos += pOffset + len;
429 }
430
431 @Override
432 public int doFinal(final byte[] pOutput,
433 final int pOutOffset) {
434 return doFinal(pOutput, pOutOffset, getDigestSize());
435 }
436
437 @Override
438 public int doFinal(final byte[] pOut,
439 final int pOutOffset,
440 final int pOutLen) {
441
442 final int length = doOutput(pOut, pOutOffset, pOutLen);
443
444
445 reset();
446 return length;
447 }
448
449 @Override
450 public int doOutput(final byte[] pOut,
451 final int pOutOffset,
452 final int pOutLen) {
453
454 if (!outputting) {
455
456 compressFinalBlock(thePos);
457 }
458
459
460 if (pOutLen < 0
461 || (outputAvailable >= 0 && pOutLen > outputAvailable)) {
462 throw new IllegalArgumentException("Insufficient bytes remaining");
463 }
464
465
466 int dataLeft = pOutLen;
467 int outPos = pOutOffset;
468 if (thePos < BLOCKLEN) {
469
470 final int dataToCopy = Math.min(dataLeft, BLOCKLEN - thePos);
471 System.arraycopy(theBuffer, thePos, pOut, outPos, dataToCopy);
472
473
474 thePos += dataToCopy;
475 outPos += dataToCopy;
476 dataLeft -= dataToCopy;
477 }
478
479
480 while (dataLeft > 0) {
481
482 nextOutputBlock();
483
484
485 final int dataToCopy = Math.min(dataLeft, BLOCKLEN);
486 System.arraycopy(theBuffer, 0, pOut, outPos, dataToCopy);
487
488
489 thePos += dataToCopy;
490 outPos += dataToCopy;
491 dataLeft -= dataToCopy;
492 }
493
494
495 outputAvailable -= pOutLen;
496
497
498 return pOutLen;
499 }
500
501 @Override
502 public void reset() {
503 resetBlockCount();
504 thePos = 0;
505 outputting = false;
506 Arrays.fill(theBuffer, (byte) 0);
507 }
508
509 @Override
510 public void reset(final Memoable pSource) {
511
512 final GordianBlake3Digest mySource = (GordianBlake3Digest) pSource;
513
514
515 theCounter = mySource.theCounter;
516 theCurrBytes = mySource.theCurrBytes;
517 theMode = mySource.theMode;
518
519
520 outputting = mySource.outputting;
521 outputAvailable = mySource.outputAvailable;
522 theOutputMode = mySource.theOutputMode;
523 theOutputDataLen = mySource.theOutputDataLen;
524
525
526 System.arraycopy(mySource.theChaining, 0, theChaining, 0, theChaining.length);
527 System.arraycopy(mySource.theK, 0, theK, 0, theK.length);
528 System.arraycopy(mySource.theM, 0, theM, 0, theM.length);
529
530
531 theStack.clear();
532 for (int[] myEntry : mySource.theStack) {
533 theStack.push(myEntry.clone());
534 }
535
536
537 System.arraycopy(mySource.theBuffer, 0, theBuffer, 0, theBuffer.length);
538 thePos = mySource.thePos;
539 }
540
541 @Override
542 public GordianBlake3Digest copy() {
543 return new GordianBlake3Digest(this);
544 }
545
546
547
548
549
550
551
552 private void compressBlock(final byte[] pMessage,
553 final int pMsgPos) {
554
555 initChunkBlock(BLOCKLEN, false);
556 initM(pMessage, pMsgPos);
557 compress();
558
559
560 if (theCurrBytes == 0) {
561 adjustStack();
562 }
563 }
564
565
566
567
568 private void adjustStack() {
569
570 long myCount = theCounter;
571 while (myCount > 0) {
572
573 if ((myCount & 1) == 1) {
574 break;
575 }
576
577
578 final int[] myLeft = theStack.pop();
579 System.arraycopy(myLeft, 0, theM, 0, NUMWORDS);
580 System.arraycopy(theChaining, 0, theM, NUMWORDS, NUMWORDS);
581
582
583 initParentBlock();
584 compress();
585
586
587 myCount >>= 1;
588 }
589
590
591 theStack.push(Arrays.copyOf(theChaining, NUMWORDS));
592 }
593
594
595
596
597
598
599 private void compressFinalBlock(final int pDataLen) {
600
601 initChunkBlock(pDataLen, true);
602 initM(theBuffer, 0);
603 compress();
604
605
606 processStack();
607 }
608
609
610
611
612 private void processStack() {
613
614 while (!theStack.isEmpty()) {
615
616 final int[] myLeft = theStack.pop();
617 System.arraycopy(myLeft, 0, theM, 0, NUMWORDS);
618 System.arraycopy(theChaining, 0, theM, NUMWORDS, NUMWORDS);
619
620
621 initParentBlock();
622 if (theStack.isEmpty()) {
623 setRoot();
624 }
625 compress();
626 }
627 }
628
629
630
631
632 private void compress() {
633
634 initIndices();
635
636
637 for (int round = 0; round < ROUNDS - 1; round++) {
638
639 performRound();
640 permuteIndices();
641 }
642 performRound();
643 adjustChaining();
644 }
645
646
647
648
649 private void performRound() {
650
651 int idx = 0;
652 mixG(idx++, CHAINING0, CHAINING4, IV0, COUNT0);
653 mixG(idx++, CHAINING1, CHAINING5, IV1, COUNT1);
654 mixG(idx++, CHAINING2, CHAINING6, IV2, DATALEN);
655 mixG(idx++, CHAINING3, CHAINING7, IV3, FLAGS);
656
657
658 mixG(idx++, CHAINING0, CHAINING5, IV2, FLAGS);
659 mixG(idx++, CHAINING1, CHAINING6, IV3, COUNT0);
660 mixG(idx++, CHAINING2, CHAINING7, IV0, COUNT1);
661 mixG(idx, CHAINING3, CHAINING4, IV1, DATALEN);
662 }
663
664
665
666
667
668
669
670 private void initM(final byte[] pMessage,
671 final int pMsgPos) {
672
673 for (int i = 0; i < NUMWORDS << 1; i++) {
674 theM[i] = Pack.littleEndianToInt(pMessage, pMsgPos + i * Integer.BYTES);
675 }
676 }
677
678
679
680
681 private void adjustChaining() {
682
683 if (outputting) {
684
685 for (int i = 0; i < NUMWORDS; i++) {
686 theV[i] ^= theV[i + NUMWORDS];
687 theV[i + NUMWORDS] ^= theChaining[i];
688 }
689
690
691 for (int i = 0; i < NUMWORDS << 1; i++) {
692 Pack.intToLittleEndian(theV[i], theBuffer, i * Integer.BYTES);
693 }
694 thePos = 0;
695
696
697 } else {
698
699 for (int i = 0; i < NUMWORDS; i++) {
700 theChaining[i] = theV[i] ^ theV[i + NUMWORDS];
701 }
702 }
703 }
704
705
706
707
708
709
710
711
712
713
714 private void mixG(final int msgIdx,
715 final int posA,
716 final int posB,
717 final int posC,
718 final int posD) {
719
720 int msg = msgIdx << 1;
721 int rot = 0;
722
723
724 theV[posA] += theV[posB] + theM[theIndices[msg++]];
725 theV[posD] = rotr32(theV[posD] ^ theV[posA], ROTATE[rot++]);
726 theV[posC] += theV[posD];
727 theV[posB] = rotr32(theV[posB] ^ theV[posC], ROTATE[rot++]);
728 theV[posA] += theV[posB] + theM[theIndices[msg]];
729 theV[posD] = rotr32(theV[posD] ^ theV[posA], ROTATE[rot++]);
730 theV[posC] += theV[posD];
731 theV[posB] = rotr32(theV[posB] ^ theV[posC], ROTATE[rot]);
732 }
733
734
735
736
737 private void initIndices() {
738 for (byte i = 0; i < theIndices.length; i++) {
739 theIndices[i] = i;
740 }
741 }
742
743
744
745
746 private void permuteIndices() {
747 for (byte i = 0; i < theIndices.length; i++) {
748 theIndices[i] = SIGMA[theIndices[i]];
749 }
750 }
751
752
753
754
755
756
757
758
759 private static int rotr32(final int x,
760 final int rot) {
761 return x >>> rot | (x << (Integer.SIZE - rot));
762 }
763
764
765
766
767 private void initNullKey() {
768 System.arraycopy(IV, 0, theK, 0, NUMWORDS);
769 }
770
771
772
773
774
775
776 private void initKey(final byte[] pKey) {
777
778 for (int i = 0; i < NUMWORDS; i++) {
779 theK[i] = Pack.littleEndianToInt(pKey, i * Integer.BYTES);
780 }
781 theMode = KEYEDHASH;
782 }
783
784
785
786
787 private void initKeyFromContext() {
788 System.arraycopy(theV, 0, theK, 0, NUMWORDS);
789 theMode = DERIVEKEY;
790 }
791
792
793
794
795
796
797
798 private void initChunkBlock(final int pDataLen,
799 final boolean pFinal) {
800
801 System.arraycopy(theCurrBytes == 0 ? theK : theChaining, 0, theV, 0, NUMWORDS);
802 System.arraycopy(IV, 0, theV, NUMWORDS, NUMWORDS >> 1);
803 theV[COUNT0] = (int) theCounter;
804 theV[COUNT1] = (int) (theCounter >> Integer.SIZE);
805 theV[DATALEN] = pDataLen;
806 theV[FLAGS] = theMode
807 + (theCurrBytes == 0 ? CHUNKSTART : 0)
808 + (pFinal ? CHUNKEND : 0);
809
810
811 theCurrBytes += pDataLen;
812 if (theCurrBytes >= CHUNKLEN) {
813 incrementBlockCount();
814 theV[FLAGS] |= CHUNKEND;
815 }
816
817
818 if (pFinal && theStack.isEmpty()) {
819 setRoot();
820 }
821 }
822
823
824
825
826 private void initParentBlock() {
827
828 System.arraycopy(theK, 0, theV, 0, NUMWORDS);
829 System.arraycopy(IV, 0, theV, NUMWORDS, NUMWORDS >> 1);
830 theV[COUNT0] = 0;
831 theV[COUNT1] = 0;
832 theV[DATALEN] = BLOCKLEN;
833 theV[FLAGS] = theMode | PARENT;
834 }
835
836
837
838
839 private void nextOutputBlock() {
840
841 theCounter++;
842
843
844 System.arraycopy(theChaining, 0, theV, 0, NUMWORDS);
845 System.arraycopy(IV, 0, theV, NUMWORDS, NUMWORDS >> 1);
846 theV[COUNT0] = (int) theCounter;
847 theV[COUNT1] = (int) (theCounter >> Integer.SIZE);
848 theV[DATALEN] = theOutputDataLen;
849 theV[FLAGS] = theOutputMode;
850
851
852 compress();
853 }
854
855
856
857
858 private void incrementBlockCount() {
859 theCounter++;
860 theCurrBytes = 0;
861 }
862
863
864
865
866 private void resetBlockCount() {
867 theCounter = 0;
868 theCurrBytes = 0;
869 }
870
871
872
873
874 private void setRoot() {
875 theV[FLAGS] |= ROOT;
876 theOutputMode = theV[FLAGS];
877 theOutputDataLen = theV[DATALEN];
878 theCounter = 0;
879 outputting = true;
880 outputAvailable = -1;
881 System.arraycopy(theV, 0, theChaining, 0, NUMWORDS);
882 }
883 }