password_hash/traits.rs
1//! Trait definitions.
2
3use crate::{Decimal, Error, Ident, ParamsString, PasswordHash, Result, Salt};
4use core::fmt::Debug;
5
6/// Trait for password hashing functions.
7pub trait PasswordHasher {
8 /// Algorithm-specific parameters.
9 type Params: Clone
10 + Debug
11 + Default
12 + for<'a> TryFrom<&'a PasswordHash<'a>, Error = Error>
13 + TryInto<ParamsString, Error = Error>;
14
15 /// Compute a [`PasswordHash`] from the provided password using an
16 /// explicit set of customized algorithm parameters as opposed to the
17 /// defaults.
18 ///
19 /// When in doubt, use [`PasswordHasher::hash_password`] instead.
20 fn hash_password_customized<'a>(
21 &self,
22 password: &[u8],
23 algorithm: Option<Ident<'a>>,
24 version: Option<Decimal>,
25 params: Self::Params,
26 salt: impl Into<Salt<'a>>,
27 ) -> Result<PasswordHash<'a>>;
28
29 /// Simple API for computing a [`PasswordHash`] from a password and
30 /// salt value.
31 ///
32 /// Uses the default recommended parameters for a given algorithm.
33 fn hash_password<'a>(
34 &self,
35 password: &[u8],
36 salt: impl Into<Salt<'a>>,
37 ) -> Result<PasswordHash<'a>> {
38 self.hash_password_customized(password, None, None, Self::Params::default(), salt)
39 }
40}
41
42/// Trait for password verification.
43///
44/// Automatically impl'd for any type that impls [`PasswordHasher`].
45///
46/// This trait is object safe and can be used to implement abstractions over
47/// multiple password hashing algorithms. One such abstraction is provided by
48/// the [`PasswordHash::verify_password`] method.
49pub trait PasswordVerifier {
50 /// Compute this password hashing function against the provided password
51 /// using the parameters from the provided password hash and see if the
52 /// computed output matches.
53 fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()>;
54}
55
56impl<T: PasswordHasher> PasswordVerifier for T {
57 fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()> {
58 if let (Some(salt), Some(expected_output)) = (&hash.salt, &hash.hash) {
59 let computed_hash = self.hash_password_customized(
60 password,
61 Some(hash.algorithm),
62 hash.version,
63 T::Params::try_from(hash)?,
64 *salt,
65 )?;
66
67 if let Some(computed_output) = &computed_hash.hash {
68 // See notes on `Output` about the use of a constant-time comparison
69 if expected_output == computed_output {
70 return Ok(());
71 }
72 }
73 }
74
75 Err(Error::Password)
76 }
77}
78
79/// Trait for password hashing algorithms which support the legacy
80/// [Modular Crypt Format (MCF)][MCF].
81///
82/// [MCF]: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
83pub trait McfHasher {
84 /// Upgrade an MCF hash to a PHC hash. MCF follow this rough format:
85 ///
86 /// ```text
87 /// $<id>$<content>
88 /// ```
89 ///
90 /// MCF hashes are otherwise largely unstructured and parsed according to
91 /// algorithm-specific rules so hashers must parse a raw string themselves.
92 fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result<PasswordHash<'a>>;
93
94 /// Verify a password hash in MCF format against the provided password.
95 fn verify_mcf_hash(&self, password: &[u8], mcf_hash: &str) -> Result<()>
96 where
97 Self: PasswordVerifier,
98 {
99 self.verify_password(password, &self.upgrade_mcf_hash(mcf_hash)?)
100 }
101}