diff --git a/Cargo.lock b/Cargo.lock index 06289242..c22f3cdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -258,9 +258,8 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "password-hash" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54986aa4bfc9b98c6a5f40184223658d187159d7b3c6af33f2b2aa25ae1db0fa" +version = "0.2.0-pre" +source = "git+https://github.com/RustCrypto/traits?branch=password-hash/v0.2-changes#c9202985f28853ebaa5235c9c460e57a202dc3a5" dependencies = [ "base64ct", "rand_core", diff --git a/Cargo.toml b/Cargo.toml index 1f251097..faf8b5e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ members = [ "scrypt", "sha-crypt" ] + +[patch.crates-io] +password-hash = { git = "https://github.com/RustCrypto/traits", branch = "password-hash/v0.2-changes" } diff --git a/argon2/Cargo.toml b/argon2/Cargo.toml index 46debf79..8f78bb4c 100644 --- a/argon2/Cargo.toml +++ b/argon2/Cargo.toml @@ -16,13 +16,13 @@ readme = "README.md" [dependencies] blake2 = { version = "0.9", default-features = false } -password-hash = { version = "0.1", optional = true } +password-hash = { version = "=0.2.0-pre", optional = true } rayon = { version = "1", optional = true } zeroize = { version = "1", optional = true } [dev-dependencies] hex-literal = "0.3" -password-hash = { version = "0.1", features = ["rand_core"] } +password-hash = { version = "=0.2.0-pre", features = ["rand_core"] } rand_core = { version = "0.6", features = ["std"] } [features] diff --git a/argon2/src/lib.rs b/argon2/src/lib.rs index f2640187..d43454ed 100644 --- a/argon2/src/lib.rs +++ b/argon2/src/lib.rs @@ -80,12 +80,19 @@ mod block; mod error; mod instance; mod memory; +mod version; -pub use crate::error::Error; +#[cfg(feature = "password-hash")] +mod params; + +pub use crate::{error::Error, version::Version}; #[cfg(feature = "password-hash")] #[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))] -pub use password_hash::{self, PasswordHash, PasswordHasher, PasswordVerifier}; +pub use { + params::Params, + password_hash::{self, PasswordHash, PasswordHasher, PasswordVerifier}, +}; use crate::{ block::Block, @@ -94,15 +101,14 @@ use crate::{ }; use blake2::{digest, Blake2b, Digest}; use core::{ - convert::TryFrom, fmt::{self, Display}, str::FromStr, }; #[cfg(feature = "password-hash")] use { - core::convert::TryInto, - password_hash::{Decimal, HasherError, Ident, ParamsError, ParamsString, Salt}, + core::convert::{TryFrom, TryInto}, + password_hash::{Ident, Salt}, }; /// Minimum and maximum number of lanes (degree of parallelism) @@ -269,60 +275,14 @@ impl From for Ident<'static> { #[cfg(feature = "password-hash")] #[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))] impl<'a> TryFrom> for Algorithm { - type Error = HasherError; + type Error = password_hash::Error; - fn try_from(ident: Ident<'a>) -> Result { + fn try_from(ident: Ident<'a>) -> Result { match ident { ARGON2D_IDENT => Ok(Algorithm::Argon2d), ARGON2I_IDENT => Ok(Algorithm::Argon2i), ARGON2ID_IDENT => Ok(Algorithm::Argon2id), - _ => Err(HasherError::Algorithm), - } - } -} - -/// Version of the algorithm. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -#[repr(u32)] -pub enum Version { - /// Version 16 (0x10 in hex) - /// - /// Performs overwrite internally - V0x10 = 0x10, - - /// Version 19 (0x13 in hex, default) - /// - /// Performs XOR internally - V0x13 = 0x13, -} - -impl Version { - /// Serialize version as little endian bytes - fn to_le_bytes(self) -> [u8; 4] { - (self as u32).to_le_bytes() - } -} - -impl Default for Version { - fn default() -> Self { - Self::V0x13 - } -} - -impl From for u32 { - fn from(version: Version) -> u32 { - version as u32 - } -} - -impl TryFrom for Version { - type Error = Error; - - fn try_from(version_id: u32) -> Result { - match version_id { - 0x10 => Ok(Version::V0x10), - 0x13 => Ok(Version::V0x13), - _ => Err(Error::VersionInvalid), + _ => Err(password_hash::Error::Algorithm), } } } @@ -536,21 +496,15 @@ impl PasswordHasher for Argon2<'_> { &self, password: &[u8], alg_id: Option>, - version_id: Option, params: Params, - salt: Salt<'a>, - ) -> Result, HasherError> { + salt: impl Into>, + ) -> Result, password_hash::Error> { let algorithm = alg_id .map(Algorithm::try_from) .transpose()? .unwrap_or_default(); - let version = version_id - .map(Version::try_from) - .transpose() - .map_err(|_| HasherError::Version)? - .unwrap_or(self.version); - + let salt = salt.into(); let mut salt_arr = [0u8; 64]; let salt_bytes = salt.b64_decode(&mut salt_arr)?; @@ -562,33 +516,33 @@ impl PasswordHasher for Argon2<'_> { params.t_cost, params.m_cost, params.p_cost, - version, + params.version, ) - .map_err(|_| HasherError::Params(ParamsError::InvalidValue))?; + .map_err(|_| password_hash::Error::ParamValueInvalid)?; if MAX_PWD_LENGTH < password.len() { - return Err(HasherError::Password); + return Err(password_hash::Error::Password); } if !(MIN_SALT_LENGTH..=MAX_SALT_LENGTH).contains(&salt_bytes.len()) { // TODO(tarcieri): better error types for this case - return Err(HasherError::Crypto); + return Err(password_hash::Error::Crypto); } // Validate associated data (optional param) if MAX_AD_LENGTH < ad.len() { // TODO(tarcieri): better error types for this case - return Err(HasherError::Crypto); + return Err(password_hash::Error::Crypto); } // TODO(tarcieri): improve this API to eliminate redundant checks above - let output = password_hash::Output::init_with(params.output_length, |out| { + let output = password_hash::Output::init_with(params.output_size, |out| { hasher .hash_password_into(algorithm, password, salt_bytes, ad, out) .map_err(|e| { match e { - Error::OutputTooShort => password_hash::OutputError::TooShort, - Error::OutputTooLong => password_hash::OutputError::TooLong, + Error::OutputTooShort => password_hash::Error::OutputTooShort, + Error::OutputTooLong => password_hash::Error::OutputTooLong, // Other cases are not returned from `hash_password_into` // TODO(tarcieri): finer-grained error types? _ => panic!("unhandled error type: {}", e), @@ -598,7 +552,7 @@ impl PasswordHasher for Argon2<'_> { let res = Ok(PasswordHash { algorithm: algorithm.ident(), - version: Some(version.into()), + version: Some(params.version.into()), params: params.try_into()?, salt: Some(salt), hash: Some(output), @@ -608,84 +562,9 @@ impl PasswordHasher for Argon2<'_> { } } -/// Argon2 password hash parameters. -/// -/// These are parameters which can be encoded into a PHC hash string. -#[cfg(feature = "password-hash")] -#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Params { - /// Memory size, expressed in kilobytes, between 1 and (2^32)-1. - /// - /// Value is an integer in decimal (1 to 10 digits). - pub m_cost: u32, - - /// Number of iterations, between 1 and (2^32)-1. - /// - /// Value is an integer in decimal (1 to 10 digits). - pub t_cost: u32, - - /// Degree of parallelism, between 1 and 255. - /// - /// Value is an integer in decimal (1 to 3 digits). - pub p_cost: u32, - - /// Size of the output (in bytes) - pub output_length: usize, -} - -#[cfg(feature = "password-hash")] -impl Default for Params { - fn default() -> Params { - let ctx = Argon2::default(); - - Params { - m_cost: ctx.m_cost, - t_cost: ctx.t_cost, - p_cost: ctx.threads, - output_length: 32, - } - } -} - -#[cfg(feature = "password-hash")] -impl TryFrom<&ParamsString> for Params { - type Error = HasherError; - - fn try_from(input: &ParamsString) -> Result { - let mut params = Params::default(); - - for (ident, value) in input.iter() { - match ident.as_str() { - "m" => params.m_cost = value.decimal()?, - "t" => params.t_cost = value.decimal()?, - "p" => params.p_cost = value.decimal()?, - "keyid" => (), // Ignored; correct key must be given to `Argon2` context - // TODO(tarcieri): `data` parameter - _ => return Err(ParamsError::InvalidName.into()), - } - } - - Ok(params) - } -} - -#[cfg(feature = "password-hash")] -impl<'a> TryFrom for ParamsString { - type Error = HasherError; - - fn try_from(params: Params) -> Result { - let mut output = ParamsString::new(); - output.add_decimal("m", params.m_cost)?; - output.add_decimal("t", params.t_cost)?; - output.add_decimal("p", params.p_cost)?; - Ok(output) - } -} - #[cfg(all(test, feature = "password-hash"))] mod tests { - use super::{Argon2, HasherError, Params, PasswordHasher, Salt}; + use super::{Argon2, Params, PasswordHasher, Salt}; /// Example password only: don't use this as a real password!!! const EXAMPLE_PASSWORD: &[u8] = b"hunter42"; @@ -697,7 +576,7 @@ mod tests { // Too short after decoding let salt = Salt::new("somesalt").unwrap(); - let res = argon2.hash_password(EXAMPLE_PASSWORD, None, None, Params::default(), salt); - assert_eq!(res, Err(HasherError::Crypto)); + let res = argon2.hash_password(EXAMPLE_PASSWORD, None, Params::default(), salt); + assert_eq!(res, Err(password_hash::Error::Crypto)); } } diff --git a/argon2/src/params.rs b/argon2/src/params.rs new file mode 100644 index 00000000..fb9bd436 --- /dev/null +++ b/argon2/src/params.rs @@ -0,0 +1,90 @@ +//! Argon2 password hash parameters. + +use crate::{Argon2, Version}; +use core::convert::{TryFrom, TryInto}; +use password_hash::{Decimal, ParamsString, PasswordHash}; + +/// Argon2 password hash parameters. +/// +/// These are parameters which can be encoded into a PHC hash string. +#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Params { + /// Memory size, expressed in kilobytes, between 1 and (2^32)-1. + /// + /// Value is an integer in decimal (1 to 10 digits). + pub m_cost: Decimal, + + /// Number of iterations, between 1 and (2^32)-1. + /// + /// Value is an integer in decimal (1 to 10 digits). + pub t_cost: Decimal, + + /// Degree of parallelism, between 1 and 255. + /// + /// Value is an integer in decimal (1 to 3 digits). + pub p_cost: Decimal, + + /// Size of the output (in bytes) + pub output_size: usize, + + /// Algorithm version + pub version: Version, +} + +impl Default for Params { + fn default() -> Params { + let ctx = Argon2::default(); + + Params { + m_cost: ctx.m_cost, + t_cost: ctx.t_cost, + p_cost: ctx.threads, + output_size: 32, + version: Version::default(), + } + } +} + +impl<'a> TryFrom<&'a PasswordHash<'a>> for Params { + type Error = password_hash::Error; + + fn try_from(hash: &'a PasswordHash<'a>) -> Result { + let mut params = Params::default(); + + for (ident, value) in hash.params.iter() { + match ident.as_str() { + "m" => params.m_cost = value.decimal()?, + "t" => params.t_cost = value.decimal()?, + "p" => params.p_cost = value.decimal()?, + "keyid" => (), // Ignored; correct key must be given to `Argon2` context + // TODO(tarcieri): `data` parameter + _ => return Err(password_hash::Error::ParamNameInvalid), + } + } + + if let Some(version) = hash.version { + params.version = version + .try_into() + .map_err(|_| password_hash::Error::Version)?; + } + + if let Some(output) = &hash.hash { + params.output_size = output.len(); + } + + Ok(params) + } +} + +impl<'a> TryFrom for ParamsString { + type Error = password_hash::Error; + + fn try_from(params: Params) -> Result { + let mut output = ParamsString::new(); + output.add_decimal("m", params.m_cost)?; + output.add_decimal("t", params.t_cost)?; + output.add_decimal("p", params.p_cost)?; + Ok(output) + } +} diff --git a/argon2/src/version.rs b/argon2/src/version.rs new file mode 100644 index 00000000..167b765c --- /dev/null +++ b/argon2/src/version.rs @@ -0,0 +1,50 @@ +//! Version of the algorithm. + +use crate::Error; +use core::convert::TryFrom; + +/// Version of the algorithm. +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(u32)] +pub enum Version { + /// Version 16 (0x10 in hex) + /// + /// Performs overwrite internally + V0x10 = 0x10, + + /// Version 19 (0x13 in hex, default) + /// + /// Performs XOR internally + V0x13 = 0x13, +} + +impl Version { + /// Serialize version as little endian bytes + pub(crate) fn to_le_bytes(self) -> [u8; 4] { + (self as u32).to_le_bytes() + } +} + +impl Default for Version { + fn default() -> Self { + Self::V0x13 + } +} + +impl From for u32 { + fn from(version: Version) -> u32 { + version as u32 + } +} + +impl TryFrom for Version { + type Error = Error; + + fn try_from(version_id: u32) -> Result { + match version_id { + 0x10 => Ok(Version::V0x10), + 0x13 => Ok(Version::V0x13), + _ => Err(Error::VersionInvalid), + } + } +} diff --git a/bcrypt-pbkdf/tests/test_vectors.rs b/bcrypt-pbkdf/tests/test_vectors.rs index 65139bb9..3b9a8ad5 100644 --- a/bcrypt-pbkdf/tests/test_vectors.rs +++ b/bcrypt-pbkdf/tests/test_vectors.rs @@ -70,7 +70,7 @@ fn test_openbsd_vectors() { for t in tests.iter() { let mut out = vec![0; t.out.len()]; - bcrypt_pbkdf(&t.password[..], &t.salt[..], t.rounds, &mut out).unwrap(); + bcrypt_pbkdf(t.password, &t.salt[..], t.rounds, &mut out).unwrap(); assert_eq!(out, t.out); } } diff --git a/pbkdf2/Cargo.toml b/pbkdf2/Cargo.toml index 85aa0cd4..7949bfde 100644 --- a/pbkdf2/Cargo.toml +++ b/pbkdf2/Cargo.toml @@ -17,7 +17,7 @@ crypto-mac = "0.10" rayon = { version = "1", optional = true } base64ct = { version = "1", default-features = false, optional = true } hmac = { version = "0.10", default-features = false, optional = true } -password-hash = { version = "0.1.3", default-features = false, optional = true, features = ["rand_core"] } +password-hash = { version = "=0.2.0-pre", default-features = false, optional = true, features = ["rand_core"] } sha1 = { version = "0.9", package = "sha-1", default-features = false, optional = true } sha2 = { version = "0.9", default-features = false, optional = true } diff --git a/pbkdf2/src/simple.rs b/pbkdf2/src/simple.rs index 6045a7f6..3f2232b6 100644 --- a/pbkdf2/src/simple.rs +++ b/pbkdf2/src/simple.rs @@ -9,8 +9,7 @@ use core::{ }; use hmac::Hmac; use password_hash::{ - Decimal, HasherError, Ident, McfHasher, Output, ParamsError, ParamsString, PasswordHash, - PasswordHasher, Salt, + Error, Ident, McfHasher, Output, ParamsString, PasswordHash, PasswordHasher, Result, Salt, }; use sha2::{Sha256, Sha512}; @@ -39,16 +38,11 @@ impl PasswordHasher for Pbkdf2 { &self, password: &[u8], alg_id: Option>, - version: Option, params: Params, - salt: Salt<'a>, - ) -> Result, HasherError> { + salt: impl Into>, + ) -> Result> { let algorithm = Algorithm::try_from(alg_id.unwrap_or(PBKDF2_SHA256))?; - - if version.is_some() { - return Err(HasherError::Version); - } - + let salt = salt.into(); let mut salt_arr = [0u8; 64]; let salt_bytes = salt.b64_decode(&mut salt_arr)?; @@ -95,7 +89,7 @@ pub enum Algorithm { impl Algorithm { /// Parse an [`Algorithm`] from the provided string. - pub fn new(id: impl AsRef) -> Result { + pub fn new(id: impl AsRef) -> Result { id.as_ref().parse() } @@ -128,9 +122,9 @@ impl Display for Algorithm { } impl FromStr for Algorithm { - type Err = HasherError; + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Ident::try_from(s)?.try_into() } } @@ -142,15 +136,15 @@ impl From for Ident<'static> { } impl<'a> TryFrom> for Algorithm { - type Error = HasherError; + type Error = Error; - fn try_from(ident: Ident<'a>) -> Result { + fn try_from(ident: Ident<'a>) -> Result { match ident { #[cfg(feature = "sha1")] PBKDF2_SHA1 => Ok(Algorithm::Pbkdf2Sha1), PBKDF2_SHA256 => Ok(Algorithm::Pbkdf2Sha256), PBKDF2_SHA512 => Ok(Algorithm::Pbkdf2Sha512), - _ => Err(HasherError::Algorithm), + _ => Err(Error::Algorithm), } } } @@ -175,33 +169,50 @@ impl Default for Params { } } -impl TryFrom<&ParamsString> for Params { - type Error = HasherError; +impl<'a> TryFrom<&'a PasswordHash<'a>> for Params { + type Error = Error; - fn try_from(input: &ParamsString) -> Result { - let mut output = Params::default(); + fn try_from(hash: &'a PasswordHash<'a>) -> Result { + let mut params = Params::default(); + let mut output_length = None; - for (ident, value) in input.iter() { + if hash.version.is_some() { + return Err(Error::Version); + } + + for (ident, value) in hash.params.iter() { match ident.as_str() { - "i" => output.rounds = value.decimal()?, + "i" => params.rounds = value.decimal()?, "l" => { - output.output_length = value - .decimal()? - .try_into() - .map_err(|_| ParamsError::InvalidValue)? + output_length = Some( + value + .decimal()? + .try_into() + .map_err(|_| Error::ParamValueInvalid)?, + ) } - _ => return Err(ParamsError::InvalidName.into()), + _ => return Err(Error::ParamNameInvalid), } } - Ok(output) + if let Some(len) = output_length { + if let Some(hash) = &hash.hash { + if hash.len() != len { + return Err(Error::ParamValueInvalid); + } + } + + params.output_length = len; + } + + Ok(params) } } impl<'a> TryFrom for ParamsString { - type Error = HasherError; + type Error = Error; - fn try_from(input: Params) -> Result { + fn try_from(input: Params) -> Result { let mut output = ParamsString::new(); output.add_decimal("i", input.rounds)?; output.add_decimal("l", input.output_length as u32)?; @@ -210,9 +221,7 @@ impl<'a> TryFrom for ParamsString { } impl McfHasher for Pbkdf2 { - fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result, HasherError> { - use password_hash::ParseError; - + fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result> { let mut parts = hash.split('$'); // prevent dynamic allocations by using a fixed-size buffer @@ -235,16 +244,13 @@ impl McfHasher for Pbkdf2 { let mut count_arr = [0u8; 4]; if Base64::decode(count, &mut count_arr)?.len() != 4 { - return Err(ParamsError::InvalidValue.into()); + return Err(Error::ParamValueInvalid); } let count = u32::from_be_bytes(count_arr); (count, salt, hash) } - _ => { - // TODO(tarcieri): better errors here? - return Err(ParseError::InvalidChar('?').into()); - } + _ => return Err(Error::ParamValueInvalid), }; let salt = Salt::new(b64_strip(salt))?; diff --git a/pbkdf2/tests/lib.rs b/pbkdf2/tests/lib.rs index fac028ca..2da7e820 100644 --- a/pbkdf2/tests/lib.rs +++ b/pbkdf2/tests/lib.rs @@ -1,5 +1,3 @@ -use pbkdf2; - use hmac::Hmac; use sha1::Sha1; diff --git a/pbkdf2/tests/simple.rs b/pbkdf2/tests/simple.rs index 92b92a9a..a19f43da 100644 --- a/pbkdf2/tests/simple.rs +++ b/pbkdf2/tests/simple.rs @@ -30,12 +30,12 @@ fn hash_with_default_algorithm() { }; let hash = Pbkdf2 - .hash_password(PASSWORD.as_bytes(), None, None, params.into(), salt) + .hash_password(PASSWORD.as_bytes(), None, params, salt) .unwrap(); assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha256.ident()); assert_eq!(hash.salt.unwrap().as_str(), SALT_B64); - assert_eq!(Params::try_from(&hash.params).unwrap(), params); + assert_eq!(Params::try_from(&hash).unwrap(), params); let expected_output = hex!("c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"); assert_eq!(hash.hash.unwrap().as_ref(), expected_output); @@ -48,7 +48,7 @@ fn upgrade_mcf_hash() { assert_eq!(phc_hash.algorithm, Algorithm::Pbkdf2Sha256.ident()); - let params = Params::try_from(&phc_hash.params).unwrap(); + let params = Params::try_from(&phc_hash).unwrap(); assert_eq!(params.rounds, 1024); assert_eq!(params.output_length, 32); assert_eq!( diff --git a/scrypt/Cargo.toml b/scrypt/Cargo.toml index 96ed843f..d06b9fb3 100644 --- a/scrypt/Cargo.toml +++ b/scrypt/Cargo.toml @@ -14,13 +14,13 @@ readme = "README.md" [dependencies] base64ct = { version = "1", default-features = false, features = ["alloc"], optional = true } hmac = "0.10" -password-hash = { version = "0.1.3", default-features = false, features = ["rand_core"], optional = true } +password-hash = { version = "=0.2.0-pre", default-features = false, features = ["rand_core"], optional = true } pbkdf2 = { version = "0.7", default-features = false, path = "../pbkdf2" } salsa20 = { version = "0.7", default-features = false, features = ["expose-core"] } sha2 = { version = "0.9", default-features = false } [dev-dependencies] -password-hash = { version = "0.1", features = ["rand_core"] } +password-hash = { version = "=0.2.0-pre", features = ["rand_core"] } rand_core = { version = "0.6", features = ["std"] } [features] diff --git a/scrypt/src/params.rs b/scrypt/src/params.rs index 38aa8bc3..6ab38c56 100644 --- a/scrypt/src/params.rs +++ b/scrypt/src/params.rs @@ -1,11 +1,11 @@ -use core::{mem::size_of, usize}; +use core::mem::size_of; use crate::errors::InvalidParams; #[cfg(feature = "simple")] use { core::convert::{TryFrom, TryInto}, - password_hash::{HasherError, ParamsError, ParamsString}, + password_hash::{Error, ParamsString, PasswordHash}, }; const RECOMMENDED_LOG_N: u8 = 15; @@ -120,38 +120,42 @@ impl Default for Params { #[cfg(feature = "simple")] #[cfg_attr(docsrs, doc(cfg(feature = "simple")))] -impl TryFrom<&ParamsString> for Params { - type Error = HasherError; +impl<'a> TryFrom<&'a PasswordHash<'a>> for Params { + type Error = password_hash::Error; - fn try_from(input: &ParamsString) -> Result { + fn try_from(hash: &'a PasswordHash<'a>) -> Result { let mut log_n = RECOMMENDED_LOG_N; let mut r = RECOMMENDED_R; let mut p = RECOMMENDED_P; - for (ident, value) in input.iter() { + if hash.version.is_some() { + return Err(Error::Version); + } + + for (ident, value) in hash.params.iter() { match ident.as_str() { "ln" => { log_n = value .decimal()? .try_into() - .map_err(|_| ParamsError::InvalidValue)? + .map_err(|_| Error::ParamValueInvalid)? } "r" => r = value.decimal()?, "p" => p = value.decimal()?, - _ => return Err(ParamsError::InvalidName.into()), + _ => return Err(password_hash::Error::ParamNameInvalid), } } - Params::new(log_n, r, p).map_err(|_| ParamsError::InvalidValue.into()) + Params::new(log_n, r, p).map_err(|_| password_hash::Error::ParamValueInvalid) } } #[cfg(feature = "simple")] #[cfg_attr(docsrs, doc(cfg(feature = "simple")))] impl<'a> TryFrom for ParamsString { - type Error = HasherError; + type Error = password_hash::Error; - fn try_from(input: Params) -> Result { + fn try_from(input: Params) -> Result { let mut output = ParamsString::new(); output.add_decimal("ln", input.log_n as u32)?; output.add_decimal("r", input.r)?; diff --git a/scrypt/src/simple.rs b/scrypt/src/simple.rs index fbb6cd70..cb770f00 100644 --- a/scrypt/src/simple.rs +++ b/scrypt/src/simple.rs @@ -3,10 +3,7 @@ use crate::{scrypt, Params}; use base64ct::{Base64, Encoding}; use core::convert::TryInto; -use password_hash::{ - Decimal, HasherError, Ident, McfHasher, Output, OutputError, ParamsError, PasswordHash, - PasswordHasher, Salt, -}; +use password_hash::{Error, Ident, McfHasher, Output, PasswordHash, PasswordHasher, Result, Salt}; /// Algorithm identifier pub const ALG_ID: Ident = Ident::new("scrypt"); @@ -23,26 +20,22 @@ impl PasswordHasher for Scrypt { &self, password: &[u8], alg_id: Option>, - version: Option, params: Params, - salt: Salt<'a>, - ) -> Result, HasherError> { + salt: impl Into>, + ) -> Result> { match alg_id { Some(ALG_ID) | None => (), - _ => return Err(HasherError::Algorithm), - } - - if version.is_some() { - return Err(HasherError::Version); + _ => return Err(Error::Algorithm), } + let salt = salt.into(); let mut salt_arr = [0u8; 64]; let salt_bytes = salt.b64_decode(&mut salt_arr)?; let output = Output::init_with(params.len, |out| { scrypt(password, &salt_bytes, ¶ms, out).map_err(|_e| { // TODO(tarcieri): handle output variants - OutputError::TooLong + Error::OutputTooLong }) })?; @@ -57,9 +50,7 @@ impl PasswordHasher for Scrypt { } impl McfHasher for Scrypt { - fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result, HasherError> { - use password_hash::ParseError; - + fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result> { let mut parts = hash.split('$'); let buf = [ @@ -77,27 +68,24 @@ impl McfHasher for Scrypt { [Some(""), Some("rscrypt"), Some("0"), Some(p), Some(s), Some(h), Some(""), None] => { let pvec = Base64::decode_vec(p)?; if pvec.len() != 3 { - return Err(ParamsError::InvalidValue.into()); + return Err(Error::ParamValueInvalid); } (pvec[0], pvec[1] as u32, pvec[2] as u32, s, h) } [Some(""), Some("rscrypt"), Some("1"), Some(p), Some(s), Some(h), Some(""), None] => { let pvec = Base64::decode_vec(p)?; if pvec.len() != 9 { - return Err(ParamsError::InvalidValue.into()); + return Err(Error::ParamValueInvalid); } let log_n = pvec[0]; let r = u32::from_le_bytes(pvec[1..5].try_into().unwrap()); let p = u32::from_le_bytes(pvec[5..9].try_into().unwrap()); (log_n, r, p, s, h) } - _ => { - // TODO(tarcieri): better errors here? - return Err(ParseError::InvalidChar('?').into()); - } + _ => return Err(Error::ParamValueInvalid), }; - let params = Params::new(log_n, r, p).map_err(|_| ParamsError::InvalidValue)?; + let params = Params::new(log_n, r, p).map_err(|_| Error::ParamValueInvalid)?; let salt = Salt::new(b64_strip(salt))?; let hash = Output::b64_decode(b64_strip(hash))?; diff --git a/sha-crypt/tests/mod.rs b/sha-crypt/tests/mod.rs index eaa64ace..24924139 100644 --- a/sha-crypt/tests/mod.rs +++ b/sha-crypt/tests/mod.rs @@ -42,7 +42,7 @@ fn test_sha512_crypt() { for t in tests.iter() { let params = Sha512Params::new(t.rounds).expect("Rounds error"); let result = sha512_crypt_b64(t.input.as_bytes(), t.salt.as_bytes(), ¶ms).unwrap(); - assert!(&result == t.result); + assert!(result == t.result); } }