use crate::types::{EncFileError, KdfParams};
use argon2::{Algorithm, Argon2, Params, Version};
use secrecy::{ExposeSecret, SecretString};
use zeroize::Zeroizing;
const MIN_MEMORY_COST_KIB: u32 = 65536;
const MIN_TIME_COST: u32 = 3;
const MAX_PARALLELISM: u32 = 16_777_215;
const MIN_SALT_LENGTH: usize = 8;
pub fn derive_key_argon2id(
password: &SecretString,
params: KdfParams,
salt: &[u8],
) -> Result<[u8; 32], EncFileError> {
if salt.len() < MIN_SALT_LENGTH {
return Err(EncFileError::Invalid("kdf: salt must be at least 8 bytes"));
}
if params.mem_kib < MIN_MEMORY_COST_KIB {
return Err(EncFileError::Invalid(
"kdf: memory cost must be at least 64 MiB",
));
}
if params.t_cost < MIN_TIME_COST {
return Err(EncFileError::Invalid("kdf: time cost must be at least 3"));
}
if params.parallelism == 0 || params.parallelism > MAX_PARALLELISM {
return Err(EncFileError::Invalid(
"kdf: parallelism must be between 1 and 16777215",
));
}
let argon_params = Params::new(params.mem_kib, params.t_cost, params.parallelism, None)
.map_err(|_| EncFileError::Invalid("kdf: invalid Argon2 params"))?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon_params);
let mut out = Zeroizing::new([0u8; 32]);
argon2
.hash_password_into(password.expose_secret().as_bytes(), salt, out.as_mut())
.map_err(|_| EncFileError::Crypto)?;
Ok(*out)
}