1use argon2::{
2 password_hash::{
3 rand_core::OsRng,
4 PasswordHash, PasswordHasher, PasswordVerifier, SaltString
5 },
6 Argon2
7};
8use thiserror::Error;
9
10#[derive(Error, Debug)]
11pub enum HasherError {
12 #[error("Hashing failed: {0}")]
13 HashError(String),
14 #[error("Verification failed: {0}")]
15 VerifyError(String),
16}
17
18pub fn hash_password(password: &str) -> Result<String, HasherError> {
20 let salt = SaltString::generate(&mut OsRng);
21 let argon2 = Argon2::default();
22
23 let password_hash = argon2.hash_password(password.as_bytes(), &salt)
24 .map_err(|e| HasherError::HashError(e.to_string()))?;
25
26 Ok(password_hash.to_string())
27}
28
29pub fn verify_password(password: &str, hash: &str) -> Result<bool, HasherError> {
31 let parsed_hash = PasswordHash::new(hash)
32 .map_err(|e| HasherError::VerifyError(e.to_string()))?;
33
34 let result = Argon2::default().verify_password(password.as_bytes(), &parsed_hash);
35
36 match result {
37 Ok(_) => Ok(true),
38 Err(argon2::password_hash::Error::Password) => Ok(false),
39 Err(e) => Err(HasherError::VerifyError(e.to_string())),
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn test_hash_and_verify() {
49 let password = "my_secure_password";
50 let hash = hash_password(password).expect("Hashing should succeed");
51
52 assert!(verify_password(password, &hash).expect("Verification should succeed"));
53 }
54
55 #[test]
56 fn test_verify_fail() {
57 let password = "password123";
58 let hash = hash_password(password).expect("Hashing should succeed");
59
60 assert!(!verify_password("wrong_password", &hash).expect("Verification should succeed"));
61 }
62}