recipes/backend/src/hash.rs
2025-03-03 00:18:56 +01:00

64 lines
1.7 KiB
Rust

use std::string::String;
use argon2::{
Argon2,
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
};
fn get_argon2<'k>() -> Argon2<'k> {
Argon2::new(
argon2::Algorithm::Argon2id,
argon2::Version::V0x13,
argon2::Params::new(
4_096, // 4 MB. The code run on raspberry pi zero, the default memory is too high.
4, // Number of iteration.
2, // Degree of parallelism.
None,
)
.unwrap(),
)
}
pub fn hash(password: &str) -> Result<String, Box<dyn std::error::Error>> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = get_argon2();
argon2
.hash_password(password.as_bytes(), &salt)
.map(|h| h.to_string())
.map_err(|e| e.into())
}
pub fn verify_password(
password: &str,
hashed_password: &str,
) -> Result<bool, Box<dyn std::error::Error>> {
let argon2 = get_argon2();
let parsed_hash = PasswordHash::new(hashed_password)?;
Ok(argon2
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn simple_case() -> Result<(), Box<dyn std::error::Error>> {
let password = "12345";
let hash = hash(password)?;
println!("hash: {}", &hash);
assert!(verify_password(password, &hash)?);
assert!(!verify_password("54321", &hash)?);
Ok(())
}
#[test]
fn password_with_special_characters() -> Result<(), Box<dyn std::error::Error>> {
let password = "éà ä_\\😺🎮🇨🇭";
let hash = hash(password)?;
println!("hash: {}", &hash);
assert!(verify_password(password, &hash)?);
Ok(())
}
}