Skip to content

Commit ed80722

Browse files
committed
pkcs5: add generate_* method prefix; support TryCryptoRng
Adds a `generate_*` prefix to all methods of `pbes2::Parameters` that accept an `rng` parameter and generate a random IV/salt, and changes the argument from `CryptoRng` to `TryCryptoRng`, propagating errors rather than using `expect`, which gets rid of a bunch of panic lint suppression. Also extracts constants to `ScryptParams` and `Pbkdf2Params`, and renames the constructor for the latter to `Pbkdf2Params::hmac_sha256`.
1 parent 42852d7 commit ed80722

6 files changed

Lines changed: 62 additions & 32 deletions

File tree

cms/tests/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ fn test_create_password_recipient_info() {
697697
Aes128CbcPwriEncryptor {
698698
challenge_password,
699699
key_encryption_iv,
700-
key_derivation_params: pkcs5::pbes2::Pbkdf2Params::hmac_with_sha256(
700+
key_derivation_params: pkcs5::pbes2::Pbkdf2Params::hmac_sha256(
701701
60_000, // use >=600_000 in real world applications
702702
b"salz",
703703
)

pkcs5/src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ pub enum Error {
2222
/// Encryption Failed
2323
EncryptFailed,
2424

25-
/// Pbes1 support is limited to parsing; encryption/decryption is not supported (won't fix)
25+
/// PBES1 support is limited to parsing; encryption/decryption is not supported (won't fix)
2626
#[cfg(feature = "pbes2")]
2727
NoPbes1CryptSupport,
2828

29+
/// Random number generation failure.
30+
#[cfg(feature = "rand_core")]
31+
Rng,
32+
2933
/// Algorithm is not supported
3034
///
3135
/// This may be due to a disabled crate feature
@@ -50,6 +54,8 @@ impl fmt::Display for Error {
5054
Error::NoPbes1CryptSupport => {
5155
f.write_str("PKCS#5 encryption/decryption unsupported for PBES1 (won't fix)")
5256
}
57+
#[cfg(feature = "rand_core")]
58+
Error::Rng => f.write_str("random number generation failure"),
5359
Error::UnsupportedAlgorithm { oid } => {
5460
write!(f, "PKCS#5 algorithm {oid} is unsupported")
5561
}

pkcs5/src/pbes2.rs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use der::{
1919
};
2020

2121
#[cfg(feature = "rand_core")]
22-
use rand_core::CryptoRng;
22+
use rand_core::TryCryptoRng;
2323

2424
#[cfg(all(feature = "alloc", feature = "pbes2"))]
2525
use alloc::vec::Vec;
@@ -89,11 +89,14 @@ impl Parameters {
8989
/// Generate PBES2 parameters using the recommended algorithm settings and
9090
/// a randomly generated salt and IV.
9191
///
92-
/// This is currently an alias for [`Parameters::scrypt`]. See that method
92+
/// This is currently an alias for [`Parameters::generate_scrypt`]. See that method
9393
/// for more information.
94+
///
95+
/// # Errors
96+
/// Returns [`Error::Rng`] in the event the random number generator `R` fails.
9497
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
95-
pub fn recommended<R: CryptoRng>(rng: &mut R) -> Self {
96-
Self::scrypt(rng)
98+
pub fn generate_recommended<R: TryCryptoRng>(rng: &mut R) -> Result<Self> {
99+
Self::generate_scrypt(rng)
97100
}
98101

99102
/// Generate PBES2 parameters using PBKDF2 as the password hashing
@@ -103,29 +106,31 @@ impl Parameters {
103106
///
104107
/// This will use AES-256-CBC as the encryption algorithm and SHA-256 as
105108
/// the hash function for PBKDF2.
109+
///
110+
/// # Errors
111+
/// Returns [`Error::Rng`] in the event the random number generator `R` fails.
106112
#[cfg(feature = "rand_core")]
107-
#[allow(clippy::missing_panics_doc, reason = "params should be valid")]
108-
pub fn pbkdf2<R: CryptoRng>(rng: &mut R) -> Self {
113+
pub fn generate_pbkdf2<R: TryCryptoRng>(rng: &mut R) -> Result<Self> {
109114
let mut iv = [0u8; Self::DEFAULT_IV_LEN];
110-
rng.fill_bytes(&mut iv);
115+
rng.try_fill_bytes(&mut iv).map_err(|_| Error::Rng)?;
111116

112117
let mut salt = [0u8; Self::DEFAULT_SALT_LEN];
113-
rng.fill_bytes(&mut salt);
118+
rng.try_fill_bytes(&mut salt).map_err(|_| Error::Rng)?;
114119

115-
Self::pbkdf2_sha256_aes256cbc(600_000, &salt, iv).expect("invalid PBKDF2 parameters")
120+
Self::generate_pbkdf2_sha256_aes256cbc(Pbkdf2Params::DEFAULT_SHA256_ITERATIONS, &salt, iv)
116121
}
117122

118123
/// Initialize PBES2 parameters using PBKDF2-SHA256 as the password-based
119124
/// key derivation function and AES-128-CBC as the symmetric cipher.
120125
///
121126
/// # Errors
122-
/// Propagates errors from [`Pbkdf2Params::hmac_with_sha256`].
123-
pub fn pbkdf2_sha256_aes128cbc(
127+
/// Propagates errors from [`Pbkdf2Params::hmac_sha256`].
128+
pub fn generate_pbkdf2_sha256_aes128cbc(
124129
pbkdf2_iterations: u32,
125130
pbkdf2_salt: &[u8],
126131
aes_iv: [u8; AES_BLOCK_SIZE],
127132
) -> Result<Self> {
128-
let kdf = Pbkdf2Params::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
133+
let kdf = Pbkdf2Params::hmac_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
129134
let encryption = EncryptionScheme::Aes128Cbc { iv: aes_iv };
130135
Ok(Self { kdf, encryption })
131136
}
@@ -134,13 +139,13 @@ impl Parameters {
134139
/// key derivation function and AES-256-CBC as the symmetric cipher.
135140
///
136141
/// # Errors
137-
/// Propagates errors from [`Pbkdf2Params::hmac_with_sha256`].
138-
pub fn pbkdf2_sha256_aes256cbc(
142+
/// Propagates errors from [`Pbkdf2Params::hmac_sha256`].
143+
pub fn generate_pbkdf2_sha256_aes256cbc(
139144
pbkdf2_iterations: u32,
140145
pbkdf2_salt: &[u8],
141146
aes_iv: [u8; AES_BLOCK_SIZE],
142147
) -> Result<Self> {
143-
let kdf = Pbkdf2Params::hmac_with_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
148+
let kdf = Pbkdf2Params::hmac_sha256(pbkdf2_iterations, pbkdf2_salt)?.into();
144149
let encryption = EncryptionScheme::Aes256Cbc { iv: aes_iv };
145150
Ok(Self { kdf, encryption })
146151
}
@@ -161,20 +166,26 @@ impl Parameters {
161166
/// - salt length: 16
162167
///
163168
/// [RustCrypto/formats#1205]: https://github.com/RustCrypto/formats/issues/1205
169+
///
170+
/// # Errors
171+
/// Returns [`Error::Rng`] in the event the random number generator `R` fails.
164172
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
165173
#[cfg(feature = "rand_core")]
166-
#[allow(clippy::missing_panics_doc, reason = "params should be valid")]
167-
pub fn scrypt<R: CryptoRng>(rng: &mut R) -> Self {
174+
pub fn generate_scrypt<R: TryCryptoRng>(rng: &mut R) -> Result<Self> {
168175
let mut iv = [0u8; Self::DEFAULT_IV_LEN];
169-
rng.fill_bytes(&mut iv);
176+
rng.try_fill_bytes(&mut iv).map_err(|_| Error::Rng)?;
170177

171178
let mut salt = [0u8; Self::DEFAULT_SALT_LEN];
172-
rng.fill_bytes(&mut salt);
179+
rng.try_fill_bytes(&mut salt).map_err(|_| Error::Rng)?;
180+
181+
let params = scrypt::Params::new(
182+
ScryptParams::DEFAULT_LOG_N,
183+
ScryptParams::DEFAULT_R,
184+
ScryptParams::DEFAULT_P,
185+
)
186+
.map_err(|_| Error::AlgorithmParametersInvalid { oid: SCRYPT_OID })?;
173187

174-
scrypt::Params::new(14, 8, 1)
175-
.ok()
176-
.and_then(|params| Self::scrypt_aes256cbc(params, &salt, iv).ok())
177-
.expect("invalid scrypt parameters")
188+
Self::generate_scrypt_aes256cbc(params, &salt, iv)
178189
}
179190

180191
/// Initialize PBES2 parameters using scrypt as the password-based
@@ -187,7 +198,7 @@ impl Parameters {
187198
/// Propagates errors from [`ScryptParams::from_params_and_salt`].
188199
// TODO(tarcieri): encapsulate `scrypt::Params`?
189200
#[cfg(feature = "pbes2")]
190-
pub fn scrypt_aes128cbc(
201+
pub fn generate_scrypt_aes128cbc(
191202
params: scrypt::Params,
192203
salt: &[u8],
193204
aes_iv: [u8; AES_BLOCK_SIZE],
@@ -210,7 +221,7 @@ impl Parameters {
210221
/// Propagates errors from [`ScryptParams::from_params_and_salt`].
211222
// TODO(tarcieri): encapsulate `scrypt::Params`?
212223
#[cfg(feature = "pbes2")]
213-
pub fn scrypt_aes256cbc(
224+
pub fn generate_scrypt_aes256cbc(
214225
params: scrypt::Params,
215226
salt: &[u8],
216227
aes_iv: [u8; AES_BLOCK_SIZE],

pkcs5/src/pbes2/kdf.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,18 @@ impl Pbkdf2Params {
213213
/// and [RFC 8018, §A.2](https://datatracker.ietf.org/doc/html/rfc8018#appendix-A.2)
214214
pub const MAX_ITERATION_COUNT: u32 = 100_000_000;
215215

216+
/// OWASP recommended number of iterations for PBKDF2-HMAC-SHA256.
217+
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
218+
pub(super) const DEFAULT_SHA256_ITERATIONS: u32 = 600_000;
219+
216220
const INVALID_ERR: Error = Error::AlgorithmParametersInvalid { oid: PBKDF2_OID };
217221

218-
/// Initialize PBKDF2-SHA256 with the given iteration count and salt.
222+
/// Initialize PBKDF2-HMAC-SHA256 with the given iteration count and salt.
219223
///
220224
/// # Errors
221225
/// Returns [`Error::AlgorithmParametersInvalid`] if `iteration_count` exceeds
222226
/// [`Pbkdf2Params::MAX_ITERATION_COUNT`] or `salt` exceeds [`Salt::MAX_LEN`].
223-
pub fn hmac_with_sha256(iteration_count: u32, salt: &[u8]) -> Result<Self> {
227+
pub fn hmac_sha256(iteration_count: u32, salt: &[u8]) -> Result<Self> {
224228
if iteration_count > Self::MAX_ITERATION_COUNT {
225229
return Err(Self::INVALID_ERR);
226230
}
@@ -412,6 +416,15 @@ pub struct ScryptParams {
412416
}
413417

414418
impl ScryptParams {
419+
// NOTE: scrypt parameters are deliberately chosen to retain compatibility with OpenSSL v3.
420+
// See RustCrypto/formats#1205 for more information.
421+
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
422+
pub(super) const DEFAULT_LOG_N: u8 = 14;
423+
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
424+
pub(super) const DEFAULT_R: u32 = 8;
425+
#[cfg(all(feature = "pbes2", feature = "rand_core"))]
426+
pub(super) const DEFAULT_P: u32 = 1;
427+
415428
#[cfg(feature = "pbes2")]
416429
const INVALID_ERR: Error = Error::AlgorithmParametersInvalid { oid: SCRYPT_OID };
417430

pkcs8/src/encrypted_private_key_info.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ where
6969
password: impl AsRef<[u8]>,
7070
doc: &[u8],
7171
) -> Result<SecretDocument> {
72-
let pbes2_params = pbes2::Parameters::recommended(rng);
72+
let pbes2_params = pbes2::Parameters::generate_recommended(rng)?;
7373
EncryptedPrivateKeyInfoOwned::encrypt_with(pbes2_params, password, doc)
7474
}
7575

pkcs8/tests/encrypted_private_key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ fn decrypt_ed25519_der_encpriv_aes256_scrypt() {
170170
#[cfg(feature = "encryption")]
171171
#[test]
172172
fn encrypt_ed25519_der_encpriv_aes256_pbkdf2_sha256() {
173-
let pbes2_params = pkcs5::pbes2::Parameters::pbkdf2_sha256_aes256cbc(
173+
let pbes2_params = pkcs5::pbes2::Parameters::generate_pbkdf2_sha256_aes256cbc(
174174
2048,
175175
&hex!("79d982e70df91a88"),
176176
hex!("b2d02d78b2efd9dff694cf8e0af40925"),
@@ -191,7 +191,7 @@ fn encrypt_ed25519_der_encpriv_aes256_pbkdf2_sha256() {
191191
#[cfg(feature = "encryption")]
192192
#[test]
193193
fn encrypt_ed25519_der_encpriv_aes256_scrypt() {
194-
let scrypt_params = pkcs5::pbes2::Parameters::scrypt_aes256cbc(
194+
let scrypt_params = pkcs5::pbes2::Parameters::generate_scrypt_aes256cbc(
195195
pkcs5::scrypt::Params::new(15, 8, 1).unwrap(),
196196
&hex!("E6211E2348AD69E0"),
197197
hex!("9BD0A6251F2254F9FD5963887C27CF01"),

0 commit comments

Comments
 (0)