Commit 66ab2db185 for openssl.org
commit 66ab2db185ac6e473d1a445e20687b9468ba344e
Author: Dimitri John Ledkov <dimitri.ledkov@surgut.co.uk>
Date: Fri Apr 18 03:24:42 2025 +0100
fips: Align PKCS5_PBKDF2_HMAC defaults with EVP_KDF-PBKDF2
EVP_KDF-PBKDF2 has provider-dependent runtime behaviour w.r.t. lower
bounds checks. The default provider does not enforce them, but can opt
into them. The fips provider does enforce them, but can opt out.
The same is not true for the PKCS5_PBKDF2_HMAC, which always opts out
of the lower bound checks.
This leads to unexpected behaviour without user consent, they may
expect in error that when using FIPS provider the lower bound checks
will be enforced by default.
There are two popular tools for ACVP testing:
- https://github.com/cisco/libacvp/blob/9ee15db6e6c6f123f5fdd72e453eca261482ea94/app/app_kdf.c#L411
- https://github.com/smuellerDD/acvpparser/blob/e1c094ae3a708a9c45cb8b270e96c252365a5376/backends/backend_openssl_common.c#L1836
One of them creates params and then calls the one-shot EVP_KDF_derive
api, whilst the other calls the PKCS5_PBKDF2_HMAC convenience
wrapper. For the same ACVP test vectors the two produce different
results: with and without lower bounds checks.
But it seems like PKCS5_PBKDF2_HMAC is popular, as it outnumbers
EVP_KDF_derive 8x when doing a global code search on github
(anecdotal, as results are skewed by the number of forks). This thus
comes down to the expectations end users have. And it feels like, at
least for this API, the FIPS 140-3 users expectation would be for the
lower bound checks to be enforced.
Modify the PKCS5_PBKDF2_HMAC wrapper around EVP_KDF_derive to not set
PKCS5 parameter, such that the provider implicit default is used
instead. Thus no change for default provider users, and FIPS
enforcement by default in the FIPS case like it always has done when
calling via EVP_KDF_derive.
Test fixes:
Tests with too short salt would fail with fips provider.
Add test that FIPS provider rejects invalid salt length.
test/certs: Re-encrypt leaf-encrypted.key with a longer salt.
This way test cases can work with a FIPS provider
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
MergeDate: Wed Mar 4 17:25:55 2026
(Merged from https://github.com/openssl/openssl/pull/27431)
diff --git a/CHANGES.md b/CHANGES.md
index ec86113531..e895ebccdf 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -464,6 +464,11 @@ OpenSSL Releases
*Igor Ustinov*
+ * Enforce lower bounds checks when using PKCS5_PBKDF2_HMAC API with
+ FIPS provider.
+
+ *Dimitri John Ledkov*
+
OpenSSL 3.6
-----------
diff --git a/NEWS.md b/NEWS.md
index 6d572984a4..3eb4fb3d4d 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -45,6 +45,18 @@ OpenSSL 4.0
* Support for the SSLv2 Client Hello was removed
+ * When using the FIPS provider via the PKCS5_PBKDF2_HMAC API,
+ password protected encrypted files will now have lower bounds
+ checks (minimum iteration count, minimum password length, salt
+ size and derived key lengths) enforced by default. Prior to
+ upgrading to this version, users may want to check if their
+ password protected key–stores are encrypted using short passwords,
+ salts, low iteration counts for PBKDF or weaker ciphers. To
+ upgrade to the new defaults one can decrypt the keys with a
+ previous OpenSSL version or the default provider, and then
+ re-encrypt them with the newer OpenSSL (using the FIPS provider),
+ thus upgrading to longer password, salt length and AES-256 CBC.
+
OpenSSL 3.6
-----------
diff --git a/crypto/evp/p5_crpt2.c b/crypto/evp/p5_crpt2.c
index b135a46a40..28110adf24 100644
--- a/crypto/evp/p5_crpt2.c
+++ b/crypto/evp/p5_crpt2.c
@@ -27,11 +27,11 @@ int ossl_pkcs5_pbkdf2_hmac_ex(const char *pass, int passlen,
OSSL_LIB_CTX *libctx, const char *propq)
{
const char *empty = "";
- int rv = 1, mode = 1;
+ int rv = 1;
EVP_KDF *kdf;
EVP_KDF_CTX *kctx;
const char *mdname = EVP_MD_get0_name(digest);
- OSSL_PARAM params[6], *p = params;
+ OSSL_PARAM params[5], *p = params;
/* Keep documented behaviour. */
if (pass == NULL) {
@@ -52,7 +52,6 @@ int ossl_pkcs5_pbkdf2_hmac_ex(const char *pass, int passlen,
return 0;
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD,
(char *)pass, (size_t)passlen);
- *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS5, &mode);
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
(unsigned char *)salt, saltlen);
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_ITER, &iter);
diff --git a/doc/man3/PKCS5_PBKDF2_HMAC.pod b/doc/man3/PKCS5_PBKDF2_HMAC.pod
index 3da271bdbf..0e20385501 100644
--- a/doc/man3/PKCS5_PBKDF2_HMAC.pod
+++ b/doc/man3/PKCS5_PBKDF2_HMAC.pod
@@ -36,6 +36,9 @@ equal to 1. RFC 2898 suggests an iteration count of at least 1000. Any
B<iter> value less than 1 is invalid; such values will result in failure
and raise the PROV_R_INVALID_ITERATION_COUNT error.
+Lower bounds checks are provider dependent and are described in the
+L<EVP_KDF-PBKDF2(7)/Supported parameters> B<OSSL_KDF_PARAM_PKCS5>.
+
B<digest> is the message digest function used in the derivation.
PKCS5_PBKDF2_HMAC_SHA1() calls PKCS5_PBKDF2_HMAC() with EVP_sha1().
diff --git a/test/certs/leaf-encrypted.key b/test/certs/leaf-encrypted.key
index 99a802dbe6..45b9422941 100644
--- a/test/certs/leaf-encrypted.key
+++ b/test/certs/leaf-encrypted.key
@@ -1,30 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
-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
+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
-----END ENCRYPTED PRIVATE KEY-----
diff --git a/test/recipes/30-test_evp_data/evppbe_pbkdf2.txt b/test/recipes/30-test_evp_data/evppbe_pbkdf2.txt
index ecf1d25ae5..97c524a72e 100644
--- a/test/recipes/30-test_evp_data/evppbe_pbkdf2.txt
+++ b/test/recipes/30-test_evp_data/evppbe_pbkdf2.txt
@@ -13,6 +13,7 @@
Title = PBKDF2 tests (using PBE)
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -20,6 +21,7 @@ iter = 1
MD = sha1
Key = 0c60c80f961f0e71f3a9b524af6012062fe037a6
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -27,6 +29,17 @@ iter = 1
MD = sha256
Key = 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
+Availablein = fips
+PBE = pbkdf2
+Password = "password"
+Salt = "salt"
+iter = 1
+MD = sha256
+Key = 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
+Result = PBKDF2_ERROR
+Reason = invalid salt length
+
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -34,6 +47,7 @@ iter = 1
MD = sha512
Key = 867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -41,6 +55,7 @@ iter = 2
MD = sha1
Key = ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -48,6 +63,7 @@ iter = 2
MD = sha256
Key = ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -55,6 +71,7 @@ iter = 2
MD = sha512
Key = e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -62,6 +79,7 @@ iter = 4096
MD = sha1
Key = 4b007901b765489abead49d926f721d065a429c1
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -69,6 +87,7 @@ iter = 4096
MD = sha256
Key = c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -97,6 +116,7 @@ iter = 4096
MD = sha512
Key = 8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8
+Availablein = default
PBE = pbkdf2
Password = 7061737300776f7264
Salt = 7361006c74
@@ -104,6 +124,7 @@ iter = 4096
MD = sha1
Key = 56fa6aa75548099dcc37d7f03425e0c3
+Availablein = default
PBE = pbkdf2
Password = 7061737300776f7264
Salt = 7361006c74
@@ -111,6 +132,7 @@ iter = 4096
MD = sha256
Key = 89b69d0516f829893c696226650a8687
+Availablein = default
PBE = pbkdf2
Password = 7061737300776f7264
Salt = 7361006c74
@@ -118,6 +140,7 @@ iter = 4096
MD = sha512
Key = 9d9e9c4cd21fe4be24d5b8244c759665
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -125,6 +148,7 @@ iter = 4096
MD = sha3-224
Key = 691292bc3683d7d41ea2910f5b3eed23
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -132,6 +156,7 @@ iter = 4096
MD = sha3-256
Key = 778b6e237a0f49621549ff70d218d208
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -139,6 +164,7 @@ iter = 4096
MD = sha3-384
Key = 9a5f1e45e8b83f1b259ba72d11c59087
+Availablein = default
PBE = pbkdf2
Password = "password"
Salt = "salt"
@@ -148,6 +174,7 @@ Key = 2bfaf2d5ceb6d10f5e262cd902488cfd
Title = PBKDF2 tests for empty and NULL inputs
+Availablein = default
PBE = pbkdf2
Password = ""
Salt = "salt"
@@ -155,6 +182,7 @@ iter = 1
MD = sha1
Key = a33dddc30478185515311f8752895d36ea4363a2
+Availablein = default
PBE = pbkdf2
Password = ""
Salt = "salt"
@@ -162,6 +190,7 @@ iter = 1
MD = sha256
Key = f135c27993baf98773c5cdb40a5706ce6a345cde
+Availablein = default
PBE = pbkdf2
Password = ""
Salt = "salt"
@@ -169,6 +198,7 @@ iter = 1
MD = sha512
Key = 00ef42cdbfc98d29db20976608e455567fdddf14
+Availablein = default
PBE = pbkdf2
Password = NULL
Salt = "salt"
@@ -176,6 +206,7 @@ iter = 1
MD = sha1
Key = a33dddc30478185515311f8752895d36ea4363a2
+Availablein = default
PBE = pbkdf2
Password = NULL
Salt = "salt"
@@ -183,6 +214,7 @@ iter = 1
MD = sha256
Key = f135c27993baf98773c5cdb40a5706ce6a345cde
+Availablein = default
PBE = pbkdf2
Password = NULL
Salt = "salt"
diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
index b146725c49..d2021743a6 100644
--- a/test/recipes/80-test_cms.t
+++ b/test/recipes/80-test_cms.t
@@ -372,10 +372,10 @@ my @smime_cms_tests = (
[ "{cmd1}", @prov, "-encrypt", "-in", $smcont, "-outform", "PEM", "-aes-128-gcm",
"-kekcipher", "aes-128-cbc",
"-stream", "-out", "{output}.cms",
- "-pwri_password", "test" ],
+ "-pwri_password", "testtest" ],
[ "{cmd2}", "-decrypt", "-in", "{output}.cms", "-out", "{output}.txt",
"-inform", "PEM",
- "-pwri_password", "test" ],
+ "-pwri_password", "testtest" ],
\&final_compare
],
@@ -393,10 +393,10 @@ my @smime_cms_tests = (
[ "enveloped content test streaming PEM format, AES-128-CBC cipher, password",
[ "{cmd1}", @prov, "-encrypt", "-in", $smcont, "-outform", "PEM", "-aes128",
"-stream", "-out", "{output}.cms",
- "-pwri_password", "test" ],
+ "-pwri_password", "testtest" ],
[ "{cmd2}", @prov, "-decrypt", "-in", "{output}.cms", "-out", "{output}.txt",
"-inform", "PEM",
- "-pwri_password", "test" ],
+ "-pwri_password", "testtest" ],
\&final_compare
],