diff --git a/Cargo.lock b/Cargo.lock
index 003ff7a42..feee9050e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1036,6 +1036,7 @@ dependencies = [
"cbc",
"der",
"des",
+ "getrandom 0.4.2",
"hex-literal",
"pbkdf2",
"rand_core 0.10.1",
diff --git a/pkcs5/Cargo.toml b/pkcs5/Cargo.toml
index 43e0fe8f1..8383bfa53 100644
--- a/pkcs5/Cargo.toml
+++ b/pkcs5/Cargo.toml
@@ -24,6 +24,7 @@ aes = { version = "0.9", optional = true, default-features = false }
cbc = { version = "0.2", optional = true }
des = { version = "0.9", optional = true, default-features = false }
pbkdf2 = { version = "0.13", optional = true, default-features = false, features = ["hmac"] }
+getrandom = { version = "0.4", optional = true, features = ["sys_rng"] }
rand_core = { version = "0.10", optional = true, default-features = false }
scrypt = { version = "0.12", optional = true, default-features = false }
sha1 = { version = "0.11", optional = true, default-features = false }
@@ -37,7 +38,9 @@ alloc = []
3des = ["dep:des", "pbes2"]
des-insecure = ["dep:des", "pbes2"]
+getrandom = ["dep:getrandom", "rand_core"]
pbes2 = ["dep:aes", "dep:cbc", "dep:pbkdf2", "dep:scrypt", "dep:sha2"]
+rand_core = ["dep:rand_core"]
sha1-insecure = ["dep:sha1", "pbes2"]
[lints]
diff --git a/pkcs5/src/lib.rs b/pkcs5/src/lib.rs
index 56ca6c246..59aaac373 100644
--- a/pkcs5/src/lib.rs
+++ b/pkcs5/src/lib.rs
@@ -40,7 +40,15 @@ pub use scrypt;
#[cfg(all(feature = "alloc", feature = "pbes2"))]
use alloc::vec::Vec;
-/// Supported PKCS#5 password-based encryption schemes.
+/// Configuration for supported PKCS#5 password-based encryption schemes.
+///
+///
+/// Security Warning
+///
+/// This type should not be used to encrypt multiple plaintexts under the same IV/salt values.
+///
+/// Instead, new values should be randomly generated for every usage.
+///
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
@@ -57,13 +65,25 @@ pub enum EncryptionScheme {
}
impl EncryptionScheme {
+ /// Generate PBES2 parameters using recommended algorithm settings and parameters (salt/IV)
+ /// generated using the system's secure random number generator.
+ ///
+ /// # Panics
+ /// In the event the system's secure random generator experiences an internal failure.
+ #[cfg(all(feature = "pbes2", feature = "getrandom"))]
+ #[must_use]
+ #[track_caller]
+ pub fn generate() -> Self {
+ Self::Pbes2(pbes2::Parameters::generate())
+ }
+
/// Attempt to decrypt the given ciphertext, allocating and returning a byte vector containing
/// the plaintext.
///
/// # Errors
/// Returns an error if the algorithm specified in this scheme's parameters is unsupported
- /// (e.g. PBES1 is completely unsupported), or if the ciphertext is malformed (e.g. not a
- /// multiple of a block mode's padding).
+ /// (e.g. PBES1 is completely unsupported), or if the ciphertext is malformed (e.g. ciphertext
+ /// length is not a multiple of a block mode's padding).
#[cfg(all(feature = "alloc", feature = "pbes2"))]
pub fn decrypt(&self, password: impl AsRef<[u8]>, ciphertext: &[u8]) -> Result> {
match self {
diff --git a/pkcs5/src/pbes2.rs b/pkcs5/src/pbes2.rs
index a53cf2af6..aed1cb4ef 100644
--- a/pkcs5/src/pbes2.rs
+++ b/pkcs5/src/pbes2.rs
@@ -18,7 +18,7 @@ use der::{
asn1::{AnyRef, ObjectIdentifier, OctetStringRef},
};
-#[cfg(feature = "rand_core")]
+#[cfg(all(feature = "pbes2", feature = "rand_core"))]
use rand_core::TryCryptoRng;
#[cfg(all(feature = "alloc", feature = "pbes2"))]
@@ -67,6 +67,18 @@ const DES_BLOCK_SIZE: usize = 8;
/// encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
/// ```
///
+/// These define a set of algorithms for password-based key derivation, as well as a salt value
+/// (typically randomly generated) to provide to the KDF algorithm, along with an encryption
+/// algorithm and its associated IV/nonce (typically randomly generated).
+///
+///
+/// Security Warning
+///
+/// This type should not be used to encrypt multiple plaintexts under the same IV/salt values.
+///
+/// Instead, new values should be randomly generated for every usage.
+///
+///
/// [RFC 8018 Appendix A.4]: https://tools.ietf.org/html/rfc8018#appendix-A.4
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Parameters {
@@ -79,15 +91,27 @@ pub struct Parameters {
impl Parameters {
/// Default length of an initialization vector.
- #[cfg(feature = "rand_core")]
+ #[cfg(all(feature = "pbes2", feature = "rand_core"))]
const DEFAULT_IV_LEN: usize = AES_BLOCK_SIZE;
/// Default length of a salt for password hashing.
- #[cfg(feature = "rand_core")]
+ #[cfg(all(feature = "pbes2", feature = "rand_core"))]
const DEFAULT_SALT_LEN: usize = 16;
- /// Generate PBES2 parameters using the recommended algorithm settings and
- /// a randomly generated salt and IV.
+ /// Generate PBES2 parameters using recommended algorithm settings and parameters (salt/IV)
+ /// generated using the system's secure random number generator.
+ ///
+ /// # Panics
+ /// In the event the system's secure random generator experiences an internal failure.
+ #[cfg(all(feature = "pbes2", feature = "getrandom"))]
+ #[must_use]
+ #[track_caller]
+ pub fn generate() -> Self {
+ Self::generate_recommended(&mut getrandom::SysRng).expect("random generation failure")
+ }
+
+ /// Generate PBES2 parameters using the recommended algorithm settings and a randomly generated
+ /// salt and IV.
///
/// This is currently an alias for [`Parameters::generate_scrypt`]. See that method
/// for more information.
@@ -109,7 +133,7 @@ impl Parameters {
///
/// # Errors
/// Returns [`Error::Rng`] in the event the random number generator `R` fails.
- #[cfg(feature = "rand_core")]
+ #[cfg(all(feature = "pbes2", feature = "rand_core"))]
pub fn generate_pbkdf2(rng: &mut R) -> Result {
let mut iv = [0u8; Self::DEFAULT_IV_LEN];
rng.try_fill_bytes(&mut iv).map_err(|_| Error::Rng)?;