1use super::constants::{BLOCK_BYTES, IV, MAX_OUTPUT_BYTES, R1, R2, R3, R4, ROUNDS, SIGMA};
8
9#[inline(always)]
11const fn rotr64(x: u64, n: u32) -> u64 {
12 x.rotate_right(n)
13}
14
15#[inline(always)]
19pub const fn g(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
20 v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
21 v[d] = rotr64(v[d] ^ v[a], R1);
22 v[c] = v[c].wrapping_add(v[d]);
23 v[b] = rotr64(v[b] ^ v[c], R2);
24 v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
25 v[d] = rotr64(v[d] ^ v[a], R3);
26 v[c] = v[c].wrapping_add(v[d]);
27 v[b] = rotr64(v[b] ^ v[c], R4);
28}
29
30pub fn bytes_to_words(bytes: &[u8]) -> [u64; 16] {
32 let mut words = [0u64; 16];
33 for (i, chunk) in bytes.chunks_exact(8).enumerate() {
34 words[i] = u64::from_le_bytes(chunk.try_into().unwrap());
35 }
36 words
37}
38
39pub fn compress(h: &mut [u64; 8], block: &[u8; 128], t: u128, last: bool) {
43 let mut v = [0u64; 16];
45
46 v[0..8].copy_from_slice(h);
48
49 v[8..16].copy_from_slice(&IV);
51
52 v[12] ^= t as u64; v[13] ^= (t >> 64) as u64; if last {
58 v[14] = !v[14];
59 }
60
61 let m = bytes_to_words(block);
63
64 for round in 0..ROUNDS {
66 let s = &SIGMA[round];
67
68 g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
70 g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
71 g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
72 g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
73
74 g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
76 g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
77 g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
78 g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
79 }
80
81 for i in 0..8 {
83 h[i] ^= v[i] ^ v[i + 8];
84 }
85}
86
87pub fn blake2b(data: &[u8], outlen: usize) -> Vec<u8> {
91 assert!(outlen > 0 && outlen <= MAX_OUTPUT_BYTES, "Output length must be 1-64 bytes");
92
93 let mut h = IV;
95
96 h[0] ^= 0x01010000 | (outlen as u64);
99
100 let mut t = 0u128; let mut offset = 0;
103
104 while offset + BLOCK_BYTES < data.len() {
106 let mut block = [0u8; BLOCK_BYTES];
107 block.copy_from_slice(&data[offset..offset + BLOCK_BYTES]);
108
109 t += BLOCK_BYTES as u128;
110 compress(&mut h, &block, t, false);
111
112 offset += BLOCK_BYTES;
113 }
114
115 let mut final_block = [0u8; BLOCK_BYTES];
117 let remaining = data.len() - offset;
118 if remaining > 0 {
119 final_block[..remaining].copy_from_slice(&data[offset..]);
120 }
121
122 t += remaining as u128;
123 compress(&mut h, &final_block, t, true); let mut output = Vec::with_capacity(outlen);
127 for word in h.iter() {
128 let bytes = word.to_le_bytes();
129 for byte in bytes {
130 if output.len() < outlen {
131 output.push(byte);
132 }
133 }
134 }
135 output.truncate(outlen);
136 output
137}
138
139pub fn blake2b_256(data: &[u8]) -> [u8; 32] {
143 let hash = blake2b(data, 32);
144 let mut result = [0u8; 32];
145 result.copy_from_slice(&hash);
146 result
147}
148
149#[cfg(test)]
150mod tests {
151 use blake2::{
152 Blake2b, Blake2b256, Digest,
153 digest::{
154 array::ArraySize,
155 consts::U64,
156 typenum::{IsLessOrEqual, True},
157 },
158 };
159
160 use super::*;
161
162 fn check_output_length<N>(msg: &[u8])
167 where
168 N: ArraySize + IsLessOrEqual<U64, Output = True>,
169 {
170 let expected = Blake2b::<N>::digest(msg);
171 assert_eq!(blake2b(msg, N::USIZE), expected.as_slice(), "output length {}", N::USIZE);
172 }
173
174 macro_rules! check_output_lengths {
176 ($msg:expr, $($len:ident),+ $(,)?) => {
177 $(check_output_length::<blake2::digest::consts::$len>($msg);)+
178 };
179 }
180
181 #[test]
183 fn test_variable_output_lengths() {
184 let msg = b"test message for variable output lengths";
185
186 check_output_lengths!(
189 msg, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18,
190 U19, U20, U21, U22, U23, U24, U25, U26, U27, U28, U29, U30, U31, U32, U33, U34, U35,
191 U36, U37, U38, U39, U40, U41, U42, U43, U44, U45, U46, U47, U48, U49, U50, U51, U52,
192 U53, U54, U55, U56, U57, U58, U59, U60, U61, U62, U63, U64
193 );
194 }
195
196 #[test]
198 fn test_incremental_hashing() {
199 let data = vec![0x55u8; 300];
200
201 let expected = blake2b_256(&data);
203
204 let mut hasher = Blake2b256::new();
206 hasher.update(&data[0..100]);
207 hasher.update(&data[100..200]);
208 hasher.update(&data[200..300]);
209 let incremental = hasher.finalize();
210
211 assert_eq!(&expected[..], &incremental[..], "Incremental hashing verification failed");
213 }
214}