]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
pbkdf2: enable setting minimum password length at build time
authorDimitri John Ledkov <dimitri.ledkov@surgut.co.uk>
Mon, 7 Oct 2024 00:59:48 +0000 (01:59 +0100)
committerPauli <paul.dale@oracle.com>
Tue, 25 Nov 2025 21:48:02 +0000 (08:48 +1100)
This is required for FIPS, allow to customize minimum password length,
allow opting in doing the same for the default provider too.

Set FIPS provider default to minimum length of 8, and default provider
to 0. Controlled by -no_pbkdf2_lower_bound_check and indicated with
fips-approved indicator.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/25621)

crypto/err/openssl.txt
doc/man7/EVP_KDF-PBKDF2.pod
include/openssl/proverr.h
providers/common/provider_err.c
providers/implementations/kdfs/pbkdf2.c
test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt

index d8333369722f281562588536b0f2d4e77e76afb5..6c7d4f40fa6ce58be81e5f7b65c36d5756c5703d 100644 (file)
@@ -1170,6 +1170,7 @@ PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS:228:\
 PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED:187:parent cannot supply entropy seed
 PROV_R_PARENT_LOCKING_NOT_ENABLED:182:parent locking not enabled
 PROV_R_PARENT_STRENGTH_TOO_WEAK:194:parent strength too weak
+PROV_R_PASSWORD_STRENGTH_TOO_WEAK:254:password strength too weak
 PROV_R_PATH_MUST_BE_ABSOLUTE:219:path must be absolute
 PROV_R_PERSONALISATION_STRING_TOO_LONG:195:personalisation string too long
 PROV_R_PSS_SALTLEN_TOO_SMALL:172:pss saltlen too small
index 8af7fd78f17dcc2865513e029ade42b76c81017b..79de97d7134ffc05566b1a51e56373c9cac3f014 100644 (file)
@@ -55,6 +55,15 @@ The checks performed are:
 
 =item - the derived key length is at least 112 bits.
 
+=item - the password length is at least minimum length.
+
+The default minimum password length can be configured when OpenSSL is
+compiled by setting B<-DKDF_PBKDF2_MIN_PASSWORD_LEN=length>. If not
+set then 1 is used for the default provider and 8 for the FIPS
+provider since 4.0.0. Please consult the security policy document of
+your FIPS provider from your vendor for further details, as
+implementation often vary by each vendor.
+
 =back
 
 The default provider uses a default mode of 1 for backwards compatibility,
@@ -66,10 +75,10 @@ to return 0.
 
 This option is used by the OpenSSL FIPS provider.
 
-A getter that returns 1 if the operation is FIPS approved, or 0 otherwise.
-This may be used after calling EVP_KDF_derive. It returns 0 if "pkcs5"
-is set to 1 and the derived key length, salt length or iteration count test
-fails.
+A getter that returns 1 if the operation is FIPS approved, or 0
+otherwise.  This may be used after calling EVP_KDF_derive. It returns
+0 if "pkcs5" is set to 1 and the password length, derived key length,
+salt length or iteration count test fails.
 
 =back
 
index 04134aecdd5df2bd7878add62948c8bf7a72c078..237cb903bbe94f29a79e68fa144e75541f60c774 100644 (file)
 # define PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED         187
 # define PROV_R_PARENT_LOCKING_NOT_ENABLED                182
 # define PROV_R_PARENT_STRENGTH_TOO_WEAK                  194
+# define PROV_R_PASSWORD_STRENGTH_TOO_WEAK                254
 # define PROV_R_PATH_MUST_BE_ABSOLUTE                     219
 # define PROV_R_PERSONALISATION_STRING_TOO_LONG           195
 # define PROV_R_PSS_SALTLEN_TOO_SMALL                     172
index b448edcb56718b969428c2a21c87cd58dd516cc6..a79d419d7bed454a13dc3e582af2c3fee018619d 100644 (file)
@@ -188,6 +188,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
      "parent locking not enabled"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_PARENT_STRENGTH_TOO_WEAK),
      "parent strength too weak"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_PASSWORD_STRENGTH_TOO_WEAK),
