From 602495c9ddf43404b41c124dbd40b4e75746dea3 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 18 Mar 2026 17:56:35 -0600 Subject: [PATCH 1/2] pbkdf2: remove `sha1` feature This came up in some discussions about hypothetical FIPS support, noting NIST already recommends against SHA-1 and plans on complete phase-out by December 31st, 2030: https://www.nist.gov/news-events/news/2022/12/nist-retires-sha-1-cryptographic-algorithm That article notes on that date FIPS 180-5 will be published which removes SHA-1, and a revision to SP 800-131A will reflect that SHA-1 is unsuitable for use in FIPS modules. Though we're not quite to that deadline, this PR proposes to remove direct support for SHA-1 in anticipitation of these changes. --- .github/workflows/pbkdf2.yml | 1 + Cargo.lock | 12 ------------ pbkdf2/Cargo.toml | 3 --- pbkdf2/benches/lib.rs | 11 ----------- pbkdf2/src/algorithm.rs | 18 ------------------ pbkdf2/src/lib.rs | 18 +++++++----------- pbkdf2/tests/pbkdf2.rs | 19 +------------------ 7 files changed, 9 insertions(+), 73 deletions(-) diff --git a/.github/workflows/pbkdf2.yml b/.github/workflows/pbkdf2.yml index 7cf32d8e..c23359b2 100644 --- a/.github/workflows/pbkdf2.yml +++ b/.github/workflows/pbkdf2.yml @@ -63,4 +63,5 @@ jobs: - run: cargo test --no-default-features --features kdf - run: cargo test --no-default-features --features password-hash - run: cargo test --no-default-features --features rand_core + - run: cargo test --no-default-features --features sha2 - run: cargo test --all-features --release diff --git a/Cargo.lock b/Cargo.lock index b88acede..0c103c04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,6 @@ dependencies = [ "kdf", "mcf", "password-hash", - "sha1", "sha2", "streebog", ] @@ -626,17 +625,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "sha1" -version = "0.11.0-rc.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - [[package]] name = "sha2" version = "0.11.0-rc.5" diff --git a/pbkdf2/Cargo.toml b/pbkdf2/Cargo.toml index d3bcb342..b5054a80 100644 --- a/pbkdf2/Cargo.toml +++ b/pbkdf2/Cargo.toml @@ -21,14 +21,12 @@ hmac = { version = "0.13.0-rc.5", optional = true, default-features = false } kdf = { version = "0.1", optional = true } mcf = { version = "0.6", optional = true, default-features = false, features = ["base64"] } password-hash = { version = "0.6", default-features = false, optional = true } -sha1 = { version = "0.11.0-rc.5", default-features = false, optional = true } sha2 = { version = "0.11.0-rc.5", default-features = false, optional = true } [dev-dependencies] belt-hash = "0.2.0-rc.5" hmac = "0.13.0-rc.5" hex-literal = "1" -sha1 = "0.11.0-rc.5" sha2 = "0.11.0-rc.5" streebog = "0.11.0-rc.5" @@ -41,7 +39,6 @@ getrandom = ["password-hash/getrandom"] mcf = ["sha2", "password-hash", "dep:mcf"] phc = ["password-hash/phc", "sha2"] rand_core = ["password-hash/rand_core"] -sha1 = ["hmac", "dep:sha1"] sha2 = ["hmac", "dep:sha2"] [package.metadata.docs.rs] diff --git a/pbkdf2/benches/lib.rs b/pbkdf2/benches/lib.rs index 7b67c98e..3045029b 100644 --- a/pbkdf2/benches/lib.rs +++ b/pbkdf2/benches/lib.rs @@ -7,17 +7,6 @@ use hmac::Hmac; use pbkdf2::pbkdf2; use test::Bencher; -#[bench] -pub fn pbkdf2_hmac_sha1_16384_20(bh: &mut Bencher) { - let password = b"my secure password"; - let salt = b"salty salt"; - let mut buf = [0u8; 20]; - bh.iter(|| { - pbkdf2::>(password, salt, 16_384, &mut buf).unwrap(); - test::black_box(&buf); - }); -} - #[bench] pub fn pbkdf2_hmac_sha256_16384_20(bh: &mut Bencher) { let password = b"my secure password"; diff --git a/pbkdf2/src/algorithm.rs b/pbkdf2/src/algorithm.rs index 5c7b8880..1b26865f 100644 --- a/pbkdf2/src/algorithm.rs +++ b/pbkdf2/src/algorithm.rs @@ -11,10 +11,6 @@ use {core::str::FromStr, password_hash::Error}; #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[non_exhaustive] pub enum Algorithm { - /// PBKDF2-HMAC-SHA1 a.k.a. `$pbkdf2` - #[cfg(feature = "sha1")] - Pbkdf2Sha1, - /// PBKDF2-HMAC-SHA-256 a.k.a. `$pbkdf2-sha256` #[cfg(feature = "sha2")] Pbkdf2Sha256, @@ -25,10 +21,6 @@ pub enum Algorithm { } impl Algorithm { - /// PBKDF2 (SHA-1) algorithm identifier - #[cfg(feature = "sha1")] - pub const PBKDF2_SHA1_ID: &'static str = "pbkdf2"; - /// PBKDF2 (SHA-256) algorithm identifier #[cfg(feature = "sha2")] pub const PBKDF2_SHA256_ID: &'static str = "pbkdf2-sha256"; @@ -37,10 +29,6 @@ impl Algorithm { #[cfg(feature = "sha2")] pub const PBKDF2_SHA512_ID: &'static str = "pbkdf2-sha512"; - /// PBKDF2 (SHA-1) algorithm identifier - #[cfg(all(feature = "phc", feature = "sha1"))] - pub(crate) const PBKDF2_SHA1_IDENT: Ident = Ident::new_unwrap(Self::PBKDF2_SHA1_ID); - /// PBKDF2 (SHA-256) algorithm identifier #[cfg(feature = "phc")] pub(crate) const PBKDF2_SHA256_IDENT: Ident = Ident::new_unwrap(Self::PBKDF2_SHA256_ID); @@ -67,8 +55,6 @@ impl Algorithm { /// Get the Modular Crypt Format algorithm identifier for this algorithm. pub const fn to_str(self) -> &'static str { match self { - #[cfg(feature = "sha1")] - Algorithm::Pbkdf2Sha1 => Self::PBKDF2_SHA1_ID, #[cfg(feature = "sha2")] Algorithm::Pbkdf2Sha256 => Self::PBKDF2_SHA256_ID, #[cfg(feature = "sha2")] @@ -109,8 +95,6 @@ impl FromStr for Algorithm { impl From for Ident { fn from(alg: Algorithm) -> Ident { match alg { - #[cfg(feature = "sha1")] - Algorithm::Pbkdf2Sha1 => Algorithm::PBKDF2_SHA1_IDENT, Algorithm::Pbkdf2Sha256 => Algorithm::PBKDF2_SHA256_IDENT, Algorithm::Pbkdf2Sha512 => Algorithm::PBKDF2_SHA512_IDENT, } @@ -123,8 +107,6 @@ impl<'a> TryFrom<&'a str> for Algorithm { fn try_from(name: &'a str) -> password_hash::Result { match name { - #[cfg(feature = "sha1")] - Self::PBKDF2_SHA1_ID => Ok(Algorithm::Pbkdf2Sha1), #[cfg(feature = "sha2")] Self::PBKDF2_SHA256_ID => Ok(Algorithm::Pbkdf2Sha256), #[cfg(feature = "sha2")] diff --git a/pbkdf2/src/lib.rs b/pbkdf2/src/lib.rs index 272cf01c..3f011d96 100644 --- a/pbkdf2/src/lib.rs +++ b/pbkdf2/src/lib.rs @@ -86,12 +86,12 @@ pub mod mcf; #[cfg(feature = "phc")] pub mod phc; -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] mod algorithm; -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] mod params; -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] pub use crate::{algorithm::Algorithm, params::Params}; #[cfg(feature = "hmac")] pub use hmac; @@ -99,8 +99,6 @@ pub use hmac; pub use password_hash; #[cfg(any(feature = "mcf", feature = "phc"))] pub use password_hash::{PasswordHasher, PasswordVerifier}; -#[cfg(feature = "sha1")] -pub use sha1; #[cfg(feature = "sha2")] pub use sha2; @@ -261,7 +259,7 @@ where /// pbkdf2_hmac_with_params(b"password", b"salt", algorithm, params, &mut buf); /// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e4563752b7a28f6eaab614ee005178")); /// ``` -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] pub fn pbkdf2_hmac_with_params( password: &[u8], salt: &[u8], @@ -270,8 +268,6 @@ pub fn pbkdf2_hmac_with_params( out: &mut [u8], ) { let f = match algorithm { - #[cfg(feature = "sha1")] - Algorithm::Pbkdf2Sha1 => pbkdf2_hmac::, #[cfg(feature = "sha2")] Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::, #[cfg(feature = "sha2")] @@ -287,7 +283,7 @@ pub fn pbkdf2_hmac_with_params( /// Supports the following password hash string formats, gated under the following crate features: /// - `mcf`: support for the Modular Crypt Format /// - `phc`: support for the Password Hashing Competition string format -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] #[cfg_attr(feature = "sha2", derive(Default))] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Pbkdf2 { @@ -307,7 +303,7 @@ impl Pbkdf2 { pub const SHA512: Self = Self::new(Algorithm::Pbkdf2Sha512, Params::RECOMMENDED); } -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] impl Pbkdf2 { /// Initialize [`Pbkdf2`] with default parameters. pub const fn new(algorithm: Algorithm, params: Params) -> Self { @@ -320,7 +316,7 @@ impl Pbkdf2 { } } -#[cfg(any(feature = "sha1", feature = "sha2"))] +#[cfg(feature = "sha2")] impl From for Pbkdf2 { fn from(algorithm: Algorithm) -> Self { Self { diff --git a/pbkdf2/tests/pbkdf2.rs b/pbkdf2/tests/pbkdf2.rs index 84ea9a8e..24cbcdee 100644 --- a/pbkdf2/tests/pbkdf2.rs +++ b/pbkdf2/tests/pbkdf2.rs @@ -1,7 +1,7 @@ #![cfg(feature = "hmac")] + use belt_hash::BeltHash; use hex_literal::hex; -use sha1::Sha1; use streebog::Streebog512; macro_rules! test { @@ -19,23 +19,6 @@ macro_rules! test { }; } -/// Test vectors from RFC 6070: -/// https://www.rfc-editor.org/rfc/rfc6070 -#[test] -fn pbkdf2_rfc6070() { - test!( - Sha1; - b"password", b"salt", 1, "0c60c80f961f0e71f3a9b524af6012062fe037a6"; - b"password", b"salt", 2, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; - b"password", b"salt", 4096, "4b007901b765489abead49d926f721d065a429c1"; - // this test passes, but takes a long time to execute - // b"password", b"salt", 16777216, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"; - b"passwordPASSWORDpassword", b"saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, - "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; - b"pass\0word", b"sa\0lt", 4096, "56fa6aa75548099dcc37d7f03425e0c3"; - ); -} - /// Test vectors from R 50.1.111-2016: /// https://tc26.ru/standard/rs/Р%2050.1.111-2016.pdf #[test] From a66e928029f9db3f8d8abf7234b8569aadd7be0e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 18 Mar 2026 18:16:50 -0600 Subject: [PATCH 2/2] Add back sha1 test; add changelog entry --- Cargo.lock | 12 ++++++++++++ pbkdf2/CHANGELOG.md | 2 ++ pbkdf2/Cargo.toml | 1 + pbkdf2/tests/pbkdf2.rs | 18 ++++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0c103c04..b88acede 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,6 +449,7 @@ dependencies = [ "kdf", "mcf", "password-hash", + "sha1", "sha2", "streebog", ] @@ -625,6 +626,17 @@ dependencies = [ "sha2", ] +[[package]] +name = "sha1" +version = "0.11.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + [[package]] name = "sha2" version = "0.11.0-rc.5" diff --git a/pbkdf2/CHANGELOG.md b/pbkdf2/CHANGELOG.md index d6cfb643..e275b3eb 100644 --- a/pbkdf2/CHANGELOG.md +++ b/pbkdf2/CHANGELOG.md @@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.13.0 (UNRELEASED) ### Removed - The `parallel` crate feature ([#702]) +- `sha1` feature ([#853]) [#702]: https://github.com/RustCrypto/password-hashes/pull/702 +[#853]: https://github.com/RustCrypto/password-hashes/pull/835 ## 0.12.2 (2023-07-08) ### Fixed diff --git a/pbkdf2/Cargo.toml b/pbkdf2/Cargo.toml index b5054a80..8358f5e9 100644 --- a/pbkdf2/Cargo.toml +++ b/pbkdf2/Cargo.toml @@ -27,6 +27,7 @@ sha2 = { version = "0.11.0-rc.5", default-features = false, optional = true } belt-hash = "0.2.0-rc.5" hmac = "0.13.0-rc.5" hex-literal = "1" +sha1 = "0.11.0-rc.5" sha2 = "0.11.0-rc.5" streebog = "0.11.0-rc.5" diff --git a/pbkdf2/tests/pbkdf2.rs b/pbkdf2/tests/pbkdf2.rs index 24cbcdee..bd81a2e3 100644 --- a/pbkdf2/tests/pbkdf2.rs +++ b/pbkdf2/tests/pbkdf2.rs @@ -2,6 +2,7 @@ use belt_hash::BeltHash; use hex_literal::hex; +use sha1::Sha1; use streebog::Streebog512; macro_rules! test { @@ -19,6 +20,23 @@ macro_rules! test { }; } +/// Test vectors from RFC 6070: +/// https://www.rfc-editor.org/rfc/rfc6070 +#[test] +fn pbkdf2_rfc6070() { + test!( + Sha1; + b"password", b"salt", 1, "0c60c80f961f0e71f3a9b524af6012062fe037a6"; + b"password", b"salt", 2, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; + b"password", b"salt", 4096, "4b007901b765489abead49d926f721d065a429c1"; + // this test passes, but takes a long time to execute + // b"password", b"salt", 16777216, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"; + b"passwordPASSWORDpassword", b"saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, + "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; + b"pass\0word", b"sa\0lt", 4096, "56fa6aa75548099dcc37d7f03425e0c3"; + ); +} + /// Test vectors from R 50.1.111-2016: /// https://tc26.ru/standard/rs/Р%2050.1.111-2016.pdf #[test]