Module: Nuckle::Internals::Blake3
- Defined in:
- lib/nuckle/internals/blake3.rb
Overview
BLAKE3 cryptographic hash (unkeyed, keyed, and derive_key modes) with XOF.
References:
Defined Under Namespace
Constant Summary collapse
- MASK32 =
0xFFFFFFFF- OUT_LEN =
32- KEY_LEN =
32- BLOCK_LEN =
64- CHUNK_LEN =
1024- CHUNK_START =
Domain-separation flags (per-compression, not global).
1- CHUNK_END =
2- PARENT =
4- ROOT =
8- KEYED_HASH =
16- DERIVE_KEY_CONTEXT =
32- DERIVE_KEY_MATERIAL =
64- IV =
Same constants as SHA-256 IV.
[ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ].freeze
- MSG_PERMUTATION =
Message-word permutation applied between rounds.
[2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].freeze
Class Method Summary collapse
-
.compress(cv, block_words, counter, block_len, flags) ⇒ Array<Integer>
BLAKE3 compression function.
-
.derive_key(context, material, length = OUT_LEN) ⇒ Object
Derive a subkey from a context string and key material.
-
.g(state, a, b, c, d, mx, my) ⇒ Object
Quarter-round (G) on a 16-word state.
-
.hash(input, length = OUT_LEN) ⇒ Object
One-shot unkeyed hash.
-
.keyed_hash(key, input, length = OUT_LEN) ⇒ Object
One-shot keyed hash (32-byte key).
- .new_derive_key(context) ⇒ Object
-
.new_hasher ⇒ Object
Build a Hasher for the unkeyed mode (for streaming use).
- .new_keyed(key) ⇒ Object
- .permute(m) ⇒ Object
- .round(state, m) ⇒ Object
-
.words_from_block(block) ⇒ Object
Parse a 64-byte string (or zero-padded short string) into 16 LE u32 words.
Class Method Details
.compress(cv, block_words, counter, block_len, flags) ⇒ Array<Integer>
BLAKE3 compression function.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/nuckle/internals/blake3.rb', line 80 def compress(cv, block_words, counter, block_len, flags) state = [ cv[0], cv[1], cv[2], cv[3], cv[4], cv[5], cv[6], cv[7], IV[0], IV[1], IV[2], IV[3], counter & MASK32, (counter >> 32) & MASK32, block_len, flags ] m = block_words.dup round(state, m) 6.times do m = permute(m) round(state, m) end # Final xor: state[0..8] ^= state[8..16]; state[8..16] ^= cv[0..8] 8.times do |i| state[i] ^= state[i + 8] state[i + 8] ^= cv[i] end state end |
.derive_key(context, material, length = OUT_LEN) ⇒ Object
Derive a subkey from a context string and key material.
276 277 278 |
# File 'lib/nuckle/internals/blake3.rb', line 276 def derive_key(context, material, length = OUT_LEN) new_derive_key(context).update(material).finalize(length) end |
.g(state, a, b, c, d, mx, my) ⇒ Object
Quarter-round (G) on a 16-word state.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/nuckle/internals/blake3.rb', line 39 def g(state, a, b, c, d, mx, my) state[a] = (state[a] + state[b] + mx) & MASK32 t = state[d] ^ state[a] state[d] = ((t >> 16) | (t << 16)) & MASK32 state[c] = (state[c] + state[d]) & MASK32 t = state[b] ^ state[c] state[b] = ((t >> 12) | (t << 20)) & MASK32 state[a] = (state[a] + state[b] + my) & MASK32 t = state[d] ^ state[a] state[d] = ((t >> 8) | (t << 24)) & MASK32 state[c] = (state[c] + state[d]) & MASK32 t = state[b] ^ state[c] state[b] = ((t >> 7) | (t << 25)) & MASK32 end |
.hash(input, length = OUT_LEN) ⇒ Object
One-shot unkeyed hash.
255 256 257 |
# File 'lib/nuckle/internals/blake3.rb', line 255 def hash(input, length = OUT_LEN) Hasher.new(IV, 0).update(input).finalize(length) end |
.keyed_hash(key, input, length = OUT_LEN) ⇒ Object
One-shot keyed hash (32-byte key).
265 266 267 |
# File 'lib/nuckle/internals/blake3.rb', line 265 def keyed_hash(key, input, length = OUT_LEN) new_keyed(key).update(input).finalize(length) end |
.new_derive_key(context) ⇒ Object
280 281 282 283 |
# File 'lib/nuckle/internals/blake3.rb', line 280 def new_derive_key(context) ctx_key_bytes = Hasher.new(IV, DERIVE_KEY_CONTEXT).update(context).finalize(KEY_LEN) Hasher.new(ctx_key_bytes.unpack("V8"), DERIVE_KEY_MATERIAL) end |
.new_hasher ⇒ Object
Build a Hasher for the unkeyed mode (for streaming use).
260 261 262 |
# File 'lib/nuckle/internals/blake3.rb', line 260 def new_hasher Hasher.new(IV, 0) end |
.new_keyed(key) ⇒ Object
269 270 271 272 273 |
# File 'lib/nuckle/internals/blake3.rb', line 269 def new_keyed(key) raise ArgumentError, "key must be 32 bytes" unless key.bytesize == KEY_LEN Hasher.new(key.b.unpack("V8"), KEYED_HASH) end |
.permute(m) ⇒ Object
67 68 69 |
# File 'lib/nuckle/internals/blake3.rb', line 67 def permute(m) MSG_PERMUTATION.map { |i| m[i] } end |
.round(state, m) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/nuckle/internals/blake3.rb', line 54 def round(state, m) # Columns g(state, 0, 4, 8, 12, m[0], m[1]) g(state, 1, 5, 9, 13, m[2], m[3]) g(state, 2, 6, 10, 14, m[4], m[5]) g(state, 3, 7, 11, 15, m[6], m[7]) # Diagonals g(state, 0, 5, 10, 15, m[8], m[9]) g(state, 1, 6, 11, 12, m[10], m[11]) g(state, 2, 7, 8, 13, m[12], m[13]) g(state, 3, 4, 9, 14, m[14], m[15]) end |
.words_from_block(block) ⇒ Object
Parse a 64-byte string (or zero-padded short string) into 16 LE u32 words.
104 105 106 107 |
# File 'lib/nuckle/internals/blake3.rb', line 104 def words_from_block(block) block = block + ("\x00".b * (BLOCK_LEN - block.bytesize)) if block.bytesize < BLOCK_LEN block.unpack("V16") end |