use argon2::{Algorithm, Argon2, Block, Params, Version};
use zeroize::{Zeroize, Zeroizing};
pub fn key<const N: usize>(
password: &[u8],
salt: &[u8],
time: u32,
memory: u32,
threads: u32,
) -> Zeroizing<[u8; N]> {
let params = Params::new(memory, time, threads, Some(N)).unwrap();
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut output = Zeroizing::new([0u8; N]);
hash(&argon2, password, salt, output.as_mut());
output
}
pub fn key_with_len(
password: &[u8],
salt: &[u8],
time: u32,
memory: u32,
threads: u32,
out: usize,
) -> Zeroizing<Vec<u8>> {
let params = Params::new(memory, time, threads, Some(out)).unwrap();
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut output = Zeroizing::new(vec![0u8; out]);
hash(&argon2, password, salt, &mut output);
output
}
fn hash(argon2: &Argon2, password: &[u8], salt: &[u8], output: &mut [u8]) {
let count = argon2.params().block_count();
let mut blocks = Vec::new();
blocks.try_reserve_exact(count).unwrap();
blocks.resize(count, Block::new());
let result =
argon2.hash_password_into_with_memory(password, salt, output, blocks.as_mut_slice());
for block in blocks.iter_mut() {
block.as_mut().zeroize();
}
result.unwrap();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_argon2id_vectors() {
struct TestCase {
time: u32,
memory: u32,
threads: u32,
hash: &'static str,
}
let tests = [
TestCase {
time: 1,
memory: 64,
threads: 1,
hash: "655ad15eac652dc59f7170a7332bf49b8469be1fdb9c28bb",
},
TestCase {
time: 2,
memory: 64,
threads: 1,
hash: "068d62b26455936aa6ebe60060b0a65870dbfa3ddf8d41f7",
},
TestCase {
time: 2,
memory: 64,
threads: 2,
hash: "350ac37222f436ccb5c0972f1ebd3bf6b958bf2071841362",
},
TestCase {
time: 3,
memory: 256,
threads: 2,
hash: "4668d30ac4187e6878eedeacf0fd83c5a0a30db2cc16ef0b",
},
TestCase {
time: 4,
memory: 4096,
threads: 4,
hash: "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a",
},
TestCase {
time: 4,
memory: 1024,
threads: 8,
hash: "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f",
},
TestCase {
time: 2,
memory: 64,
threads: 3,
hash: "4a15b31aec7c2590b87d1f520be7d96f56658172deaa3079",
},
TestCase {
time: 3,
memory: 1024,
threads: 6,
hash: "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016",
},
];
let password = b"password";
let salt = b"somesalt";
for v in tests {
let want = hex::decode(v.hash).unwrap();
let have = key::<24>(password, salt, v.time, v.memory, v.threads);
assert_eq!(have.as_slice(), want.as_slice());
let have = key_with_len(password, salt, v.time, v.memory, v.threads, 24);
assert_eq!(have.as_slice(), want.as_slice());
}
}
}