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 = isBlake2b() ? 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 public abstract boolean isBlake2b();
179
180
181
182
183
184
185 void setDigestLength(final int pLength) {
186 if (pLength < 0 || pLength > theBuffer.length << 2) {
187 throw new IllegalArgumentException("DigestLength out of range");
188 }
189 theDigestLen = (short) pLength;
190 }
191
192 @Override
193 public int getDigestSize() {
194 return theDigestLen;
195 }
196
197
198
199
200
201
202 public void init(final GordianBlake2Parameters pParams) {
203
204 setKey(pParams.getKey());
205 setSalt(pParams.getSalt());
206 setPersonalisation(pParams.getPersonalisation());
207 setXofLen(pParams.getMaxOutputLength());
208 setTreeConfig(pParams.getTreeFanOut(), pParams.getTreeMaxDepth(), pParams.getTreeLeafLen());
209
210
211 reset();
212 }
213
214
215
216
217
218
219 void setKey(final byte[] pKey) {
220 if (pKey == null || pKey.length == 0) {
221 clearKey();
222 theKey = null;
223 } else {
224 if (pKey.length > theBuffer.length >> 1) {
225 throw new IllegalArgumentException("Key too long");
226 }
227 clearKey();
228 theKey = Arrays.copyOf(pKey, pKey.length);
229 }
230 }
231
232
233
234
235 private void clearKey() {
236 if (theKey != null) {
237 Arrays.fill(theKey, (byte) 0);
238 }
239 }
240
241
242
243
244
245
246 int getKeyLen() {
247 return theKey == null ? 0 : theKey.length;
248 }
249
250
251
252
253
254
255 void setSalt(final byte[] pSalt) {
256 if (pSalt == null || pSalt.length == 0) {
257 theSalt = null;
258 } else {
259 if (pSalt.length != theBuffer.length >> 3) {
260 throw new IllegalArgumentException("Salt incorrect length");
261 }
262 theSalt = Arrays.copyOf(pSalt, pSalt.length);
263 }
264 }
265
266
267
268
269
270
271 byte[] getSalt() {
272 return theSalt;
273 }
274
275
276
277
278
279
280 void setPersonalisation(final byte[] pPersonal) {
281 if (pPersonal == null || pPersonal.length == 0) {
282 thePersonal = null;
283 } else {
284 if (pPersonal.length != theBuffer.length >> 3) {
285 throw new IllegalArgumentException("Personalisation incorrect length");
286 }
287 thePersonal = Arrays.copyOf(pPersonal, pPersonal.length);
288 }
289 }
290
291
292
293
294
295
296 byte[] getPersonal() {
297 return thePersonal;
298 }
299
300
301
302
303
304
305 void setXofLen(final long pXofLen) {
306 if (pXofLen < -1 || pXofLen > theMaxXofLen) {
307 throw new IllegalArgumentException("XofLength out of range");
308 }
309 theXofLen = (int) pXofLen;
310 }
311
312
313
314
315
316
317 int getXofLen() {
318 return theXofLen;
319 }
320
321
322
323
324
325
326
327
328 void setTreeConfig(final int pFanOut,
329 final int pMaxDepth,
330 final int pLeafLen) {
331
332 if (pFanOut < 0 || pFanOut > MAXBYTE) {
333 throw new IllegalArgumentException("FanOut out of range");
334 }
335 final boolean seqMode = pFanOut == 1;
336
337
338 final boolean xofMode = pMaxDepth == 0;
339 if (pMaxDepth < 0 || pMaxDepth > MAXBYTE) {
340 throw new IllegalArgumentException("MaxDepth out of range");
341 }
342 if (seqMode != (pMaxDepth == 1)) {
343 throw new IllegalArgumentException("Inconsistent treeConfig for sequentialMode");
344 }
345
346
347 if (pLeafLen < 0) {
348 throw new IllegalArgumentException("LeafLength out of range");
349 }
350 if (seqMode != (pLeafLen == 0)) {
351 throw new IllegalArgumentException("Inconsistent treeConfig for LeafLen and fanOut");
352 }
353 if (xofMode && pFanOut != 0) {
354 throw new IllegalArgumentException("Inconsistent treeConfig for xofMode");
355 }
356
357
358 theFanOut = (short) pFanOut;
359 theMaxDepth = (short) pMaxDepth;
360 theLeafLen = pLeafLen;
361 }
362
363
364
365
366
367
368 short getFanOut() {
369 return theFanOut;
370 }
371
372
373
374
375
376
377 short getMaxDepth() {
378 return theMaxDepth;
379 }
380
381
382
383
384
385
386 int getLeafLen() {
387 return theLeafLen;
388 }
389
390
391
392
393
394
395
396 void setNodePosition(final int pOffset,
397 final int pDepth) {
398 if (pOffset < 0) {
399 throw new IllegalArgumentException("NodeOffset out of range");
400 }
401 theNodeOffset = pOffset;
402 if (pDepth < 0 || pDepth > MAXBYTE) {
403 throw new IllegalArgumentException("NodeDepth out of range");
404 }
405 theNodeDepth = (byte) pDepth;
406 reset();
407 }
408
409
410
411
412
413
414 int getNodeOffset() {
415 return theNodeOffset;
416 }
417
418
419
420
421
422
423 short getNodeDepth() {
424 return theNodeDepth;
425 }
426
427
428
429
430
431
432 boolean isLastBlock() {
433 return isLastBlock;
434 }
435
436
437
438
439 void setLastNode() {
440 isLastNode = true;
441 }
442
443
444
445
446
447
448 boolean isLastNode() {
449 return isLastNode;
450 }
451
452
453
454
455
456
457 void setInnerLength(final int pInnerLen) {
458 if (pInnerLen < 0 || pInnerLen > MAXBYTE) {
459 throw new IllegalArgumentException("InnerLength out of range");
460 }
461 theInnerLen = (short) pInnerLen;
462 }
463
464
465
466
467
468
469 short getInnerLen() {
470 return theInnerLen;
471 }
472
473 @Override
474 public void update(final byte b) {
475
476 final int blockLen = theBuffer.length;
477 final int remainingLength = blockLen - thePos;
478 if (remainingLength == 0) {
479
480 adjustCounter(blockLen);
481 compressF(theBuffer, 0);
482
483
484 Arrays.fill(theBuffer, (byte) 0);
485 thePos = 0;
486 }
487
488
489 theBuffer[thePos] = b;
490 thePos++;
491 }
492
493 @Override
494 public void update(final byte[] pMessage,
495 final int pOffset,
496 final int pLen) {
497
498 if (pMessage == null || pLen == 0) {
499 return;
500 }
501
502
503 final int blockLen = theBuffer.length;
504 int remainingLen = 0;
505 if (thePos != 0) {
506
507 remainingLen = blockLen - thePos;
508
509
510 if (remainingLen >= pLen) {
511
512 System.arraycopy(pMessage, pOffset, theBuffer, thePos, pLen);
513 thePos += pLen;
514 return;
515 }
516
517
518 System.arraycopy(pMessage, pOffset, theBuffer, thePos, remainingLen);
519
520
521 adjustCounter(blockLen);
522
523
524 compressF(theBuffer, 0);
525
526
527 thePos = 0;
528 Arrays.fill(theBuffer, (byte) 0);
529 }
530
531
532 int messagePos;
533 final int blockWiseLastPos = pOffset + pLen - blockLen;
534 for (messagePos = pOffset + remainingLen; messagePos < blockWiseLastPos; messagePos += blockLen) {
535
536 adjustCounter(blockLen);
537
538
539 compressF(pMessage, messagePos);
540 }
541
542
543 final int len = pLen - messagePos;
544 System.arraycopy(pMessage, messagePos, theBuffer, 0, pOffset + len);
545 thePos += pOffset + len;
546 }
547
548 @Override
549 public int doFinal(final byte[] pOut,
550 final int pOutOffset) {
551
552 isLastBlock = true;
553 completeCounter(thePos);
554
555
556 compressF(theBuffer, 0);
557 Arrays.fill(theBuffer, (byte) 0);
558
559
560 outputDigest(pOut, pOutOffset);
561
562
563 reset();
564
565
566 return theDigestLen;
567 }
568
569 @Override
570 public void reset() {
571
572 isLastBlock = false;
573 isLastNode = false;
574
575
576 thePos = 0;
577 Arrays.fill(theBuffer, (byte) 0);
578
579
580 activateH();
581 }
582
583
584
585
586
587
588 void reset(final GordianBlake2Base pSource) {
589
590 theDigestLen = pSource.theDigestLen;
591 theInnerLen = pSource.theInnerLen;
592 theLeafLen = pSource.theLeafLen;
593 theXofLen = pSource.theXofLen;
594 theFanOut = pSource.theFanOut;
595 theMaxDepth = pSource.theMaxDepth;
596 theNodeDepth = pSource.theNodeDepth;
597 theNodeOffset = pSource.theNodeOffset;
598
599
600 isLastNode = pSource.isLastNode;
601
602
603 theKey = Arrays.clone(pSource.theKey);
604 theSalt = Arrays.clone(pSource.theSalt);
605 thePersonal = Arrays.clone(pSource.thePersonal);
606
607
608 System.arraycopy(pSource.theBuffer, 0, theBuffer, 0, theBuffer.length);
609 thePos = pSource.thePos;
610 }
611
612
613
614
615
616
617 abstract void adjustCounter(int pCount);
618
619
620
621
622
623
624 abstract void completeCounter(int pCount);
625
626
627
628
629
630
631
632 abstract void outputDigest(byte[] pOut,
633 int pOutOffset);
634
635
636
637
638 void initKeyBlock() {
639
640 if (theKey != null) {
641
642 System.arraycopy(theKey, 0, theBuffer, 0, theKey.length);
643 thePos = theBuffer.length;
644 }
645 }
646
647
648
649
650 abstract void activateH();
651
652
653
654
655
656
657
658 private static byte[] getSigmaForRound(final int pRound) {
659 return SIGMA[pRound % SIGMA.length];
660 }
661
662
663
664
665
666
667
668 private void compressF(final byte[] pMessage,
669 final int pMsgPos) {
670
671 initV();
672 initM(pMessage, pMsgPos);
673
674
675 for (int round = 0; round < theRounds; round++) {
676
677 final byte[] sigma = getSigmaForRound(round);
678
679
680 mixG(sigma[0], sigma[1], 0, 4, 8, 12);
681 mixG(sigma[2], sigma[3], 1, 5, 9, 13);
682 mixG(sigma[4], sigma[5], 2, 6, 10, 14);
683 mixG(sigma[6], sigma[7], 3, 7, 11, 15);
684
685
686 mixG(sigma[8], sigma[9], 0, 5, 10, 15);
687 mixG(sigma[10], sigma[11], 1, 6, 11, 12);
688 mixG(sigma[12], sigma[13], 2, 7, 8, 13);
689 mixG(sigma[14], sigma[15], 3, 4, 9, 14);
690 }
691
692
693 adjustH();
694 }
695
696
697
698
699 abstract void initV();
700
701
702
703
704
705
706
707 abstract void initM(byte[] pMessage,
708 int pMsgPos);
709
710
711
712
713
714
715
716
717
718
719
720 abstract void mixG(int msgIdx1,
721 int msgIdx2,
722 int posA,
723 int posB,
724 int posC,
725 int posD);
726
727
728
729
730 abstract void adjustH();
731 }