001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.codec.digest; 018 019import java.util.Arrays; 020import java.util.Objects; 021 022/** 023 * Implements the Blake3 algorithm providing a {@linkplain #initHash() hash function} with extensible output (XOF), a 024 * {@linkplain #initKeyedHash(byte[]) keyed hash function} (MAC, PRF), and a 025 * {@linkplain #initKeyDerivationFunction(byte[]) key derivation function} (KDF). Blake3 has a 128-bit security level 026 * and a default output length of 256 bits (32 bytes) which can extended up to 2<sup>64</sup> bytes. 027 * <h2>Hashing</h2> 028 * <p>Hash mode calculates the same output hash given the same input bytes and can be used as both a message digest and 029 * and extensible output function.</p> 030 * <pre>{@code 031 * Blake3 hasher = Blake3.initHash(); 032 * hasher.update("Hello, world!".getBytes(StandardCharsets.UTF_8)); 033 * byte[] hash = new byte[32]; 034 * hasher.doFinalize(hash); 035 * }</pre> 036 * <h2>Keyed Hashing</h2> 037 * <p>Keyed hashes take a 32-byte secret key and calculates a message authentication code on some input bytes. These 038 * also work as pseudo-random functions (PRFs) with extensible output similar to the extensible hash output. Note that 039 * Blake3 keyed hashes have the same performance as plain hashes; the key is used in initialization in place of a 040 * standard initialization vector used for plain hashing.</p> 041 * <pre>{@code 042 * SecureRandom random = SecureRandom.getInstanceStrong(); 043 * byte[] key = new byte[32]; 044 * random.nextBytes(key); 045 * Blake3 hasher = Blake3.initKeyedHash(key); 046 * hasher.update("Hello, Alice!".getBytes(StandardCharsets.UTF_8)); 047 * byte[] mac = new byte[32]; 048 * hasher.doFinalize(mac); 049 * }</pre> 050 * <h2>Key Derivation</h2> 051 * <p>A specific hash mode for deriving session keys and other derived keys in a unique key derivation context 052 * identified by some sequence of bytes. These context strings should be unique but do not need to be kept secret. 053 * Additional input data is hashed for key material which can be finalized to derive subkeys.</p> 054 * <pre>{@code 055 * String context = "org.apache.commons.codec.digest.Blake3Example"; 056 * byte[] sharedSecret = ...; 057 * byte[] senderId = ...; 058 * byte[] recipientId = ...; 059 * Blake3 kdf = Blake3.initKeyDerivationFunction(context.getBytes(StandardCharsets.UTF_8)); 060 * kdf.update(sharedSecret); 061 * kdf.update(senderId); 062 * kdf.update(recipientId); 063 * byte[] txKey = new byte[32]; 064 * byte[] rxKey = new byte[32]; 065 * kdf.doFinalize(txKey); 066 * kdf.doFinalize(rxKey); 067 * }</pre> 068 * <p> 069 * Adapted from the ISC-licensed O(1) Cryptography library by Matt Sicker and ported from the reference public domain 070 * implementation by Jack O'Connor. 071 * </p> 072 * 073 * @see <a href="https://github.com/BLAKE3-team/BLAKE3">BLAKE3 hash function</a> 074 * @since 1.16 075 */ 076public final class Blake3 { 077 078 private static final class ChunkState { 079 080 private int[] chainingValue; 081 private final long chunkCounter; 082 private final int flags; 083 084 private final byte[] block = new byte[BLOCK_LEN]; 085 private int blockLength; 086 private int blocksCompressed; 087 088 private ChunkState(final int[] key, final long chunkCounter, final int flags) { 089 chainingValue = key; 090 this.chunkCounter = chunkCounter; 091 this.flags = flags; 092 } 093 094 private int length() { 095 return BLOCK_LEN * blocksCompressed + blockLength; 096 } 097 098 private Output output() { 099 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 // If the block buffer is full, compress it and clear it. More 112 // input is coming, so this compression is not CHUNK_END. 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 // Space for 54 subtree chaining values: 2^54 * CHUNK_LEN = 2^64 135 // No more than 54 entries can ever be added to this stack (after updating 2^64 bytes and not finalizing any) 136 // so we preallocate the stack here. This can be smaller in environments where the data limit is expected to 137 // be much lower. 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 // Section 5.1.2 of the BLAKE3 spec explains this algorithm in more detail. 149 private void addChunkCV(final int[] firstCV, final long totalChunks) { 150 // This chunk might complete some subtrees. For each completed subtree, 151 // its left child will be the current top entry in the CV stack, and 152 // its right child will be the current value of `newCV`. Pop each left 153 // child off the stack, merge it with `newCV`, and overwrite `newCV` 154 // with the result. After all these merges, push the final value of 155 // `newCV` onto the stack. The number of completed subtrees is given 156 // by the number of trailing 0-bits in the new total number of chunks. 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 // If the current chunk is complete, finalize it and reset the 169 // chunk state. More input is coming, so this chunk is not ROOT. 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 // Compress input bytes into the current chunk state. 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 // Starting with the Output from the current chunk, compute all the 188 // parent chaining values along the right edge of the tree, until we 189 // have the root Output. 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 * Represents the state just prior to either producing an eight word chaining value or any number of output bytes 216 * when the ROOT flag is set. 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 * Standard hash key used for plain hashes; same initialization vector as Blake2s. 265 */ 266 private static final int[] IV = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; 267 268 // domain flags 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 * Pre-permuted for all 7 rounds; the second row (2,6,3,...) indicates the base permutation. 279 */ 280 // @formatter:off 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 // @formatter:on 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 * The mixing function, G, which mixes either a column or a diagonal. 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 * Calculates the Blake3 hash of the provided data. 340 * 341 * @param data source array to absorb data from 342 * @return 32-byte hash squeezed from the provided data 343 * @throws NullPointerException if data is null 344 */ 345 public static byte[] hash(final byte[] data) { 346 return Blake3.initHash().update(data).doFinalize(OUT_LEN); 347 } 348 349 /** 350 * Constructs a fresh Blake3 hash function. The instance returned functions as an arbitrary length message digest. 351 * 352 * @return fresh Blake3 instance in hashed mode 353 */ 354 public static Blake3 initHash() { 355 return new Blake3(IV, 0); 356 } 357 358 /** 359 * Constructs a fresh Blake3 key derivation function using the provided key derivation context byte string. 360 * The instance returned functions as a key-derivation function which can further absorb additional context data 361 * before squeezing derived key data. 362 * 363 * @param kdfContext a globally unique key-derivation context byte string to separate key derivation contexts from each other 364 * @return fresh Blake3 instance in key derivation mode 365 * @throws NullPointerException if kdfContext is null 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 * Constructs a fresh Blake3 keyed hash function. The instance returned functions as a pseudorandom function (PRF) or as a 378 * message authentication code (MAC). 379 * 380 * @param key 32-byte secret key 381 * @return fresh Blake3 instance in keyed mode using the provided key 382 * @throws NullPointerException if key is null 383 * @throws IllegalArgumentException if key is not 32 bytes 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 * Calculates the Blake3 keyed hash (MAC) of the provided data. 395 * 396 * @param key 32-byte secret key 397 * @param data source array to absorb data from 398 * @return 32-byte mac squeezed from the provided data 399 * @throws NullPointerException if key or data are null 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 // Mix the columns. 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 // Mix the diagonals. 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 * Finalizes hash output data that depends on the sequence of updated bytes preceding this invocation and any 455 * previously finalized bytes. Note that this can finalize up to 2<sup>64</sup> bytes per instance. 456 * 457 * @param out destination array to finalize bytes into 458 * @return {@code this} instance. 459 * @throws NullPointerException if out is null 460 */ 461 public Blake3 doFinalize(final byte[] out) { 462 return doFinalize(out, 0, out.length); 463 } 464 465 /** 466 * Finalizes an arbitrary number of bytes into the provided output array that depends on the sequence of previously 467 * updated and finalized bytes. Note that this can finalize up to 2<sup>64</sup> bytes per instance. 468 * 469 * @param out destination array to finalize bytes into 470 * @param offset where in the array to begin writing bytes to 471 * @param length number of bytes to finalize 472 * @return {@code this} instance. 473 * @throws NullPointerException if out is null 474 * @throws IndexOutOfBoundsException if offset or length are negative or if offset + length is greater than the 475 * length of the provided array 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 * Squeezes and returns an arbitrary number of bytes dependent on the sequence of previously absorbed and squeezed bytes. 485 * 486 * @param nrBytes number of bytes to finalize 487 * @return requested number of finalized bytes 488 * @throws IllegalArgumentException if nrBytes is negative 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 * Resets this instance back to its initial state when it was first constructed. 501 * @return {@code this} instance. 502 */ 503 public Blake3 reset() { 504 engineState.reset(); 505 return this; 506 } 507 508 /** 509 * Updates this hash state using the provided bytes. 510 * 511 * @param in source array to update data from 512 * @return {@code this} instance. 513 * @throws NullPointerException if in is null 514 */ 515 public Blake3 update(final byte[] in) { 516 return update(in, 0, in.length); 517 } 518 519 /** 520 * Updates this hash state using the provided bytes at an offset. 521 * 522 * @param in source array to update data from 523 * @param offset where in the array to begin reading bytes 524 * @param length number of bytes to update 525 * @return {@code this} instance. 526 * @throws NullPointerException if in is null 527 * @throws IndexOutOfBoundsException if offset or length are negative or if offset + length is greater than the 528 * length of the provided array 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}