Skip to main content

irox_tools/hash/
blake2.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5#![allow(clippy::integer_division_remainder_used)]
6
7use crate::buf::ArrayBuf;
8use crate::buf::FixedU8Buf;
9use crate::hash::HashAlgorithm;
10use crate::hash::HashDigest;
11use core::ops::{BitXorAssign, Not};
12use irox_bits::WriteToLEBits;
13
14macro_rules! g {
15    (
16        $a:expr,
17        $b:expr,
18        $c:expr,
19        $d:expr,
20        $x:expr,
21        $y:expr,
22        $r:expr
23    ) => {
24        *$a = ($a.wrapping_add(*$b).wrapping_add($x));
25        *$d ^= *$a;
26        *$d = ($d.rotate_right($r[0]));
27        *$c = ($c.wrapping_add(*$d));
28        *$b ^= *$c;
29        *$b = ($b.rotate_right($r[1]));
30        *$a = ($a.wrapping_add(*$b).wrapping_add($y));
31        *$d ^= *$a;
32        *$d = ($d.rotate_right($r[2]));
33        *$c = ($c.wrapping_add(*$d));
34        *$b ^= *$c;
35        *$b = ($b.rotate_right($r[3]));
36    };
37}
38static BLAKE2B_R: &[u32; 4] = &[32, 24, 16, 63];
39static BLAKE2S_R: &[u32; 4] = &[16, 12, 8, 7];
40static BLAKE2B_IV: &[u64; 8] = &[
41    0x6A09E667F3BCC908,
42    0xBB67AE8584CAA73B,
43    0x3C6EF372FE94F82B,
44    0xA54FF53A5F1D36F1,
45    0x510E527FADE682D1,
46    0x9B05688C2B3E6C1F,
47    0x1F83D9ABFB41BD6B,
48    0x5BE0CD19137E2179,
49];
50static BLAKE2S_IV: &[u32; 8] = &[
51    0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
52];
53static SIGMA: &[&[usize; 16]; 12] = &[
54    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
55    &[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
56    &[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
57    &[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
58    &[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
59    &[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
60    &[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
61    &[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
62    &[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
63    &[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
64    &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
65    &[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
66];
67
68macro_rules! impl_blake2 {
69    ($name:ident,$prim:ty,$nprim:ty,$nb:literal,$bb:literal,$w:literal, $r:literal, $iv:ident, $RND:ident) => {
70        #[derive(Clone)]
71        pub struct $name<const NN: usize> {
72            h: [$prim; 8],
73            written: $nprim,
74            buf: ArrayBuf<16, $nb, $prim>,
75        }
76        impl<const NN: usize> Default for $name<NN> {
77            fn default() -> Self {
78                Self::new(&[])
79            }
80        }
81        impl<const NN: usize> $name<NN> {
82            pub fn new(key: &[u8]) -> Self {
83                let mut out = Self {
84                    h: *$iv,
85                    written: 0,
86                    buf: Default::default(),
87                };
88                let nn = (NN as $prim) & 0xFF;
89                let kk = ((key.len() as $prim) & 0xFF) << 8;
90                let p = (0x01010000 as $prim) | nn | kk;
91                out.h[0].bitxor_assign(p);
92                if kk > 0 {
93                    let mut key = FixedU8Buf::<$bb>::from_slice(key);
94                    out.write(key.as_mut_full());
95                }
96
97                out
98            }
99
100            fn chomp(&mut self, last: bool) {
101                if self.buf.is_empty() && self.written > 0 && !last {
102                    return;
103                }
104                let m = self.buf.take_le_buf();
105
106                let counter = self.written; // + ($nb - (self.written & Self::MASK));
107                let mut v: [$prim; 16] = [0; 16];
108                (v[0..8]).copy_from_slice(&self.h);
109                (v[8..]).copy_from_slice($iv);
110                //     self.h[0], self.h[1], self.h[2], self.h[3], self.h[4], self.h[5], self.h[6],
111                //     self.h[7], $iv[0], $iv[1], $iv[2], $iv[3], $iv[4], $iv[5], $iv[6], $iv[7],
112                // ];
113                v[12].bitxor_assign(counter as $prim);
114                v[13].bitxor_assign((counter >> $w) as $prim);
115                if last {
116                    v[14] = v[14].not();
117                }
118                for i in 0..$r {
119                    let s = SIGMA[i % 10];
120                    g!(&mut v[0], &mut v[4], &mut v[8], &mut v[12], m[s[0]], m[s[1]], $RND);
121                    g!(&mut v[1], &mut v[5], &mut v[9], &mut v[13], m[s[2]], m[s[3]], $RND);
122                    g!(&mut v[2], &mut v[6], &mut v[10], &mut v[14], m[s[4]], m[s[5]], $RND);
123                    g!(&mut v[3], &mut v[7], &mut v[11], &mut v[15], m[s[6]], m[s[7]], $RND);
124
125                    g!(&mut v[0], &mut v[5], &mut v[10], &mut v[15], m[s[8]], m[s[9]], $RND);
126                    g!(&mut v[1], &mut v[6], &mut v[11], &mut v[12], m[s[10]], m[s[11]], $RND);
127                    g!(&mut v[2], &mut v[7], &mut v[8], &mut v[13], m[s[12]], m[s[13]], $RND);
128                    g!(&mut v[3], &mut v[4], &mut v[9], &mut v[14], m[s[14]], m[s[15]], $RND);
129                }
130                for i in 0..8 {
131                    self.h[i].bitxor_assign(v[i]);
132                    self.h[i].bitxor_assign(v[i + 8]);
133                }
134            }
135            pub fn write(&mut self, mut v: &[u8]) {
136                let align = self.buf.rem_align();
137                if align < 4 && align < v.len() {
138                    let (a, b) = v.split_at(self.buf.rem_align());
139                    v = b;
140                    for val in a {
141                        if self.buf.is_full() {
142                            self.chomp(false);
143                        }
144                        let _ = self.buf.write_le_u8(*val);
145                        self.written += 1;
146                    }
147                }
148
149                let mut chunks = v.chunks_exact($nb);
150                for c in chunks.by_ref() {
151                    if self.buf.is_full() {
152                        self.chomp(false);
153                    }
154                    let _ = self
155                        .buf
156                        .push_prim(<$prim>::from_le_bytes(c.try_into().unwrap_or_default()));
157                    self.written += $nb;
158                }
159                for val in chunks.remainder() {
160                    if self.buf.is_full() {
161                        self.chomp(false);
162                    }
163                    let _ = self.buf.write_le_u8(*val);
164                    self.written += 1;
165                }
166            }
167            pub fn hash(mut self, v: &[u8]) -> [u8; NN] {
168                self.write(v);
169                self.finish()
170            }
171            pub fn finish(mut self) -> [u8; NN] {
172                self.chomp(true);
173
174                // let out: [u8; NN] = unsafe { self.h.align_to::<u8>().1 }.copy_subset();
175                let mut out: FixedU8Buf<NN> = FixedU8Buf::default();
176                for v in self.h {
177                    let _ = v.write_le_to(&mut out);
178                }
179                // out
180                out.take()
181            }
182        }
183        impl<const NN: usize> HashDigest<$bb, NN> for $name<NN> {
184            fn finish(self) -> [u8; NN] {
185                Self::finish(self)
186            }
187            fn hash(self, v: &[u8]) -> [u8; NN] {
188                Self::hash(self, v)
189            }
190            fn write(&mut self, v: &[u8]) {
191                Self::write(self, v)
192            }
193            fn algorithm() -> HashAlgorithm {
194                todo!()
195            }
196        }
197    };
198}
199
200impl_blake2!(BLAKE2s, u32, u64, 4, 64, 32, 10, BLAKE2S_IV, BLAKE2S_R);
201impl_blake2!(BLAKE2b, u64, u128, 8, 128, 64, 12, BLAKE2B_IV, BLAKE2B_R);
202pub type BLAKE2s128 = BLAKE2s<16>;
203pub type BLAKE2s160 = BLAKE2s<20>;
204pub type BLAKE2s224 = BLAKE2s<28>;
205pub type BLAKE2s256 = BLAKE2s<32>;
206pub type BLAKE2b160 = BLAKE2b<20>;
207pub type BLAKE2b224 = BLAKE2b<28>;
208pub type BLAKE2b256 = BLAKE2b<32>;
209pub type BLAKE2b384 = BLAKE2b<48>;
210pub type BLAKE2b512 = BLAKE2b<64>;
211
212#[cfg(test)]
213mod tests {
214    use crate::hash::{BLAKE2b384, BLAKE2b512, BLAKE2s224, BLAKE2s256};
215    use crate::hex;
216
217    #[test]
218    pub fn test0() {
219        let h = BLAKE2s224::default().hash(b"");
220        let exp = hex!("1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4");
221        assert_eq_hex_slice!(exp, h);
222        let h = BLAKE2s256::default().hash(b"");
223        let exp = hex!("69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9");
224        assert_eq_hex_slice!(exp, h);
225        let h = BLAKE2b384::default().hash(b"");
226        let exp = hex!("b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100");
227        assert_eq_hex_slice!(exp, h);
228        let h = BLAKE2b512::default().hash(b"");
229        let exp = hex!("786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
230        assert_eq_hex_slice!(exp, h);
231    }
232    #[test]
233    pub fn test_qbf() {
234        let h = BLAKE2b512::default().hash(b"The quick brown fox jumps over the lazy dog");
235        let exp = hex!("a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918");
236        assert_eq_hex_slice!(exp, h);
237        let h = BLAKE2b512::default().hash(b"The quick brown fox jumps over the lazy dof");
238        let exp = hex!("ab6b007747d8068c02e25a6008db8a77c218d94f3b40d2291a7dc8a62090a744c082ea27af01521a102e42f480a31e9844053f456b4b41e8aa78bbe5c12957bb");
239        assert_eq_hex_slice!(exp, h);
240    }
241
242    #[test]
243    pub fn test_abc() {
244        let h = BLAKE2s256::default().hash(b"abc");
245        let exp = hex!("508C5E8C327C14E2E1A72BA34EEB452F37458B209ED63A294D999B4C86675982");
246        assert_eq_hex_slice!(exp, h);
247        let h = BLAKE2b512::default().hash(b"abc");
248        let exp = hex!("BA80A53F981C4D0D6A2797B69F12F6E94C212F14685AC4B74B12BB6FDBFFA2D17D87C5392AAB792DC252D5DE4533CC9518D38AA8DBF1925AB92386EDD4009923");
249        assert_eq_hex_slice!(exp, h);
250    }
251}