From: Dimitri John Ledkov Date: Mon, 7 Oct 2024 00:59:48 +0000 (+0100) Subject: pbkdf2: enable setting minimum password length at build time X-Git-Tag: 4.0-PRE-CLANG-FORMAT-WEBKIT~137 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=71ed0fc8b3cdb33cd06059416686f8972ede0248;p=thirdparty%2Fopenssl.git 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 Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/25621) --- diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index d8333369722..6c7d4f40fa6 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 8af7fd78f17..79de97d7134 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 04134aecdd5..237cb903bbe 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 b448edcb567..a79d419d7be 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 37b838b4a42..182525552de 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 f403c62cff5..d101ceead33 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