From 5080349bd053c069e16bd4bf839ccd7130651453 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 27 Aug 2021 17:41:20 -0600 Subject: [PATCH] Remove `McfHasher` impls for `pbkdf2` and `scrypt` crates (#219) The Modular Crypt Format is a legacy format, and the implementations provided in the `pbkdf2` and `scrypt` crates implement a bespoke RustCrypto-specific format. Since that format has been introduced, support has been added to both crates for the newer PHC String Format: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md See the "Usage" rustdoc for the respective crates for information on how to use the PHC string format. Previous releases of these respective crates support converting from the MCF hash format to the PHC format using `McfHasher::upgrade_mcf_hash`: https://docs.rs/password-hash/0.3.0/password_hash/trait.McfHasher.html#tymethod.upgrade_mcf_hash Before upgrading these crates to the new release which removes MCF hash support, please use the older releases to upgrade all of your password hashes in MCF format to the new PHC format. --- Cargo.lock | 4 +-- pbkdf2/Cargo.toml | 3 +- pbkdf2/src/simple.rs | 62 +--------------------------------------- pbkdf2/tests/simple.rs | 18 +----------- scrypt/Cargo.toml | 3 +- scrypt/src/simple.rs | 65 +----------------------------------------- scrypt/tests/mod.rs | 30 ++----------------- 7 files changed, 8 insertions(+), 177 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55725160..93e4e7f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "argon2" -version = "0.2.4" +version = "0.3.0-pre.1" dependencies = [ "base64ct", "blake2", @@ -282,7 +282,6 @@ dependencies = [ name = "pbkdf2" version = "0.8.0" dependencies = [ - "base64ct", "crypto-mac 0.11.1", "hex-literal", "hmac", @@ -383,7 +382,6 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" name = "scrypt" version = "0.7.0" dependencies = [ - "base64ct", "hmac", "password-hash", "pbkdf2", diff --git a/pbkdf2/Cargo.toml b/pbkdf2/Cargo.toml index 37709cfe..e6d3ad40 100644 --- a/pbkdf2/Cargo.toml +++ b/pbkdf2/Cargo.toml @@ -15,7 +15,6 @@ readme = "README.md" crypto-mac = "0.11" rayon = { version = "1", optional = true } -base64ct = { version = "1", default-features = false, optional = true } hmac = { version = "0.11", default-features = false, optional = true } password-hash = { version = "0.3", default-features = false, optional = true, features = ["rand_core"] } sha1 = { version = "0.9", package = "sha-1", default-features = false, optional = true } @@ -31,7 +30,7 @@ streebog = "0.9" [features] default = ["simple"] parallel = ["rayon", "std"] -simple = ["sha2", "hmac", "password-hash", "base64ct"] +simple = ["hmac", "password-hash", "sha2"] std = ["password-hash/std"] [package.metadata.docs.rs] diff --git a/pbkdf2/src/simple.rs b/pbkdf2/src/simple.rs index ca5245f5..aa215f0c 100644 --- a/pbkdf2/src/simple.rs +++ b/pbkdf2/src/simple.rs @@ -1,7 +1,6 @@ //! Implementation of the `password-hash` crate API. use crate::pbkdf2; -use base64ct::{Base64, Encoding}; use core::cmp::Ordering; use core::{ convert::{TryFrom, TryInto}, @@ -10,7 +9,7 @@ use core::{ }; use hmac::Hmac; use password_hash::{ - errors::InvalidValue, Decimal, Error, Ident, McfHasher, Output, ParamsString, PasswordHash, + errors::InvalidValue, Decimal, Error, Ident, Output, ParamsString, PasswordHash, PasswordHasher, Result, Salt, }; use sha2::{Sha256, Sha512}; @@ -230,62 +229,3 @@ impl<'a> TryFrom for ParamsString { Ok(output) } } - -impl McfHasher for Pbkdf2 { - fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result> { - let mut parts = hash.split('$'); - - // prevent dynamic allocations by using a fixed-size buffer - let buf = [ - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - ]; - - // check the format of the input: there may be no tokens before the first - // and after the last `$`, tokens must have correct information and length. - let (rounds, salt, hash) = match buf { - [Some(""), Some("rpbkdf2"), Some("0"), Some(count), Some(salt), Some(hash), Some(""), None] => - { - let mut count_arr = [0u8; 4]; - - if Base64::decode(count, &mut count_arr)?.len() != 4 { - return Err(InvalidValue::Malformed.param_error()); - } - - let count = u32::from_be_bytes(count_arr); - (count, salt, hash) - } - _ => return Err(InvalidValue::Malformed.param_error()), - }; - - let salt = Salt::new(b64_strip(salt))?; - let hash = Output::b64_decode(b64_strip(hash))?; - - let params = Params { - rounds, - output_length: hash.len(), - }; - - Ok(PasswordHash { - algorithm: PBKDF2_SHA256, - version: None, - params: params.try_into()?, - salt: Some(salt), - hash: Some(hash), - }) - } -} - -/// Strip trailing `=` signs off a Base64 value to make a valid B64 value -pub fn b64_strip(mut s: &str) -> &str { - while s.ends_with('=') { - s = &s[..(s.len() - 1)] - } - s -} diff --git a/pbkdf2/tests/simple.rs b/pbkdf2/tests/simple.rs index 2c81b2f7..c9765350 100644 --- a/pbkdf2/tests/simple.rs +++ b/pbkdf2/tests/simple.rs @@ -6,7 +6,7 @@ use hex_literal::hex; use pbkdf2::{ - password_hash::{McfHasher, PasswordHasher, Salt}, + password_hash::{PasswordHasher, Salt}, Algorithm, Params, Pbkdf2, }; use std::convert::TryFrom; @@ -40,19 +40,3 @@ fn hash_with_default_algorithm() { let expected_output = hex!("c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"); assert_eq!(hash.hash.unwrap().as_ref(), expected_output); } - -#[test] -fn upgrade_mcf_hash() { - let mcf_hash = "$rpbkdf2$0$AAAEAA==$w7Y1w07wETYY7CXw5W07TA==$wRwXwI/764oNt1HvTeQcIrqr9rfzfq/KySgcCROy1HU=$"; - let phc_hash = Pbkdf2.upgrade_mcf_hash(&mcf_hash).unwrap(); - - assert_eq!(phc_hash.algorithm, Algorithm::Pbkdf2Sha256.ident()); - - let params = Params::try_from(&phc_hash).unwrap(); - assert_eq!(params.rounds, 1024); - assert_eq!(params.output_length, 32); - assert_eq!( - Pbkdf2.verify_mcf_hash(PASSWORD.as_bytes(), &mcf_hash), - Ok(()) - ); -} diff --git a/scrypt/Cargo.toml b/scrypt/Cargo.toml index 593f6b56..778951e1 100644 --- a/scrypt/Cargo.toml +++ b/scrypt/Cargo.toml @@ -12,7 +12,6 @@ edition = "2018" readme = "README.md" [dependencies] -base64ct = { version = "1", default-features = false, features = ["alloc"], optional = true } hmac = "0.11" password-hash = { version = "0.3", default-features = false, features = ["rand_core"], optional = true } pbkdf2 = { version = "0.8", default-features = false, path = "../pbkdf2" } @@ -24,7 +23,7 @@ password-hash = { version = "0.3", features = ["rand_core"] } [features] default = ["simple", "std"] -simple = ["password-hash", "base64ct"] +simple = ["password-hash"] std = ["password-hash/std"] [package.metadata.docs.rs] diff --git a/scrypt/src/simple.rs b/scrypt/src/simple.rs index 79494ea5..9c490db5 100644 --- a/scrypt/src/simple.rs +++ b/scrypt/src/simple.rs @@ -1,12 +1,8 @@ //! Implementation of the `password-hash` crate API. use crate::{scrypt, Params}; -use base64ct::{Base64, Encoding}; use core::convert::TryInto; -use password_hash::{ - errors::InvalidValue, Decimal, Error, Ident, McfHasher, Output, PasswordHash, PasswordHasher, - Result, Salt, -}; +use password_hash::{Decimal, Error, Ident, Output, PasswordHash, PasswordHasher, Result, Salt}; /// Algorithm identifier pub const ALG_ID: Ident = Ident::new("scrypt"); @@ -56,62 +52,3 @@ impl PasswordHasher for Scrypt { }) } } - -impl McfHasher for Scrypt { - fn upgrade_mcf_hash<'a>(&self, hash: &'a str) -> Result> { - let mut parts = hash.split('$'); - - let buf = [ - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - parts.next(), - ]; - - let (log_n, r, p, salt, hash) = match buf { - [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(InvalidValue::Malformed.param_error()); - } - (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(InvalidValue::Malformed.param_error()); - } - 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) - } - _ => return Err(InvalidValue::Malformed.param_error()), - }; - - let params = Params::new(log_n, r, p).map_err(|_| InvalidValue::Malformed.param_error())?; - - let salt = Salt::new(b64_strip(salt))?; - let hash = Output::b64_decode(b64_strip(hash))?; - - Ok(PasswordHash { - algorithm: ALG_ID, - version: None, - params: params.try_into()?, - salt: Some(salt), - hash: Some(hash), - }) - } -} - -/// Strip trailing `=` signs off a Base64 value to make a valid B64 value -pub fn b64_strip(mut s: &str) -> &str { - while s.ends_with('=') { - s = &s[..(s.len() - 1)] - } - s -} diff --git a/scrypt/tests/mod.rs b/scrypt/tests/mod.rs index 59dd0893..24885876 100644 --- a/scrypt/tests/mod.rs +++ b/scrypt/tests/mod.rs @@ -2,8 +2,8 @@ use scrypt::{scrypt, Params}; #[cfg(feature = "simple")] use { - password_hash::{McfHasher, PasswordHash, PasswordVerifier}, - scrypt::{Scrypt, ALG_ID}, + password_hash::{PasswordHash, PasswordVerifier}, + scrypt::Scrypt, }; struct Test { @@ -101,29 +101,3 @@ fn simple_reject_incorrect_password() { let hash = PasswordHash::new(EXAMPLE_PASSWORD_HASH).unwrap(); assert!(Scrypt.verify_password(b"invalid", &hash).is_err()); } - -#[cfg(feature = "simple")] -fn upgrade_mcf_hash(mcf_hash: &str) { - let password = "password"; - let phc_hash = Scrypt.upgrade_mcf_hash(&mcf_hash).unwrap(); - - assert_eq!(phc_hash.algorithm, ALG_ID); - assert_eq!( - Scrypt.verify_mcf_hash(password.as_bytes(), &mcf_hash), - Ok(()) - ); -} - -#[cfg(feature = "simple")] -#[test] -fn upgrade_mcf_hash_compact() { - upgrade_mcf_hash( - "$rscrypt$0$BwgB$flwNu8vqUpUSgFzZSajHLw==$RxR+nD/NG8J5ISXHlLlt+K9ObCFtt7JlFuToDKf1dwY=$", - ); -} - -#[cfg(feature = "simple")] -#[test] -fn upgrade_mcf_hash_expanded() { - upgrade_mcf_hash("$rscrypt$1$AwEAAAAAAQAA$e+yvke4tpXh81X6xjumIDg==$P05p56eFrxfrnULgondqX0s/Kj6Ht+vCI03AO9kvMHU=$"); -}