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 GordianBlake2bDigest
27 extends GordianBlake2Base {
28
29
30
31 private static final int ROUNDS = 12;
32
33
34
35
36 private static final int BLOCK_LENGTH_BYTES = NUMWORDS * Long.BYTES << 1;
37
38
39
40
41 private static final long[] IV = {
42
43
44
45 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL,
46 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL,
47 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L
48 };
49
50
51
52
53 private final long[] theH = new long[NUMWORDS];
54
55
56
57
58 private final long[] theV = new long[NUMWORDS << 1];
59
60
61
62
63 private final long[] theM = new long[NUMWORDS << 1];
64
65
66
67
68 private long t0;
69
70
71
72
73 private long t1;
74
75
76
77
78 public GordianBlake2bDigest() {
79
80 this(512);
81 }
82
83
84
85
86
87
88 public GordianBlake2bDigest(final int pLength) {
89
90 super(ROUNDS, BLOCK_LENGTH_BYTES);
91
92
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
102
103
104
105 private GordianBlake2bDigest(final GordianBlake2bDigest pSource) {
106
107 super(pSource);
108
109
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
131 t0 = 0L;
132 t1 = 0L;
133
134
135 super.reset();
136 }
137
138 @Override
139 public void reset(final Memoable pSource) {
140
141 final GordianBlake2bDigest mySource = (GordianBlake2bDigest) pSource;
142
143
144 super.reset(mySource);
145
146
147 t0 = mySource.t0;
148 t1 = mySource.t1;
149
150
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
179 final int myDigestLen = getDigestSize();
180 for (int i = 0, j = 0; i < NUMWORDS && j < myDigestLen; i++, j += Long.BYTES) {
181
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
195 System.arraycopy(IV, 0, theH, 0, IV.length);
196
197
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
203 theH[1] ^= getNodeOffset() | (((long) getXofLen()) << Integer.SIZE);
204
205
206 theH[2] ^= (getNodeDepth() | (getInnerLen() << Byte.SIZE));
207
208
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
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
223 initKeyBlock();
224 }
225
226 @Override
227 void initV() {
228
229 System.arraycopy(theH, 0, theV, 0, NUMWORDS);
230 System.arraycopy(IV, 0, theV, NUMWORDS, NUMWORDS);
231
232
233 theV[12] ^= t0;
234 theV[13] ^= t1;
235
236
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
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
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
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
282
283
284
285
286
287 private static long rotr64(final long x,
288 final int rot) {
289 return x >>> rot | (x << (Long.SIZE - rot));
290 }
291 }