Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ let input_password = "password";
let password_hash = phc::PasswordHash::new(&hash_string)?;

// Trait objects for algorithms to support
let algs: &[&dyn PasswordVerifier<phc::PasswordHash>] = &[&Argon2::default(), &Pbkdf2, &Scrypt::default()];
let algs: &[&dyn PasswordVerifier<phc::PasswordHash>] = &[
&Argon2::default(),
&Pbkdf2::default(),
&Scrypt::default()
];

for alg in algs {
if alg.verify_password(input_password.as_ref(), &password_hash).is_ok() {
Expand Down
4 changes: 2 additions & 2 deletions password-auth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn generate_phc_hash(password: &[u8]) -> password_hash::Result<PasswordHash> {
return Scrypt::default().hash_password(password);

#[cfg(feature = "pbkdf2")]
return Pbkdf2.hash_password(password);
return Pbkdf2::default().hash_password(password);
}

/// Verify the provided password against the provided password hash.
Expand All @@ -84,7 +84,7 @@ pub fn verify_password(password: impl AsRef<[u8]>, hash: &str) -> Result<(), Ver
#[cfg(feature = "argon2")]
&Argon2::default(),
#[cfg(feature = "pbkdf2")]
&Pbkdf2,
&Pbkdf2::default(),
#[cfg(feature = "scrypt")]
&Scrypt::default(),
];
Expand Down
5 changes: 3 additions & 2 deletions pbkdf2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,15 @@
//! Pbkdf2
//! };
//!
//! let pbkdf2 = Pbkdf2::new(); // Uses `Params::RECOMMENDED`
//! let password = b"hunter42"; // Bad password; don't actually use!
//!
//! // Hash password to PHC string ($pbkdf2-sha256$...)
//! let password_hash = Pbkdf2.hash_password(password)?.to_string();
//! let password_hash = pbkdf2.hash_password(password)?.to_string();
//!
//! // Verify password against PHC string
//! let parsed_hash = PasswordHash::new(&password_hash)?;
//! assert!(Pbkdf2.verify_password(password, &parsed_hash).is_ok());
//! assert!(pbkdf2.verify_password(password, &parsed_hash).is_ok());
//! # Ok(())
//! # }
//! ```
Expand Down
45 changes: 40 additions & 5 deletions pbkdf2/src/phc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,35 @@ use sha2::{Sha256, Sha512};
use sha1::Sha1;

/// PBKDF2 type for use with [`PasswordHasher`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Pbkdf2;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Pbkdf2 {
/// Algorithm to use
algorithm: Algorithm,

/// Default parameters to use.
params: Params,
}

impl Pbkdf2 {
/// Initialize [`Pbkdf2`] with default parameters.
pub const fn new() -> Self {
Self::new_with_params(Params::RECOMMENDED)
}

/// Initialize [`Pbkdf2`] with the provided parameters.
pub const fn new_with_params(params: Params) -> Self {
Self {
algorithm: Algorithm::RECOMMENDED,
params,
}
}
}

impl From<Params> for Pbkdf2 {
fn from(params: Params) -> Self {
Self::new_with_params(params)
}
}

impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
type Params = Params;
Expand All @@ -32,7 +59,7 @@ impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
let algorithm = alg_id
.map(Algorithm::try_from)
.transpose()?
.unwrap_or_default();
.unwrap_or(self.algorithm);

// Versions unsupported
if version.is_some() {
Expand Down Expand Up @@ -68,7 +95,7 @@ impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {

impl PasswordHasher<PasswordHash> for Pbkdf2 {
fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result<PasswordHash> {
self.hash_password_customized(password, salt, None, None, Params::default())
self.hash_password_customized(password, salt, None, None, self.params)
}
}

Expand Down Expand Up @@ -97,7 +124,7 @@ impl Default for Algorithm {
///
/// [OWASP cheat sheet]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
fn default() -> Self {
Self::Pbkdf2Sha256
Self::RECOMMENDED
}
}

Expand All @@ -112,6 +139,14 @@ impl Algorithm {
/// PBKDF2 (SHA-512) algorithm identifier
pub const PBKDF2_SHA512_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha512");

/// Default algorithm suggested by the [OWASP cheat sheet]:
///
/// > Use PBKDF2 with a work factor of 600,000 or more and set with an
/// > internal hash function of HMAC-SHA-256.
///
/// [OWASP cheat sheet]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
const RECOMMENDED: Self = Self::Pbkdf2Sha256;

/// Parse an [`Algorithm`] from the provided string.
pub fn new(id: impl AsRef<str>) -> Result<Self> {
id.as_ref().parse()
Expand Down
2 changes: 1 addition & 1 deletion pbkdf2/tests/phc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn hash_with_default_algorithm() {
output_length: 40,
};

let hash = Pbkdf2
let hash = Pbkdf2::new()
.hash_password_customized(PASSWORD, SALT, None, None, params)
.unwrap();

Expand Down