+     "password strength too weak"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_PATH_MUST_BE_ABSOLUTE),
      "path must be absolute"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_PERSONALISATION_STRING_TOO_LONG),
index 37b838b4a42ec44455a41836f39b698fafb648e9..182525552deafc1bc8bf540f1ba5a62781813ace 100644 (file)
 #define KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO 0xFFFFFFFF
 #define KDF_PBKDF2_MIN_ITERATIONS 1000
 #define KDF_PBKDF2_MIN_SALT_LEN   (128 / 8)
+/*
+ * The Implementation Guidance for FIPS 140-3 says in section D.N
+ * "Password-Based Key Derivation for Storage Applications" that "the vendor
+ * shall document in the module's Security Policy the length of
+ * a password/passphrase used in key derivation and establish an upper bound
+ * for the probability of having this parameter guessed at random. This
+ * probability shall take into account not only the length of the
+ * password/passphrase, but also the difficulty of guessing it. The decision on
+ * the minimum length of a password used for key derivation is the vendor's,
+ * but the vendor shall at a minimum informally justify the decision."
+ *
+ * ACVP may assume 8, most FIPS modules choose 8, BC-FJA chose 14.
+ *
+ * Allow setting this for default provider too, in case consistency is
+ * desired for FIPS and Default providers. Because password being
+ * accepted on one system, but not the other, is very confusing.
+ */
+#ifndef KDF_PBKDF2_MIN_PASSWORD_LEN
+# ifdef FIPS_MODULE
+#  define KDF_PBKDF2_MIN_PASSWORD_LEN (8)
+# else
+#  define KDF_PBKDF2_MIN_PASSWORD_LEN (1)
+# endif
+#endif
 
 static OSSL_FUNC_kdf_newctx_fn kdf_pbkdf2_new;
 static OSSL_FUNC_kdf_dupctx_fn kdf_pbkdf2_dup;
@@ -183,9 +207,15 @@ static int pbkdf2_set_membuf(unsigned char **buffer, size_t *buflen,
 }
 
 static int pbkdf2_lower_bound_check_passed(int saltlen, uint64_t iter,
-                                           size_t keylen, int *error,
-                                           const char **desc)
+                                           size_t keylen, size_t passlen,
+                                           int *error, const char **desc)
 {
+    if (passlen < KDF_PBKDF2_MIN_PASSWORD_LEN) {
+        *error = PROV_R_PASSWORD_STRENGTH_TOO_WEAK;
+        if (desc != NULL)
+            *desc = "Weak password";
+        return 0;
+    }
     if ((keylen * 8) < KDF_PBKDF2_MIN_KEY_LEN_BITS) {
         *error = PROV_R_KEY_SIZE_TOO_SMALL;
         if (desc != NULL)
@@ -210,13 +240,14 @@ static int pbkdf2_lower_bound_check_passed(int saltlen, uint64_t iter,
 
 #ifdef FIPS_MODULE
 static int fips_lower_bound_check_passed(KDF_PBKDF2 *ctx, int saltlen,
-                                         uint64_t iter, size_t keylen)
+                                         uint64_t iter, size_t keylen,
+                                         size_t passlen)
 {
     OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
     int error = 0;
     const char *desc = NULL;
     int approved = pbkdf2_lower_bound_check_passed(saltlen, iter, keylen,
-                                                   &error, &desc);
+                                                   passlen, &error, &desc);
 
     if (!approved) {
         if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, libctx,
@@ -231,16 +262,17 @@ static int fips_lower_bound_check_passed(KDF_PBKDF2 *ctx, int saltlen,
 #endif
 
 static int lower_bound_check_passed(KDF_PBKDF2 *ctx, int saltlen, uint64_t iter,
-                                    size_t keylen, int lower_bound_checks)
+                                    size_t keylen, size_t passlen,
+                                    int lower_bound_checks)
 {
 #ifdef FIPS_MODULE
-    if (!fips_lower_bound_check_passed(ctx, saltlen, iter, keylen))
+    if (!fips_lower_bound_check_passed(ctx, saltlen, iter, keylen, passlen))
         return 0;
 #else
     if (lower_bound_checks) {
         int error = 0;
         int passed = pbkdf2_lower_bound_check_passed(saltlen, iter, keylen,
-                                                     &error, NULL);
+                                                     passlen, &error, NULL);
 
         if (!passed) {
             ERR_raise(ERR_LIB_PROV, error);
@@ -316,12 +348,19 @@ static int kdf_pbkdf2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
 #endif
     }
 
-    if (p.pw != NULL && !pbkdf2_set_membuf(&ctx->pass, &ctx->pass_len, p.pw))
+    if (p.pw != NULL) {
+        if (ctx->lower_bound_checks != 0
+            && p.pw->data_size < KDF_PBKDF2_MIN_PASSWORD_LEN) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_PASSWORD_STRENGTH_TOO_WEAK);
             return 0;
+        }
+        if (!pbkdf2_set_membuf(&ctx->pass, &ctx->pass_len, p.pw))
+            return 0;
+    }
 
     if (p.salt != NULL) {
         if (!lower_bound_check_passed(ctx, (int)p.salt->data_size, UINT64_MAX, SIZE_MAX,
-                                      ctx->lower_bound_checks))
+                                      SIZE_MAX, ctx->lower_bound_checks))
             return 0;
         if (!pbkdf2_set_membuf(&ctx->salt, &ctx->salt_len, p.salt))
             return 0;
@@ -331,7 +370,7 @@ static int kdf_pbkdf2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
         if (!OSSL_PARAM_get_uint64(p.iter, &iter))
             return 0;
         if (!lower_bound_check_passed(ctx, INT_MAX, iter, SIZE_MAX,
-                                      ctx->lower_bound_checks))
+                                      SIZE_MAX, ctx->lower_bound_checks))
             return 0;
         ctx->iter = iter;
     }
