From 574f7f50a248a886f365273cb1f39911fe2160cf Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 4 Dec 2025 16:23:27 -0700 Subject: [PATCH] password-hash: extract `phc` submodule Extracts all functionality related to the Password Hashing Competition (PHC) string format into a `phc` submodule. The longer-term goal would be to extract this into its own crate, however that will need some trait redesign, which can be better accomplished by first adding a `phc` feature and feature-gating the relevant functionality. This is just an initial step to begin isolating the relevant code. --- password-hash/src/lib.rs | 325 +------------------------ password-hash/src/phc.rs | 329 ++++++++++++++++++++++++++ password-hash/src/{ => phc}/ident.rs | 0 password-hash/src/{ => phc}/output.rs | 4 +- password-hash/src/{ => phc}/params.rs | 7 +- password-hash/src/{ => phc}/salt.rs | 2 +- password-hash/src/{ => phc}/value.rs | 2 +- password-hash/src/traits.rs | 5 +- password-hash/tests/encoding.rs | 2 +- password-hash/tests/hashing.rs | 4 +- password-hash/tests/password_hash.rs | 2 +- password-hash/tests/test_vectors.rs | 2 +- 12 files changed, 348 insertions(+), 336 deletions(-) create mode 100644 password-hash/src/phc.rs rename password-hash/src/{ => phc}/ident.rs (100%) rename password-hash/src/{ => phc}/output.rs (98%) rename password-hash/src/{ => phc}/params.rs (99%) rename password-hash/src/{ => phc}/salt.rs (99%) rename password-hash/src/{ => phc}/value.rs (99%) diff --git a/password-hash/src/lib.rs b/password-hash/src/lib.rs index 50dc85020..66c4ad30c 100644 --- a/password-hash/src/lib.rs +++ b/password-hash/src/lib.rs @@ -32,335 +32,16 @@ extern crate alloc; pub use rand_core; pub mod errors; +pub mod phc; -mod ident; -mod output; -mod params; -mod salt; mod traits; -mod value; pub use crate::{ errors::{Error, Result}, - ident::Ident, - output::Output, - params::ParamsString, - salt::{Salt, SaltString}, traits::{McfHasher, PasswordHasher, PasswordVerifier}, - value::{Decimal, Value}, }; -use core::fmt::{self, Debug}; +pub use phc::PasswordHash; #[cfg(feature = "alloc")] -use alloc::{ - str::FromStr, - string::{String, ToString}, -}; - -/// Separator character used in password hashes (e.g. `$6$...`). -const PASSWORD_HASH_SEPARATOR: char = '$'; - -/// Password hash. -/// -/// This type corresponds to the parsed representation of a PHC string as -/// described in the [PHC string format specification][1]. -/// -/// PHC strings have the following format: -/// -/// ```text -/// $[$v=][$=(,=)*][$[$]] -/// ``` -/// -/// where: -/// -/// - `` is the symbolic name for the function -/// - `` is the algorithm version -/// - `` is a parameter name -/// - `` is a parameter value -/// - `` is an encoding of the salt -/// - `` is an encoding of the hash output -/// -/// The string is then the concatenation, in that order, of: -/// -/// - a `$` sign; -/// - the function symbolic name; -/// - optionally, a `$` sign followed by the algorithm version with a `v=version` format; -/// - optionally, a `$` sign followed by one or several parameters, each with a `name=value` format; -/// the parameters are separated by commas; -/// - optionally, a `$` sign followed by the (encoded) salt value; -/// - optionally, a `$` sign followed by the (encoded) hash output (the hash output may be present -/// only if the salt is present). -/// -/// [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#specification -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PasswordHash<'a> { - /// Password hashing algorithm identifier. - /// - /// This corresponds to the `` field in a PHC string, a.k.a. the - /// symbolic name for the function. - pub algorithm: Ident<'a>, - - /// Optional version field. - /// - /// This corresponds to the `` field in a PHC string. - pub version: Option, - - /// Algorithm-specific parameters. - /// - /// This corresponds to the set of `$=(,=)*` - /// name/value pairs in a PHC string. - pub params: ParamsString, - - /// [`Salt`] string for personalizing a password hash output. - /// - /// This corresponds to the `` value in a PHC string. - pub salt: Option>, - - /// Password hashing function [`Output`], a.k.a. hash/digest. - /// - /// This corresponds to the `` output in a PHC string. - pub hash: Option, -} - -impl<'a> PasswordHash<'a> { - /// Parse a password hash from a string in the PHC string format. - pub fn new(s: &'a str) -> Result { - if s.is_empty() { - return Err(Error::PhcStringField); - } - - let mut fields = s.split(PASSWORD_HASH_SEPARATOR); - let beginning = fields.next().expect("no first field"); - - if beginning.chars().next().is_some() { - return Err(Error::PhcStringField); - } - - let algorithm = fields - .next() - .ok_or(Error::PhcStringField) - .and_then(Ident::try_from)?; - - let mut version = None; - let mut params = ParamsString::new(); - let mut salt = None; - let mut hash = None; - - let mut next_field = fields.next(); - - if let Some(field) = next_field { - // v= - if field.starts_with("v=") && !field.contains(params::PARAMS_DELIMITER) { - version = Some(Value::new(&field[2..]).and_then(|value| value.decimal())?); - next_field = None; - } - } - - if next_field.is_none() { - next_field = fields.next(); - } - - if let Some(field) = next_field { - // = - if field.contains(params::PAIR_DELIMITER) { - params = field.parse()?; - next_field = None; - } - } - - if next_field.is_none() { - next_field = fields.next(); - } - - if let Some(s) = next_field { - salt = Some(s.try_into()?); - } - - if let Some(field) = fields.next() { - hash = Some(Output::decode(field)?); - } - - if fields.next().is_some() { - return Err(Error::PhcStringTrailingData); - } - - Ok(Self { - algorithm, - version, - params, - salt, - hash, - }) - } - - /// Generate a password hash using the supplied algorithm. - pub fn generate( - phf: impl PasswordHasher, - password: impl AsRef<[u8]>, - salt: impl Into>, - ) -> Result { - phf.hash_password(password.as_ref(), salt) - } - - /// Verify this password hash using the specified set of supported - /// [`PasswordHasher`] trait objects. - pub fn verify_password( - &self, - phfs: &[&dyn PasswordVerifier], - password: impl AsRef<[u8]>, - ) -> Result<()> { - for &phf in phfs { - if phf.verify_password(password.as_ref(), self).is_ok() { - return Ok(()); - } - } - - Err(Error::Password) - } - - /// Serialize this [`PasswordHash`] as a [`PasswordHashString`]. - #[cfg(feature = "alloc")] - pub fn serialize(&self) -> PasswordHashString { - self.into() - } -} - -// Note: this uses `TryFrom` instead of `FromStr` to support a lifetime on -// the `str` the value is being parsed from. -impl<'a> TryFrom<&'a str> for PasswordHash<'a> { - type Error = Error; - - fn try_from(s: &'a str) -> Result { - Self::new(s) - } -} - -impl fmt::Display for PasswordHash<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}{}", PASSWORD_HASH_SEPARATOR, self.algorithm)?; - - if let Some(version) = self.version { - write!(f, "{PASSWORD_HASH_SEPARATOR}v={version}")?; - } - - if !self.params.is_empty() { - write!(f, "{}{}", PASSWORD_HASH_SEPARATOR, self.params)?; - } - - if let Some(salt) = &self.salt { - write!(f, "{PASSWORD_HASH_SEPARATOR}{salt}")?; - - if let Some(hash) = &self.hash { - write!(f, "{PASSWORD_HASH_SEPARATOR}{hash}")?; - } - } - - Ok(()) - } -} - -/// Serialized [`PasswordHash`]. -/// -/// This type contains a serialized password hash string which is ensured to -/// parse successfully. -// TODO(tarcieri): cached parsed representations? or at least structural data -#[cfg(feature = "alloc")] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PasswordHashString { - /// String value - string: String, -} - -#[cfg(feature = "alloc")] -#[allow(clippy::len_without_is_empty)] -impl PasswordHashString { - /// Parse a password hash from a string in the PHC string format. - pub fn new(s: &str) -> Result { - PasswordHash::new(s).map(Into::into) - } - - /// Parse this owned string as a [`PasswordHash`]. - pub fn password_hash(&self) -> PasswordHash<'_> { - PasswordHash::new(&self.string).expect("malformed password hash") - } - - /// Borrow this value as a `str`. - pub fn as_str(&self) -> &str { - self.string.as_str() - } - - /// Borrow this value as bytes. - pub fn as_bytes(&self) -> &[u8] { - self.as_str().as_bytes() - } - - /// Get the length of this value in ASCII characters. - pub fn len(&self) -> usize { - self.as_str().len() - } - - /// Password hashing algorithm identifier. - pub fn algorithm(&self) -> Ident<'_> { - self.password_hash().algorithm - } - - /// Optional version field. - pub fn version(&self) -> Option { - self.password_hash().version - } - - /// Algorithm-specific parameters. - pub fn params(&self) -> ParamsString { - self.password_hash().params - } - - /// [`Salt`] string for personalizing a password hash output. - pub fn salt(&self) -> Option> { - self.password_hash().salt - } - - /// Password hashing function [`Output`], a.k.a. hash/digest. - pub fn hash(&self) -> Option { - self.password_hash().hash - } -} - -#[cfg(feature = "alloc")] -impl AsRef for PasswordHashString { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -#[cfg(feature = "alloc")] -impl From> for PasswordHashString { - fn from(hash: PasswordHash<'_>) -> PasswordHashString { - PasswordHashString::from(&hash) - } -} - -#[cfg(feature = "alloc")] -impl From<&PasswordHash<'_>> for PasswordHashString { - fn from(hash: &PasswordHash<'_>) -> PasswordHashString { - PasswordHashString { - string: hash.to_string(), - } - } -} - -#[cfg(feature = "alloc")] -impl FromStr for PasswordHashString { - type Err = Error; - - fn from_str(s: &str) -> Result { - Self::new(s) - } -} - -#[cfg(feature = "alloc")] -impl fmt::Display for PasswordHashString { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} +pub use phc::PasswordHashString; diff --git a/password-hash/src/phc.rs b/password-hash/src/phc.rs new file mode 100644 index 000000000..048989b74 --- /dev/null +++ b/password-hash/src/phc.rs @@ -0,0 +1,329 @@ +//! Password Hashing Competition string format implementation. + +mod ident; +mod output; +mod params; +mod salt; +mod value; + +use crate::{Error, PasswordHasher, PasswordVerifier}; +pub use ident::Ident; +pub use output::Output; +pub use params::ParamsString; +pub use salt::{Salt, SaltString}; +pub use value::{Decimal, Value}; + +use core::fmt; + +#[cfg(feature = "alloc")] +use alloc::{ + str::FromStr, + string::{String, ToString}, +}; + +/// Separator character used in password hashes (e.g. `$6$...`). +const PASSWORD_HASH_SEPARATOR: char = '$'; + +/// Password hash. +/// +/// This type corresponds to the parsed representation of a PHC string as +/// described in the [PHC string format specification][1]. +/// +/// PHC strings have the following format: +/// +/// ```text +/// $[$v=][$=(,=)*][$[$]] +/// ``` +/// +/// where: +/// +/// - `` is the symbolic name for the function +/// - `` is the algorithm version +/// - `` is a parameter name +/// - `` is a parameter value +/// - `` is an encoding of the salt +/// - `` is an encoding of the hash output +/// +/// The string is then the concatenation, in that order, of: +/// +/// - a `$` sign; +/// - the function symbolic name; +/// - optionally, a `$` sign followed by the algorithm version with a `v=version` format; +/// - optionally, a `$` sign followed by one or several parameters, each with a `name=value` format; +/// the parameters are separated by commas; +/// - optionally, a `$` sign followed by the (encoded) salt value; +/// - optionally, a `$` sign followed by the (encoded) hash output (the hash output may be present +/// only if the salt is present). +/// +/// [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#specification +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PasswordHash<'a> { + /// Password hashing algorithm identifier. + /// + /// This corresponds to the `` field in a PHC string, a.k.a. the + /// symbolic name for the function. + pub algorithm: Ident<'a>, + + /// Optional version field. + /// + /// This corresponds to the `` field in a PHC string. + pub version: Option, + + /// Algorithm-specific parameters. + /// + /// This corresponds to the set of `$=(,=)*` + /// name/value pairs in a PHC string. + pub params: ParamsString, + + /// [`Salt`] string for personalizing a password hash output. + /// + /// This corresponds to the `` value in a PHC string. + pub salt: Option>, + + /// Password hashing function [`Output`], a.k.a. hash/digest. + /// + /// This corresponds to the `` output in a PHC string. + pub hash: Option, +} + +impl<'a> PasswordHash<'a> { + /// Parse a password hash from a string in the PHC string format. + pub fn new(s: &'a str) -> crate::Result { + if s.is_empty() { + return Err(Error::PhcStringField); + } + + let mut fields = s.split(PASSWORD_HASH_SEPARATOR); + let beginning = fields.next().expect("no first field"); + + if beginning.chars().next().is_some() { + return Err(Error::PhcStringField); + } + + let algorithm = fields + .next() + .ok_or(Error::PhcStringField) + .and_then(Ident::try_from)?; + + let mut version = None; + let mut params = ParamsString::new(); + let mut salt = None; + let mut hash = None; + + let mut next_field = fields.next(); + + if let Some(field) = next_field { + // v= + if field.starts_with("v=") && !field.contains(params::PARAMS_DELIMITER) { + version = Some(Value::new(&field[2..]).and_then(|value| value.decimal())?); + next_field = None; + } + } + + if next_field.is_none() { + next_field = fields.next(); + } + + if let Some(field) = next_field { + // = + if field.contains(params::PAIR_DELIMITER) { + params = field.parse()?; + next_field = None; + } + } + + if next_field.is_none() { + next_field = fields.next(); + } + + if let Some(s) = next_field { + salt = Some(s.try_into()?); + } + + if let Some(field) = fields.next() { + hash = Some(Output::decode(field)?); + } + + if fields.next().is_some() { + return Err(Error::PhcStringTrailingData); + } + + Ok(Self { + algorithm, + version, + params, + salt, + hash, + }) + } + + /// Generate a password hash using the supplied algorithm. + pub fn generate( + phf: impl PasswordHasher, + password: impl AsRef<[u8]>, + salt: impl Into>, + ) -> crate::Result { + phf.hash_password(password.as_ref(), salt) + } + + /// Verify this password hash using the specified set of supported + /// [`PasswordHasher`] trait objects. + pub fn verify_password( + &self, + phfs: &[&dyn PasswordVerifier], + password: impl AsRef<[u8]>, + ) -> crate::Result<()> { + for &phf in phfs { + if phf.verify_password(password.as_ref(), self).is_ok() { + return Ok(()); + } + } + + Err(Error::Password) + } + + /// Serialize this [`PasswordHash`] as a [`PasswordHashString`]. + #[cfg(feature = "alloc")] + pub fn serialize(&self) -> PasswordHashString { + self.into() + } +} + +// Note: this uses `TryFrom` instead of `FromStr` to support a lifetime on +// the `str` the value is being parsed from. +impl<'a> TryFrom<&'a str> for PasswordHash<'a> { + type Error = Error; + + fn try_from(s: &'a str) -> crate::Result { + Self::new(s) + } +} + +impl fmt::Display for PasswordHash<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}{}", PASSWORD_HASH_SEPARATOR, self.algorithm)?; + + if let Some(version) = self.version { + write!(f, "{PASSWORD_HASH_SEPARATOR}v={version}")?; + } + + if !self.params.is_empty() { + write!(f, "{}{}", PASSWORD_HASH_SEPARATOR, self.params)?; + } + + if let Some(salt) = &self.salt { + write!(f, "{PASSWORD_HASH_SEPARATOR}{salt}")?; + + if let Some(hash) = &self.hash { + write!(f, "{PASSWORD_HASH_SEPARATOR}{hash}")?; + } + } + + Ok(()) + } +} + +/// Serialized [`PasswordHash`]. +/// +/// This type contains a serialized password hash string which is ensured to +/// parse successfully. +// TODO(tarcieri): cached parsed representations? or at least structural data +#[cfg(feature = "alloc")] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PasswordHashString { + /// String value + string: String, +} + +#[cfg(feature = "alloc")] +#[allow(clippy::len_without_is_empty)] +impl PasswordHashString { + /// Parse a password hash from a string in the PHC string format. + pub fn new(s: &str) -> crate::Result { + PasswordHash::new(s).map(Into::into) + } + + /// Parse this owned string as a [`PasswordHash`]. + pub fn password_hash(&self) -> PasswordHash<'_> { + PasswordHash::new(&self.string).expect("malformed password hash") + } + + /// Borrow this value as a `str`. + pub fn as_str(&self) -> &str { + self.string.as_str() + } + + /// Borrow this value as bytes. + pub fn as_bytes(&self) -> &[u8] { + self.as_str().as_bytes() + } + + /// Get the length of this value in ASCII characters. + pub fn len(&self) -> usize { + self.as_str().len() + } + + /// Password hashing algorithm identifier. + pub fn algorithm(&self) -> Ident<'_> { + self.password_hash().algorithm + } + + /// Optional version field. + pub fn version(&self) -> Option { + self.password_hash().version + } + + /// Algorithm-specific parameters. + pub fn params(&self) -> ParamsString { + self.password_hash().params + } + + /// [`Salt`] string for personalizing a password hash output. + pub fn salt(&self) -> Option> { + self.password_hash().salt + } + + /// Password hashing function [`Output`], a.k.a. hash/digest. + pub fn hash(&self) -> Option { + self.password_hash().hash + } +} + +#[cfg(feature = "alloc")] +impl AsRef for PasswordHashString { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +#[cfg(feature = "alloc")] +impl From> for PasswordHashString { + fn from(hash: PasswordHash<'_>) -> PasswordHashString { + PasswordHashString::from(&hash) + } +} + +#[cfg(feature = "alloc")] +impl From<&PasswordHash<'_>> for PasswordHashString { + fn from(hash: &PasswordHash<'_>) -> PasswordHashString { + PasswordHashString { + string: hash.to_string(), + } + } +} + +#[cfg(feature = "alloc")] +impl FromStr for PasswordHashString { + type Err = Error; + + fn from_str(s: &str) -> crate::Result { + Self::new(s) + } +} + +#[cfg(feature = "alloc")] +impl fmt::Display for PasswordHashString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} diff --git a/password-hash/src/ident.rs b/password-hash/src/phc/ident.rs similarity index 100% rename from password-hash/src/ident.rs rename to password-hash/src/phc/ident.rs diff --git a/password-hash/src/output.rs b/password-hash/src/phc/output.rs similarity index 98% rename from password-hash/src/output.rs rename to password-hash/src/phc/output.rs index 63fba7e2e..e5c1fb2e2 100644 --- a/password-hash/src/output.rs +++ b/password-hash/src/phc/output.rs @@ -89,8 +89,8 @@ use subtle::{Choice, ConstantTimeEq}; /// time, we would also suggest preventing such attacks by using randomly /// generated salts and keeping those salts secret. /// -/// The [`SaltString::from_rng`][`crate::SaltString::from_rng`] and -/// [`SaltString::try_from_rng`][`crate::SaltString::try_from_rng`] functions can be +/// The [`SaltString::from_rng`][`crate::phc::SaltString::from_rng`] and +/// [`SaltString::try_from_rng`][`crate::phc::SaltString::try_from_rng`] functions can be /// used to generate random high-entropy salt values. /// /// [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#function-duties diff --git a/password-hash/src/params.rs b/password-hash/src/phc/params.rs similarity index 99% rename from password-hash/src/params.rs rename to password-hash/src/phc/params.rs index 70b3ca2c3..4bdd80f0a 100644 --- a/password-hash/src/params.rs +++ b/password-hash/src/phc/params.rs @@ -1,10 +1,7 @@ //! Algorithm parameters. -use crate::{ - Error, Ident, Result, - errors::InvalidValue, - value::{Decimal, Value}, -}; +use crate::phc::value::{Decimal, Value}; +use crate::{Error, Result, errors::InvalidValue, phc::Ident}; use base64ct::{Base64Unpadded as B64, Encoding}; use core::{ fmt::{self, Debug, Write}, diff --git a/password-hash/src/salt.rs b/password-hash/src/phc/salt.rs similarity index 99% rename from password-hash/src/salt.rs rename to password-hash/src/phc/salt.rs index 7896a5716..b7523b5f7 100644 --- a/password-hash/src/salt.rs +++ b/password-hash/src/phc/salt.rs @@ -1,6 +1,6 @@ //! Salt string support. -use crate::{Error, Result, Value, errors::InvalidValue}; +use crate::{Error, Result, errors::InvalidValue, phc::Value}; use base64ct::{Base64Unpadded as B64, Encoding}; use core::{fmt, str}; diff --git a/password-hash/src/value.rs b/password-hash/src/phc/value.rs similarity index 99% rename from password-hash/src/value.rs rename to password-hash/src/phc/value.rs index 46a3365a6..fc7080120 100644 --- a/password-hash/src/value.rs +++ b/password-hash/src/phc/value.rs @@ -43,7 +43,7 @@ pub struct Value<'a>(&'a str); impl<'a> Value<'a> { /// Maximum length of an [`Value`] - 64 ASCII characters (i.e. 64-bytes). /// - /// This value is selected to match the maximum length of a [`Salt`][`crate::Salt`] + /// This value is selected to match the maximum length of a [`Salt`][`crate::phc::Salt`] /// as this library internally uses this type to represent salts. pub const MAX_LENGTH: usize = 64; diff --git a/password-hash/src/traits.rs b/password-hash/src/traits.rs index 4c9952e90..b7da3b993 100644 --- a/password-hash/src/traits.rs +++ b/password-hash/src/traits.rs @@ -1,6 +1,9 @@ //! Trait definitions. -use crate::{Decimal, Error, Ident, ParamsString, PasswordHash, Result, Salt}; +use crate::{ + Error, PasswordHash, Result, + phc::{Decimal, Ident, ParamsString, Salt}, +}; use core::fmt::Debug; /// Trait for password hashing functions. diff --git a/password-hash/tests/encoding.rs b/password-hash/tests/encoding.rs index 0d7c16f2e..8ce812ba2 100644 --- a/password-hash/tests/encoding.rs +++ b/password-hash/tests/encoding.rs @@ -8,7 +8,7 @@ //! //! -use password_hash::{Output, Salt}; +use password_hash::phc::{Output, Salt}; // Example salt encoded as a B64 string. const EXAMPLE_SALT_B64: &str = "REVBREJFRUZERUFEQkVFRg"; diff --git a/password-hash/tests/hashing.rs b/password-hash/tests/hashing.rs index 7c53a72fc..07c595101 100644 --- a/password-hash/tests/hashing.rs +++ b/password-hash/tests/hashing.rs @@ -1,7 +1,9 @@ //! Password hashing tests pub use password_hash::{ - Decimal, Error, Ident, Output, ParamsString, PasswordHash, PasswordHasher, Result, Salt, + PasswordHasher, + errors::{Error, Result}, + phc::{Decimal, Ident, Output, ParamsString, PasswordHash, Salt}, }; const ALG: Ident = Ident::new_unwrap("example"); diff --git a/password-hash/tests/password_hash.rs b/password-hash/tests/password_hash.rs index a8c7a2cc9..fd4c7e8ba 100644 --- a/password-hash/tests/password_hash.rs +++ b/password-hash/tests/password_hash.rs @@ -4,7 +4,7 @@ //! of the string encoding, and ensures password hashes round trip under each //! of the conditions. -use password_hash::{Ident, ParamsString, PasswordHash, Salt}; +use password_hash::phc::{Ident, ParamsString, PasswordHash, Salt}; const EXAMPLE_ALGORITHM: Ident = Ident::new_unwrap("argon2d"); const EXAMPLE_SALT: &str = "saltsaltsaltsaltsalt"; diff --git a/password-hash/tests/test_vectors.rs b/password-hash/tests/test_vectors.rs index d1c20a2b4..0df4f83af 100644 --- a/password-hash/tests/test_vectors.rs +++ b/password-hash/tests/test_vectors.rs @@ -1,6 +1,6 @@ //! Test vectors for commonly used password hashing algorithms. -use password_hash::{Ident, PasswordHash}; +use password_hash::phc::{Ident, PasswordHash}; const ARGON2D_HASH: &str = "$argon2d$v=19$m=512,t=3,p=2$5VtWOO3cGWYQHEMaYGbsfQ$AcmqasQgW/wI6wAHAMk4aQ";