authx_core/crypto/
hashing.rs1use argon2::{
2 Argon2, Params, Version,
3 password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
4};
5use sha2::{Digest, Sha256};
6use tracing::instrument;
7
8use crate::error::{AuthError, Result};
9
10fn argon2() -> Result<Argon2<'static>> {
11 let params = Params::new(65536, 3, 4, None).map_err(|e| AuthError::HashError(e.to_string()))?;
12 Ok(Argon2::new(
13 argon2::Algorithm::Argon2id,
14 Version::V0x13,
15 params,
16 ))
17}
18
19#[instrument(skip(password))]
20pub fn hash_password(password: &str) -> Result<String> {
21 let salt = SaltString::generate(&mut OsRng);
22 let hash = argon2()?
23 .hash_password(password.as_bytes(), &salt)
24 .map_err(|e| AuthError::HashError(e.to_string()))?
25 .to_string();
26
27 tracing::debug!("password hashed");
28 Ok(hash)
29}
30
31#[instrument(skip(password, hash))]
32pub fn verify_password(hash: &str, password: &str) -> Result<bool> {
33 let parsed = PasswordHash::new(hash).map_err(|e| AuthError::HashError(e.to_string()))?;
34
35 let ok = argon2()?
36 .verify_password(password.as_bytes(), &parsed)
37 .is_ok();
38
39 tracing::debug!(matched = ok, "password verified");
40 Ok(ok)
41}
42
43pub fn sha256_hex(data: &[u8]) -> String {
45 let mut hasher = Sha256::new();
46 hasher.update(data);
47 hex::encode(hasher.finalize())
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn round_trip_correct_password() {
56 let hash = hash_password("correct-horse-battery-staple").unwrap();
57 assert!(verify_password(&hash, "correct-horse-battery-staple").unwrap());
58 }
59
60 #[test]
61 fn wrong_password_returns_false() {
62 let hash = hash_password("secret").unwrap();
63 assert!(!verify_password(&hash, "wrong").unwrap());
64 }
65
66 #[test]
67 fn sha256_hex_is_deterministic() {
68 let a = sha256_hex(b"hello");
69 let b = sha256_hex(b"hello");
70 assert_eq!(a, b);
71 assert_eq!(a.len(), 64);
72 }
73
74 #[test]
75 fn sha256_hex_different_inputs_differ() {
76 assert_ne!(sha256_hex(b"a"), sha256_hex(b"b"));
77 }
78}