@@ -416,7 +455,7 @@ static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen,
         return 0;
     }
 
-    if (!lower_bound_check_passed(ctx, saltlen, iter, keylen, lower_bound_checks))
+    if (!lower_bound_check_passed(ctx, saltlen, iter, keylen, passlen, lower_bound_checks))
         return 0;
 
     hctx_tpl = HMAC_CTX_new();
index f403c62cff58d47a7b952c790cbabcd7620b731b..d101ceead33da1af650d03313cf320c6a69db633 100644 (file)
@@ -284,3 +284,38 @@ Ctrl.iter = iter:0
 Ctrl.digest = digest:sha1
 Result = KDF_CTRL_ERROR
 Reason = invalid iteration count
+
+Title = Test that a short password is blocked
+
+Availablein = fips
+FIPSversion = >=4.0.0
+KDF = PBKDF2
+Ctrl.pass = pass:7charpw
+Ctrl.salt = salt:saltSALTsaltSALTsaltSALTsaltSALTsalt
+Ctrl.iter = iter:4096
+Ctrl.digest = digest:sha256
+Result = KDF_CTRL_ERROR
+Reason = password strength too weak
+
+Title = but with a flag is Unapproved
+
+Availablein = fips
+FIPSversion = >=4.0.0
+KDF = PBKDF2
+Unapproved = 1
+Ctrl.pkcs5 = pkcs5:1
+Ctrl.pass = pass:7charpw
+Ctrl.salt = salt:saltSALTsaltSALTsaltSALTsaltSALTsalt
+Ctrl.iter = iter:4096
+Ctrl.digest = digest:sha256
+Output = 2764a19fdf2c10c0f0782eb80afeb7c3813d36d5
+
+Title = and long password is good (adjust to chosen length)
+
+Availablein = fips
+KDF = PBKDF2
+Ctrl.pass = pass:SomeModuleInsistOn20
+Ctrl.salt = salt:saltSALTsaltSALTsaltSALTsaltSALTsalt
+Ctrl.iter = iter:4096
+Ctrl.digest = digest:sha256
+Output = cba2e3bf10cd9c9f0cb2d6b2dfa62bfc2b3a7fca