Skip to main content

arcana/hash/
blake2.rs

1//! BLAKE2b and BLAKE2s hash functions (RFC 7693).
2//!
3//! BLAKE2b (64-bit, 1..64 byte output) and BLAKE2s (32-bit, 1..32
4//! byte output) are cryptographic hash functions designed as faster
5//! replacements for MD5 / SHA-1. Not FIPS-approved, but widely used
6//! in Argon2, libsodium, WireGuard, and many file-integrity tools.
7
8use crate::Hasher;
9
10// ====================================================================
11// BLAKE2b (64-bit)
12// ====================================================================
13
14const BLAKE2B_IV: [u64; 8] = [
15    0x6A09E667F3BCC908,
16    0xBB67AE8584CAA73B,
17    0x3C6EF372FE94F82B,
18    0xA54FF53A5F1D36F1,
19    0x510E527FADE682D1,
20    0x9B05688C2B3E6C1F,
21    0x1F83D9ABFB41BD6B,
22    0x5BE0CD19137E2179,
23];
24
25const BLAKE2B_SIGMA: [[usize; 16]; 12] = [
26    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
27    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
28    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
29    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
30    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
31    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
32    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
33    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
34    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
35    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
36    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
37    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
38];
39
40#[inline(always)]
41fn blake2b_g(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
42    v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
43    v[d] = (v[d] ^ v[a]).rotate_right(32);
44    v[c] = v[c].wrapping_add(v[d]);
45    v[b] = (v[b] ^ v[c]).rotate_right(24);
46    v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
47    v[d] = (v[d] ^ v[a]).rotate_right(16);
48    v[c] = v[c].wrapping_add(v[d]);
49    v[b] = (v[b] ^ v[c]).rotate_right(63);
50}
51
52fn blake2b_compress(h: &mut [u64; 8], block: &[u8; 128], t: u128, last: bool) {
53    let mut v = [0u64; 16];
54    v[..8].copy_from_slice(h);
55    v[8..16].copy_from_slice(&BLAKE2B_IV);
56    v[12] ^= t as u64;
57    v[13] ^= (t >> 64) as u64;
58    if last {
59        v[14] = !v[14];
60    }
61
62    let mut m = [0u64; 16];
63    for i in 0..16 {
64        m[i] = u64::from_le_bytes([
65            block[8 * i],
66            block[8 * i + 1],
67            block[8 * i + 2],
68            block[8 * i + 3],
69            block[8 * i + 4],
70            block[8 * i + 5],
71            block[8 * i + 6],
72            block[8 * i + 7],
73        ]);
74    }
75
76    for i in 0..12 {
77        let s = &BLAKE2B_SIGMA[i];
78        blake2b_g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
79        blake2b_g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
80        blake2b_g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
81        blake2b_g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
82        blake2b_g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
83        blake2b_g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
84        blake2b_g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
85        blake2b_g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
86    }
87
88    for i in 0..8 {
89        h[i] ^= v[i] ^ v[i + 8];
90    }
91}
92
93/// BLAKE2b hasher (RFC 7693). Default: 64-byte output, 128-byte blocks.
94///
95/// Not FIPS-approved. Used by Argon2, libsodium, WireGuard.
96#[derive(Clone)]
97pub struct Blake2b {
98    h: [u64; 8],
99    buf: [u8; 128],
100    buf_len: usize,
101    total: u128,
102    out_len: usize,
103}
104
105impl Blake2b {
106    /// Create a BLAKE2b hasher with a custom output length (1..=64 bytes).
107    pub fn with_output_len(out_len: usize) -> Self {
108        assert!(out_len >= 1 && out_len <= 64, "BLAKE2b output must be 1..64");
109        let mut h = BLAKE2B_IV;
110        // Parameter block: digest_length || key_length=0 || fanout=1 || depth=1
111        h[0] ^= 0x01010000 ^ (out_len as u64);
112        Self {
113            h,
114            buf: [0u8; 128],
115            buf_len: 0,
116            total: 0,
117            out_len,
118        }
119    }
120}
121
122impl Hasher for Blake2b {
123    const OUTPUT_LEN: usize = 64;
124    const BLOCK_LEN: usize = 128;
125
126    fn new() -> Self {
127        Self::with_output_len(64)
128    }
129
130    fn update(&mut self, data: &[u8]) {
131        let mut pos = 0;
132        while pos < data.len() {
133            // If buffer is full and there's more data, compress.
134            if self.buf_len == 128 {
135                self.total += 128;
136                let block = self.buf;
137                blake2b_compress(&mut self.h, &block, self.total, false);
138                self.buf_len = 0;
139            }
140            let take = (128 - self.buf_len).min(data.len() - pos);
141            self.buf[self.buf_len..self.buf_len + take].copy_from_slice(&data[pos..pos + take]);
142            self.buf_len += take;
143            pos += take;
144        }
145    }
146
147    fn finalize(self) -> Vec<u8> {
148        let mut out = vec![0u8; self.out_len];
149        self.finalize_into(&mut out);
150        out
151    }
152
153    fn finalize_into(mut self, out: &mut [u8]) {
154        self.total += self.buf_len as u128;
155        // Zero-pad the remaining buffer.
156        for b in &mut self.buf[self.buf_len..] {
157            *b = 0;
158        }
159        blake2b_compress(&mut self.h, &self.buf, self.total, true);
160        let len = out.len().min(self.out_len);
161        let mut full = [0u8; 64];
162        for i in 0..8 {
163            full[8 * i..8 * i + 8].copy_from_slice(&self.h[i].to_le_bytes());
164        }
165        out[..len].copy_from_slice(&full[..len]);
166    }
167}
168
169// ====================================================================
170// BLAKE2s (32-bit)
171// ====================================================================
172
173const BLAKE2S_IV: [u32; 8] = [
174    0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
175];
176
177const BLAKE2S_SIGMA: [[usize; 16]; 10] = [
178    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
179    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
180    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
181    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
182    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
183    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
184    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
185    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
186    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
187    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
188];
189
190#[inline(always)]
191fn blake2s_g(v: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32) {
192    v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
193    v[d] = (v[d] ^ v[a]).rotate_right(16);
194    v[c] = v[c].wrapping_add(v[d]);
195    v[b] = (v[b] ^ v[c]).rotate_right(12);
196    v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
197    v[d] = (v[d] ^ v[a]).rotate_right(8);
198    v[c] = v[c].wrapping_add(v[d]);
199    v[b] = (v[b] ^ v[c]).rotate_right(7);
200}
201
202fn blake2s_compress(h: &mut [u32; 8], block: &[u8; 64], t: u64, last: bool) {
203    let mut v = [0u32; 16];
204    v[..8].copy_from_slice(h);
205    v[8..16].copy_from_slice(&BLAKE2S_IV);
206    v[12] ^= t as u32;
207    v[13] ^= (t >> 32) as u32;
208    if last {
209        v[14] = !v[14];
210    }
211
212    let mut m = [0u32; 16];
213    for i in 0..16 {
214        m[i] = u32::from_le_bytes([block[4 * i], block[4 * i + 1], block[4 * i + 2], block[4 * i + 3]]);
215    }
216
217    for i in 0..10 {
218        let s = &BLAKE2S_SIGMA[i];
219        blake2s_g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
220        blake2s_g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
221        blake2s_g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
222        blake2s_g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
223        blake2s_g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
224        blake2s_g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
225        blake2s_g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
226        blake2s_g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
227    }
228
229    for i in 0..8 {
230        h[i] ^= v[i] ^ v[i + 8];
231    }
232}
233
234/// BLAKE2s hasher (RFC 7693). Default: 32-byte output, 64-byte blocks.
235///
236/// 32-bit oriented sibling of BLAKE2b. Faster on embedded (ARM
237/// Cortex-M) and 32-bit RISC-V targets. Used in WireGuard, Zcash
238/// (Equihash), and various file-integrity tools.
239#[derive(Clone)]
240pub struct Blake2s {
241    h: [u32; 8],
242    buf: [u8; 64],
243    buf_len: usize,
244    total: u64,
245    out_len: usize,
246}
247
248impl Blake2s {
249    /// Create a BLAKE2s hasher with a custom output length (1..=32 bytes).
250    pub fn with_output_len(out_len: usize) -> Self {
251        assert!(out_len >= 1 && out_len <= 32, "BLAKE2s output must be 1..32");
252        let mut h = BLAKE2S_IV;
253        h[0] ^= 0x01010000 ^ (out_len as u32);
254        Self {
255            h,
256            buf: [0u8; 64],
257            buf_len: 0,
258            total: 0,
259            out_len,
260        }
261    }
262}
263
264impl Hasher for Blake2s {
265    const OUTPUT_LEN: usize = 32;
266    const BLOCK_LEN: usize = 64;
267
268    fn new() -> Self {
269        Self::with_output_len(32)
270    }
271
272    fn update(&mut self, data: &[u8]) {
273        let mut pos = 0;
274        while pos < data.len() {
275            if self.buf_len == 64 {
276                self.total += 64;
277                let block = self.buf;
278                blake2s_compress(&mut self.h, &block, self.total, false);
279                self.buf_len = 0;
280            }
281            let take = (64 - self.buf_len).min(data.len() - pos);
282            self.buf[self.buf_len..self.buf_len + take].copy_from_slice(&data[pos..pos + take]);
283            self.buf_len += take;
284            pos += take;
285        }
286    }
287
288    fn finalize(self) -> Vec<u8> {
289        let mut out = vec![0u8; self.out_len];
290        self.finalize_into(&mut out);
291        out
292    }
293
294    fn finalize_into(mut self, out: &mut [u8]) {
295        self.total += self.buf_len as u64;
296        for b in &mut self.buf[self.buf_len..] {
297            *b = 0;
298        }
299        blake2s_compress(&mut self.h, &self.buf, self.total, true);
300        let len = out.len().min(self.out_len);
301        let mut full = [0u8; 32];
302        for i in 0..8 {
303            full[4 * i..4 * i + 4].copy_from_slice(&self.h[i].to_le_bytes());
304        }
305        out[..len].copy_from_slice(&full[..len]);
306    }
307}
308
309// ====================================================================
310// Tests — RFC 7693 Appendix A
311// ====================================================================
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::Hasher;
317
318    fn hex(bytes: &[u8]) -> String {
319        bytes.iter().map(|b| format!("{:02x}", b)).collect()
320    }
321
322    // RFC 7693 §Appendix A: BLAKE2b-512("abc")
323    #[test]
324    fn blake2b_abc() {
325        let digest = Blake2b::hash(b"abc");
326        assert_eq!(
327            hex(&digest),
328            "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1\
329             7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
330        );
331    }
332
333    // BLAKE2b-512("")
334    #[test]
335    fn blake2b_empty() {
336        let digest = Blake2b::hash(b"");
337        assert_eq!(
338            hex(&digest),
339            "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419\
340             d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"
341        );
342    }
343
344    // BLAKE2b with custom output length (32 bytes).
345    #[test]
346    fn blake2b_32_empty() {
347        let mut h = Blake2b::with_output_len(32);
348        h.update(b"");
349        let digest = h.finalize();
350        assert_eq!(digest.len(), 32);
351        assert_eq!(
352            hex(&digest),
353            "0e5751c026e543b2e8ab2eb06099daa1\
354             d1e5df47778f7787faab45cdf12fe3a8"
355        );
356    }
357
358    // RFC 7693 §Appendix A: BLAKE2s-256("abc")
359    #[test]
360    fn blake2s_abc() {
361        let digest = Blake2s::hash(b"abc");
362        assert_eq!(
363            hex(&digest),
364            "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"
365        );
366    }
367
368    // BLAKE2s-256("")
369    #[test]
370    fn blake2s_empty() {
371        let digest = Blake2s::hash(b"");
372        assert_eq!(
373            hex(&digest),
374            "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"
375        );
376    }
377
378    // Streaming: multi-update produces same result.
379    #[test]
380    fn blake2b_streaming() {
381        let mut h = Blake2b::new();
382        h.update(b"a");
383        h.update(b"b");
384        h.update(b"c");
385        let stream = h.finalize();
386        let oneshot = Blake2b::hash(b"abc");
387        assert_eq!(stream, oneshot);
388    }
389
390    #[test]
391    fn blake2s_streaming() {
392        let mut h = Blake2s::new();
393        h.update(b"a");
394        h.update(b"b");
395        h.update(b"c");
396        let stream = h.finalize();
397        let oneshot = Blake2s::hash(b"abc");
398        assert_eq!(stream, oneshot);
399    }
400}