Skip to main content

yo_common/
blake3.rs

1//! BLAKE3, the hash the shape tag is made of (`15` section 3.1).
2//!
3//! Written here rather than depended on, for the same reason `crc`, `wyhash`
4//! and `xxh3` are written here: the shape tag is a number six language bindings
5//! have to agree on byte for byte forever, so the algorithm is part of the
6//! format and belongs next to the other two hashes the format already pins.
7//! The published crate also compiles assembly through a C toolchain by default,
8//! which is a build dependency the whole workspace would inherit for a hash
9//! that runs once when a collection is opened.
10//!
11//! This is the portable implementation, without the SIMD kernels. A shape
12//! description is a few hundred bytes and it is hashed at open time, never on
13//! a hot path, so a kernel that is four times faster would save nothing that
14//! could be measured. If a caller ever needs BLAKE3 on a hot path, that is the
15//! moment to add the kernels, and the test vectors here already cover them.
16//!
17//! Only the plain hash is implemented. Keyed hashing, key derivation and the
18//! extended output are the parts of BLAKE3 nothing in `yo` uses.
19//!
20//! ```
21//! use yo_common::blake3;
22//!
23//! let h = blake3::hash(b"");
24//! assert_eq!(
25//!     blake3::to_hex(&h),
26//!     "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",
27//! );
28//! ```
29
30const OUT_LEN: usize = 32;
31const BLOCK_LEN: usize = 64;
32const CHUNK_LEN: usize = 1024;
33
34const CHUNK_START: u32 = 1 << 0;
35const CHUNK_END: u32 = 1 << 1;
36const PARENT: u32 = 1 << 2;
37const ROOT: u32 = 1 << 3;
38
39const IV: [u32; 8] = [
40    0x6A09_E667,
41    0xBB67_AE85,
42    0x3C6E_F372,
43    0xA54F_F53A,
44    0x510E_527F,
45    0x9B05_688C,
46    0x1F83_D9AB,
47    0x5BE0_CD19,
48];
49
50const MSG_PERMUTATION: [usize; 16] = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8];
51
52/// The 32 byte hash of a whole input.
53///
54/// The streaming form is [`Hasher`]; this is the one call every caller in the
55/// tree actually wants.
56#[must_use]
57pub fn hash(input: &[u8]) -> [u8; OUT_LEN] {
58    let mut h = Hasher::new();
59    h.update(input);
60    h.finalize()
61}
62
63/// Lower case hex, which is the spelling every binding prints and every test
64/// vector is written in.
65#[must_use]
66pub fn to_hex(bytes: &[u8]) -> String {
67    const DIGITS: &[u8; 16] = b"0123456789abcdef";
68    let mut s = String::with_capacity(bytes.len() * 2);
69    for b in bytes {
70        s.push(DIGITS[usize::from(b >> 4)] as char);
71        s.push(DIGITS[usize::from(b & 0x0f)] as char);
72    }
73    s
74}
75
76/// The BLAKE3 mixing function on one word quadruple.
77#[inline]
78fn g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, mx: u32, my: u32) {
79    state[a] = state[a].wrapping_add(state[b]).wrapping_add(mx);
80    state[d] = (state[d] ^ state[a]).rotate_right(16);
81    state[c] = state[c].wrapping_add(state[d]);
82    state[b] = (state[b] ^ state[c]).rotate_right(12);
83    state[a] = state[a].wrapping_add(state[b]).wrapping_add(my);
84    state[d] = (state[d] ^ state[a]).rotate_right(8);
85    state[c] = state[c].wrapping_add(state[d]);
86    state[b] = (state[b] ^ state[c]).rotate_right(7);
87}
88
89#[inline]
90fn round(state: &mut [u32; 16], m: &[u32; 16]) {
91    // Columns.
92    g(state, 0, 4, 8, 12, m[0], m[1]);
93    g(state, 1, 5, 9, 13, m[2], m[3]);
94    g(state, 2, 6, 10, 14, m[4], m[5]);
95    g(state, 3, 7, 11, 15, m[6], m[7]);
96    // Diagonals.
97    g(state, 0, 5, 10, 15, m[8], m[9]);
98    g(state, 1, 6, 11, 12, m[10], m[11]);
99    g(state, 2, 7, 8, 13, m[12], m[13]);
100    g(state, 3, 4, 9, 14, m[14], m[15]);
101}
102
103#[inline]
104fn permute(m: &mut [u32; 16]) {
105    let old = *m;
106    for (i, &p) in MSG_PERMUTATION.iter().enumerate() {
107        m[i] = old[p];
108    }
109}
110
111/// The compression function, returning the whole 16 word state because the
112/// root node needs the second half and a chaining value needs only the first.
113fn compress(
114    chaining_value: &[u32; 8],
115    block_words: &[u32; 16],
116    counter: u64,
117    block_len: u32,
118    flags: u32,
119) -> [u32; 16] {
120    let counter_low = counter as u32;
121    let counter_high = (counter >> 32) as u32;
122    let mut state = [
123        chaining_value[0],
124        chaining_value[1],
125        chaining_value[2],
126        chaining_value[3],
127        chaining_value[4],
128        chaining_value[5],
129        chaining_value[6],
130        chaining_value[7],
131        IV[0],
132        IV[1],
133        IV[2],
134        IV[3],
135        counter_low,
136        counter_high,
137        block_len,
138        flags,
139    ];
140    let mut block = *block_words;
141
142    for _ in 0..6 {
143        round(&mut state, &block);
144        permute(&mut block);
145    }
146    round(&mut state, &block);
147
148    for i in 0..8 {
149        state[i] ^= state[i + 8];
150        state[i + 8] ^= chaining_value[i];
151    }
152    state
153}
154
155fn first_8(state: [u32; 16]) -> [u32; 8] {
156    let mut cv = [0u32; 8];
157    cv.copy_from_slice(&state[..8]);
158    cv
159}
160
161fn words_from_le(block: &[u8; BLOCK_LEN]) -> [u32; 16] {
162    let mut words = [0u32; 16];
163    let (quads, _) = block.as_chunks::<4>();
164    for (word, quad) in words.iter_mut().zip(quads) {
165        *word = u32::from_le_bytes(*quad);
166    }
167    words
168}
169
170/// A node that is ready to be either chained into its parent or, if it turns
171/// out to be the root, finalized.
172struct Output {
173    input_chaining_value: [u32; 8],
174    block_words: [u32; 16],
175    counter: u64,
176    block_len: u32,
177    flags: u32,
178}
179
180impl Output {
181    fn chaining_value(&self) -> [u32; 8] {
182        first_8(compress(
183            &self.input_chaining_value,
184            &self.block_words,
185            self.counter,
186            self.block_len,
187            self.flags,
188        ))
189    }
190
191    fn root_bytes(&self) -> [u8; OUT_LEN] {
192        let state = compress(
193            &self.input_chaining_value,
194            &self.block_words,
195            0,
196            self.block_len,
197            self.flags | ROOT,
198        );
199        let mut out = [0u8; OUT_LEN];
200        let (quads, _) = out.as_chunks_mut::<4>();
201        for (word, quad) in state[..8].iter().zip(quads) {
202            *quad = word.to_le_bytes();
203        }
204        out
205    }
206}
207
208/// One 1 KiB chunk being filled a block at a time.
209struct ChunkState {
210    chaining_value: [u32; 8],
211    counter: u64,
212    block: [u8; BLOCK_LEN],
213    block_len: u8,
214    blocks_compressed: u8,
215}
216
217impl ChunkState {
218    fn new(counter: u64) -> ChunkState {
219        ChunkState {
220            chaining_value: IV,
221            counter,
222            block: [0; BLOCK_LEN],
223            block_len: 0,
224            blocks_compressed: 0,
225        }
226    }
227
228    fn len(&self) -> usize {
229        BLOCK_LEN * usize::from(self.blocks_compressed) + usize::from(self.block_len)
230    }
231
232    fn start_flag(&self) -> u32 {
233        if self.blocks_compressed == 0 {
234            CHUNK_START
235        } else {
236            0
237        }
238    }
239
240    fn update(&mut self, mut input: &[u8]) {
241        while !input.is_empty() {
242            if usize::from(self.block_len) == BLOCK_LEN {
243                let words = words_from_le(&self.block);
244                self.chaining_value = first_8(compress(
245                    &self.chaining_value,
246                    &words,
247                    self.counter,
248                    BLOCK_LEN as u32,
249                    self.start_flag(),
250                ));
251                self.blocks_compressed += 1;
252                self.block = [0; BLOCK_LEN];
253                self.block_len = 0;
254            }
255
256            let want = BLOCK_LEN - usize::from(self.block_len);
257            let take = want.min(input.len());
258            let at = usize::from(self.block_len);
259            self.block[at..at + take].copy_from_slice(&input[..take]);
260            self.block_len += take as u8;
261            input = &input[take..];
262        }
263    }
264
265    fn output(&self) -> Output {
266        Output {
267            input_chaining_value: self.chaining_value,
268            block_words: words_from_le(&self.block),
269            counter: self.counter,
270            block_len: u32::from(self.block_len),
271            flags: self.start_flag() | CHUNK_END,
272        }
273    }
274}
275
276fn parent_output(left: [u32; 8], right: [u32; 8]) -> Output {
277    let mut block_words = [0u32; 16];
278    block_words[..8].copy_from_slice(&left);
279    block_words[8..].copy_from_slice(&right);
280    Output {
281        input_chaining_value: IV,
282        block_words,
283        counter: 0,
284        block_len: BLOCK_LEN as u32,
285        flags: PARENT,
286    }
287}
288
289/// The streaming hasher.
290///
291/// Feed it with [`update`](Hasher::update) as many times as suits the caller
292/// and the answer does not depend on where the boundaries fell.
293///
294/// The stack is 54 chaining values because that is the deepest a tree can get:
295/// one entry per bit of the chunk counter, and a chunk is a kibibyte.
296pub struct Hasher {
297    chunk: ChunkState,
298    stack: [[u32; 8]; 54],
299    stack_len: u8,
300}
301
302impl Default for Hasher {
303    fn default() -> Hasher {
304        Hasher::new()
305    }
306}
307
308impl Hasher {
309    /// An empty hasher.
310    #[must_use]
311    pub fn new() -> Hasher {
312        Hasher {
313            chunk: ChunkState::new(0),
314            stack: [[0; 8]; 54],
315            stack_len: 0,
316        }
317    }
318
319    fn push(&mut self, cv: [u32; 8]) {
320        self.stack[usize::from(self.stack_len)] = cv;
321        self.stack_len += 1;
322    }
323
324    fn pop(&mut self) -> [u32; 8] {
325        self.stack_len -= 1;
326        self.stack[usize::from(self.stack_len)]
327    }
328
329    /// Merge a finished chunk in, collapsing every subtree the new chunk
330    /// completes. The trailing zeros of the chunk count say how many merges
331    /// that is, which is the whole trick that makes the tree implicit.
332    fn add_chunk(&mut self, mut cv: [u32; 8], total_chunks: u64) {
333        let mut chunks = total_chunks;
334        while chunks & 1 == 0 {
335            let left = self.pop();
336            cv = parent_output(left, cv).chaining_value();
337            chunks >>= 1;
338        }
339        self.push(cv);
340    }
341
342    /// Add more input.
343    pub fn update(&mut self, mut input: &[u8]) {
344        while !input.is_empty() {
345            if self.chunk.len() == CHUNK_LEN {
346                let cv = self.chunk.output().chaining_value();
347                let total = self.chunk.counter + 1;
348                self.add_chunk(cv, total);
349                self.chunk = ChunkState::new(total);
350            }
351
352            let want = CHUNK_LEN - self.chunk.len();
353            let take = want.min(input.len());
354            self.chunk.update(&input[..take]);
355            input = &input[take..];
356        }
357    }
358
359    /// The 32 byte hash of everything fed in so far.
360    #[must_use]
361    pub fn finalize(&self) -> [u8; OUT_LEN] {
362        let mut output = self.chunk.output();
363        let mut remaining = usize::from(self.stack_len);
364        while remaining > 0 {
365            remaining -= 1;
366            output = parent_output(self.stack[remaining], output.chaining_value());
367        }
368        output.root_bytes()
369    }
370}
371
372#[cfg(test)]
373mod tests {
374    use super::*;
375
376    /// The input the official test vectors use: bytes 0, 1, 2 ... 250, then
377    /// back to 0. Written out here so the vectors below are checked against
378    /// the same thing every other implementation checks them against.
379    fn pattern(len: usize) -> Vec<u8> {
380        (0..len).map(|i| (i % 251) as u8).collect()
381    }
382
383    /// Every case from the BLAKE3 team's `test_vectors.json`, unkeyed. The
384    /// lengths are chosen to land on every boundary that matters: empty, part
385    /// of a block, a whole block, part of a chunk, a whole chunk, and the
386    /// chunk counts that force one, two and many levels of parent nodes.
387    const VECTORS: &[(usize, &str)] = &[
388        (
389            0,
390            "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",
391        ),
392        (
393            1,
394            "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213",
395        ),
396        (
397            2,
398            "7b7015bb92cf0b318037702a6cdd81dee41224f734684c2c122cd6359cb1ee63",
399        ),
400        (
401            3,
402            "e1be4d7a8ab5560aa4199eea339849ba8e293d55ca0a81006726d184519e647f",
403        ),
404        (
405            4,
406            "f30f5ab28fe047904037f77b6da4fea1e27241c5d132638d8bedce9d40494f32",
407        ),
408        (
409            5,
410            "b40b44dfd97e7a84a996a91af8b85188c66c126940ba7aad2e7ae6b385402aa2",
411        ),
412        (
413            6,
414            "06c4e8ffb6872fad96f9aaca5eee1553eb62aed0ad7198cef42e87f6a616c844",
415        ),
416        (
417            7,
418            "3f8770f387faad08faa9d8414e9f449ac68e6ff0417f673f602a646a891419fe",
419        ),
420        (
421            8,
422            "2351207d04fc16ade43ccab08600939c7c1fa70a5c0aaca76063d04c3228eaeb",
423        ),
424        (
425            63,
426            "e9bc37a594daad83be9470df7f7b3798297c3d834ce80ba85d6e207627b7db7b",
427        ),
428        (
429            64,
430            "4eed7141ea4a5cd4b788606bd23f46e212af9cacebacdc7d1f4c6dc7f2511b98",
431        ),
432        (
433            65,
434            "de1e5fa0be70df6d2be8fffd0e99ceaa8eb6e8c93a63f2d8d1c30ecb6b263dee",
435        ),
436        (
437            127,
438            "d81293fda863f008c09e92fc382a81f5a0b4a1251cba1634016a0f86a6bd640d",
439        ),
440        (
441            128,
442            "f17e570564b26578c33bb7f44643f539624b05df1a76c81f30acd548c44b45ef",
443        ),
444        (
445            129,
446            "683aaae9f3c5ba37eaaf072aed0f9e30bac0865137bae68b1fde4ca2aebdcb12",
447        ),
448        (
449            1023,
450            "10108970eeda3eb932baac1428c7a2163b0e924c9a9e25b35bba72b28f70bd11",
451        ),
452        (
453            1024,
454            "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af7",
455        ),
456        (
457            1025,
458            "d00278ae47eb27b34faecf67b4fe263f82d5412916c1ffd97c8cb7fb814b8444",
459        ),
460        (
461            2048,
462            "e776b6028c7cd22a4d0ba182a8bf62205d2ef576467e838ed6f2529b85fba24a",
463        ),
464        (
465            2049,
466            "5f4d72f40d7a5f82b15ca2b2e44b1de3c2ef86c426c95c1af0b6879522563030",
467        ),
468        (
469            3072,
470            "b98cb0ff3623be03326b373de6b9095218513e64f1ee2edd2525c7ad1e5cffd2",
471        ),
472        (
473            3073,
474            "7124b49501012f81cc7f11ca069ec9226cecb8a2c850cfe644e327d22d3e1cd3",
475        ),
476        (
477            4096,
478            "015094013f57a5277b59d8475c0501042c0b642e531b0a1c8f58d2163229e969",
479        ),
480        (
481            4097,
482            "9b4052b38f1c5fc8b1f9ff7ac7b27cd242487b3d890d15c96a1c25b8aa0fb995",
483        ),
484        (
485            5120,
486            "9cadc15fed8b5d854562b26a9536d9707cadeda9b143978f319ab34230535833",
487        ),
488        (
489            5121,
490            "628bd2cb2004694adaab7bbd778a25df25c47b9d4155a55f8fbd79f2fe154cff",
491        ),
492        (
493            6144,
494            "3e2e5b74e048f3add6d21faab3f83aa44d3b2278afb83b80b3c35164ebeca205",
495        ),
496        (
497            6145,
498            "f1323a8631446cc50536a9f705ee5cb619424d46887f3c376c695b70e0f0507f",
499        ),
500        (
501            7168,
502            "61da957ec2499a95d6b8023e2b0e604ec7f6b50e80a9678b89d2628e99ada77a",
503        ),
504        (
505            7169,
506            "a003fc7a51754a9b3c7fae0367ab3d782dccf28855a03d435f8cfe74605e7817",
507        ),
508        (
509            8192,
510            "aae792484c8efe4f19e2ca7d371d8c467ffb10748d8a5a1ae579948f718a2a63",
511        ),
512        (
513            8193,
514            "bab6c09cb8ce8cf459261398d2e7aef35700bf488116ceb94a36d0f5f1b7bc3b",
515        ),
516        (
517            16_384,
518            "f875d6646de28985646f34ee13be9a576fd515f76b5b0a26bb324735041ddde4",
519        ),
520        (
521            31_744,
522            "62b6960e1a44bcc1eb1a611a8d6235b6b4b78f32e7abc4fb4c6cdcce94895c47",
523        ),
524        (
525            102_400,
526            "bc3e3d41a1146b069abffad3c0d44860cf664390afce4d9661f7902e7943e085",
527        ),
528    ];
529
530    #[test]
531    fn the_official_vectors_pass() {
532        for &(len, want) in VECTORS {
533            let got = to_hex(&hash(&pattern(len)));
534            assert_eq!(got, want, "length {len}");
535        }
536    }
537
538    /// Where the caller cut its input cannot change the answer, which is the
539    /// property the shape writer relies on when it emits a description field
540    /// by field instead of in one buffer.
541    #[test]
542    fn the_split_does_not_matter() {
543        let input = pattern(4097);
544        let want = hash(&input);
545        for step in [1usize, 7, 63, 64, 65, 1023, 1024, 1025, 1500] {
546            let mut h = Hasher::new();
547            for piece in input.chunks(step) {
548                h.update(piece);
549            }
550            assert_eq!(h.finalize(), want, "step {step}");
551        }
552    }
553
554    /// An empty update is not an event.
555    #[test]
556    fn empty_updates_are_free() {
557        let mut h = Hasher::new();
558        h.update(b"");
559        h.update(b"yo");
560        h.update(b"");
561        assert_eq!(h.finalize(), hash(b"yo"));
562    }
563
564    #[test]
565    fn hex_is_lower_case_and_padded() {
566        assert_eq!(to_hex(&[0x00, 0x0f, 0xa0, 0xff]), "000fa0ff");
567        assert_eq!(to_hex(&[]), "");
568    }
569}