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}