argon2/
blake2b_long.rs

1//! The variable length hash function used in the Argon2 algorithm.
2
3use 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    // Use blake2b directly if the output is small enough.
22    if out.len() <= Blake2b512::output_size() {
23        let mut digest = Blake2bVar::new(out.len()).map_err(|_| Error::OutputTooLong)?;
24
25        // Conflicting method name from `Digest` and `Update` traits
26        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    // 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    out[..half_hash_len].copy_from_slice(&last_output[..half_hash_len]);
51
52    // Next, we write a number of 32 byte blocks to the output.
53    // Each block is the first 32 bytes of the hash of the last block.
54    // The very last block of the output is excluded, and has a variable
55    // length in range [1, 32].
56    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    // Calculate the last block with VarBlake2b.
70    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}