Skip to main content

sarhash_core/
hasher.rs

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
18/// Hashes a password using Argon2 with a random salt.
19pub 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
29/// Verifies a password against a provided hash.
30pub 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}