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 org.bouncycastle.util.Memoable;
20  import org.bouncycastle.util.Pack;
21  
22  /**
23   * Blake2b digest.
24   */
25  @SuppressWarnings("checkstyle:MagicNumber")
26  public class GordianBlake2bDigest
27          extends GordianBlake2Base {
28      /**
29       * Number of Rounds.
30       */
31      private static final int ROUNDS = 12;
32  
33      /**
34       * Block length.
35       */
36      private static final int BLOCK_LENGTH_BYTES = NUMWORDS * Long.BYTES << 1;
37  
38      /**
39       * Blake2b Initialization Vector.
40       */
41      private static final long[] IV = {
42              // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
43              // The same as SHA-512 IV.
44  
45              0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL,
46              0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL,
47              0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L
48      };
49  
50      /**
51       * The state.
52       */
53      private final long[] theH = new long[NUMWORDS];
54  
55      /**
56       * The workBuffer.
57       */
58      private final long[] theV = new long[NUMWORDS << 1];
59  
60      /**
61       * The messageBuffer.
62       */
63      private final long[] theM = new long[NUMWORDS << 1];
64  
65      /**
66       * Low Counter.
67       */
68      private long t0;
69  
70      /**
71       * High Counter.
72       */
73      private long t1;
74  
75      /**
76       * Constructor.
77       */
78      public GordianBlake2bDigest() {
79          /* Default to 512 bits */
80          this(512);
81      }
82  
83      /**
84       * Constructor.
85       *
86       * @param pLength the digest length in bits.
87       */
88      public GordianBlake2bDigest(final int pLength) {
89          /* Initialise underlying class */
90          super(ROUNDS, BLOCK_LENGTH_BYTES);
91  
92          /* Check digest length */
93          if ((pLength % Byte.SIZE) != 0 || pLength < 0 || pLength > 512) {
94              throw new IllegalArgumentException("Incorrect digest length");
95          }
96          setDigestLength(pLength / Byte.SIZE);
97          activateH();
98      }
99  
100     /**
101      * Constructor.
102      *
103      * @param pSource the source digest.
104      */
105     private GordianBlake2bDigest(final GordianBlake2bDigest pSource) {
106         /* Initialise underlying class */
107         super(pSource);
108 
109         /* Initialise from source */
110         reset((Memoable) pSource);
111     }
112 
113     @Override
114     public boolean isBlake2b() {
115         return true;
116     }
117 
118     @Override
119     public String getAlgorithmName() {
120         return "Blake2b-" + getDigestSize() * Byte.SIZE;
121     }
122 
123     @Override
124     public int getByteLength() {
125         return BLOCK_LENGTH_BYTES;
126     }
127 
128     @Override
129     public void reset() {
130         /* Reset counter */
131         t0 = 0L;
132         t1 = 0L;
133 
134         /* reset underlying class */
135         super.reset();
136     }
137 
138     @Override
139     public void reset(final Memoable pSource) {
140         /* Access source */
141         final GordianBlake2bDigest mySource = (GordianBlake2bDigest) pSource;
142 
143         /* reset underlying class */
144         super.reset(mySource);
145 
146         /* Reset counter */
147         t0 = mySource.t0;
148         t1 = mySource.t1;
149 
150         /* Copy state */
151         System.arraycopy(mySource.theH, 0, theH, 0, theH.length);
152     }
153 
154     @Override
155     public GordianBlake2bDigest copy() {
156         return new GordianBlake2bDigest(this);
157     }
158 
159     @Override
160     void adjustCounter(final int pCount) {
161         t0 += pCount;
162         if (t0 == 0) {
163             t1++;
164         }
165     }
166 
167     @Override
168     void completeCounter(final int pCount) {
169         t0 += pCount;
170         if (pCount > 0 && t0 == 0) {
171             t1++;
172         }
173     }
174 
175     @Override
176     void outputDigest(final byte[] pOut,
177                       final int pOutOffset) {
178         /* Loop to provide the output */
179         final int myDigestLen = getDigestSize();
180         for (int i = 0, j = 0; i < NUMWORDS && j < myDigestLen; i++, j += Long.BYTES) {
181             /* Convert the next word to bytes */
182             final byte[] bytes = Pack.longToLittleEndian(theH[i]);
183 
184             if (j + Long.BYTES < myDigestLen) {
185                 System.arraycopy(bytes, 0, pOut, pOutOffset + j, Long.BYTES);
186             } else {
187                 System.arraycopy(bytes, 0, pOut, pOutOffset + j, myDigestLen - j);
188             }
189         }
190     }
191 
192     @Override
193     void activateH() {
194         /* Initialise from IV */
195         System.arraycopy(IV, 0, theH, 0, IV.length);
196 
197         /* Initialise first word */
198         theH[0] ^= getDigestSize() | (getKeyLen() << Byte.SIZE);
199         theH[0] ^= (getFanOut() | (getMaxDepth() << Byte.SIZE)) << Short.SIZE;
200         theH[0] ^= ((long) getLeafLen()) << Integer.SIZE;
201 
202         /* Initialise second word */
203         theH[1] ^= getNodeOffset() | (((long) getXofLen()) << Integer.SIZE);
204 
205         /* Initialise third word */
206         theH[2] ^= (getNodeDepth() | (getInnerLen() << Byte.SIZE));
207 
208         /* Build salt section */
209         final byte[] mySalt = getSalt();
210         if (mySalt != null) {
211             theH[4] ^= Pack.littleEndianToLong(mySalt, 0);
212             theH[5] ^= Pack.littleEndianToLong(mySalt, Long.BYTES);
213         }
214 
215         /* Build personalisation section */
216         final byte[] myPersonal = getPersonal();
217         if (myPersonal != null) {
218             theH[6] ^= Pack.littleEndianToLong(myPersonal, 0);
219             theH[7] ^= Pack.littleEndianToLong(myPersonal, Long.BYTES);
220         }
221 
222         /* Initialise any keyBlock */
223         initKeyBlock();
224     }
225 
226     @Override
227     void initV() {
228         /* Copy in H and IV */
229         System.arraycopy(theH, 0, theV, 0, NUMWORDS);
230         System.arraycopy(IV, 0, theV, NUMWORDS, NUMWORDS);
231 
232         /* Fold in counters */
233         theV[12] ^= t0;
234         theV[13] ^= t1;
235 
236         /* Fold in finalisation flags */
237         if (isLastBlock()) {
238             theV[14] ^= -1L;
239             if (isLastNode()) {
240                 theV[15] ^= -1L;
241             }
242         }
243     }
244 
245     @Override
246     void initM(final byte[] pMessage,
247                final int pMsgPos) {
248         /* Copy message bytes into word array */
249         for (int i = 0; i < NUMWORDS << 1; i++) {
250             theM[i] = Pack.littleEndianToLong(pMessage, pMsgPos + i * Long.BYTES);
251         }
252     }
253 
254     @Override
255     void adjustH() {
256         /* Combine V into H */
257         for (int i = 0; i < NUMWORDS; i++) {
258             theH[i] ^= theV[i] ^ theV[i + NUMWORDS];
259         }
260     }
261 
262     @Override
263     void mixG(final int msgIdx1,
264               final int msgIdx2,
265               final int posA,
266               final int posB,
267               final int posC,
268               final int posD) {
269         /* Perform the Round */
270         theV[posA] += theV[posB] + theM[msgIdx1];
271         theV[posD] = rotr64(theV[posD] ^ theV[posA], 32);
272         theV[posC] += theV[posD];
273         theV[posB] = rotr64(theV[posB] ^ theV[posC], 24);
274         theV[posA] += theV[posB] + theM[msgIdx2];
275         theV[posD] = rotr64(theV[posD] ^ theV[posA], 16);
276         theV[posC] += theV[posD];
277         theV[posB] = rotr64(theV[posB] ^ theV[posC], 63);
278     }
279 
280     /**
281      * Rotate a long right.
282      *
283      * @param x   the value to rotate
284      * @param rot the number of bits to rotate
285      * @return the result
286      */
287     private static long rotr64(final long x,
288                                final int rot) {
289         return x >>> rot | (x << (Long.SIZE - rot));
290     }
291 }