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-----
-MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIEBBNanZFjs8CAggA
-MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBDr8bhquxPf762O3jk0LAtJBIIE
-0FQB7c06dpoHn1KBn8jTzsLIdVR0SeKUvq4edZfUPbB/6go97j48BwSzAaXY7BNL
-90GRMrNNjKZDLeNf0wwf1+67YX7neGnb+LdxpQdqEjOTGQdwTx9SG6XIqT8x4R67
-rI2DQqI937FSor9292koXQNM9Asoenn6kOCITaa8chsPdKCtFjfVmqZRMaewr5PW
-W1rooFuCVAIfgBOOaEeN7OMTJRdAGtWWOJqyLB29gXxwaI1+PnXmkHPgRGXZYz7W
-N5lTp1xvFPY+Rp/cK0DfeR5MrMYSVvrXbi6usjteJ2h0Rzcy8SY6Jnvuaoowi+rj
-lDUP0K/51tTQBd6bpsvcmc2cBx+7pg4BAkf9SnuKQpYCWPjiwrCiDJIP/o5GYIn2
-m/3K2pLahjOeGZAmhGUi0fZPZhaq37IQKwuzLDuYw1CkR7LhaJcJ9V1vXMPePgCY
-+BvjFG5z0mLDwUNvzCHQokav3Z/QT6CfOgTL10qKuBgylT1d5Cw7bfv8Lnc6C/YK
-aVXosCaKTJO8r4t7NgJX4PYQP/DZIl5CJIoUzJkrAkShLwcGtXMHVNSWx4LS60QY
-lfjz80cWWE6Tx/XjBkae0AQJW8S9nDB8/X80ox8jJ/sdd5XNZqUQhDxBP5/4GiAS
-pZlgp/IwssoG5HUnwn/4AUgD7Gdo5QRqFlkXeCFlHgjBrEHBkevHECRHAdWwrK7X
-5td662K1B9hm6EfA1R51jiOKBuM0bwYtI+tpmpT5zeDGeaOWuPUYPUFjfo9xt1Lx
-cmX3ouBt34uT/cQesPxP8gJwRdo0KqPK+KLjtQazXmHFu+FStZ29gUvhqAw9kcxq
-ps9neGAl3DJgYbB1QqqefGqFWBhJzt4toqxcgm6Z0PJSYQlxJEC3yWWs5w5wfLJJ
-KGfnpsY1IGYsbw9Caa84XqnzHosGWx724GJeb3YSwwMj311oMi9s8J/d/NpJZHOu
-uk/mQWezCfdEFSnkOtIDJWTQUtRtRfIZQp243c25E3/rJySuSoMfn4eolAGurse8
-6r7SEJ6MUjCTd3ZcA+XZAtFxPQnNBYm691hvGE6uclxYy9L6bmws9dosNlpCyvIQ
-+OYdB9Mvx9hs0KwAWZ6bnIxa3tc6Ob9mxV7ycMS43d4ShEqzy44DZD02Z0iQIRym
-1AoGwgLbc2d9NouUiw2ur5n6ByYCTHwmMSAstVovuBoS2XDF23BzLL7KuCnkHH0y
-+M6CRaXW0ceTP4DfEvBphxfj4NNEZpjm8j6ERvnnQvC5tRAaMglhg1WOvUVUtPg5
-cJPIiSn+yVuoFDnLKJ53N9NzDtUKSBQgwNGyVVPTzpfxLmjg00bNQ7eyoRr6uK0l
-ezmHemo52JpCaBGV01tnvVKzGouFN/KxP9GxvPQY8UQxVkE+E/p0UjGOpNLIDmzl
-/qVKxky9lMBoHc+neeCbOrtgwkyYgpPkKlmTTsi/yUxpbUmobFZJTUbOWrpeRbw3
-Pt9u8NeVmD4Ys/NenHIJwksOqmWxSy7IjJpzQsee1CZXV7McAYsg24tP4Bdj9aGT
-hsMyiaiNB+rjkNxhUCm39nJsaN1AoTZ3Br1UYfHrfocif12yNGOEBy2swfjQIGNH
-fjGk3px34MZZv3S0bM/ZPi9ankzAZnf8qkHoDVtsP+Gk
+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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
     ],