Skip to main content

reddb_server/storage/encryption/
blake2b.rs

1//! BLAKE2b Hash Function (RFC 7693)
2//!
3//! BLAKE2b is a cryptographic hash function faster than MD5, SHA-1, SHA-2, and SHA-3,
4//! yet is at least as secure as the latest standard SHA-3.
5//!
6//! This implementation supports:
7//! - Variable output length (1 to 64 bytes)
8//! - Keyed hashing (MAC) up to 64 bytes key
9//!
10//! # References
11//! - [RFC 7693](https://tools.ietf.org/html/rfc7693)
12
13#![allow(clippy::needless_range_loop)]
14
15use std::convert::TryInto;
16
17/// BLAKE2b context state
18#[derive(Clone)]
19pub struct Blake2b {
20    h: [u64; 8],
21    t: [u64; 2],
22    f: [u64; 2],
23    buf: [u8; 128],
24    buf_len: usize,
25    out_len: usize,
26}
27
28const IV: [u64; 8] = [
29    0x6a09e667f3bcc908,
30    0xbb67ae8584caa73b,
31    0x3c6ef372fe94f82b,
32    0xa54ff53a5f1d36f1,
33    0x510e527fade682d1,
34    0x9b05688c2b3e6c1f,
35    0x1f83d9abfb41bd6b,
36    0x5be0cd19137e2179,
37];
38
39const SIGMA: [[usize; 16]; 12] = [
40    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
41    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
42    [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
43    [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
44    [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
45    [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
46    [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
47    [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
48    [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
49    [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
50    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // Duplicate of round 0 for 12 rounds
51    [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // Duplicate of round 1
52];
53
54impl Blake2b {
55    /// Create a new BLAKE2b context with specified output length (1-64 bytes)
56    pub fn new(out_len: usize) -> Self {
57        Self::new_keyed(out_len, &[])
58    }
59
60    /// Create a new keyed BLAKE2b context
61    pub fn new_keyed(out_len: usize, key: &[u8]) -> Self {
62        assert!(out_len > 0 && out_len <= 64);
63        assert!(key.len() <= 64);
64
65        let mut h = IV;
66        h[0] ^= 0x01010000 ^ ((key.len() as u64) << 8) ^ (out_len as u64);
67
68        let mut state = Self {
69            h,
70            t: [0, 0],
71            f: [0, 0],
72            buf: [0; 128],
73            buf_len: 0,
74            out_len,
75        };
76
77        if !key.is_empty() {
78            state.update(key);
79            state.buf_len = 128; // Pad with zeros to block size
80        }
81
82        state
83    }
84
85    /// Update hash state with input data
86    pub fn update(&mut self, data: &[u8]) {
87        let mut offset = 0;
88        let mut len = data.len();
89
90        while len > 0 {
91            if self.buf_len == 128 {
92                self.t[0] = self.t[0].wrapping_add(128);
93                if self.t[0] < 128 {
94                    self.t[1] = self.t[1].wrapping_add(1);
95                }
96                self.compress();
97                self.buf_len = 0;
98            }
99
100            let space = 128 - self.buf_len;
101            let chunk = if len < space { len } else { space };
102
103            self.buf[self.buf_len..self.buf_len + chunk]
104                .copy_from_slice(&data[offset..offset + chunk]);
105
106            self.buf_len += chunk;
107            offset += chunk;
108            len -= chunk;
109        }
110    }
111
112    /// Finalize hash and return output
113    pub fn finalize(mut self) -> Vec<u8> {
114        self.t[0] = self.t[0].wrapping_add(self.buf_len as u64);
115        if self.t[0] < self.buf_len as u64 {
116            self.t[1] = self.t[1].wrapping_add(1);
117        }
118
119        self.f[0] = !0; // Final block flag
120
121        // Pad with zeros
122        for i in self.buf_len..128 {
123            self.buf[i] = 0;
124        }
125
126        self.compress();
127
128        let mut out = Vec::with_capacity(self.out_len);
129        for i in 0..8 {
130            let bytes = self.h[i].to_le_bytes();
131            out.extend_from_slice(&bytes);
132        }
133
134        out.truncate(self.out_len);
135        out
136    }
137
138    fn compress(&mut self) {
139        let mut v = [0u64; 16];
140        let mut m = [0u64; 16];
141
142        // Load message into u64 words
143        for i in 0..16 {
144            m[i] = u64::from_le_bytes(self.buf[i * 8..(i + 1) * 8].try_into().unwrap());
145        }
146
147        // Initialize state vector v
148        v[0..8].copy_from_slice(&self.h);
149        v[8..16].copy_from_slice(&IV);
150
151        v[12] ^= self.t[0];
152        v[13] ^= self.t[1];
153        v[14] ^= self.f[0];
154        v[15] ^= self.f[1];
155
156        // Mixing rounds
157        for i in 0..12 {
158            // Column step
159            mix(&mut v, 0, 4, 8, 12, m[SIGMA[i][0]], m[SIGMA[i][1]]);
160            mix(&mut v, 1, 5, 9, 13, m[SIGMA[i][2]], m[SIGMA[i][3]]);
161            mix(&mut v, 2, 6, 10, 14, m[SIGMA[i][4]], m[SIGMA[i][5]]);
162            mix(&mut v, 3, 7, 11, 15, m[SIGMA[i][6]], m[SIGMA[i][7]]);
163
164            // Diagonal step
165            mix(&mut v, 0, 5, 10, 15, m[SIGMA[i][8]], m[SIGMA[i][9]]);
166            mix(&mut v, 1, 6, 11, 12, m[SIGMA[i][10]], m[SIGMA[i][11]]);
167            mix(&mut v, 2, 7, 8, 13, m[SIGMA[i][12]], m[SIGMA[i][13]]);
168            mix(&mut v, 3, 4, 9, 14, m[SIGMA[i][14]], m[SIGMA[i][15]]);
169        }
170
171        // Finalize
172        for i in 0..8 {
173            self.h[i] ^= v[i] ^ v[i + 8];
174        }
175    }
176}
177
178#[inline(always)]
179fn mix(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
180    v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
181    v[d] = (v[d] ^ v[a]).rotate_right(32);
182    v[c] = v[c].wrapping_add(v[d]);
183    v[b] = (v[b] ^ v[c]).rotate_right(24);
184
185    v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
186    v[d] = (v[d] ^ v[a]).rotate_right(16);
187    v[c] = v[c].wrapping_add(v[d]);
188    v[b] = (v[b] ^ v[c]).rotate_right(63);
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194
195    #[test]
196    fn test_blake2b_empty() {
197        let mut hasher = Blake2b::new(64);
198        hasher.update(b"");
199        let hash = hasher.finalize();
200
201        let expected = hex::decode("786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce").unwrap();
202        assert_eq!(hash, expected);
203    }
204
205    #[test]
206    fn test_blake2b_hello() {
207        let mut hasher = Blake2b::new(64);
208        hasher.update(b"Hello, world!");
209        let hash = hasher.finalize();
210
211        // Verified with Python hashlib.blake2b(b"Hello, world!").digest()
212        let expected = hex::decode("a2764d133a16816b5847a737a786f2ece4c148095c5faa73e24b4cc5d666c3e45ec271504e14dc6127ddfce4e144fb23b91a6f7b04b53d695502290722953b0f").unwrap();
213        assert_eq!(hash, expected);
214    }
215
216    #[test]
217    fn test_blake2b_keyed() {
218        let key = b"secret key";
219        let mut hasher = Blake2b::new_keyed(64, key);
220        hasher.update(b"Hello, world!");
221        let hash = hasher.finalize();
222
223        // Verify with another implementation or just stability
224        // For now just ensure it's different from unkeyed
225        let mut unkeyed = Blake2b::new(64);
226        unkeyed.update(b"Hello, world!");
227        assert_ne!(hash, unkeyed.finalize());
228    }
229}