ej_auth/secret_hash.rs
1//! Secure password hashing and verification using Argon2.
2//!
3//! This module provides secure password hashing functionality using the Argon2
4//! algorithm, which is the recommended approach for password storage. It includes
5//! both password hashing and verification functions with secure defaults.
6//!
7//! # Usage
8//!
9//! The module provides two main functions:
10//! - [`generate_secret_hash`]: Create secure password hashes
11//! - [`is_secret_valid`]: Verify passwords against stored hashes
12//!
13//! # Examples
14//!
15//! ```rust
16//! use ej_auth::secret_hash::{generate_secret_hash, is_secret_valid};
17//!
18//! // Hash a user's password
19//! let password = "user_password_123";
20//! let hash = generate_secret_hash(password).unwrap();
21//!
22//! // Store the hash in your database
23//! // database.store_user_hash(&hash);
24//!
25//! // Later, verify a login attempt
26//! let login_password = "user_password_123";
27//! let is_valid = is_secret_valid(login_password, &hash).unwrap();
28//! assert!(is_valid);
29//!
30//! // Wrong password fails verification
31//! let wrong_password = "wrong_password";
32//! let is_valid = is_secret_valid(wrong_password, &hash).unwrap();
33//! assert!(!is_valid);
34//! ```
35
36use argon2::{
37 Argon2, PasswordHasher, PasswordVerifier,
38 password_hash::{self, PasswordHashString, SaltString},
39};
40use rand::rngs::OsRng;
41
42use crate::prelude::*;
43
44/// Generates a secure hash for the provided password.
45///
46/// This function creates a cryptographically secure hash of the password using
47/// the Argon2 algorithm with a randomly generated salt. The resulting hash is
48/// safe to store in databases and includes all necessary parameters for verification.
49///
50/// # Arguments
51///
52/// * `pw` - The plaintext password to hash
53///
54/// # Returns
55///
56/// * `Ok(String)` - Secure hash ready for storage
57/// * `Err(Error)` - Password hashing errors
58///
59/// # Example
60///
61/// ```rust
62/// use ej_auth::secret_hash::generate_secret_hash;
63///
64/// let password = "my_secure_password";
65/// let hash = generate_secret_hash(password).unwrap();
66/// println!("Secure hash: {}", hash);
67/// ```
68pub fn generate_secret_hash(pw: &str) -> Result<String> {
69 let salt = SaltString::generate(&mut OsRng);
70 let argon2 = Argon2::default();
71 Ok(argon2.hash_password(pw.as_bytes(), &salt)?.to_string())
72}
73
74/// Verifies a password against a stored hash.
75///
76/// This function performs constant-time verification of a password against
77/// a previously generated hash. It extracts the salt and parameters from
78/// the hash string and re-computes the hash for comparison.
79///
80/// # Arguments
81///
82/// * `pw` - The plaintext password to verify
83/// * `hash` - The stored hash string to verify against
84///
85/// # Returns
86///
87/// * `Ok(true)` - Password matches the hash
88/// * `Ok(false)` - Password does not match the hash
89/// * `Err(Error)` - Hash parsing or verification errors
90///
91/// # Example
92///
93/// ```rust
94/// use ej_auth::secret_hash::{generate_secret_hash, is_secret_valid};
95///
96/// let password = "user_password";
97/// let hash = generate_secret_hash(password).unwrap();
98/// let is_valid = is_secret_valid(password, &hash).unwrap();
99/// assert!(is_valid);
100/// ```
101pub fn is_secret_valid(pw: &str, hash: &str) -> Result<bool> {
102 let hash = PasswordHashString::new(hash)?;
103
104 Ok(Argon2::default()
105 .verify_password(pw.as_bytes(), &hash.password_hash())
106 .is_ok())
107}
108
109impl From<password_hash::Error> for Error {
110 fn from(value: password_hash::Error) -> Self {
111 Self::PasswordHash(value)
112 }
113}