use argon2::{
password_hash::{
rand_core::OsRng,
PasswordHash, PasswordHasher, PasswordVerifier, SaltString
},
Argon2
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum HasherError {
#[error("Hashing failed: {0}")]
HashError(String),
#[error("Verification failed: {0}")]
VerifyError(String),
}
pub fn hash_password(password: &str) -> Result<String, HasherError> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = argon2.hash_password(password.as_bytes(), &salt)
.map_err(|e| HasherError::HashError(e.to_string()))?;
Ok(password_hash.to_string())
}
pub fn verify_password(password: &str, hash: &str) -> Result<bool, HasherError> {
let parsed_hash = PasswordHash::new(hash)
.map_err(|e| HasherError::VerifyError(e.to_string()))?;
let result = Argon2::default().verify_password(password.as_bytes(), &parsed_hash);
match result {
Ok(_) => Ok(true),
Err(argon2::password_hash::Error::Password) => Ok(false),
Err(e) => Err(HasherError::VerifyError(e.to_string())),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_and_verify() {
let password = "my_secure_password";
let hash = hash_password(password).expect("Hashing should succeed");
assert!(verify_password(password, &hash).expect("Verification should succeed"));
}
#[test]
fn test_verify_fail() {
let password = "password123";
let hash = hash_password(password).expect("Hashing should succeed");
assert!(!verify_password("wrong_password", &hash).expect("Verification should succeed"));
}
}