firefish/packages/backend-rs/src/misc/password.rs
2024-04-14 14:41:01 +09:00

70 lines
2.4 KiB
Rust

use argon2::{
password_hash,
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
#[crate::export]
pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Error> {
let salt = SaltString::generate(&mut OsRng);
Ok(Argon2::default()
.hash_password(password.as_bytes(), &salt)?
.to_string())
}
#[derive(thiserror::Error, Debug)]
pub enum VerifyError {
#[error("An error occured while bcrypt verification: {0}")]
BcryptError(#[from] bcrypt::BcryptError),
#[error("Invalid argon2 password hash: {0}")]
InvalidArgon2Hash(#[from] password_hash::Error),
#[error("An error occured while argon2 verification: {0}")]
Argon2Error(#[from] argon2::Error),
}
#[crate::export]
pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError> {
if is_old_password_algorithm(hash) {
Ok(bcrypt::verify(password, hash)?)
} else {
let parsed_hash = PasswordHash::new(hash)?;
Ok(Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok())
}
}
#[inline]
#[crate::export]
pub fn is_old_password_algorithm(hash: &str) -> bool {
// bcrypt hashes start with $2[ab]$
hash.starts_with("$2")
}
#[cfg(test)]
mod unit_test {
use super::{hash_password, is_old_password_algorithm, verify_password};
#[test]
fn verify_password_test() {
let password = "omWc*%sD^fn7o2cXmc9e2QasBdrbRuhNB*gx!J5";
let hash = hash_password(password).unwrap();
assert!(verify_password(password, hash.as_str()).unwrap());
let argon2_hash = "$argon2id$v=19$m=19456,t=2,p=1$jty3puDFd4ENv/lgHn3ROQ$kRHDdEoVv2rruvnF731E74NxnYlvj5FMgePdGIIq3Jk";
let argon2_invalid_hash = "$argon2id$v=19$m=19456,t=2,p=1$jty3puDFd4ENv/lgHn3ROQ$kRHDdEoVv2rruvnF731E74NxnYlvj4FMgePdGIIq3Jk";
let bcrypt_hash = "$2a$12$WzUc.20jgbHmQjUMqTr8vOhKqYbS1BUvubapv/GLjCK1IN.h4e4la";
let bcrypt_invalid_hash = "$2a$12$WzUc.20jgbHmQjUMqTr7vOhKqYbS1BUvubapv/GLjCK1IN.h4e4la";
assert!(!is_old_password_algorithm(argon2_hash));
assert!(is_old_password_algorithm(bcrypt_hash));
assert!(verify_password(password, argon2_hash).unwrap());
assert!(verify_password(password, bcrypt_hash).unwrap());
assert!(!verify_password(password, argon2_invalid_hash).unwrap());
assert!(!verify_password(password, bcrypt_invalid_hash).unwrap());
}
}