ctoolbox/utilities/
password.rs

1use anyhow::Result;
2use argon2::{
3    Argon2,
4    password_hash::{
5        PasswordHash, PasswordHasher, PasswordVerifier, SaltString,
6        rand_core::OsRng,
7    },
8};
9use zeroize::ZeroizeOnDrop;
10
11#[derive(ZeroizeOnDrop, PartialEq, Clone)]
12pub struct Password {
13    pub password: Vec<u8>,
14}
15
16impl Password {
17    pub fn as_string_not_zeroizing(&self) -> String {
18        self.password.iter().map(|&b| char::from(b)).collect()
19    }
20
21    pub fn from_string(s: &str) -> Self {
22        Password {
23            password: s.as_bytes().to_vec(),
24        }
25    }
26}
27
28pub fn hash(password: &Password) -> Result<String> {
29    // If in debug or test and password is TEST_USER_PASS use hardcoded string
30    #[cfg(any(debug_assertions, test))]
31    {
32        if password.password == crate::storage::user::TEST_USER_PASS.as_bytes()
33        {
34            use crate::storage::user::TEST_USER_PHC;
35
36            return Ok(TEST_USER_PHC.to_string());
37        }
38    }
39    let salt = SaltString::generate(&mut OsRng);
40    let result_or_err =
41        Argon2::default().hash_password(&password.password, &salt);
42    if result_or_err.is_err() {
43        return Err(anyhow::anyhow!(
44            "Failed to hash password: {}",
45            result_or_err.err().unwrap()
46        ));
47    }
48    Ok(result_or_err.expect("Checked").to_string())
49}
50
51pub fn verify(password: &Password, hash: &str) -> Result<bool> {
52    // If in debug or test and password is TEST_USER_PASS accept
53    #[cfg(any(debug_assertions, test))]
54    {
55        if password.password == crate::storage::user::TEST_USER_PASS.as_bytes()
56        {
57            return Ok(true);
58        }
59    }
60    let parsed_hash_or_err = PasswordHash::new(hash);
61    if parsed_hash_or_err.is_err() {
62        return Err(anyhow::anyhow!(
63            "Failed to parse password hash: {}",
64            parsed_hash_or_err.err().unwrap()
65        ));
66    }
67    Ok(Argon2::default()
68        .verify_password(&password.password, &parsed_hash_or_err.unwrap())
69        .is_ok())
70}