Commit 71ed0fc8b3 for openssl.org
commit 71ed0fc8b3cdb33cd06059416686f8972ede0248
Author: Dimitri John Ledkov <dimitri.ledkov@surgut.co.uk>
Date: Mon Oct 7 01:59:48 2024 +0100
pbkdf2: enable setting minimum password length at build time
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)
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index d833336972..6c7d4f40fa 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -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
diff --git a/doc/man7/EVP_KDF-PBKDF2.pod b/doc/man7/EVP_KDF-PBKDF2.pod
index 8af7fd78f1..79de97d713 100644
--- a/doc/man7/EVP_KDF-PBKDF2.pod
+++ b/doc/man7/EVP_KDF-PBKDF2.pod
@@ -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
diff --git a/include/openssl/proverr.h b/include/openssl/proverr.h
index 04134aecdd..237cb903bb 100644
--- a/include/openssl/proverr.h
+++ b/include/openssl/proverr.h
@@ -131,6 +131,7 @@
# 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
diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c
index b448edcb56..a79d419d7b 100644
--- a/providers/common/provider_err.c
+++ b/providers/common/provider_err.c
@@ -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),
diff --git a/providers/implementations/kdfs/pbkdf2.c b/providers/implementations/kdfs/pbkdf2.c
index 37b838b4a4..182525552d 100644
--- a/providers/implementations/kdfs/pbkdf2.c
+++ b/providers/implementations/kdfs/pbkdf2.c
@@ -36,6 +36,30 @@
#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();
diff --git a/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt b/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt
index f403c62cff..d101ceead3 100644
--- a/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt
+++ b/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt
@@ -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