Skip to main content

argon2/
blake2b_long.rs

1//! The variable length hash function used in the Argon2 algorithm.
2
3use crate::{Error, Result};
4
5use blake2::{
6    Blake2b512, Blake2bVarCore,
7    digest::{
8        Digest,
9        block_api::{UpdateCore, VariableOutputCore},
10        block_buffer::LazyBuffer,
11    },
12};
13
14pub fn blake2b_long(inputs: &[&[u8]], out: &mut [u8]) -> Result<()> {
15    if out.is_empty() {
16        return Err(Error::OutputTooShort);
17    }
18
19    let len_bytes = u32::try_from(out.len())
20        .map(u32::to_le_bytes)
21        .map_err(|_| Error::OutputTooLong)?;
22
23    // Use blake2b directly if the output is small enough.
24    if let Ok(mut hasher) = Blake2bVarCore::new(out.len()) {
25        let mut buf = LazyBuffer::new(&len_bytes);
26
27        for input in inputs {
28            buf.digest_blocks(input, |blocks| hasher.update_blocks(blocks));
29        }
30
31        let mut full_out = Default::default();
32        hasher.finalize_variable_core(&mut buf, &mut full_out);
33        let out_src = &full_out[..out.len()];
34        out.copy_from_slice(out_src);
35
36        return Ok(());
37    }
38
39    // Calculate longer hashes by first calculating a full 64 byte hash
40    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    // Then we write the first 32 bytes of this hash to the output
50    let (first_chunk, mut out) = out.split_at_mut(half_hash_len);
51    first_chunk.copy_from_slice(&last_output[..half_hash_len]);
52
53    // Next, we write a number of 32 byte blocks to the output.
54    // Each block is the first 32 bytes of the hash of the last block.
55    // The very last block of the output is excluded, and has a variable
56    // length in range [1, 32].
57    while out.len() > 64 {
58        let (chunk, tail) = out.split_at_mut(half_hash_len);
59        out = tail;
60        last_output = Blake2b512::digest(last_output);
61        chunk.copy_from_slice(&last_output[..half_hash_len]);
62    }
63
64    // Calculate the last block with VarBlake2b.
65    let mut hasher = Blake2bVarCore::new(out.len())
66        .expect("`out.len()` is guaranteed to be smaller or equal to 64");
67    let mut buf = LazyBuffer::new(&last_output);
68    let mut full_out = Default::default();
69    hasher.finalize_variable_core(&mut buf, &mut full_out);
70    let out_src = &full_out[..out.len()];
71    out.copy_from_slice(out_src);
72
73    Ok(())
74}