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 org.bouncycastle.util.Memoable;
20 import org.bouncycastle.util.Pack;
21
22
23
24
25 @SuppressWarnings("checkstyle:MagicNumber")
26 public class GordianBlake2sDigest
27 extends GordianBlake2Base {
28
29
30
31 private static final int ROUNDS = 10;
32
33
34
35
36 private static final int BLOCK_LENGTH_BYTES = NUMWORDS * Integer.BYTES << 1;
37
38
39
40
41 private static final int[] IV = {
42
43
44
45 0x6a09e667, 0xbb67ae85, 0x3c6ef372,
46 0xa54ff53a, 0x510e527f, 0x9b05688c,
47 0x1f83d9ab, 0x5be0cd19
48 };
49
50
51
52
53 private final int[] theH = new int[NUMWORDS];
54
55
56
57
58 private final int[] theV = new int[NUMWORDS << 1];
59
60
61
62
63 private final int[] theM = new int[NUMWORDS << 1];
64
65
66
67
68 private int t0;
69
70
71
72
73 private int t1;
74
75
76
77
78 public GordianBlake2sDigest() {
79
80 this(256);
81 }
82
83
84
85
86
87
88 public GordianBlake2sDigest(final int pLength) {
89
90 super(ROUNDS, BLOCK_LENGTH_BYTES);
91
92
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
102
103
104
105 private GordianBlake2sDigest(final GordianBlake2sDigest pSource) {
106
107 super(pSource);
108
109
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
126 t0 = 0;
127 t1 = 0;
128
129
130 super.reset();
131 }
132
133 @Override
134 public void reset(final Memoable pSource) {
135
136 final GordianBlake2sDigest mySource = (GordianBlake2sDigest) pSource;
137
138
139 super.reset(mySource);
140
141
142 t0 = mySource.t0;
143 t1 = mySource.t1;
144
145
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
174 final int myDigestLen = getDigestSize();
175 for (int i = 0, j = 0; i < NUMWORDS && j < myDigestLen; i++, j += Integer.BYTES) {
176
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
190 System.arraycopy(IV, 0, theH, 0, IV.length);
191
192
193 theH[0] ^= getDigestSize() | (getKeyLen() << Byte.SIZE);
194 theH[0] ^= (getFanOut() | (getMaxDepth() << Byte.SIZE)) << Short.SIZE;
195
196
197 theH[1] ^= getLeafLen();
198
199
200 theH[2] ^= getNodeOffset();
201
202
203 theH[3] ^= getXofLen();
204 theH[3] ^= (getNodeDepth() | (getInnerLen() << Byte.SIZE)) << Short.SIZE;
205
206
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
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
221 initKeyBlock();
222 }
223
224 @Override
225 protected void initV() {
226
227 System.arraycopy(theH, 0, theV, 0, NUMWORDS);
228 System.arraycopy(IV, 0, theV, NUMWORDS, NUMWORDS);
229
230
231 theV[12] ^= t0;
232 theV[13] ^= t1;
233
234
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
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
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
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
280
281
282
283
284
285 private static int rotr32(final int x,
286 final int rot) {
287 return x >>> rot | (x << (Integer.SIZE - rot));
288 }
289 }