From b8e17bc8954dadbb76b291cd48faaae7688ab64a Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Thu, 10 Aug 2023 12:39:58 +0000 Subject: [PATCH 1/4] pkcs5: downstream api changes (#1196) Signed-off-by: Arthur Gautier --- cms/tests/enveloped_data.rs | 4 ++-- cms/tests/tests_from_pkcs7_crate.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cms/tests/enveloped_data.rs b/cms/tests/enveloped_data.rs index 9cd7019e8..13744995c 100644 --- a/cms/tests/enveloped_data.rs +++ b/cms/tests/enveloped_data.rs @@ -217,7 +217,7 @@ fn reencode_enveloped_data_pwri_test() { ); let enc_pbkdf2 = kdf_alg.parameters.as_ref().unwrap().to_der().unwrap(); let pbkdf2 = Pbkdf2Params::from_der(enc_pbkdf2.as_slice()).unwrap(); - assert_eq!(hex!("7F EE A8 FD 56 8E 8F 07"), pbkdf2.salt); + assert_eq!(hex!("7F EE A8 FD 56 8E 8F 07"), pbkdf2.salt.as_ref()); assert_eq!(2048, pbkdf2.iteration_count); assert_eq!( ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.16.3.9"), @@ -431,7 +431,7 @@ fn reencode_enveloped_data_multi_test() { ); let enc_pbkdf2 = kdf_alg.parameters.as_ref().unwrap().to_der().unwrap(); let pbkdf2 = Pbkdf2Params::from_der(enc_pbkdf2.as_slice()).unwrap(); - assert_eq!(hex!("39 04 A7 33 A0 6A 1B 27"), pbkdf2.salt); + assert_eq!(hex!("39 04 A7 33 A0 6A 1B 27"), pbkdf2.salt.as_ref()); assert_eq!(2048, pbkdf2.iteration_count); assert_eq!( ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.16.3.9"), diff --git a/cms/tests/tests_from_pkcs7_crate.rs b/cms/tests/tests_from_pkcs7_crate.rs index 75448e6be..e422b141f 100644 --- a/cms/tests/tests_from_pkcs7_crate.rs +++ b/cms/tests/tests_from_pkcs7_crate.rs @@ -40,7 +40,7 @@ fn cms_decode_encrypted_key_example() { .to_der() .unwrap(); let pbkdf2 = Pbkdf2Params::from_der(enc_pbkdf2.as_slice()).unwrap(); - assert_eq!(hex!("ad2d4b4e87b34d67"), pbkdf2.salt); + assert_eq!(hex!("ad2d4b4e87b34d67"), pbkdf2.salt.as_ref()); assert_eq!(2048, pbkdf2.iteration_count); assert_eq!( 552u32, From 482cb5ba5e7f5caf2f9ee0fcf02ac86f7550abf1 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 27 Aug 2023 17:48:47 -0600 Subject: [PATCH 2/4] [WIP] pkcs5: simple `pbes2::Params` constructors Adds constructors which handle generating PHF salts and cipher IVs from a provided RNG. --- Cargo.lock | 1 + pkcs5/Cargo.toml | 2 ++ pkcs5/src/lib.rs | 3 ++ pkcs5/src/pbes2.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9dc998c11..7bc40d410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1061,6 +1061,7 @@ dependencies = [ "des", "hex-literal 0.4.1", "pbkdf2", + "rand_core", "scrypt", "sha1", "sha2", diff --git a/pkcs5/Cargo.toml b/pkcs5/Cargo.toml index 7f84aee58..7e57dd0fe 100644 --- a/pkcs5/Cargo.toml +++ b/pkcs5/Cargo.toml @@ -23,6 +23,7 @@ cbc = { version = "0.1.2", optional = true } aes = { version = "0.8.3", optional = true, default-features = false } des = { version = "0.8.1", optional = true, default-features = false } pbkdf2 = { version = "0.12.1", optional = true, default-features = false } +rand_core = { version = "0.6.4", optional = true, default-features = false } scrypt = { version = "0.11", optional = true, default-features = false } sha1 = { version = "0.10.6", optional = true, default-features = false } sha2 = { version = "0.10.8", optional = true, default-features = false } @@ -34,6 +35,7 @@ hex-literal = "0.4" alloc = [] 3des = ["dep:des", "pbes2"] des-insecure = ["dep:des", "pbes2"] +getrandom = ["rand_core/getrandom"] pbes2 = ["dep:aes", "dep:cbc", "dep:pbkdf2", "dep:scrypt", "dep:sha2"] sha1-insecure = ["dep:sha1", "pbes2"] diff --git a/pkcs5/src/lib.rs b/pkcs5/src/lib.rs index f8439ad90..550cd0d5e 100644 --- a/pkcs5/src/lib.rs +++ b/pkcs5/src/lib.rs @@ -21,6 +21,9 @@ //! the [`Decode`] and [`Encode`] traits from the [`der`] crate, and can be //! used for decoding/encoding PKCS#5 `AlgorithmIdentifier` fields. //! +//! The [`pbes2::Parameters`] struct can be used to generate new encryption +//! parameters when encrypting new keys. +//! //! [RFC 8018]: https://tools.ietf.org/html/rfc8018 #[cfg(all(feature = "alloc", feature = "pbes2"))] diff --git a/pkcs5/src/pbes2.rs b/pkcs5/src/pbes2.rs index 095ae1ddb..50483dd5e 100644 --- a/pkcs5/src/pbes2.rs +++ b/pkcs5/src/pbes2.rs @@ -18,6 +18,9 @@ use der::{ Decode, DecodeValue, Encode, EncodeValue, ErrorKind, Length, Reader, Sequence, Tag, Writer, }; +#[cfg(feature = "rand_core")] +use rand_core::CryptoRngCore; + #[cfg(all(feature = "alloc", feature = "pbes2"))] use alloc::vec::Vec; @@ -75,6 +78,42 @@ pub struct Parameters { } impl Parameters { + /// Default length of an initialization vector. + #[cfg(feature = "rand_core")] + const DEFAULT_IV_LEN: usize = AES_BLOCK_SIZE; + + /// Default length of a salt for password hashing. + #[cfg(feature = "rand_core")] + const DEFAULT_SALT_LEN: usize = 16; + + /// Generate PBES2 parameters using the recommended algorithm settings and + /// a randomly generated salt and IV. + /// + /// This is currently an alias for [`Parameters::scrypt`]. See that method + /// for more information. + #[cfg(all(feature = "pbes2", feature = "rand_core"))] + pub fn recommended(rng: &mut impl CryptoRngCore) -> Self { + Self::scrypt(rng) + } + + /// Generate PBES2 parameters using PBKDF2 as the password hashing + /// algorithm, using that algorithm's recommended algorithm settings + /// (OWASP recommended default: 600,000 rounds) along with a randomly + /// generated salt and IV. + /// + /// This will use AES-256-CBC as the encryption algorithm and SHA-256 as + /// the hash function for PBKDF2. + #[cfg(feature = "rand_core")] + pub fn pbkdf2(rng: &mut impl CryptoRngCore) -> Self { + let mut iv = [0u8; Self::DEFAULT_IV_LEN]; + rng.fill_bytes(&mut iv); + + let mut salt = [0u8; Self::DEFAULT_SALT_LEN]; + rng.fill_bytes(&mut salt); + + Self::pbkdf2_sha256_aes256cbc(600_000, &salt, iv).expect("invalid PBKDF2 parameters") + } + /// Initialize PBES2 parameters using PBKDF2-SHA256 as the password-based /// key derivation function and AES-128-CBC as the symmetric cipher. pub fn pbkdf2_sha256_aes128cbc( @@ -99,6 +138,36 @@ impl Parameters { Ok(Self { kdf, encryption }) } + /// Generate PBES2 parameters using scrypt as the password hashing + /// algorithm, using that algorithm's recommended algorithm settings + /// along with a randomly generated salt and IV. + /// + /// This will use AES-256-CBC as the encryption algorithm. + /// + /// scrypt parameters are deliberately chosen to retain compatibility with + /// OpenSSL v3. See [RustCrypto/formats#1205] for more information. + /// Parameter choices are as follows: + /// + /// - `log_n`: 14 + /// - `r`: 8 + /// - `p`: 1 + /// - salt length: 16 + /// + /// [RustCrypto/formats#1205]: https://github.com/RustCrypto/formats/issues/1205 + #[cfg(all(feature = "pbes2", feature = "rand_core"))] + pub fn scrypt(rng: &mut impl CryptoRngCore) -> Self { + let mut iv = [0u8; Self::DEFAULT_IV_LEN]; + rng.fill_bytes(&mut iv); + + let mut salt = [0u8; Self::DEFAULT_SALT_LEN]; + rng.fill_bytes(&mut salt); + + scrypt::Params::new(14, 8, 1, 32) + .ok() + .and_then(|params| Self::scrypt_aes256cbc(params, &salt, iv).ok()) + .expect("invalid scrypt parameters") + } + /// Initialize PBES2 parameters using scrypt as the password-based /// key derivation function and AES-128-CBC as the symmetric cipher. /// From ea4f6e5eaa8bf4e9e2db7a164e8d5b133eb52295 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 7 Jan 2024 16:14:58 -0700 Subject: [PATCH 3/4] Fix clippy --- cms/tests/enveloped_data.rs | 4 ++-- cms/tests/tests_from_pkcs7_crate.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cms/tests/enveloped_data.rs b/cms/tests/enveloped_data.rs index 13744995c..9cd7019e8 100644 --- a/cms/tests/enveloped_data.rs +++ b/cms/tests/enveloped_data.rs @@ -217,7 +217,7 @@ fn reencode_enveloped_data_pwri_test() { ); let enc_pbkdf2 = kdf_alg.parameters.as_ref().unwrap().to_der().unwrap(); let pbkdf2 = Pbkdf2Params::from_der(enc_pbkdf2.as_slice()).unwrap(); - assert_eq!(hex!("7F EE A8 FD 56 8E 8F 07"), pbkdf2.salt.as_ref()); + assert_eq!(hex!("7F EE A8 FD 56 8E 8F 07"), pbkdf2.salt); assert_eq!(2048, pbkdf2.iteration_count); assert_eq!( ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.16.3.9"), @@ -431,7 +431,7 @@ fn reencode_enveloped_data_multi_test() { ); let enc_pbkdf2 = kdf_alg.parameters.as_ref().unwrap().to_der().unwrap(); let pbkdf2 = Pbkdf2Params::from_der(enc_pbkdf2.as_slice()).unwrap(); - assert_eq!(hex!("39 04 A7 33 A0 6A 1B 27"), pbkdf2.salt.as_ref()); + assert_eq!(hex!("39 04 A7 33 A0 6A 1B 27"), pbkdf2.salt); assert_eq!(2048, pbkdf2.iteration_count); assert_eq!( ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.16.3.9"), diff --git a/cms/tests/tests_from_pkcs7_crate.rs b/cms/tests/tests_from_pkcs7_crate.rs index e422b141f..75448e6be 100644 --- a/cms/tests/tests_from_pkcs7_crate.rs +++ b/cms/tests/tests_from_pkcs7_crate.rs @@ -40,7 +40,7 @@ fn cms_decode_encrypted_key_example() { .to_der() .unwrap(); let pbkdf2 = Pbkdf2Params::from_der(enc_pbkdf2.as_slice()).unwrap(); - assert_eq!(hex!("ad2d4b4e87b34d67"), pbkdf2.salt.as_ref()); + assert_eq!(hex!("ad2d4b4e87b34d67"), pbkdf2.salt); assert_eq!(2048, pbkdf2.iteration_count); assert_eq!( 552u32, From 58452e4c3451b16da60798dfbb6709a5122ad59c Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 7 Jan 2024 17:26:57 -0700 Subject: [PATCH 4/4] Exclude getrandom feature on no_std targets --- .github/workflows/pkcs5.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pkcs5.yml b/.github/workflows/pkcs5.yml index 80dfef981..6a0b03a4c 100644 --- a/.github/workflows/pkcs5.yml +++ b/.github/workflows/pkcs5.yml @@ -38,7 +38,7 @@ jobs: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - uses: RustCrypto/actions/cargo-hack-install@master - - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features std + - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-features getrandom,std # TODO(tarcieri): re-enable this when we're not using unpublished prerelease dependencies # minimal-versions: