askar_crypto/kdf/
argon2.rs

1//! Argon2 key derivation from a password
2
3pub use argon2::{Algorithm, Version};
4
5use super::KeyDerivation;
6use crate::{
7    error::Error,
8    generic_array::typenum::{Unsigned, U16},
9};
10
11/// The size of the password salt
12pub type SaltSize = U16;
13
14/// The length of the password salt
15pub const SALT_LENGTH: usize = SaltSize::USIZE;
16
17/// Standard parameters for 'interactive' level
18pub const PARAMS_INTERACTIVE: Params = Params {
19    alg: Algorithm::Argon2i,
20    version: Version::V0x13,
21    mem_cost: 32768,
22    time_cost: 4,
23};
24/// Standard parameters for 'moderate' level
25pub const PARAMS_MODERATE: Params = Params {
26    alg: Algorithm::Argon2i,
27    version: Version::V0x13,
28    mem_cost: 131072,
29    time_cost: 6,
30};
31
32/// Parameters to the argon2 key derivation
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub struct Params {
35    alg: Algorithm,
36    version: Version,
37    mem_cost: u32,
38    time_cost: u32,
39}
40
41/// Struct wrapping the KDF functionality
42#[derive(Debug)]
43pub struct Argon2<'a> {
44    password: &'a [u8],
45    salt: &'a [u8],
46    params: Params,
47}
48
49impl<'a> Argon2<'a> {
50    /// Create a new Argon2 key derivation instance
51    pub fn new(password: &'a [u8], salt: &'a [u8], params: Params) -> Result<Self, Error> {
52        if salt.len() < SALT_LENGTH {
53            return Err(err_msg!(Usage, "Invalid salt for argon2i hash"));
54        }
55        Ok(Self {
56            password,
57            salt,
58            params,
59        })
60    }
61}
62
63impl KeyDerivation for Argon2<'_> {
64    fn derive_key_bytes(&mut self, key_output: &mut [u8]) -> Result<(), Error> {
65        if key_output.len() > u32::MAX as usize {
66            return Err(err_msg!(
67                Usage,
68                "Output length exceeds max for argon2i hash"
69            ));
70        }
71        let mut pbuild = argon2::ParamsBuilder::new();
72        pbuild
73            .m_cost(self.params.mem_cost)
74            .t_cost(self.params.time_cost);
75        argon2::Argon2::new(
76            self.params.alg,
77            self.params.version,
78            pbuild.build().unwrap(),
79        )
80        .hash_password_into(self.password, self.salt, key_output)
81        .map_err(|_| err_msg!(Unexpected, "Error deriving key"))
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn expected() {
91        let pass = b"my password";
92        let salt = b"long enough salt";
93        let mut output = [0u8; 32];
94        Argon2::new(pass, salt, PARAMS_INTERACTIVE)
95            .unwrap()
96            .derive_key_bytes(&mut output)
97            .unwrap();
98        assert_eq!(
99            output,
100            hex!("9ef87bcf828c46c0136a0d1d9e391d713f75b327c6dc190455bd36c1bae33259")
101        );
102    }
103}