use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::{Salt, SaltString};
use crate::{Result, new_err, Key};
pub fn hash_password(password: &str) -> Result<(String, Key)> {
let salt = SaltString::generate(&mut OsRng);
let mut password_key = Key::default();
let argon2 = Argon2::default();
let password_hash = argon2.hash_password(password.as_bytes(), &salt)
.map_err(|err| new_err!(EncryptionError: HashError, err))?.to_string();
argon2.hash_password_into(password.as_bytes(), salt.as_str().as_bytes(), &mut password_key)
.map_err(|err| new_err!(EncryptionError: HashError, err))?;
Ok((password_hash, password_key))
}
pub fn verify_password<'a>(password_hash: &'a str, password: &str) -> Result<Salt<'a>> {
let hash = PasswordHash::new(password_hash)
.map_err(|_| new_err!(InvalidData: InvalidLength, "password hash"))?;
let argon2 = Argon2::default();
argon2.verify_password(password.as_bytes(), &hash)
.map_err(|_| new_err!(ProfileError: AuthenticationFailed))?;
let salt = hash.salt
.ok_or_else(|| new_err!(InvalidData: MissingData, "Salt for the password hash"))?;
Ok(salt)
}
pub fn get_password_key(password_hash: &str, password: &str) -> Result<Key> {
let salt = verify_password(password_hash, password)?;
let mut password_key = Key::default();
let argon2 = Argon2::default();
argon2.hash_password_into(password.as_bytes(), salt.as_str().as_bytes(), &mut password_key)
.map_err(|err| new_err!(EncryptionError: HashError, err))?;
Ok(password_key)
}
#[cfg(test)]
mod tests {
use crate::core::encryption::cipher;
use super::*;
#[test]
fn test_password_hash() -> Result<()> {
let password = "my_password123";
let (hash, _) = hash_password(password)?;
assert!(verify_password(&hash, password).is_ok());
Ok(())
}
#[test]
fn test_password_key() -> Result<()> {
let password = "my_password123";
let text = "Hello, world!";
let nonce = cipher::generate_nonce();
let (hash, key) = hash_password(password)?;
let encrypted_text = cipher::encrypt(&key, &nonce, text.as_bytes())?;
let key = get_password_key(&hash, password)?;
let decrypted_bytes = cipher::decrypt(&key, &nonce, &encrypted_text)?;
let decrypted_text = String::from_utf8_lossy(&decrypted_bytes);
assert_eq!(decrypted_text, text);
Ok(())
}
}