1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.codec.digest;
18
19 import java.util.Arrays;
20 import java.util.Objects;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public final class Blake3 {
77
78 private static final class ChunkState {
79
80 private int[] chainingValue;
81 private final long chunkCounter;
82 private final int flags;
83
84 private final byte[] block = new byte[BLOCK_LEN];
85 private int blockLength;
86 private int blocksCompressed;
87
88 private ChunkState(final int[] key, final long chunkCounter, final int flags) {
89 chainingValue = key;
90 this.chunkCounter = chunkCounter;
91 this.flags = flags;
92 }
93
94 private int length() {
95 return BLOCK_LEN * blocksCompressed + blockLength;
96 }
97
98 private Output output() {
99 final int[] blockWords = unpackInts(block, BLOCK_INTS);
100 final int outputFlags = flags | startFlag() | CHUNK_END;
101 return new Output(chainingValue, blockWords, chunkCounter, blockLength, outputFlags);
102 }
103
104 private int startFlag() {
105 return blocksCompressed == 0 ? CHUNK_START : 0;
106 }
107
108 private void update(final byte[] input, int offset, int length) {
109 while (length > 0) {
110 if (blockLength == BLOCK_LEN) {
111
112
113 final int[] blockWords = unpackInts(block, BLOCK_INTS);
114 chainingValue = Arrays.copyOf(
115 compress(chainingValue, blockWords, BLOCK_LEN, chunkCounter, flags | startFlag()),
116 CHAINING_VALUE_INTS);
117 blocksCompressed++;
118 blockLength = 0;
119 Arrays.fill(block, (byte) 0);
120 }
121
122 final int want = BLOCK_LEN - blockLength;
123 final int take = Math.min(want, length);
124 System.arraycopy(input, offset, block, blockLength, take);
125 blockLength += take;
126 offset += take;
127 length -= take;
128 }
129 }
130 }
131 private static final class EngineState {
132 private final int[] key;
133 private final int flags;
134
135
136
137
138 private final int[][] cvStack = new int[54][];
139 private int stackLen;
140 private ChunkState state;
141
142 private EngineState(final int[] key, final int flags) {
143 this.key = key;
144 this.flags = flags;
145 state = new ChunkState(key, 0, flags);
146 }
147
148
149 private void addChunkCV(final int[] firstCV, final long totalChunks) {
150
151
152
153
154
155
156
157 int[] newCV = firstCV;
158 long chunkCounter = totalChunks;
159 while ((chunkCounter & 1) == 0) {
160 newCV = parentChainingValue(popCV(), newCV, key, flags);
161 chunkCounter >>= 1;
162 }
163 pushCV(newCV);
164 }
165
166 private void inputData(final byte[] in, int offset, int length) {
167 while (length > 0) {
168
169
170 if (state.length() == CHUNK_LEN) {
171 final int[] chunkCV = state.output().chainingValue();
172 final long totalChunks = state.chunkCounter + 1;
173 addChunkCV(chunkCV, totalChunks);
174 state = new ChunkState(key, totalChunks, flags);
175 }
176
177
178 final int want = CHUNK_LEN - state.length();
179 final int take = Math.min(want, length);
180 state.update(in, offset, take);
181 offset += take;
182 length -= take;
183 }
184 }
185
186 private void outputHash(final byte[] out, final int offset, final int length) {
187
188
189
190 Output output = state.output();
191 int parentNodesRemaining = stackLen;
192 while (parentNodesRemaining-- > 0) {
193 final int[] parentCV = cvStack[parentNodesRemaining];
194 output = parentOutput(parentCV, output.chainingValue(), key, flags);
195 }
196 output.rootOutputBytes(out, offset, length);
197 }
198
199 private int[] popCV() {
200 return cvStack[--stackLen];
201 }
202
203 private void pushCV(final int[] cv) {
204 cvStack[stackLen++] = cv;
205 }
206
207 private void reset() {
208 stackLen = 0;
209 Arrays.fill(cvStack, null);
210 state = new ChunkState(key, 0, flags);
211 }
212 }
213
214
215
216
217
218 private static final class Output {
219
220 private final int[] inputChainingValue;
221 private final int[] blockWords;
222 private final long counter;
223 private final int blockLength;
224 private final int flags;
225
226 private Output(final int[] inputChainingValue, final int[] blockWords, final long counter, final int blockLength, final int flags) {
227 this.inputChainingValue = inputChainingValue;
228 this.blockWords = blockWords;
229 this.counter = counter;
230 this.blockLength = blockLength;
231 this.flags = flags;
232 }
233
234 private int[] chainingValue() {
235 return Arrays.copyOf(compress(inputChainingValue, blockWords, blockLength, counter, flags), CHAINING_VALUE_INTS);
236 }
237
238 private void rootOutputBytes(final byte[] out, int offset, int length) {
239 int outputBlockCounter = 0;
240 while (length > 0) {
241 int chunkLength = Math.min(OUT_LEN * 2, length);
242 length -= chunkLength;
243 final int[] words = compress(inputChainingValue, blockWords, blockLength, outputBlockCounter++, flags | ROOT);
244 int wordCounter = 0;
245 while (chunkLength > 0) {
246 final int wordLength = Math.min(Integer.BYTES, chunkLength);
247 packInt(words[wordCounter++], out, offset, wordLength);
248 offset += wordLength;
249 chunkLength -= wordLength;
250 }
251 }
252 }
253 }
254
255 private static final int BLOCK_LEN = 64;
256 private static final int BLOCK_INTS = BLOCK_LEN / Integer.BYTES;
257 private static final int KEY_LEN = 32;
258 private static final int KEY_INTS = KEY_LEN / Integer.BYTES;
259 private static final int OUT_LEN = 32;
260 private static final int CHUNK_LEN = 1024;
261 private static final int CHAINING_VALUE_INTS = 8;
262
263
264
265
266 private static final int[] IV = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 };
267
268
269 private static final int CHUNK_START = 1;
270 private static final int CHUNK_END = 1 << 1;
271 private static final int PARENT = 1 << 2;
272 private static final int ROOT = 1 << 3;
273 private static final int KEYED_HASH = 1 << 4;
274 private static final int DERIVE_KEY_CONTEXT = 1 << 5;
275 private static final int DERIVE_KEY_MATERIAL = 1 << 6;
276
277
278
279
280
281 private static final byte[][] MSG_SCHEDULE = {
282 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
283 { 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8 },
284 { 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1 },
285 { 10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6 },
286 { 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4 },
287 { 9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7 },
288 { 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13 }
289 };
290
291
292 private static void checkBufferArgs(final byte[] buffer, final int offset, final int length) {
293 Objects.requireNonNull(buffer);
294 if (offset < 0) {
295 throw new IndexOutOfBoundsException("Offset must be non-negative");
296 }
297 if (length < 0) {
298 throw new IndexOutOfBoundsException("Length must be non-negative");
299 }
300 final int bufferLength = buffer.length;
301 if (offset > bufferLength - length) {
302 throw new IndexOutOfBoundsException("Offset " + offset + " and length " + length + " out of bounds with buffer length " + bufferLength);
303 }
304 }
305
306 private static int[] compress(final int[] chainingValue, final int[] blockWords, final int blockLength, final long counter, final int flags) {
307 final int[] state = Arrays.copyOf(chainingValue, BLOCK_INTS);
308 System.arraycopy(IV, 0, state, 8, 4);
309 state[12] = (int) counter;
310 state[13] = (int) (counter >> Integer.SIZE);
311 state[14] = blockLength;
312 state[15] = flags;
313 for (int i = 0; i < 7; i++) {
314 final byte[] schedule = MSG_SCHEDULE[i];
315 round(state, blockWords, schedule);
316 }
317 for (int i = 0; i < state.length / 2; i++) {
318 state[i] ^= state[i + 8];
319 state[i + 8] ^= chainingValue[i];
320 }
321 return state;
322 }
323
324
325
326
327 private static void g(final int[] state, final int a, final int b, final int c, final int d, final int mx, final int my) {
328 state[a] += state[b] + mx;
329 state[d] = Integer.rotateRight(state[d] ^ state[a], 16);
330 state[c] += state[d];
331 state[b] = Integer.rotateRight(state[b] ^ state[c], 12);
332 state[a] += state[b] + my;
333 state[d] = Integer.rotateRight(state[d] ^ state[a], 8);
334 state[c] += state[d];
335 state[b] = Integer.rotateRight(state[b] ^ state[c], 7);
336 }
337
338
339
340
341
342
343
344
345 public static byte[] hash(final byte[] data) {
346 return Blake3.initHash().update(data).doFinalize(OUT_LEN);
347 }
348
349
350
351
352
353
354 public static Blake3 initHash() {
355 return new Blake3(IV, 0);
356 }
357
358
359
360
361
362
363
364
365
366
367 public static Blake3 initKeyDerivationFunction(final byte[] kdfContext) {
368 Objects.requireNonNull(kdfContext);
369 final EngineState kdf = new EngineState(IV, DERIVE_KEY_CONTEXT);
370 kdf.inputData(kdfContext, 0, kdfContext.length);
371 final byte[] key = new byte[KEY_LEN];
372 kdf.outputHash(key, 0, key.length);
373 return new Blake3(unpackInts(key, KEY_INTS), DERIVE_KEY_MATERIAL);
374 }
375
376
377
378
379
380
381
382
383
384
385 public static Blake3 initKeyedHash(final byte[] key) {
386 Objects.requireNonNull(key);
387 if (key.length != KEY_LEN) {
388 throw new IllegalArgumentException("Blake3 keys must be 32 bytes");
389 }
390 return new Blake3(unpackInts(key, KEY_INTS), KEYED_HASH);
391 }
392
393
394
395
396
397
398
399
400
401 public static byte[] keyedHash(final byte[] key, final byte[] data) {
402 return Blake3.initKeyedHash(key).update(data).doFinalize(OUT_LEN);
403 }
404
405 private static void packInt(final int value, final byte[] dst, final int off, final int len) {
406 for (int i = 0; i < len; i++) {
407 dst[off + i] = (byte) (value >>> i * Byte.SIZE);
408 }
409 }
410
411 private static int[] parentChainingValue(final int[] leftChildCV, final int[] rightChildCV, final int[] key, final int flags) {
412 return parentOutput(leftChildCV, rightChildCV, key, flags).chainingValue();
413 }
414
415 private static Output parentOutput(final int[] leftChildCV, final int[] rightChildCV, final int[] key, final int flags) {
416 final int[] blockWords = Arrays.copyOf(leftChildCV, BLOCK_INTS);
417 System.arraycopy(rightChildCV, 0, blockWords, 8, CHAINING_VALUE_INTS);
418 return new Output(key.clone(), blockWords, 0, BLOCK_LEN, flags | PARENT);
419 }
420
421 private static void round(final int[] state, final int[] msg, final byte[] schedule) {
422
423 g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);
424 g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);
425 g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);
426 g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);
427
428
429 g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);
430 g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
431 g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
432 g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
433 }
434
435 private static int unpackInt(final byte[] buf, final int off) {
436 return buf[off] & 0xFF | (buf[off + 1] & 0xFF) << 8 | (buf[off + 2] & 0xFF) << 16 | (buf[off + 3] & 0xFF) << 24;
437 }
438
439 private static int[] unpackInts(final byte[] buf, final int nrInts) {
440 final int[] values = new int[nrInts];
441 for (int i = 0, off = 0; i < nrInts; i++, off += Integer.BYTES) {
442 values[i] = unpackInt(buf, off);
443 }
444 return values;
445 }
446
447 private final EngineState engineState;
448
449 private Blake3(final int[] key, final int flags) {
450 engineState = new EngineState(key, flags);
451 }
452
453
454
455
456
457
458
459
460
461 public Blake3 doFinalize(final byte[] out) {
462 return doFinalize(out, 0, out.length);
463 }
464
465
466
467
468
469
470
471
472
473
474
475
476
477 public Blake3 doFinalize(final byte[] out, final int offset, final int length) {
478 checkBufferArgs(out, offset, length);
479 engineState.outputHash(out, offset, length);
480 return this;
481 }
482
483
484
485
486
487
488
489
490 public byte[] doFinalize(final int nrBytes) {
491 if (nrBytes < 0) {
492 throw new IllegalArgumentException("Requested bytes must be non-negative");
493 }
494 final byte[] hash = new byte[nrBytes];
495 doFinalize(hash);
496 return hash;
497 }
498
499
500
501
502
503 public Blake3 reset() {
504 engineState.reset();
505 return this;
506 }
507
508
509
510
511
512
513
514
515 public Blake3 update(final byte[] in) {
516 return update(in, 0, in.length);
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530 public Blake3 update(final byte[] in, final int offset, final int length) {
531 checkBufferArgs(in, offset, length);
532 engineState.inputData(in, offset, length);
533 return this;
534 }
535
536 }