1use crate::{Error, Result};
4
5use blake2::{
6 digest::{self, Digest, VariableOutput},
7 Blake2b512, Blake2bVar,
8};
9
10use core::convert::TryFrom;
11
12pub fn blake2b_long(inputs: &[&[u8]], out: &mut [u8]) -> Result<()> {
13 if out.is_empty() {
14 return Err(Error::OutputTooShort);
15 }
16
17 let len_bytes = u32::try_from(out.len())
18 .map(|v| v.to_le_bytes())
19 .map_err(|_| Error::OutputTooLong)?;
20
21 if out.len() <= Blake2b512::output_size() {
23 let mut digest = Blake2bVar::new(out.len()).map_err(|_| Error::OutputTooLong)?;
24
25 digest::Update::update(&mut digest, &len_bytes);
27
28 for input in inputs {
29 digest::Update::update(&mut digest, input);
30 }
31
32 digest
33 .finalize_variable(out)
34 .map_err(|_| Error::OutputTooLong)?;
35
36 return Ok(());
37 }
38
39 let half_hash_len = Blake2b512::output_size() / 2;
41 let mut digest = Blake2b512::new();
42
43 digest.update(len_bytes);
44 for input in inputs {
45 digest.update(input);
46 }
47 let mut last_output = digest.finalize();
48
49 out[..half_hash_len].copy_from_slice(&last_output[..half_hash_len]);
51
52 let mut counter = 0;
57 let out_len = out.len();
58 for chunk in out[half_hash_len..]
59 .chunks_exact_mut(half_hash_len)
60 .take_while(|_| {
61 counter += half_hash_len;
62 out_len - counter > 64
63 })
64 {
65 last_output = Blake2b512::digest(last_output);
66 chunk.copy_from_slice(&last_output[..half_hash_len]);
67 }
68
69 let last_block_size = out.len() - counter;
71 let mut digest = Blake2bVar::new(last_block_size).map_err(|_| Error::OutputTooLong)?;
72
73 digest::Update::update(&mut digest, &last_output);
74 digest
75 .finalize_variable(&mut out[counter..])
76 .expect("invalid Blake2bVar out length");
77
78 Ok(())
79}