diff --git a/include/wolfprovider/internal.h b/include/wolfprovider/internal.h index e0660520..a7ef0529 100644 --- a/include/wolfprovider/internal.h +++ b/include/wolfprovider/internal.h @@ -58,6 +58,13 @@ /** Maximum supported digest name size. */ #define WP_MAX_PROPS_SIZE 80 +/** Minimum accepted DH prime size in bits for parameter/key generation. */ +#ifdef HAVE_FIPS +#define WP_DH_MIN_BITS 2048 +#else +#define WP_DH_MIN_BITS 1024 +#endif + /* DER key encoding/decoding formats. */ /** SubjectPublicKeyInfo encoding format. */ #define WP_ENC_FORMAT_SPKI 1 diff --git a/src/wp_dh_kmgmt.c b/src/wp_dh_kmgmt.c index c3dba0c1..7e039ffd 100644 --- a/src/wp_dh_kmgmt.c +++ b/src/wp_dh_kmgmt.c @@ -51,13 +51,6 @@ static const int wp_dh_decode_nids[] = { /** Maximum size of the group name string. */ #define WP_MAX_DH_GROUP_NAME_SZ 10 -/* Min accepted bitlen for keygen */ -#ifdef HAVE_FIPS -#define WP_DH_MIN_BITS 2048 -#else -#define WP_DH_MIN_BITS 1024 -#endif - /** * DH key. */ @@ -1546,18 +1539,19 @@ static wp_DhGenCtx* wp_dh_gen_init(WOLFPROV_CTX* provCtx, WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_InitRng", rc); ok = 0; } - if (ok) { - if (!wp_dh_gen_set_params(ctx, params)) { - wc_FreeRng(&ctx->rng); - ok = 0; - } - } + /* Defaults first so init-time parameters override them. */ if (ok) { ctx->provCtx = provCtx; ctx->selection = selection; ctx->bits = 2048; ctx->generator = 2; } + if (ok) { + if (!wp_dh_gen_set_params(ctx, params)) { + wc_FreeRng(&ctx->rng); + ok = 0; + } + } if (!ok) { /* Rng freed when parameters fail to set. */ @@ -1621,8 +1615,15 @@ static int wp_dh_gen_set_params(wp_DhGenCtx* ctx, const OSSL_PARAM params[]) WOLFPROV_ENTER(WP_LOG_COMP_DH, "wp_dh_gen_set_params"); p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS); - if ((p != NULL) && (!OSSL_PARAM_get_int(p, &ctx->bits))) { - ok = 0; + if (p != NULL) { + if (!OSSL_PARAM_get_int(p, &ctx->bits)) { + ok = 0; + } + /* Reject a prime size below the provider minimum. */ + if (ok && (ctx->bits < WP_DH_MIN_BITS)) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL); + ok = 0; + } } if (ok) { p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PRIV_LEN); @@ -1897,12 +1898,13 @@ static wp_Dh* wp_dh_gen(wp_DhGenCtx *ctx, OSSL_CALLBACK *cb, void *cbArg) else if (!wp_dh_gen_copy_parameters(ctx, dh)) { ok = 0; } + /* Validate parameters meet minimum requirements in every path. */ + if (ok && (!wp_dh_params_validate(dh))) { + ok = 0; + } /* Generate key pair if requested. */ if (ok && ((ctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)) { - if (!wp_dh_params_validate(dh)) { - ok = 0; - } - if (ok && (!wp_dh_gen_keypair(ctx, dh))) { + if (!wp_dh_gen_keypair(ctx, dh)) { ok = 0; } } diff --git a/test/test_dh.c b/test/test_dh.c index 46fbb8b2..b2125f0a 100644 --- a/test/test_dh.c +++ b/test/test_dh.c @@ -21,6 +21,7 @@ #include "unit.h" #include #include +#include #ifdef WP_HAVE_DH @@ -1428,4 +1429,90 @@ int test_dh_import_group_no_nul(void *data) return err; } +/* Enforce the minimum prime size: a below-minimum request must not produce + * parameters, while a request at the minimum must still succeed. Sizes come + * from the provider's own WP_DH_MIN_BITS so the two cannot diverge. */ +#define TEST_DH_MIN_BITS WP_DH_MIN_BITS +#define TEST_DH_WEAK_BITS (WP_DH_MIN_BITS / 2) +int test_dh_pgen_min_bits(void *data) +{ + int err = 0; + int rc; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *keyParams = NULL; + EVP_PKEY *weakParams = NULL; + BIGNUM *prime = NULL; + + (void)data; + + PRINT_MSG("Testing DH parameter generation minimum prime size enforcement"); + + /* Below-minimum size must be rejected at set-params time. */ + ctx = EVP_PKEY_CTX_new_from_name(wpLibCtx, "DH", NULL); + if (ctx == NULL) { + err = 1; + } + if (err == 0) { + err = EVP_PKEY_paramgen_init(ctx) != 1; + } + if (err == 0) { + rc = EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, TEST_DH_WEAK_BITS); + if (rc == 1) { + PRINT_MSG("set_dh_paramgen_prime_len accepted a below-minimum size"); + err = 1; + } + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + /* A caller that ignores the set-params failure must never end up with + * below-minimum parameters: generation either fails, or the prime it + * produced is at least the minimum size. */ + if (err == 0) { + ctx = EVP_PKEY_CTX_new_from_name(wpLibCtx, "DH", NULL); + err = ctx == NULL; + } + if (err == 0) { + err = EVP_PKEY_paramgen_init(ctx) != 1; + } + if (err == 0) { + (void)EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, TEST_DH_WEAK_BITS); + if (EVP_PKEY_paramgen(ctx, &weakParams) == 1) { + err = EVP_PKEY_get_bn_param(weakParams, OSSL_PKEY_PARAM_FFC_P, + &prime) != 1; + if ((err == 0) && (BN_num_bits(prime) < TEST_DH_MIN_BITS)) { + PRINT_MSG("paramgen produced a prime below the minimum size"); + err = 1; + } + } + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + /* Size at the minimum must still succeed. */ + if (err == 0) { + ctx = EVP_PKEY_CTX_new_from_name(wpLibCtx, "DH", NULL); + err = ctx == NULL; + } + if (err == 0) { + err = EVP_PKEY_paramgen_init(ctx) != 1; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, TEST_DH_MIN_BITS) + != 1; + } + if (err == 0) { + err = EVP_PKEY_paramgen(ctx, &keyParams) != 1; + if (err != 0) { + PRINT_MSG("DH paramgen failed at the minimum prime size"); + } + } + + BN_free(prime); + EVP_PKEY_free(weakParams); + EVP_PKEY_free(keyParams); + EVP_PKEY_CTX_free(ctx); + return err; +} + #endif /* WP_HAVE_DH */ diff --git a/test/unit.c b/test/unit.c index 84aab20c..2e8ed64e 100644 --- a/test/unit.c +++ b/test/unit.c @@ -321,6 +321,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_dh_fromdata_oversize, NULL), TEST_DECL(test_dh_param_check_explicit, NULL), TEST_DECL(test_dh_import_group_no_nul, NULL), + TEST_DECL(test_dh_pgen_min_bits, NULL), #ifndef WOLFPROV_QUICKTEST TEST_DECL(test_dh_get_params, NULL), #endif diff --git a/test/unit.h b/test/unit.h index 9e89c1f0..8f38002c 100644 --- a/test/unit.h +++ b/test/unit.h @@ -331,6 +331,7 @@ int test_dh_x963_kdf(void *data); int test_dh_fromdata_oversize(void *data); int test_dh_param_check_explicit(void *data); int test_dh_import_group_no_nul(void *data); +int test_dh_pgen_min_bits(void *data); #endif /* WP_HAVE_DH */ #ifdef WP_HAVE_ECC