diff --git a/doc/man7/EVP_KDF-PBKDF2.pod b/doc/man7/EVP_KDF-PBKDF2.pod index 9e9195bf40c88..a1ad3b088f55a 100644 --- a/doc/man7/EVP_KDF-PBKDF2.pod +++ b/doc/man7/EVP_KDF-PBKDF2.pod @@ -59,6 +59,16 @@ The default provider uses a default mode of 1 for backwards compatibility, and the FIPS provider uses a default mode of 0. The value string is expected to be a decimal number 0 or 1. +Setting this to zero will ignore the error and set the approved "fips-indicator" +to 0. + +=item "fips-indicator" (B) + +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 the "pkcs5" +is set to 1 and the derived key length, salt length or iteration count test +fails. +This option is used by the OpenSSL FIPS provider. =back diff --git a/providers/implementations/kdfs/pbkdf2.c b/providers/implementations/kdfs/pbkdf2.c index eb61b83516f93..cf95517f7a6b9 100644 --- a/providers/implementations/kdfs/pbkdf2.c +++ b/providers/implementations/kdfs/pbkdf2.c @@ -28,10 +28,11 @@ #include "prov/providercommon.h" #include "prov/implementations.h" #include "prov/provider_util.h" +#include "prov/fipsindicator.h" #include "pbkdf2.h" /* Constants specified in SP800-132 */ -#define KDF_PBKDF2_MIN_KEY_LEN_BITS 112 +#define KDF_PBKDF2_MIN_KEY_LEN_BITS 112 #define KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO 0xFFFFFFFF #define KDF_PBKDF2_MIN_ITERATIONS 1000 #define KDF_PBKDF2_MIN_SALT_LEN (128 / 8) @@ -46,11 +47,6 @@ static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pbkdf2_set_ctx_params; static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pbkdf2_gettable_ctx_params; static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pbkdf2_get_ctx_params; -static int pbkdf2_derive(const char *pass, size_t passlen, - const unsigned char *salt, int saltlen, uint64_t iter, - const EVP_MD *digest, unsigned char *key, - size_t keylen, int extra_checks); - typedef struct { void *provctx; unsigned char *pass; @@ -60,8 +56,14 @@ typedef struct { uint64_t iter; PROV_DIGEST digest; int lower_bound_checks; + OSSL_FIPS_IND_DECLARE } KDF_PBKDF2; +static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen, + const unsigned char *salt, int saltlen, uint64_t iter, + const EVP_MD *digest, unsigned char *key, + size_t keylen, int lower_bound_checks); + static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx); static void *kdf_pbkdf2_new_no_init(void *provctx) @@ -75,6 +77,7 @@ static void *kdf_pbkdf2_new_no_init(void *provctx) if (ctx == NULL) return NULL; ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx); return ctx; } @@ -135,6 +138,7 @@ static void *kdf_pbkdf2_dup(void *vctx) goto err; dest->iter = src->iter; dest->lower_bound_checks = src->lower_bound_checks; + OSSL_FIPS_IND_COPY(dest, src) } return dest; @@ -174,6 +178,58 @@ static int pbkdf2_set_membuf(unsigned char **buffer, size_t *buflen, return 1; } +static int pbkdf2_lower_bound_check_passed(int saltlen, uint64_t iter, + size_t keylen, int *error, + const char **desc) +{ + if ((keylen * 8) < KDF_PBKDF2_MIN_KEY_LEN_BITS) { + *error = PROV_R_KEY_SIZE_TOO_SMALL; + if (desc != NULL) + *desc = "Key size"; + return 0; + } + if (saltlen < KDF_PBKDF2_MIN_SALT_LEN) { + *error = PROV_R_INVALID_SALT_LENGTH; + if (desc != NULL) + *desc = "Salt size"; + return 0; + } + if (iter < KDF_PBKDF2_MIN_ITERATIONS) { + *error = PROV_R_INVALID_ITERATION_COUNT; + if (desc != NULL) + *desc = "Iteration count"; + return 0; + } + + return 1; +} + +#ifdef FIPS_MODULE +static int fips_lower_bound_check_enabled(OSSL_LIB_CTX *libctx) +{ + return ossl_kdf_pbkdf2_default_checks; /* Always is 1 */ +} + +static int fips_lower_bound_check_passed(KDF_PBKDF2 *ctx, size_t keylen) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int error = 0; + const char *desc = NULL; + int approved = pbkdf2_lower_bound_check_passed(ctx->salt_len, ctx->iter, + keylen, &error, &desc); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, libctx, + "PBKDF2", desc, + fips_lower_bound_check_enabled)) { + ERR_raise(ERR_LIB_PROV, error); + return 0; + } + } + return 1; +} +#endif + static int kdf_pbkdf2_derive(void *vctx, unsigned char *key, size_t keylen, const OSSL_PARAM params[]) { @@ -194,7 +250,7 @@ static int kdf_pbkdf2_derive(void *vctx, unsigned char *key, size_t keylen, } md = ossl_prov_digest_md(&ctx->digest); - return pbkdf2_derive((char *)ctx->pass, ctx->pass_len, + return pbkdf2_derive(ctx, (char *)ctx->pass, ctx->pass_len, ctx->salt, ctx->salt_len, ctx->iter, md, key, keylen, ctx->lower_bound_checks); } @@ -225,6 +281,11 @@ static int kdf_pbkdf2_set_ctx_params(void *vctx, const OSSL_PARAM params[]) if (!OSSL_PARAM_get_int(p, &pkcs5)) return 0; ctx->lower_bound_checks = pkcs5 == 0; +#ifdef FIPS_MODULE + ossl_FIPS_IND_set_settable(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE0, + ctx->lower_bound_checks); +#endif } if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL) @@ -274,8 +335,12 @@ static int kdf_pbkdf2_get_ctx_params(void *vctx, OSSL_PARAM params[]) OSSL_PARAM *p; if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) - return OSSL_PARAM_set_size_t(p, SIZE_MAX); - return -2; + if (!OSSL_PARAM_set_size_t(p, SIZE_MAX)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM((KDF_PBKDF2 *) vctx, params)) + return 0; + return 1; } static const OSSL_PARAM *kdf_pbkdf2_gettable_ctx_params(ossl_unused void *ctx, @@ -283,6 +348,7 @@ static const OSSL_PARAM *kdf_pbkdf2_gettable_ctx_params(ossl_unused void *ctx, { static const OSSL_PARAM known_gettable_ctx_params[] = { OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() OSSL_PARAM_END }; return known_gettable_ctx_params; @@ -313,7 +379,7 @@ const OSSL_DISPATCH ossl_kdf_pbkdf2_functions[] = { * - Minimum iteration count of 1000. * - Randomly-generated portion of the salt shall be at least 128 bits. */ -static int pbkdf2_derive(const char *pass, size_t passlen, +static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen, const unsigned char *salt, int saltlen, uint64_t iter, const EVP_MD *digest, unsigned char *key, size_t keylen, int lower_bound_checks) @@ -338,20 +404,21 @@ static int pbkdf2_derive(const char *pass, size_t passlen, return 0; } +#ifdef FIPS_MODULE + if (!fips_lower_bound_check_passed(ctx, keylen)) + return 0; +#else if (lower_bound_checks) { - if ((keylen * 8) < KDF_PBKDF2_MIN_KEY_LEN_BITS) { - ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL); - return 0; - } - if (saltlen < KDF_PBKDF2_MIN_SALT_LEN) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH); - return 0; - } - if (iter < KDF_PBKDF2_MIN_ITERATIONS) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT); + int error = 0; + int passed = pbkdf2_lower_bound_check_passed(saltlen, iter, keylen, + &error, NULL); + + if (!passed) { + ERR_raise(ERR_LIB_PROV, error); return 0; } } +#endif hctx_tpl = HMAC_CTX_new(); if (hctx_tpl == NULL) diff --git a/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt b/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt index 73c3c5ebd62c3..36f7375c24c9d 100644 --- a/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt +++ b/test/recipes/30-test_evp_data/evpkdf_pbkdf2.txt @@ -13,6 +13,7 @@ Title = PBKDF2 tests +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -21,6 +22,7 @@ Ctrl.iter = iter:1 Ctrl.digest = digest:sha1 Output = 0c60c80f961f0e71f3a9b524af6012062fe037a6 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -29,6 +31,7 @@ Ctrl.iter = iter:1 Ctrl.digest = digest:sha256 Output = 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -37,6 +40,7 @@ Ctrl.iter = iter:1 Ctrl.digest = digest:sha512 Output = 867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -45,6 +49,7 @@ Ctrl.iter = iter:2 Ctrl.digest = digest:sha1 Output = ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -53,6 +58,7 @@ Ctrl.iter = iter:2 Ctrl.digest = digest:sha256 Output = ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -61,6 +67,7 @@ Ctrl.iter = iter:2 Ctrl.digest = digest:sha512 Output = e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -69,6 +76,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha1 Output = 4b007901b765489abead49d926f721d065a429c1 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -77,6 +85,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha256 Output = c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -106,6 +115,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha512 Output = 8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.hexpass = hexpass:7061737300776f7264 @@ -114,6 +124,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha1 Output = 56fa6aa75548099dcc37d7f03425e0c3 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.hexpass = hexpass:7061737300776f7264 @@ -122,6 +133,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha256 Output = 89b69d0516f829893c696226650a8687 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.hexpass = hexpass:7061737300776f7264 @@ -130,6 +142,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha512 Output = 9d9e9c4cd21fe4be24d5b8244c759665 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -138,6 +151,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha3-224 Output = 691292bc3683d7d41ea2910f5b3eed23 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -146,6 +160,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha3-256 Output = 778b6e237a0f49621549ff70d218d208 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -154,6 +169,7 @@ Ctrl.iter = iter:4096 Ctrl.digest = digest:sha3-384 Output = 9a5f1e45e8b83f1b259ba72d11c59087 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass:password @@ -164,6 +180,7 @@ Output = 2bfaf2d5ceb6d10f5e262cd902488cfd Title = PBKDF2 tests for empty inputs +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass: @@ -172,6 +189,7 @@ Ctrl.iter = iter:1 Ctrl.digest = digest:sha1 Output = a33dddc30478185515311f8752895d36ea4363a2 +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass: @@ -180,6 +198,7 @@ Ctrl.iter = iter:1 Ctrl.digest = digest:sha256 Output = f135c27993baf98773c5cdb40a5706ce6a345cde +Availablein = default KDF = PBKDF2 Ctrl.pkcs5 = pkcs5:1 Ctrl.pass = pass: @@ -205,3 +224,36 @@ Ctrl.salt = salt:salt Ctrl.iter = iter:1 Ctrl.digest = digest:shake-128 Result = KDF_CTRL_ERROR + +Title = FIPS indicator tests + +# Test that the operation with unapproved parameters is rejected +FIPSversion = >=3.4.0 +KDF = PBKDF2 +Ctrl.pass = pass:password +Ctrl.salt = salt:salt +Ctrl.iter = iter:4096 +Ctrl.digest = digest:sha1 +Result = KDF_CTRL_ERROR + +# Test that the operation with unapproved parameters is reported as unapproved +FIPSversion = >=3.4.0 +KDF = PBKDF2 +Unapproved = 1 +Ctrl.pkcs5 = pkcs5:1 +Ctrl.pass = pass:password +Ctrl.salt = salt:salt +Ctrl.iter = iter:4096 +Ctrl.digest = digest:sha1 +Output = 4b007901b765489abead49d926f721d065a429c1 + +# Test that the operation with approved parameters and unapproved pkcs5 value is +# reposted as approved +FIPSversion = >=3.4.0 +KDF = PBKDF2 +Ctrl.pkcs5 = pkcs5:1 +Ctrl.pass = pass:password +Ctrl.salt = salt:saltSALTsaltSALTsaltSALTsaltSALTsalt +Ctrl.iter = iter:4096 +Ctrl.digest = digest:sha1 +Output = 043c508e57c6427036fd2c6cd2a02ec7530a412c