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