tuwunel_core/utils/hash/
argon.rs1use std::sync::OnceLock;
2
3use argon2::{
4 Algorithm, Argon2, Params, PasswordHash, PasswordHasher, PasswordVerifier, Version,
5 password_hash,
6 password_hash::{Salt, SaltString},
7};
8
9use crate::{Error, Result, err};
10
11const M_COST: u32 = Params::DEFAULT_M_COST; const T_COST: u32 = Params::DEFAULT_T_COST; const P_COST: u32 = Params::DEFAULT_P_COST; static ARGON: OnceLock<Argon2<'static>> = OnceLock::new();
16
17fn init_argon() -> Argon2<'static> {
18 debug_assert!(M_COST == 19_456, "M_COST default changed");
21 debug_assert!(T_COST == 2, "T_COST default changed");
22 debug_assert!(P_COST == 1, "P_COST default changed");
23
24 let algorithm = Algorithm::Argon2id;
25 let version = Version::default();
26 let out_len: Option<usize> = None;
27 let params = Params::new(M_COST, T_COST, P_COST, out_len).expect("valid parameters");
28 Argon2::new(algorithm, version, params)
29}
30
31pub(super) fn password(password: &str) -> Result<String> {
32 let mut bytes = [0_u8; Salt::RECOMMENDED_LENGTH];
33 rand::fill(&mut bytes);
34
35 let salt = SaltString::encode_b64(&bytes).map_err(map_err)?;
36 ARGON
37 .get_or_init(init_argon)
38 .hash_password(password.as_bytes(), &salt)
39 .map(|it| it.to_string())
40 .map_err(map_err)
41}
42
43pub(super) fn verify_password(password: &str, password_hash: &str) -> Result {
44 let password_hash = PasswordHash::new(password_hash).map_err(map_err)?;
45 ARGON
46 .get_or_init(init_argon)
47 .verify_password(password.as_bytes(), &password_hash)
48 .map_err(map_err)
49}
50
51fn map_err(e: password_hash::Error) -> Error { err!("{e}") }
52
53#[cfg(test)]
54mod tests {
55 #[test]
56 fn password_hash_and_verify() {
57 use crate::utils::hash;
58 let preimage = "temp123";
59 let digest = hash::password(preimage).expect("digest");
60 hash::verify_password(preimage, &digest).expect("verified");
61 }
62
63 #[test]
64 #[should_panic(expected = "unverified")]
65 fn password_hash_and_verify_fail() {
66 use crate::utils::hash;
67 let preimage = "temp123";
68 let fakeimage = "temp321";
69 let digest = hash::password(preimage).expect("digest");
70 hash::verify_password(fakeimage, &digest).expect("unverified");
71 }
72}