Skip to content

Commit 489bb17

Browse files
authored
password-hash: move trait definitions to toplevel (#2104)
This change puts the traits front-and-center in the library's implementation, which is what you'd expect for a crate in the "traits" repository
1 parent 76e826d commit 489bb17

File tree

2 files changed

+101
-111
lines changed

2 files changed

+101
-111
lines changed

password-hash/src/lib.rs

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,108 @@ pub use rand_core;
3434
pub mod errors;
3535
pub mod phc;
3636

37-
mod traits;
38-
39-
pub use crate::{
40-
errors::{Error, Result},
41-
traits::{McfHasher, PasswordHasher, PasswordVerifier},
42-
};
43-
37+
pub use crate::errors::{Error, Result};
4438
pub use phc::PasswordHash;
4539

4640
#[cfg(feature = "alloc")]
4741
pub use phc::PasswordHashString;
42+
43+
use crate::phc::{Decimal, Ident, ParamsString, Salt};
44+
use core::fmt::Debug;
45+
46+
/// Trait for password hashing functions.
47+
pub trait PasswordHasher {
48+
/// Algorithm-specific parameters.
49+
type Params: Clone
50+
+ Debug
51+
+ Default
52+
+ for<'a> TryFrom<&'a PasswordHash<'a>, Error = Error>
53+
+ TryInto<ParamsString, Error = Error>;
54+
55+
/// Compute a [`PasswordHash`] from the provided password using an
56+
/// explicit set of customized algorithm parameters as opposed to the
57+
/// defaults.
58+
///
59+
/// When in doubt, use [`PasswordHasher::hash_password`] instead.
60+
fn hash_password_customized<'a>(
61+
&self,
62+
password: &[u8],
63+
algorithm: Option<Ident<'a>>,
64+
version: Option<Decimal>,
65+
params: Self::Params,
66+
salt: impl Into<Salt<'a>>,
67+
) -> Result<PasswordHash<'a>>;
68+
69+
/// Simple API for computing a [`PasswordHash`] from a password and
70+
/// salt value.
71+
///
72+
/// Uses the default recommended parameters for a given algorithm.
73+
fn hash_password<'a>(
74+
&self,
75+
password: &[u8],
76+
salt: impl Into<Salt<'a>>,
77+
) -> Result<PasswordHash<'a>> {
78+
self.hash_password_customized(password, None, None, Self::Params::default(), salt)
79+
}
80+
}
81+
82+
/// Trait for password verification.
83+
///
84+
/// Automatically impl'd for any type that impls [`PasswordHasher`].
85+
///
86+
/// This trait is object safe and can be used to implement abstractions over
87+
/// multiple password hashing algorithms. One such abstraction is provided by
88+
/// the [`PasswordHash::verify_password`] method.
89+
pub trait PasswordVerifier {
90+
/// Compute this password hashing function against the provided password
91+
/// using the parameters from the provided password hash and see if the
92+
/// computed output matches.
93+
fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()>;
94+
}
95+
96+
impl<T: PasswordHasher> PasswordVerifier for T {
97+
fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()> {
98+
if let (Some(salt), Some(expected_output)) = (&hash.salt, &hash.hash) {
99+
let computed_hash = self.hash_password_customized(
100+
password,
101+
Some(hash.algorithm),
102+
hash.version,
103+
T::Params::try_from(hash)?,
104+
*salt,
105+
)?;
106+
107+
if let Some(computed_output) = &computed_hash.hash {
108+
// See notes on `Output` about the use of a constant-time comparison
109+
if expected_output == computed_output {
110+
return Ok(());
111+
}
112+
}
113+
}
114+
115+
Err(Error::Password)
116+
}
117+
}
118+
119+
/// Trait for password hashing algorithms which support the legacy
120+
/// [Modular Crypt Format (MCF)][MCF].
121+
///
122+
/// [MCF]: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
123+
pub trait McfHasher {
124+
/// Upgrade an MCF hash to a PHC hash. MCF follow this rough format:
125+
///
126+
/// ```text
127+
/// $<id>$<content>
128+
/// ```
129+
///
130+
/// MCF hashes are otherwise largely unstructured and parsed according to
131+
/// algorithm-specific rules so hashers must parse a raw string themselves.
132+
fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result<PasswordHash<'a>>;
133+
134+
/// Verify a password hash in MCF format against the provided password.
135+
fn verify_mcf_hash(&self, password: &[u8], mcf_hash: &str) -> Result<()>
136+
where
137+
Self: PasswordVerifier,
138+
{
139+
self.verify_password(password, &self.upgrade_mcf_hash(mcf_hash)?)
140+
}
141+
}

password-hash/src/traits.rs

Lines changed: 0 additions & 104 deletions
This file was deleted.

0 commit comments

Comments
 (0)