Skip to content

Commit 85ce438

Browse files
authored
password-hash: extract CustomizedPasswordHasher trait (#2105)
Splits out `PasswordHasher::hash_password_customized` into its own separate `CustomizedPasswordHasher` trait. This trait retains the associated `Params` type since that's used as a method argument. This also adds a type alias `Version` for `u32` to use in place of `Decimal` (also a `u32` alias) for the `Version` parameter to `hash_password_customized`, and changes the `salt` parameter to be `&'a str`, which removes any PHC-related types as input arguments to the method. However, there is still a dependency on `ParamsString` and `PasswordHash`, which still needs to be addressed to fully decouple the trait from PHC types.
1 parent 489bb17 commit 85ce438

File tree

3 files changed

+34
-27
lines changed

3 files changed

+34
-27
lines changed

password-hash/src/lib.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,23 @@ pub use phc::PasswordHash;
4040
#[cfg(feature = "alloc")]
4141
pub use phc::PasswordHashString;
4242

43-
use crate::phc::{Decimal, Ident, ParamsString, Salt};
43+
use crate::phc::ParamsString;
4444
use core::fmt::Debug;
4545

46+
/// Numeric version identifier for password hashing algorithms.
47+
pub type Version = u32;
48+
4649
/// Trait for password hashing functions.
4750
pub trait PasswordHasher {
51+
/// Simple API for computing a [`PasswordHash`] from a password and
52+
/// salt value.
53+
///
54+
/// Uses the default recommended parameters for a given algorithm.
55+
fn hash_password<'a>(&self, password: &[u8], salt: &'a str) -> Result<PasswordHash<'a>>;
56+
}
57+
58+
/// Trait for password hashing functions which support customization.
59+
pub trait CustomizedPasswordHasher {
4860
/// Algorithm-specific parameters.
4961
type Params: Clone
5062
+ Debug
@@ -60,23 +72,11 @@ pub trait PasswordHasher {
6072
fn hash_password_customized<'a>(
6173
&self,
6274
password: &[u8],
63-
algorithm: Option<Ident<'a>>,
64-
version: Option<Decimal>,
75+
algorithm: Option<&'a str>,
76+
version: Option<Version>,
6577
params: Self::Params,
66-
salt: impl Into<Salt<'a>>,
78+
salt: &'a str,
6779
) -> 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-
}
8080
}
8181

8282
/// Trait for password verification.
@@ -93,15 +93,15 @@ pub trait PasswordVerifier {
9393
fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()>;
9494
}
9595

96-
impl<T: PasswordHasher> PasswordVerifier for T {
96+
impl<T: CustomizedPasswordHasher> PasswordVerifier for T {
9797
fn verify_password(&self, password: &[u8], hash: &PasswordHash<'_>) -> Result<()> {
9898
if let (Some(salt), Some(expected_output)) = (&hash.salt, &hash.hash) {
9999
let computed_hash = self.hash_password_customized(
100100
password,
101-
Some(hash.algorithm),
101+
Some(hash.algorithm.as_str()),
102102
hash.version,
103103
T::Params::try_from(hash)?,
104-
*salt,
104+
salt.as_str(),
105105
)?;
106106

107107
if let Some(computed_output) = &computed_hash.hash {

password-hash/src/phc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<'a> PasswordHash<'a> {
161161
pub fn generate(
162162
phf: impl PasswordHasher,
163163
password: impl AsRef<[u8]>,
164-
salt: impl Into<Salt<'a>>,
164+
salt: &'a str,
165165
) -> crate::Result<Self> {
166166
phf.hash_password(password.as_ref(), salt)
167167
}

password-hash/tests/hashing.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Password hashing tests
22
3+
use password_hash::PasswordHasher;
34
pub use password_hash::{
4-
PasswordHasher,
5+
CustomizedPasswordHasher,
56
errors::{Error, Result},
67
phc::{Decimal, Ident, Output, ParamsString, PasswordHash, Salt},
78
};
@@ -12,21 +13,27 @@ const ALG: Ident = Ident::new_unwrap("example");
1213
pub struct StubPasswordHasher;
1314

1415
impl PasswordHasher for StubPasswordHasher {
16+
fn hash_password<'a>(&self, password: &[u8], salt: &'a str) -> Result<PasswordHash<'a>> {
17+
self.hash_password_customized(password, None, None, StubParams, salt)
18+
}
19+
}
20+
21+
impl CustomizedPasswordHasher for StubPasswordHasher {
1522
type Params = StubParams;
1623

1724
fn hash_password_customized<'a>(
1825
&self,
1926
password: &[u8],
20-
algorithm: Option<Ident<'a>>,
27+
algorithm: Option<&'a str>,
2128
version: Option<Decimal>,
2229
params: StubParams,
23-
salt: impl Into<Salt<'a>>,
30+
salt: &'a str,
2431
) -> Result<PasswordHash<'a>> {
25-
let salt = salt.into();
32+
let salt = Salt::from_b64(salt)?;
2633
let mut output = Vec::new();
2734

2835
if let Some(alg) = algorithm {
29-
if alg != ALG {
36+
if Ident::new(alg)? != ALG {
3037
return Err(Error::Algorithm);
3138
}
3239
}
@@ -70,12 +77,12 @@ impl TryFrom<StubParams> for ParamsString {
7077
#[test]
7178
fn verify_password_hash() {
7279
let valid_password = "test password";
73-
let salt = Salt::from_b64("test-salt").unwrap();
80+
let salt = "test-salt";
7481
let hash = PasswordHash::generate(StubPasswordHasher, valid_password, salt).unwrap();
7582

7683
// Sanity tests for StubFunction impl above
7784
assert_eq!(hash.algorithm, ALG);
78-
assert_eq!(hash.salt.unwrap(), salt);
85+
assert_eq!(hash.salt.unwrap().as_str(), salt);
7986

8087
// Tests for generic password verification logic
8188
assert_eq!(

0 commit comments

Comments
 (0)