1use ic_core::{ensure, Result, Zeroize};
16
17pub const BLOCK_LEN: usize = 128;
19
20pub const MAX_OUTPUT_LEN: usize = 64;
22
23pub const MAX_KEY_LEN: usize = 64;
25
26const IV: [u64; 8] = [
28 0x6a09e667f3bcc908,
29 0xbb67ae8584caa73b,
30 0x3c6ef372fe94f82b,
31 0xa54ff53a5f1d36f1,
32 0x510e527fade682d1,
33 0x9b05688c2b3e6c1f,
34 0x1f83d9abfb41bd6b,
35 0x5be0cd19137e2179,
36];
37
38const SIGMA: [[usize; 16]; 10] = [
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];
51
52#[inline(always)]
54#[allow(clippy::too_many_arguments)]
55fn g(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
56 v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
57 v[d] = (v[d] ^ v[a]).rotate_right(32);
58 v[c] = v[c].wrapping_add(v[d]);
59 v[b] = (v[b] ^ v[c]).rotate_right(24);
60 v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
61 v[d] = (v[d] ^ v[a]).rotate_right(16);
62 v[c] = v[c].wrapping_add(v[d]);
63 v[b] = (v[b] ^ v[c]).rotate_right(63);
64}
65
66#[derive(Clone)]
68pub struct Blake2b {
69 h: [u64; 8],
70 buf: [u8; BLOCK_LEN],
71 buffered: usize,
72 counter: u128,
73 output_len: usize,
74}
75
76impl Drop for Blake2b {
77 fn drop(&mut self) {
78 self.h.zeroize();
79 self.buf.zeroize();
80 }
81}
82
83impl Blake2b {
84 pub fn new(output_len: usize) -> Result<Self> {
86 Self::with_key(output_len, &[])
87 }
88
89 pub fn with_key(output_len: usize, key: &[u8]) -> Result<Self> {
94 ensure!(
95 (1..=MAX_OUTPUT_LEN).contains(&output_len),
96 InvalidLength,
97 "blake2b output must be 1..=64 bytes"
98 );
99 ensure!(
100 key.len() <= MAX_KEY_LEN,
101 InvalidLength,
102 "blake2b key must be at most 64 bytes"
103 );
104
105 let mut h = IV;
106 h[0] ^= 0x0101_0000 ^ ((key.len() as u64) << 8) ^ (output_len as u64);
108
109 let mut state = Self {
110 h,
111 buf: [0u8; BLOCK_LEN],
112 buffered: 0,
113 counter: 0,
114 output_len,
115 };
116
117 if !key.is_empty() {
118 let mut block = [0u8; BLOCK_LEN];
119 block[..key.len()].copy_from_slice(key);
120 state.update(&block);
121 block.zeroize();
122 }
123 Ok(state)
124 }
125
126 fn compress(&mut self, block: &[u8; BLOCK_LEN], last: bool) {
128 let mut m = [0u64; 16];
129 for (i, word) in m.iter_mut().enumerate() {
130 let mut b = [0u8; 8];
131 b.copy_from_slice(&block[i * 8..i * 8 + 8]);
132 *word = u64::from_le_bytes(b);
133 }
134
135 let mut v = [0u64; 16];
136 v[..8].copy_from_slice(&self.h);
137 v[8..].copy_from_slice(&IV);
138 v[12] ^= self.counter as u64;
139 v[13] ^= (self.counter >> 64) as u64;
140 if last {
141 v[14] = !v[14];
142 }
143
144 for round in 0..12 {
145 let s = &SIGMA[round % 10];
146 g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
147 g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
148 g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
149 g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
150 g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
151 g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
152 g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
153 g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
154 }
155
156 for i in 0..8 {
157 self.h[i] ^= v[i] ^ v[i + 8];
158 }
159 m.zeroize();
160 v.zeroize();
161 }
162
163 pub fn update(&mut self, mut data: &[u8]) {
168 if self.buffered > 0 {
169 let take = core::cmp::min(BLOCK_LEN - self.buffered, data.len());
170 self.buf[self.buffered..self.buffered + take].copy_from_slice(&data[..take]);
171 self.buffered += take;
172 data = &data[take..];
173 if self.buffered < BLOCK_LEN || data.is_empty() {
174 return;
175 }
176 let block = self.buf;
177 self.counter = self.counter.wrapping_add(BLOCK_LEN as u128);
178 self.compress(&block, false);
179 self.buffered = 0;
180 }
181
182 while data.len() > BLOCK_LEN {
183 let mut block = [0u8; BLOCK_LEN];
184 block.copy_from_slice(&data[..BLOCK_LEN]);
185 self.counter = self.counter.wrapping_add(BLOCK_LEN as u128);
186 self.compress(&block, false);
187 data = &data[BLOCK_LEN..];
188 }
189
190 self.buf[..data.len()].copy_from_slice(data);
191 self.buffered = data.len();
192 }
193
194 pub fn finalize_into(mut self, out: &mut [u8]) -> Result<()> {
196 ensure!(
197 out.len() == self.output_len,
198 InvalidLength,
199 "blake2b output buffer"
200 );
201
202 self.counter = self.counter.wrapping_add(self.buffered as u128);
203 let mut block = [0u8; BLOCK_LEN];
204 block[..self.buffered].copy_from_slice(&self.buf[..self.buffered]);
205 self.compress(&block, true);
206 block.zeroize();
207
208 let mut full = [0u8; MAX_OUTPUT_LEN];
209 for (i, word) in self.h.iter().enumerate() {
210 full[i * 8..i * 8 + 8].copy_from_slice(&word.to_le_bytes());
211 }
212 out.copy_from_slice(&full[..self.output_len]);
213 full.zeroize();
214 Ok(())
215 }
216
217 pub fn hash(data: &[u8], out: &mut [u8]) -> Result<()> {
219 let mut h = Self::new(out.len())?;
220 h.update(data);
221 h.finalize_into(out)
222 }
223
224 pub fn keyed_hash(key: &[u8], data: &[u8], out: &mut [u8]) -> Result<()> {
226 let mut h = Self::with_key(out.len(), key)?;
227 h.update(data);
228 h.finalize_into(out)
229 }
230}
231
232pub fn blake2b_long(input: &[&[u8]], out: &mut [u8]) -> Result<()> {
240 ensure!(!out.is_empty(), InvalidLength, "blake2b_long output");
241 let len_prefix = (out.len() as u32).to_le_bytes();
242
243 if out.len() <= MAX_OUTPUT_LEN {
244 let mut h = Blake2b::new(out.len())?;
245 h.update(&len_prefix);
246 for part in input {
247 h.update(part);
248 }
249 return h.finalize_into(out);
250 }
251
252 let r = out.len().div_ceil(32) - 2;
263
264 let mut v = [0u8; MAX_OUTPUT_LEN];
265 let mut h = Blake2b::new(MAX_OUTPUT_LEN)?;
266 h.update(&len_prefix);
267 for part in input {
268 h.update(part);
269 }
270 h.finalize_into(&mut v)?;
271
272 for (i, piece) in out.chunks_exact_mut(32).take(r).enumerate() {
273 piece.copy_from_slice(&v[..32]);
274 if i + 1 < r {
275 let previous = v;
276 Blake2b::hash(&previous, &mut v)?;
277 }
278 }
279
280 let tail_len = out.len() - 32 * r;
282 let mut tail = [0u8; MAX_OUTPUT_LEN];
283 Blake2b::hash(&v, &mut tail[..tail_len])?;
284 out[32 * r..].copy_from_slice(&tail[..tail_len]);
285
286 v.zeroize();
287 tail.zeroize();
288 Ok(())
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294 use ic_core::codec::hex;
295
296 #[test]
298 fn rfc7693_abc_vector() {
299 let mut out = [0u8; 64];
300 Blake2b::hash(b"abc", &mut out).unwrap();
301 assert_eq!(
302 hex(&out),
303 "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1\
304 7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
305 .replace(char::is_whitespace, "")
306 );
307 }
308
309 #[test]
311 fn empty_message_vector() {
312 let mut out = [0u8; 64];
313 Blake2b::hash(b"", &mut out).unwrap();
314 assert_eq!(
315 hex(&out),
316 "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419\
317 d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"
318 .replace(char::is_whitespace, "")
319 );
320 }
321
322 #[test]
323 fn output_length_changes_the_digest() {
324 let mut a = [0u8; 32];
325 let mut b = [0u8; 64];
326 Blake2b::hash(b"same input", &mut a).unwrap();
327 Blake2b::hash(b"same input", &mut b).unwrap();
328 assert_ne!(&b[..32], &a[..]);
331 }
332
333 #[test]
334 fn keying_changes_the_digest() {
335 let mut unkeyed = [0u8; 32];
336 let mut keyed = [0u8; 32];
337 Blake2b::hash(b"message", &mut unkeyed).unwrap();
338 Blake2b::keyed_hash(b"key", b"message", &mut keyed).unwrap();
339 assert_ne!(unkeyed, keyed);
340
341 let mut other = [0u8; 32];
342 Blake2b::keyed_hash(b"kez", b"message", &mut other).unwrap();
343 assert_ne!(keyed, other);
344 }
345
346 #[test]
349 fn streaming_matches_one_shot() {
350 let data: Vec<u8> = (0..400u32).map(|i| (i * 7) as u8).collect();
351 let mut expected = [0u8; 64];
352 Blake2b::hash(&data, &mut expected).unwrap();
353
354 for split in [0usize, 1, 127, 128, 129, 200, 255, 256, 257, 400] {
355 let mut h = Blake2b::new(64).unwrap();
356 h.update(&data[..split]);
357 h.update(&data[split..]);
358 let mut got = [0u8; 64];
359 h.finalize_into(&mut got).unwrap();
360 assert_eq!(got, expected, "split at {split}");
361 }
362 }
363
364 #[test]
367 fn exactly_one_block_is_handled() {
368 let data = [0x61u8; BLOCK_LEN];
369 let mut a = [0u8; 64];
370 Blake2b::hash(&data, &mut a).unwrap();
371
372 let mut h = Blake2b::new(64).unwrap();
373 for byte in data.iter() {
374 h.update(&[*byte]);
375 }
376 let mut b = [0u8; 64];
377 h.finalize_into(&mut b).unwrap();
378 assert_eq!(a, b);
379 }
380
381 #[test]
382 fn rejects_invalid_parameters() {
383 assert!(Blake2b::new(0).is_err());
384 assert!(Blake2b::new(65).is_err());
385 assert!(Blake2b::with_key(32, &[0u8; 65]).is_err());
386 let h = Blake2b::new(32).unwrap();
387 assert!(h.finalize_into(&mut [0u8; 31]).is_err());
388 }
389
390 #[test]
393 fn long_hash_matches_short_path_and_extends() {
394 for len in [1usize, 32, 64] {
395 let mut via_long = vec![0u8; len];
396 blake2b_long(&[b"input"], &mut via_long).unwrap();
397
398 let mut direct = vec![0u8; len];
399 let mut h = Blake2b::new(len).unwrap();
400 h.update(&(len as u32).to_le_bytes());
401 h.update(b"input");
402 h.finalize_into(&mut direct).unwrap();
403
404 assert_eq!(via_long, direct, "len {len}");
405 }
406
407 let mut a = [0u8; 1024];
408 let mut b = [0u8; 1024];
409 blake2b_long(&[b"input"], &mut a).unwrap();
410 blake2b_long(&[b"input"], &mut b).unwrap();
411 assert_eq!(a, b, "must be deterministic");
412 assert_ne!(&a[..64], &a[64..128], "must not repeat");
413
414 let mut short = [0u8; 128];
416 blake2b_long(&[b"input"], &mut short).unwrap();
417 assert_ne!(&a[..128], &short[..]);
418 }
419
420 #[test]
421 fn long_hash_concatenates_its_inputs() {
422 let mut joined = [0u8; 100];
423 let mut split = [0u8; 100];
424 blake2b_long(&[b"abcdef"], &mut joined).unwrap();
425 blake2b_long(&[b"abc", b"def"], &mut split).unwrap();
426 assert_eq!(joined, split);
427 }
428}