use argon2::{Algorithm, Argon2, Params, Version};
pub const KEK_LEN: usize = 32;
#[derive(Debug, Clone, Copy)]
pub struct Argon2Params {
pub mem_kib: u32,
pub iterations: u32,
pub parallelism: u32,
}
impl Default for Argon2Params {
fn default() -> Self {
Self {
mem_kib: 64 * 1024,
iterations: 3,
parallelism: 1,
}
}
}
pub fn argon2id_derive_key(
secret: &[u8],
salt: &[u8],
params: Argon2Params,
) -> Result<[u8; KEK_LEN], argon2::Error> {
let argon2_params = Params::new(
params.mem_kib,
params.iterations,
params.parallelism,
Some(KEK_LEN),
)?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, argon2_params);
let mut out = [0u8; KEK_LEN];
argon2.hash_password_into(secret, salt, &mut out)?;
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
fn test_params() -> Argon2Params {
Argon2Params {
mem_kib: 8 * 1024,
iterations: 1,
parallelism: 1,
}
}
#[test]
fn same_secret_and_salt_derive_the_same_key() {
let secret = b"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";
let salt = b"0123456789abcdef";
let a = argon2id_derive_key(secret, salt, test_params()).unwrap();
let b = argon2id_derive_key(secret, salt, test_params()).unwrap();
assert_eq!(a, b);
}
#[test]
fn different_salts_derive_different_keys() {
let secret = b"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX";
let a = argon2id_derive_key(secret, b"0123456789abcdef", test_params()).unwrap();
let b = argon2id_derive_key(secret, b"fedcba9876543210", test_params()).unwrap();
assert_ne!(a, b);
}
#[test]
fn different_secrets_derive_different_keys() {
let salt = b"0123456789abcdef";
let a = argon2id_derive_key(b"correct-code", salt, test_params()).unwrap();
let b = argon2id_derive_key(b"wrong-code", salt, test_params()).unwrap();
assert_ne!(a, b);
}
#[test]
fn derives_a_full_length_key() {
let key = argon2id_derive_key(b"secret", b"0123456789abcdef", test_params()).unwrap();
assert_eq!(key.len(), KEK_LEN);
}
}