shared/utils/crypto/
password_hasher.rs1use std::str::FromStr;
2
3use argon2::{
4 Argon2, Params, PasswordHash, PasswordHasher, PasswordVerifier,
5 password_hash::{SaltString, rand_core::OsRng},
6};
7
8use crate::error::{CoreError, InternalError, Result};
9
10pub trait Hashable: Send + Sync {
11 fn hash(&self, value: &str) -> Result<String>;
12 fn verify(&self, a: &str, b: &str) -> Result<bool>;
13}
14
15#[derive(Clone)]
19pub struct Argon2Password {
20 memory_kib: u32,
21 iterations: u32,
22 parallelism: u32,
23}
24impl Default for Argon2Password {
25 fn default() -> Self {
26 pub const DEFAULT_M_COST: u32 = 19 * 1024; pub const DEFAULT_T_COST: u32 = 2;
28 pub const DEFAULT_P_COST: u32 = 1;
29
30 Self {
31 memory_kib: DEFAULT_M_COST,
32 iterations: DEFAULT_T_COST,
33 parallelism: DEFAULT_P_COST,
34 }
35 }
36}
37impl Argon2Password {
38 pub fn new(m_cost: u32, t_cost: u32, p_cost: u32) -> Self {
39 Self {
40 memory_kib: m_cost,
41 iterations: t_cost,
42 parallelism: p_cost,
43 }
44 }
45}
46
47impl Hashable for Argon2Password {
48 fn hash(&self, password: &str) -> Result<String> {
49 let params = Params::new(
50 self.memory_kib, self.iterations, self.parallelism, None, )
55 .unwrap();
56
57 let salt = SaltString::generate(&mut OsRng);
58
59 let hash = Argon2::new(
60 argon2::Algorithm::default(),
61 argon2::Version::default(),
62 params,
63 )
64 .hash_password(password.as_bytes(), &salt)
65 .map_err(|e| {
66 tracing::error!("Failed to hash user password: {:?}", e);
67 CoreError::Internal(InternalError::Hashing)
68 })?;
69
70 Ok(hash.to_string())
71 }
72
73 fn verify(&self, password: &str, hash: &str) -> Result<bool> {
74 static DUMMY_HASH: &str = "$argon2id$v=19$m=65536,t=3,p=4$\
75 Lm1Jk9XQ2E1o8XxZMZ1jPQ$\
76 8vBxrT9uC1NQb3lQfa2RyEBJxK2Sr6ELrRvsGqIzJxA";
77
78 let parsed = PasswordHash::new(hash)
79 .or_else(|_| PasswordHash::new(DUMMY_HASH))
80 .map_err(|_| CoreError::Internal(InternalError::Hashing))?;
81
82 let isvalid = Argon2::default()
83 .verify_password(password.as_bytes(), &parsed)
84 .is_ok();
85
86 Ok(isvalid)
87 }
88}
89
90#[derive(Clone)]
94pub struct BcryptPassword {
95 cost: u32,
96}
97impl BcryptPassword {
98 pub fn new(cost: u32) -> Self {
99 Self { cost }
100 }
101}
102
103impl Hashable for BcryptPassword {
104 fn hash(&self, value: &str) -> Result<String> {
105 let hash = bcrypt::hash(value, self.cost).map_err(|e| {
106 tracing::error!("Failed to hash user password: {:?}", e);
107 CoreError::Internal(InternalError::Hashing)
108 })?;
109
110 Ok(hash.to_string())
111 }
112
113 fn verify(&self, password: &str, hash: &str) -> Result<bool> {
114 static DUMMY_HASH: &str = "$2b$12$kgQ2Rl.I22hVIJVklF9OceDIv8EBoMXtlw6U15pVLlmleTLfRUMRe";
115
116 let hash_to_verify = if bcrypt::HashParts::from_str(hash).is_ok() {
117 hash.to_string()
118 } else {
119 DUMMY_HASH.to_string()
120 };
121
122 let is_valid = bcrypt::verify(password, &hash_to_verify)
123 .map_err(|_| CoreError::Internal(InternalError::Hashing))?;
124
125 Ok(is_valid)
126 }
127}