use argon2::{Algorithm, Argon2, Params, Version};
use zeroize::Zeroizing;
use crate::error::{KeystoreError, Result};
use crate::format::KdfParams;
pub(crate) fn derive_key(
password: &[u8],
salt: &[u8; 16],
params: &KdfParams,
) -> Result<Zeroizing<[u8; 32]>> {
validate_params(params)?;
let argon_params = Params::new(
params.memory_kib,
params.iterations,
params.lanes as u32,
Some(32),
)
.map_err(|_| KeystoreError::InvalidKdfParams("argon2 params invalid"))?;
let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon_params);
let mut out: Zeroizing<[u8; 32]> = Zeroizing::new([0u8; 32]);
argon
.hash_password_into(password, salt, out.as_mut())
.map_err(|_| KeystoreError::InvalidKdfParams("argon2 hash failed"))?;
Ok(out)
}
fn validate_params(params: &KdfParams) -> Result<()> {
if params.memory_kib < 8 * 1024 {
return Err(KeystoreError::InvalidKdfParams("memory_kib < 8192"));
}
if params.iterations < 1 {
return Err(KeystoreError::InvalidKdfParams("iterations < 1"));
}
if params.lanes < 1 {
return Err(KeystoreError::InvalidKdfParams("lanes < 1"));
}
if params.memory_kib > 1024 * 1024 {
return Err(KeystoreError::InvalidKdfParams("memory_kib > 1048576"));
}
if params.iterations > 256 {
return Err(KeystoreError::InvalidKdfParams("iterations > 256"));
}
if params.lanes > 64 {
return Err(KeystoreError::InvalidKdfParams("lanes > 64"));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn test_params() -> KdfParams {
KdfParams {
id: crate::format::KdfId::Argon2id,
memory_kib: 8 * 1024,
iterations: 1,
lanes: 1,
}
}
#[test]
fn deterministic_given_same_inputs() {
let salt = [7u8; 16];
let p = test_params();
let k1 = derive_key(b"pw", &salt, &p).unwrap();
let k2 = derive_key(b"pw", &salt, &p).unwrap();
assert_eq!(*k1, *k2);
}
#[test]
fn different_salt_different_key() {
let p = test_params();
let k1 = derive_key(b"pw", &[1u8; 16], &p).unwrap();
let k2 = derive_key(b"pw", &[2u8; 16], &p).unwrap();
assert_ne!(*k1, *k2);
}
#[test]
fn different_password_different_key() {
let salt = [7u8; 16];
let p = test_params();
let k1 = derive_key(b"pw1", &salt, &p).unwrap();
let k2 = derive_key(b"pw2", &salt, &p).unwrap();
assert_ne!(*k1, *k2);
}
#[test]
fn validates_too_small_memory() {
let p = KdfParams {
id: crate::format::KdfId::Argon2id,
memory_kib: 1024,
iterations: 1,
lanes: 1,
};
assert!(derive_key(b"pw", &[0u8; 16], &p).is_err());
}
#[test]
fn validates_zero_iterations() {
let p = KdfParams {
id: crate::format::KdfId::Argon2id,
memory_kib: 8 * 1024,
iterations: 0,
lanes: 1,
};
assert!(derive_key(b"pw", &[0u8; 16], &p).is_err());
}
#[test]
fn validates_zero_lanes() {
let p = KdfParams {
id: crate::format::KdfId::Argon2id,
memory_kib: 8 * 1024,
iterations: 1,
lanes: 0,
};
assert!(derive_key(b"pw", &[0u8; 16], &p).is_err());
}
}