From eedceef23d369b1902142d3ee3d8afdacca64ab7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 3 Aug 2026 23:44:26 -0500 Subject: [PATCH] wolfssl/wolfcrypt/dh.h, wolfcrypt/src/dh.c: add wc_dh_enable/disable/ is_enabled, WC_DH_INITIAL_RUNTIME_ENABLEMENT, WC_DH_HAVE_RUNTIME_ENABLEMENT. Place the enablement check AFTER key->heap/trustedGroup init in the five entry points (wc_InitDhKey_ex, wc_DhGenerateKeyPair, wc_DhAgree, wc_DhAgree_ct, _DhSetKey) so a disabled-DH early return never leaves a half-initialized key for wc_FreeDhKey to mp_clear on garbage. configure.ac: add --enable-dh=conditional; when DH is enabled (directly or via all-crypto) set it initially usable under FIPS v7 with -DWC_DH_INITIAL_RUNTIME_ENABLEMENT=1; remove the FIPS-v7 DH force-off (in FIPS v7+, disable build by default, unless building in kernel mode with DH registration enabled). linuxkm/lkcapi_glue.c: bracket LKCAPI registration with `need_dh_disable = (wc_dh_enable() == 0)` ... `if (need_dh_disable) wc_dh_disable();`, so DH is disabled on every exit path, and only by the caller that actually enabled it (wc_dh_enable returns ALREADY_E if DH was already on, so this never disables a DH some other context legitimately enabled). tests/unit.c, wolfcrypt/test/test.c: bracket the DH tests with enable/disable so they succeed regardless of runtime initial default enablement. --- configure.ac | 54 ++++++++++++++++++++++++++++++++++-------- linuxkm/lkcapi_glue.c | 11 +++++++++ tests/unit.c | 10 ++++++++ wolfcrypt/src/dh.c | 48 +++++++++++++++++++++++++++++++++++++ wolfcrypt/test/test.c | 16 ++++++++++++- wolfssl/wolfcrypt/dh.h | 12 ++++++++++ 6 files changed, 140 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index b631ebfc04..3543577b8e 100644 --- a/configure.ac +++ b/configure.ac @@ -1698,6 +1698,7 @@ then test "$enable_md5" = "" && enable_md5=yes test "$enable_ssh" = "" && test "$enable_hmac" != "no" && enable_ssh=yes test "$enable_rng_bank" = "" && enable_rng_bank=yes + test "$enable_dh" = "" && enable_dh=yes if test "$KERNEL_MODE_DEFAULTS" != "yes" then @@ -1742,7 +1743,7 @@ then test "$enable_pkcallbacks" = "" && enable_pkcallbacks=yes fi - if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -lt 7 + if test "$enable_dh" = "yes" then # Enable DH const table speedups (eliminates `-lm` math lib dependency) AM_CFLAGS="$AM_CFLAGS -DHAVE_FFDHE_2048 -DHAVE_FFDHE_3072" @@ -1775,6 +1776,8 @@ then AM_CFLAGS="$AM_CFLAGS -DHAVE_AES_DECRYPT -DHAVE_AES_ECB -DWOLFSSL_ALT_NAMES" + # Caution, asym key bits above 4096 can disrupt TLS fragment size dynamics + # (see tests/test-maxfrag.conf). DEFAULT_MAX_CLASSIC_ASYM_KEY_BITS=4096 # Enable all parsing features for ASN */ @@ -1842,13 +1845,31 @@ then test "$enable_shake256" = "" && test "$enable_sha3" = "yes" && enable_shake256=yes test "$enable_compkey" = "" && enable_compkey=yes fi - if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -lt 7 + + # Enable DH even for FIPS v7, to allow non-FIPS pro forma DH in kernel + # modules (effectively disabling it kernel-wide at runtime). The + # "conditional" value leaves it disabled (unusable) after registration. + if test "$HAVE_FIPS_VERSION" -ge 7 && test "$enable_dh" = "" && test "$enable_linuxkm_lkcapi_register" != "" then - # Enable DH const table speedups (eliminates `-lm` math lib dependency) - AM_CFLAGS="$AM_CFLAGS -DHAVE_FFDHE_2048 -DHAVE_FFDHE_3072" + for lkcapi_alg in $(echo "$enable_linuxkm_lkcapi_register" | tr ',' ' ') + do + case "$lkcapi_alg" in + all | all-kconfig | dh) enable_dh=conditional ;; + esac + done + for lkcapi_alg in $(echo "$enable_linuxkm_lkcapi_register" | tr ',' ' ') + do + case "$lkcapi_alg" in + -dh) enable_dh="" ;; + esac + done fi + + # Enable WOLFSSL_DH_EXTRA and DH const table speedups (eliminates `-lm` math lib dependency) + # No effect if DH is disabled. + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DH_EXTRA -DHAVE_FFDHE_2048 -DHAVE_FFDHE_3072" + DEFAULT_MAX_CLASSIC_ASYM_KEY_BITS=4096 - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DH_EXTRA" AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT" fi @@ -6345,16 +6366,28 @@ then ENABLED_DH_DEFAULT=yes else ENABLED_DH_DEFAULT=no + # DH is outside the boundary in FIPS v7+, but if it's enabled directly or + # via --enable-all-crypto, then callers and the test suite will expect it to + # work. + if test "$enable_dh" = "yes" + then + AM_CFLAGS="$AM_CFLAGS -DWC_DH_INITIAL_RUNTIME_ENABLEMENT=1" + fi +fi + +if test "$enable_dh" = "conditional" +then + AM_CFLAGS="$AM_CFLAGS -DWC_DH_INITIAL_RUNTIME_ENABLEMENT=0" + enable_dh=yes fi # DH AC_ARG_ENABLE([dh], - [AS_HELP_STRING([--enable-dh],[Enable DH (default: enabled). Set to "nonblock" to enable non-blocking DH key agreement via SP small mod_exp_nb])], + [AS_HELP_STRING([--enable-dh],[Enable DH (default: ${ENABLED_DH_DEFAULT}). Set to "nonblock" to enable non-blocking DH key agreement via SP small mod_exp_nb])], [ ENABLED_DH=$enableval ], [ ENABLED_DH=$ENABLED_DH_DEFAULT ] ) -# note, this will be forced back off for FIPS v7+ below. if test "$ENABLED_OPENSSH" = "yes" && test "$ENABLED_DH" = "no" then ENABLED_DH="yes" @@ -6987,9 +7020,10 @@ AS_CASE([$FIPS_VERSION], (test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_rsapss" != "no")], [ENABLED_RSAPSS="yes"; AM_CFLAGS="$AM_CFLAGS -DWC_RSA_PSS"]) - AS_IF([test "$ENABLED_DH" != "no" && - (test "$FIPS_VERSION" != "dev" || test "$enable_dh" != "yes")], - [enable_dh="no"; ENABLED_DH="no"; AM_CFLAGS="$AM_CFLAGS -DNO_DH"]) +# DH is outside the FIPS v7 boundary and off by default in v7, but freely +# configurable for non-FIPS use, provided the user first calls wc_dh_enable(), +# defines WC_DH_INITIAL_RUNTIME_ENABLEMENT to 1, or enables it explicitly with +# --enable-dh or indirectly with --enable-all-crypto. AS_IF([test "$ENABLED_ECC" != "yes" && (test "$ENABLED_FIPS_DEV" != "yes" || test "$enable_ecc" != "no")], diff --git a/linuxkm/lkcapi_glue.c b/linuxkm/lkcapi_glue.c index 1fbaf973d1..710e486925 100644 --- a/linuxkm/lkcapi_glue.c +++ b/linuxkm/lkcapi_glue.c @@ -708,6 +708,11 @@ static int linuxkm_lkcapi_register(void) #endif #ifdef LINUXKM_LKCAPI_REGISTER_DH + { + #ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + int need_dh_disable = (wc_dh_enable() == 0); + #endif + #ifdef HAVE_FFDHE_2048 REGISTER_ALG(ffdhe2048, kpp, linuxkm_test_ffdhe2048); #endif /* HAVE_FFDHE_2048 */ @@ -731,6 +736,12 @@ static int linuxkm_lkcapi_register(void) #ifdef LINUXKM_DH REGISTER_ALG(dh, kpp, linuxkm_test_dh); #endif /* LINUXKM_DH */ + + #ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + if (need_dh_disable) + (void)wc_dh_disable(); + #endif + } #endif /* LINUXKM_LKCAPI_REGISTER_DH */ #undef REGISTER_ALG diff --git a/tests/unit.c b/tests/unit.c index 5f788fe051..856c89b3f9 100644 --- a/tests/unit.c +++ b/tests/unit.c @@ -79,8 +79,13 @@ int unit_test(int argc, char** argv) { int ret = 0; + #ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + int need_dh_disable = (wc_dh_enable() == 0); + #endif + (void)argc; (void)argv; + #ifdef WOLFSSL_FORCE_MALLOC_FAIL_TEST if (argc > 1) { int memFailCount = atoi(argv[1]); @@ -408,5 +413,10 @@ exit: fflush(stdout); } + #ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + if (need_dh_disable) + (void)wc_dh_disable(); + #endif + return ret; } diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index 803c56aa37..d8ee106414 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -57,6 +57,29 @@ } #endif +#ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT +static volatile int wc_dh_enabled = WC_DH_INITIAL_RUNTIME_ENABLEMENT; +int wc_dh_enable(void) { + if (wc_dh_enabled) + return ALREADY_E; + else { + wc_dh_enabled = 1; + return 0; + } +} +int wc_dh_disable(void) { + if (wc_dh_enabled) { + wc_dh_enabled = 0; + return 0; + } + else + return ALREADY_E; +} +int wc_dh_is_enabled(void) { + return wc_dh_enabled; +} +#endif + /* Possible DH enable options: * NO_RSA: Overall control of DH default: on (not defined) @@ -943,6 +966,11 @@ int wc_InitDhKey_ex(DhKey* key, void* heap, int devId) key->heap = heap; /* for XMALLOC/XFREE in future */ key->trustedGroup = 0; +#ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT + if (! wc_dh_enabled) + return FIPS_NOT_ALLOWED_E; +#endif + #ifdef WOLFSSL_DH_EXTRA if (mp_init_multi(&key->p, &key->g, &key->q, &key->pub, &key->priv, NULL) != MP_OKAY) #else @@ -2012,6 +2040,11 @@ int wc_DhGenerateKeyPair(DhKey* key, WC_RNG* rng, return BAD_FUNC_ARG; } +#ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT + if (! wc_dh_enabled) + return FIPS_NOT_ALLOWED_E; +#endif + #ifdef WOLFSSL_KCAPI_DH (void)priv; (void)privSz; @@ -2381,6 +2414,11 @@ int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv, return BAD_FUNC_ARG; } +#ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT + if (! wc_dh_enabled) + return FIPS_NOT_ALLOWED_E; +#endif + #ifdef WOLFSSL_KCAPI_DH (void)priv; (void)privSz; @@ -2424,6 +2462,11 @@ int wc_DhAgree_ct(DhKey* key, byte* agree, word32 *agreeSz, const byte* priv, return BAD_FUNC_ARG; } +#ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT + if (! wc_dh_enabled) + return FIPS_NOT_ALLOWED_E; +#endif + requested_agreeSz = (word32)mp_unsigned_bin_size(&key->p); if (requested_agreeSz > *agreeSz) { return BUFFER_E; @@ -2594,6 +2637,11 @@ static int _DhSetKey(DhKey* key, const byte* p, word32 pSz, const byte* g, ret = BAD_FUNC_ARG; } +#ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT + if ((ret == 0) && (! wc_dh_enabled)) + ret = FIPS_NOT_ALLOWED_E; +#endif + if (ret == 0) { /* may have leading 0 */ if (p[0] == 0) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 51cd6dbc51..0ec8dece59 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32315,7 +32315,9 @@ static wc_test_ret_t dh_key_import_export_test(DhKey* key, DhKey* key2, /* for HAVE_WOLF_BIGINT prevent leak */ wc_FreeDhKey(key); - (void)wc_InitDhKey_ex(key, HEAP_HINT, devId); + ret = wc_InitDhKey_ex(key, HEAP_HINT, devId); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_dh_import_export); idx = 0; XMEMSET(tmp2, 0, DH_TEST_TMP_SIZE); @@ -32381,6 +32383,9 @@ exit_dh_set_check: WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dh_test(void) { +#ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + int need_dh_disable = 0; +#endif wc_test_ret_t ret; word32 bytes; word32 idx = 0; @@ -32426,6 +32431,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dh_test(void) #endif #endif /* !WC_NO_RNG */ +#ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + need_dh_disable = (wc_dh_enable() == 0); +#endif + WOLFSSL_ENTER("dh_test"); #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) @@ -32664,6 +32673,11 @@ done: (void)pubSz2; (void)privSz2; + #ifdef WC_DH_HAVE_RUNTIME_ENABLEMENT + if (need_dh_disable) + (void)wc_dh_disable(); + #endif + return ret; } diff --git a/wolfssl/wolfcrypt/dh.h b/wolfssl/wolfcrypt/dh.h index 7790a66780..85d3e95f4a 100644 --- a/wolfssl/wolfcrypt/dh.h +++ b/wolfssl/wolfcrypt/dh.h @@ -161,6 +161,18 @@ enum { WOLFSSL_LOCAL int wolfCrypt_FIPS_DH_sanity(void); #endif +#if FIPS_VERSION3_GE(7,0,0) || defined(WC_DH_INITIAL_RUNTIME_ENABLEMENT) + #ifndef WC_DH_INITIAL_RUNTIME_ENABLEMENT + #define WC_DH_INITIAL_RUNTIME_ENABLEMENT 0 + #endif + #define WC_DH_HAVE_RUNTIME_ENABLEMENT + WOLFSSL_API int wc_dh_enable(void); + WOLFSSL_API int wc_dh_disable(void); + WOLFSSL_API int wc_dh_is_enabled(void); +#else + #undef WC_DH_HAVE_RUNTIME_ENABLEMENT +#endif + #ifdef HAVE_PUBLIC_FFDHE #ifdef HAVE_FFDHE_2048 WOLFSSL_API const DhParams* wc_Dh_ffdhe2048_Get(void);