diff --git a/README.md b/README.md index 000fbd91..e499d719 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ let input_password = "password"; let password_hash = phc::PasswordHash::new(&hash_string)?; // Trait objects for algorithms to support -let algs: &[&dyn PasswordVerifier] = &[&Argon2::default(), &Pbkdf2, &Scrypt]; +let algs: &[&dyn PasswordVerifier] = &[&Argon2::default(), &Pbkdf2, &Scrypt::default()]; for alg in algs { if alg.verify_password(input_password.as_ref(), &password_hash).is_ok() { diff --git a/password-auth/src/lib.rs b/password-auth/src/lib.rs index aec59fb2..c16aa529 100644 --- a/password-auth/src/lib.rs +++ b/password-auth/src/lib.rs @@ -64,7 +64,7 @@ fn generate_phc_hash(password: &[u8]) -> password_hash::Result { return Argon2::default().hash_password(password); #[cfg(feature = "scrypt")] - return Scrypt.hash_password(password); + return Scrypt::default().hash_password(password); #[cfg(feature = "pbkdf2")] return Pbkdf2.hash_password(password); @@ -86,7 +86,7 @@ pub fn verify_password(password: impl AsRef<[u8]>, hash: &str) -> Result<(), Ver #[cfg(feature = "pbkdf2")] &Pbkdf2, #[cfg(feature = "scrypt")] - &Scrypt, + &Scrypt::default(), ]; for &alg in algs { diff --git a/scrypt/src/lib.rs b/scrypt/src/lib.rs index e080acf4..72087c42 100644 --- a/scrypt/src/lib.rs +++ b/scrypt/src/lib.rs @@ -29,15 +29,16 @@ //! Scrypt //! }; //! +//! let scrypt = Scrypt::default(); // Uses `Params::RECOMMENDED` //! let password = b"hunter42"; // Bad password; don't actually use! //! //! // Hash password to PHC string ($scrypt$...) -//! let hash: PasswordHash = Scrypt.hash_password(password)?; +//! let hash: PasswordHash = scrypt.hash_password(password)?; //! let hash_string = hash.to_string(); //! //! // Verify password against PHC string //! let parsed_hash = PasswordHash::new(&hash_string)?; -//! assert!(Scrypt.verify_password(password, &parsed_hash).is_ok()); +//! assert!(scrypt.verify_password(password, &parsed_hash).is_ok()); //! # Ok(()) //! # } //! ``` @@ -142,9 +143,34 @@ fn romix_parallel(nr128: usize, r128: usize, n: usize, b: &mut [u8]) { /// Competition (PHC) string format which begin with `$scrypt$`, or in Modular Crypt Format (MCF) /// which begin with `$7$`. /// -/// This is a ZST which impls traits from the [`password-hash`][`password_hash`] crate, notably -/// the [`PasswordHasher`], [`PasswordVerifier`], and [`CustomizedPasswordHasher`] traits. +/// This type holds the default parameters to use when computing password hashes. /// /// See the toplevel documentation for a code example. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Scrypt; +#[cfg(any(feature = "mcf", feature = "phc"))] +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub struct Scrypt { + /// Default parameters to use. + params: Params, +} + +#[cfg(any(feature = "mcf", feature = "phc"))] +impl Scrypt { + /// Initialize [`Scrypt`] with default parameters. + pub const fn new() -> Self { + Self { + params: Params::RECOMMENDED, + } + } + + /// Initialize [`Scrypt`] with the provided parameters. + pub const fn new_with_params(params: Params) -> Self { + Self { params } + } +} + +#[cfg(any(feature = "mcf", feature = "phc"))] +impl From for Scrypt { + fn from(params: Params) -> Self { + Self::new_with_params(params) + } +} diff --git a/scrypt/src/mcf.rs b/scrypt/src/mcf.rs index 6212b2db..7e8d33b3 100644 --- a/scrypt/src/mcf.rs +++ b/scrypt/src/mcf.rs @@ -74,7 +74,7 @@ impl CustomizedPasswordHasher for Scrypt { impl PasswordHasher for Scrypt { fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result { - self.hash_password_customized(password, salt, None, None, Params::RECOMMENDED) + self.hash_password_customized(password, salt, None, None, self.params) } } @@ -273,7 +273,7 @@ mod tests { let salt = SCRYPT_BASE64.decode_vec(EXAMPLE_SALT).unwrap(); let params = Params::new(EXAMPLE_LOG_N, EXAMPLE_R, EXAMPLE_P).unwrap(); - let actual_hash: PasswordHash = Scrypt + let actual_hash: PasswordHash = Scrypt::new() .hash_password_with_params(EXAMPLE_PASSWORD, &salt, params) .unwrap(); @@ -284,10 +284,13 @@ mod tests { #[test] fn verify_password() { let hash = PasswordHashRef::new(EXAMPLE_MCF_HASH).unwrap(); - assert_eq!(Scrypt.verify_password(EXAMPLE_PASSWORD, hash), Ok(())); + assert_eq!( + Scrypt::new().verify_password(EXAMPLE_PASSWORD, hash), + Ok(()) + ); assert_eq!( - Scrypt.verify_password(b"bogus", hash), + Scrypt::new().verify_password(b"bogus", hash), Err(Error::PasswordInvalid) ); } diff --git a/scrypt/src/phc.rs b/scrypt/src/phc.rs index 90460e77..ad718b6c 100644 --- a/scrypt/src/phc.rs +++ b/scrypt/src/phc.rs @@ -52,7 +52,7 @@ impl CustomizedPasswordHasher for Scrypt { impl PasswordHasher for Scrypt { fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result { - self.hash_password_customized(password, salt, None, None, Params::default()) + self.hash_password_customized(password, salt, None, None, self.params) } } @@ -72,13 +72,16 @@ mod tests { fn password_hash_verify_password() { let password = "password"; let hash = PasswordHash::new(EXAMPLE_PASSWORD_HASH).unwrap(); - assert_eq!(Scrypt.verify_password(password.as_bytes(), &hash), Ok(())); + assert_eq!( + Scrypt::new().verify_password(password.as_bytes(), &hash), + Ok(()) + ); } #[cfg(feature = "password-hash")] #[test] fn password_hash_reject_incorrect_password() { let hash = PasswordHash::new(EXAMPLE_PASSWORD_HASH).unwrap(); - assert!(Scrypt.verify_password(b"invalid", &hash).is_err()); + assert!(Scrypt::new().verify_password(b"invalid", &hash).is_err()); } }