View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
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   * Blake2 Base class.
26   */
27  @SuppressWarnings("checkstyle:MagicNumber")
28  public abstract class GordianBlake2Base
29          implements ExtendedDigest, Memoable {
30      /**
31       * Number of Words.
32       */
33      static final int NUMWORDS = 8;
34  
35      /**
36       * Maximum Byte value.
37       */
38      private static final int MAXBYTE = 0xFF;
39  
40      /**
41       * Message word permutations.
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       * The number of Rounds.
58       */
59      private final int theRounds;
60  
61      /**
62       * The maximum xofLen.
63       */
64      private final long theMaxXofLen;
65  
66      /**
67       * The buffer.
68       */
69      private final byte[] theBuffer;
70  
71      /**
72       * Position of last inserted byte.
73       */
74      private int thePos;
75  
76      /**
77       * The digestLength.
78       */
79      private short theDigestLen;
80  
81      /**
82       * The key.
83       */
84      private byte[] theKey;
85  
86      /**
87       * The salt.
88       */
89      private byte[] theSalt;
90  
91      /**
92       * The personalisation.
93       */
94      private byte[] thePersonal;
95  
96      /**
97       * The fanOut.
98       */
99      private short theFanOut;
100 
101     /**
102      * The maxDepth.
103      */
104     private short theMaxDepth;
105 
106     /**
107      * The leafLength.
108      */
109     private int theLeafLen;
110 
111     /**
112      * The nodeOffSet.
113      */
114     private int theNodeOffset;
115 
116     /**
117      * The nodeDepth.
118      */
119     private short theNodeDepth;
120 
121     /**
122      * The xofLength.
123      */
124     private int theXofLen;
125 
126     /**
127      * The innerLength.
128      */
129     private short theInnerLen;
130 
131     /**
132      * Is this the final block?
133      */
134     private boolean isLastBlock;
135 
136     /**
137      * Is this the last node at this depth?
138      */
139     private boolean isLastNode;
140 
141     /**
142      * Constructor.
143      *
144      * @param pRounds   the number of rounds.
145      * @param pBlockLen the blockLength
146      */
147     GordianBlake2Base(final int pRounds,
148                       final int pBlockLen) {
149         /* Store parameters */
150         theRounds = pRounds;
151         theBuffer = new byte[pBlockLen];
152         theFanOut = 1;
153         theMaxDepth = 1;
154 
155         /* Determine maxXofLen */
156         theMaxXofLen = this instanceof GordianBlake2bDigest ? 0xFFFFFFFEL : 0xFFFEL;
157     }
158 
159     /**
160      * Constructor.
161      *
162      * @param pSource the source
163      */
164     GordianBlake2Base(final GordianBlake2Base pSource) {
165         /* Store parameters */
166         theRounds = pSource.theRounds;
167         theBuffer = new byte[pSource.theBuffer.length];
168 
169         /* Determine maxXofLen */
170         theMaxXofLen = pSource.theMaxXofLen;
171     }
172 
173     /**
174      * Set the digestLength.
175      *
176      * @param pLength the digestLength.
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      * Initialise.
192      *
193      * @param pParams the parameters.
194      */
195     public void init(final GordianBlake2Parameters pParams) {
196         /* Store parameters */
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         /* Reset the cipher */
204         reset();
205     }
206 
207     /**
208      * Set the key.
209      *
210      * @param pKey the key.
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      * Clear the key.
227      */
228     private void clearKey() {
229         if (theKey != null) {
230             Arrays.fill(theKey, (byte) 0);
231         }
232     }
233 
234     /**
235      * Obtain the keyLength.
236      *
237      * @return the keyLength
238      */
239     int getKeyLen() {
240         return theKey == null ? 0 : theKey.length;
241     }
242 
243     /**
244      * Set the salt.
245      *
246      * @param pSalt the salt.
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      * Obtain the salt.
261      *
262      * @return the salt
263      */
264     byte[] getSalt() {
265         return theSalt;
266     }
267 
268     /**
269      * Set the personalisation.
270      *
271      * @param pPersonal the personalisation.
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      * Obtain the personalisation.
286      *
287      * @return the personalisation
288      */
289     byte[] getPersonal() {
290         return thePersonal;
291     }
292 
293     /**
294      * Set the xofLen.
295      *
296      * @param pXofLen the xofLength.
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      * Obtain the xofLength.
307      *
308      * @return the xofLength
309      */
310     int getXofLen() {
311         return theXofLen;
312     }
313 
314     /**
315      * Set the treeConfig.
316      *
317      * @param pFanOut   the fanOut.
318      * @param pMaxDepth the maxDepth.
319      * @param pLeafLen  the leafLength.
320      */
321     void setTreeConfig(final int pFanOut,
322                        final int pMaxDepth,
323                        final int pLeafLen) {
324         /* Check that fanOut value makes sense */
325         if (pFanOut < 0 || pFanOut > MAXBYTE) {
326             throw new IllegalArgumentException("FanOut out of range");
327         }
328         final boolean seqMode = pFanOut == 1;
329 
330         /* Check that maxDepth value makes sense */
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         /* Check that leaf value makes sense */
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         /* Record the values */
351         theFanOut = (short) pFanOut;
352         theMaxDepth = (short) pMaxDepth;
353         theLeafLen = pLeafLen;
354     }
355 
356     /**
357      * Obtain the fanout.
358      *
359      * @return the fanout
360      */
361     short getFanOut() {
362         return theFanOut;
363     }
364 
365     /**
366      * Obtain the maxDepth.
367      *
368      * @return the maxDepth
369      */
370     short getMaxDepth() {
371         return theMaxDepth;
372     }
373 
374     /**
375      * Obtain the leafLength.
376      *
377      * @return the leafLength
378      */
379     int getLeafLen() {
380         return theLeafLen;
381     }
382 
383     /**
384      * Set the nodePosition.
385      *
386      * @param pOffset the offset.
387      * @param pDepth  the depth.
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      * Obtain the nodeOffset.
404      *
405      * @return the nodeOffset
406      */
407     int getNodeOffset() {
408         return theNodeOffset;
409     }
410 
411     /**
412      * Obtain the nodeDepth.
413      *
414      * @return the nodeDepth
415      */
416     short getNodeDepth() {
417         return theNodeDepth;
418     }
419 
420     /**
421      * is this the last node?
422      *
423      * @return true/false
424      */
425     boolean isLastBlock() {
426         return isLastBlock;
427     }
428 
429     /**
430      * Set the lastNode indicator.
431      */
432     void setLastNode() {
433         isLastNode = true;
434     }
435 
436     /**
437      * is this the last node?
438      *
439      * @return true/false
440      */
441     boolean isLastNode() {
442         return isLastNode;
443     }
444 
445     /**
446      * Set the innerLength.
447      *
448      * @param pInnerLen the innerLength.
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      * Obtain the innerLength.
459      *
460      * @return the innerLength
461      */
462     short getInnerLen() {
463         return theInnerLen;
464     }
465 
466     @Override
467     public void update(final byte b) {
468         /* If the buffer is full */
469         final int blockLen = theBuffer.length;
470         final int remainingLength = blockLen - thePos;
471         if (remainingLength == 0) {
472             /* Process the buffer */
473             adjustCounter(blockLen);
474             compressF(theBuffer, 0);
475 
476             /* Reset the buffer */
477             Arrays.fill(theBuffer, (byte) 0);
478             thePos = 0;
479         }
480 
481         /* Store the byte */
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         /* Ignore null operation */
491         if (pMessage == null || pLen == 0) {
492             return;
493         }
494 
495         /* Process any bytes currently in the buffer */
496         final int blockLen = theBuffer.length;
497         int remainingLen = 0; // left bytes of buffer
498         if (thePos != 0) {
499             /* Calculate space remaining in the buffer */
500             remainingLen = blockLen - thePos;
501 
502             /* If there is sufficient space in the buffer */
503             if (remainingLen >= pLen) {
504                 /* Copy date into byffer and return */
505                 System.arraycopy(pMessage, pOffset, theBuffer, thePos, pLen);
506                 thePos += pLen;
507                 return;
508             }
509 
510             /* Fill the buffer */
511             System.arraycopy(pMessage, pOffset, theBuffer, thePos, remainingLen);
512 
513             /* Adjust bytes count */
514             adjustCounter(blockLen);
515 
516             /* Process the buffer */
517             compressF(theBuffer, 0);
518 
519             /* Reset the buffer */
520             thePos = 0;
521             Arrays.fill(theBuffer, (byte) 0);
522         }
523 
524         /* process all blocks except the last one */
525         int messagePos;
526         final int blockWiseLastPos = pOffset + pLen - blockLen;
527         for (messagePos = pOffset + remainingLen; messagePos < blockWiseLastPos; messagePos += blockLen) {
528             /* Adjust bytes count */
529             adjustCounter(blockLen);
530 
531             /* Process the buffer */
532             compressF(pMessage, messagePos);
533         }
534 
535         /* Fill the buffer with the remaining bytes of the message */
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         /* Adjust flags and counter */
545         isLastBlock = true;
546         completeCounter(thePos);
547 
548         /* Process the buffer */
549         compressF(theBuffer, 0);
550         Arrays.fill(theBuffer, (byte) 0);
551 
552         /* Output the digest */
553         outputDigest(pOut, pOutOffset);
554 
555         /* Reset the state */
556         reset();
557 
558         /* Return the digest length */
559         return theDigestLen;
560     }
561 
562     @Override
563     public void reset() {
564         /* Reset flags */
565         isLastBlock = false;
566         isLastNode = false;
567 
568         /* Reset the data Buffer */
569         thePos = 0;
570         Arrays.fill(theBuffer, (byte) 0);
571 
572         /* Activate */
573         activateH();
574     }
575 
576     /**
577      * Copy state from source.
578      *
579      * @param pSource the source
580      */
581     void reset(final GordianBlake2Base pSource) {
582         /* Copy config */
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         /* Copy flags */
593         isLastNode = pSource.isLastNode;
594 
595         /* Clone arrays */
596         theKey = Arrays.clone(pSource.theKey);
597         theSalt = Arrays.clone(pSource.theSalt);
598         thePersonal = Arrays.clone(pSource.thePersonal);
599 
600         /* Copy buffer */
601         System.arraycopy(pSource.theBuffer, 0, theBuffer, 0, theBuffer.length);
602         thePos = pSource.thePos;
603     }
604 
605     /**
606      * Adjust Counter.
607      *
608      * @param pCount bytes processed
609      */
610     abstract void adjustCounter(int pCount);
611 
612     /**
613      * Complete Counter.
614      *
615      * @param pCount bytes processed
616      */
617     abstract void completeCounter(int pCount);
618 
619     /**
620      * Output the digest.
621      *
622      * @param pOut       the output buffer
623      * @param pOutOffset the offset in the output buffer
624      */
625     abstract void outputDigest(byte[] pOut,
626                                int pOutOffset);
627 
628     /**
629      * Init the keyBlock.
630      */
631     void initKeyBlock() {
632         /* If we have a key */
633         if (theKey != null) {
634             /* Initialise the first data block */
635             System.arraycopy(theKey, 0, theBuffer, 0, theKey.length);
636             thePos = theBuffer.length;
637         }
638     }
639 
640     /**
641      * ActivateH.
642      */
643     abstract void activateH();
644 
645     /**
646      * Obtain the Sigma for the round.
647      *
648      * @param pRound the round
649      * @return the Sigma
650      */
651     private static byte[] getSigmaForRound(final int pRound) {
652         return SIGMA[pRound % SIGMA.length];
653     }
654 
655     /**
656      * Compress a message.
657      *
658      * @param pMessage the message buffer
659      * @param pMsgPos  the position within the message buffer
660      */
661     private void compressF(final byte[] pMessage,
662                            final int pMsgPos) {
663         /* Initialise the buffers */
664         initV();
665         initM(pMessage, pMsgPos);
666 
667         /* Loop through the rounds */
668         for (int round = 0; round < theRounds; round++) {
669             /* Obtain the relevant SIGMA */
670             final byte[] sigma = getSigmaForRound(round);
671 
672             /* Apply to columns of V */
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             /* Apply to diagonals of V */
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         /* Adjust H */
686         adjustH();
687     }
688 
689     /**
690      * Initialise V.
691      */
692     abstract void initV();
693 
694     /**
695      * Initialise M.
696      *
697      * @param pMessage the message buffer
698      * @param pMsgPos  the position in the message buffer
699      */
700     abstract void initM(byte[] pMessage,
701                         int pMsgPos);
702 
703     /**
704      * Mix function.
705      *
706      * @param msgIdx1 the first msgIndex
707      * @param msgIdx2 the second msgIndex
708      * @param posA    position A
709      * @param posB    position B
710      * @param posC    position C
711      * @param posD    position D
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      * Adjust H.
722      */
723     abstract void adjustH();
724 }