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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
//! Trait definitions.
use crate::{Decimal, Error, Ident, ParamsString, PasswordHash, Result, Salt};
use core::fmt::Debug;
/// Trait for password hashing functions.
pub trait PasswordHasher {
/// Algorithm-specific parameters.
type Params: Clone
+ Debug
+ Default
+ for<'a> TryFrom<&'a PasswordHash<'a>, Error = Error>
+ TryInto<ParamsString, Error = Error>;
/// Compute a [`PasswordHash`] from the provided password using an
/// explicit set of customized algorithm parameters as opposed to the
/// defaults.
///
/// When in doubt, use [`PasswordHasher::hash_password`] instead.
fn hash_password_customized<'a>(
&self,
password: &[u8],
algorithm: Option<Ident<'a>>,
version: Option<Decimal>,
params: Self::Params,
salt: impl Into<Salt<'a>>,
) -> Result<PasswordHash<'a>>;
/// Simple API for computing a [`PasswordHash`] from a password and
/// salt value.
///
/// Uses the default recommended parameters for a given algorithm.
fn hash_password<'a, S>(&self, password: &[u8], salt: &'a S) -> Result<PasswordHash<'a>>
where
S: AsRef<str> + ?Sized,
{
self.hash_password_customized(
password,
None,
None,
Self::Params::default(),
Salt::try_from(salt.as_ref())?,
)
}
}
/// Trait for password verification.
///
/// Automatically impl'd for any type that impls [`PasswordHasher`].
///
/// This trait is object safe and can be used to implement abstractions over
/// multiple password hashing algorithms. One such abstraction is provided by
/// the [`PasswordHash::verify_password`] method.
pub trait PasswordVerifier {
/// Compute this password hashing function against the provided password
/// using the parameters from the provided password hash and see if the
/// computed output matches.
fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()>;
}
impl<T: PasswordHasher> PasswordVerifier for T {
fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()> {
if let (Some(salt), Some(expected_output)) = (&hash.salt, &hash.hash) {
let computed_hash = self.hash_password_customized(
password,
Some(hash.algorithm),
hash.version,
T::Params::try_from(hash)?,
*salt,
)?;
if let Some(computed_output) = &computed_hash.hash {
// See notes on `Output` about the use of a constant-time comparison
if expected_output == computed_output {
return Ok(());
}
}
}
Err(Error::Password)
}
}
/// Trait for password hashing algorithms which support the legacy
/// [Modular Crypt Format (MCF)][MCF].
///
/// [MCF]: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
pub trait McfHasher {
/// Upgrade an MCF hash to a PHC hash. MCF follow this rough format:
///
/// ```text
/// $<id>$<content>
/// ```
///
/// MCF hashes are otherwise largely unstructured and parsed according to
/// algorithm-specific rules so hashers must parse a raw string themselves.
fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result<PasswordHash<'a>>;
/// Verify a password hash in MCF format against the provided password.
fn verify_mcf_hash(&self, password: &[u8], mcf_hash: &str) -> Result<()>
where
Self: PasswordVerifier,
{
self.verify_password(password, &self.upgrade_mcf_hash(mcf_hash)?)
}
}