1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
use std::fmt;
use argon2::Argon2;
use password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
use rand_core::OsRng;
#[derive(Debug, sqlx::Type)]
#[sqlx(transparent)]
pub struct StoredHash(String);
impl StoredHash {
pub fn verify(&self, password: &Password) -> Result<bool, password_hash::Error> {
let hash = PasswordHash::new(&self.0)?;
match Argon2::default().verify_password(password.as_bytes(), &hash) {
// Successful authentication, not an error
Ok(()) => Ok(true),
// Unsuccessful authentication, also not an error
Err(password_hash::errors::Error::Password) => Ok(false),
// Password validation failed for some other reason, treat as an error
Err(err) => Err(err),
}
}
}
#[derive(serde::Deserialize)]
#[serde(transparent)]
pub struct Password(String);
impl Password {
pub fn hash(&self) -> Result<StoredHash, password_hash::Error> {
let Self(password) = self;
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2
.hash_password(password.as_bytes(), &salt)?
.to_string();
Ok(StoredHash(hash))
}
fn as_bytes(&self) -> &[u8] {
let Self(value) = self;
value.as_bytes()
}
}
impl fmt::Debug for Password {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Password").field(&"********").finish()
}
}
#[cfg(test)]
impl From<String> for Password {
fn from(password: String) -> Self {
Self(password)
}
}
|