use crate::errors::{CoreError, CoreResult};
use argon2::password_hash::{
PasswordHash, PasswordHasher, PasswordVerifier, SaltString,
};
use argon2::{Algorithm, Argon2, Params, Version};
fn argon2() -> Argon2<'static> {
let params = Params::new(64 * 1024, 2, 1, None).unwrap_or_else(|_| Params::default());
Argon2::new(Algorithm::Argon2id, Version::V0x13, params)
}
pub fn hash_password(password: &str) -> CoreResult<String> {
let mut salt_bytes = [0u8; 16];
getrandom::fill(&mut salt_bytes).expect("system RNG unavailable");
let salt = SaltString::encode_b64(&salt_bytes).map_err(|_| CoreError::Password)?;
let phc = argon2()
.hash_password(password.as_bytes(), &salt)
.map_err(|_| CoreError::Password)?;
Ok(phc.to_string())
}
pub fn verify_password(password: &str, stored_phc: &str) -> CoreResult<()> {
let parsed = PasswordHash::new(stored_phc).map_err(|_| CoreError::Password)?;
argon2()
.verify_password(password.as_bytes(), &parsed)
.map_err(|_| CoreError::InvalidCredentials)
}
pub fn check_password_policy(password: &str) -> CoreResult<()> {
if password.chars().count() < 12 {
return Err(CoreError::BadRequest(
"password must be at least 12 characters long".into(),
));
}
if password.chars().count() > 256 {
return Err(CoreError::BadRequest(
"password is unreasonably long (max 256)".into(),
));
}
Ok(())
}
#[cfg(test)]
mod tests;