ctoolbox/utilities/
password.rs1use 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 #[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 #[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}