66 lines
1.9 KiB
Rust
66 lines
1.9 KiB
Rust
use std::string::String;
|
|
|
|
use argon2::{
|
|
Argon2,
|
|
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
|
|
};
|
|
|
|
fn get_argon2<'k>() -> Argon2<'k> {
|
|
// Note: It's not neccessary to have only one Argon2 object, creating a new one
|
|
// when we need it is lightweight.
|
|
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(())
|
|
}
|
|
}
|