2024-06-25 00:03:05 +00:00
|
|
|
use std::sync::OnceLock;
|
2024-06-04 23:51:02 +00:00
|
|
|
|
|
|
|
use argon2::{
|
|
|
|
password_hash, password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher,
|
|
|
|
PasswordVerifier, Version,
|
|
|
|
};
|
|
|
|
|
|
|
|
const M_COST: u32 = Params::DEFAULT_M_COST; // memory size in 1 KiB blocks
|
|
|
|
const T_COST: u32 = Params::DEFAULT_T_COST; // nr of iterations
|
|
|
|
const P_COST: u32 = Params::DEFAULT_P_COST; // parallelism
|
|
|
|
|
2024-06-25 00:03:05 +00:00
|
|
|
static ARGON: OnceLock<Argon2<'static>> = OnceLock::new();
|
2024-06-04 23:51:02 +00:00
|
|
|
|
2024-06-25 00:03:05 +00:00
|
|
|
pub fn password(password: &str) -> Result<String, password_hash::Error> {
|
|
|
|
let salt = SaltString::generate(rand::thread_rng());
|
|
|
|
ARGON
|
|
|
|
.get_or_init(init_argon)
|
|
|
|
.hash_password(password.as_bytes(), &salt)
|
|
|
|
.map(|it| it.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verify_password(password: &str, password_hash: &str) -> Result<(), password_hash::Error> {
|
|
|
|
let password_hash = PasswordHash::new(password_hash)?;
|
|
|
|
ARGON
|
|
|
|
.get_or_init(init_argon)
|
|
|
|
.verify_password(password.as_bytes(), &password_hash)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_argon() -> Argon2<'static> {
|
2024-06-04 23:51:02 +00:00
|
|
|
// 19456 Kib blocks, iterations = 2, parallelism = 1
|
|
|
|
// * <https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id>
|
|
|
|
debug_assert!(M_COST == 19_456, "M_COST default changed");
|
|
|
|
debug_assert!(T_COST == 2, "T_COST default changed");
|
|
|
|
debug_assert!(P_COST == 1, "P_COST default changed");
|
|
|
|
|
|
|
|
let algorithm = Algorithm::Argon2id;
|
|
|
|
let version = Version::default();
|
|
|
|
let out_len: Option<usize> = None;
|
|
|
|
let params = Params::new(M_COST, T_COST, P_COST, out_len).expect("valid parameters");
|
2024-06-25 00:03:05 +00:00
|
|
|
Argon2::new(algorithm, version, params)
|
2024-06-04 23:51:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
#[test]
|
|
|
|
fn password_hash_and_verify() {
|
|
|
|
use crate::utils::hash;
|
|
|
|
let preimage = "temp123";
|
|
|
|
let digest = hash::password(preimage).expect("digest");
|
|
|
|
hash::verify_password(preimage, &digest).expect("verified");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "unverified")]
|
|
|
|
fn password_hash_and_verify_fail() {
|
|
|
|
use crate::utils::hash;
|
|
|
|
let preimage = "temp123";
|
|
|
|
let fakeimage = "temp321";
|
|
|
|
let digest = hash::password(preimage).expect("digest");
|
|
|
|
hash::verify_password(fakeimage, &digest).expect("unverified");
|
|
|
|
}
|
|
|
|
}
|