diff --git a/password-hash/src/error.rs b/password-hash/src/error.rs index 765188305..6a8dd8949 100644 --- a/password-hash/src/error.rs +++ b/password-hash/src/error.rs @@ -66,6 +66,14 @@ impl fmt::Display for Error { impl core::error::Error for Error {} +#[cfg(feature = "getrandom")] +impl From for Error { + fn from(_: getrandom::Error) -> Self { + // TODO(tarcieri): should we have a specific variant for RNGs errors? + Error::Crypto + } +} + #[cfg(feature = "phc")] impl From for Error { fn from(err: phc::Error) -> Self { diff --git a/password-hash/src/lib.rs b/password-hash/src/lib.rs index 09813c2b1..4f956ff99 100644 --- a/password-hash/src/lib.rs +++ b/password-hash/src/lib.rs @@ -37,7 +37,7 @@ pub use crate::error::{Error, Result}; pub use phc; #[cfg(feature = "rand_core")] -pub use rand_core::{self, TryCryptoRng}; +pub use rand_core; /// DEPRECATED: import this as `password_hash::phc::PasswordHash`. #[cfg(feature = "phc")] @@ -61,6 +61,9 @@ use core::{ str::FromStr, }; +#[cfg(feature = "rand_core")] +use rand_core::TryCryptoRng; + /// Numeric version identifier for password hashing algorithms. pub type Version = u32; @@ -94,8 +97,7 @@ pub trait PasswordHasher { /// A large random salt will be generated automatically. #[cfg(feature = "getrandom")] fn hash_password(&self, password: &[u8]) -> Result { - let mut salt = [0u8; RECOMMENDED_SALT_LEN]; - getrandom::fill(&mut salt).map_err(|_| Error::Crypto)?; + let salt = try_generate_salt()?; self.hash_password_with_salt(password, &salt) } @@ -211,3 +213,18 @@ pub trait McfHasher { /// algorithm-specific rules so hashers must parse a raw string themselves. fn upgrade_mcf_hash(&self, hash: &str) -> Result; } + +/// Generate a random salt value of the recommended length using the system's secure RNG. +#[cfg(feature = "getrandom")] +pub fn generate_salt() -> [u8; RECOMMENDED_SALT_LEN] { + try_generate_salt().expect("RNG failure") +} + +/// Try generating a random salt value of the recommended length using the system's secure RNG, +/// returning errors if they occur. +#[cfg(feature = "getrandom")] +pub fn try_generate_salt() -> core::result::Result<[u8; RECOMMENDED_SALT_LEN], getrandom::Error> { + let mut salt = [0u8; RECOMMENDED_SALT_LEN]; + getrandom::fill(&mut salt)?; + Ok(salt) +}