From f95bbdf4de2dc85689a4a3c2066ff94693f3f921 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 2 Sep 2026 11:32:02 -0600 Subject: [PATCH 01/17] Reject HMAC keys longer than 1024 bits in the FIPS v7 approved mode, since FIPS 198-1 Section 4 folds them to K0 = H(K) but they fall outside the module's CAVP-tested key range (Fenrir F-9546). --- wolfcrypt/src/hmac.c | 13 +++++++++++++ wolfcrypt/test/test.c | 25 +++++++++++++++++++++++++ wolfssl/wolfcrypt/hmac.h | 8 ++++++++ 3 files changed, 46 insertions(+) diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index cc13c5b414e..426d512bd6e 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -773,6 +773,19 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, WOLFSSL_ERROR_VERBOSE(HMAC_MIN_KEYLEN_E); return HMAC_MIN_KEYLEN_E; } +#if FIPS_VERSION3_GE(7,0,0) + /* FIPS 198-1 Section 4 (step 2) sets K0 = H(K) when the key is + * longer than the block size, so any length computes correctly, but + * the module's CAVP HMAC testing covers key lengths only up to + * 1024 bits and FIPS 140-3 IG C.B does not permit an algorithm + * implementation to be used in the approved mode outside its tested + * scope, so reject longer keys unless the caller explicitly opts out + * of the approved-mode limits with allowFlag. */ + if (length > HMAC_FIPS_MAX_KEY) { + WOLFSSL_ERROR_VERBOSE(BAD_LENGTH_E); + return BAD_LENGTH_E; + } +#endif } #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c18f069631d..a5558062efb 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -12274,6 +12274,31 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha_test(void) #endif } +#if FIPS_VERSION3_GE(7,0,0) + /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's + * CAVP-tested key range: wc_HmacSetKey must reject them, a key at the + * maximum must pass, and the wc_HmacSetKey_ex allowFlag escape must + * still accept the longer key as a non-approved use. */ + { + byte maxKey[HMAC_FIPS_MAX_KEY + 1]; + XMEMSET(maxKey, 0x0b, sizeof(maxKey)); + if ((ret = wc_HmacInit(&hmac, HEAP_HINT, devId)) != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey(&hmac, WC_SHA, maxKey, (word32)sizeof(maxKey)); + if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey(&hmac, WC_SHA, maxKey, + (word32)sizeof(maxKey) - 1); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey_ex(&hmac, WC_SHA, maxKey, (word32)sizeof(maxKey), + allowShortKeyWithFips); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + wc_HmacFree(&hmac); + } +#endif + out: wc_HmacFree(&hmac); diff --git a/wolfssl/wolfcrypt/hmac.h b/wolfssl/wolfcrypt/hmac.h index a874c31d3f3..725bcc45d56 100644 --- a/wolfssl/wolfcrypt/hmac.h +++ b/wolfssl/wolfcrypt/hmac.h @@ -77,6 +77,14 @@ enum { HMAC_FIPS_MIN_KEY = 14, /* 112 bit key length minimum. Note that this * minimum also applies to the salt length for * HKDF. */ +#if FIPS_VERSION3_GE(7,0,0) + HMAC_FIPS_MAX_KEY = 128, /* 1024 bit key length maximum for the approved + * mode. FIPS 198-1 Section 4 (step 2) reduces + * a key longer than the block size to + * K0 = H(K), so longer keys compute correctly, + * but key lengths above 1024 bits are outside + * the module's CAVP-tested HMAC key range. */ +#endif IPAD = 0x36, OPAD = 0x5C, From f4c807a3fcdaf5cf5d499c89cb3a9bec004cda49 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 2 Sep 2026 11:32:02 -0600 Subject: [PATCH 02/17] Refuse ECDSA key pair generation and signature generation on curves below 224 bits under FIPS v7 per SP 800-131A Rev. 2 Table 2, leaving signature verification with those curves available as legacy use (Fenrir F-7359). --- wolfcrypt/src/ecc.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 0c060d34517..f2375af3804 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -6532,6 +6532,26 @@ int wc_ecc_make_key_ex2(WC_RNG* rng, int keysize, ecc_key* key, int curve_id, { int err; +#if FIPS_VERSION3_GE(7,0,0) + /* SP 800-131A Rev. 2 Table 2 disallows ECDSA key pair generation + * providing fewer than 112 bits of security strength (len(n) < 224), and + * SP 800-186 Section 3.2.1.1 retains P-192 for legacy use only, so + * refuse to generate on any curve smaller than 224 bits. The bound + * mirrors wc_ecc_set_curve(): an explicit curve_id selects the curve + * directly, otherwise keysize selects the smallest compiled curve that + * fits, so keysize 0 would land on the smallest compiled curve and is + * rejected too. Signature verification with the small curves stays + * available (legacy use per SP 800-131A Rev. 2 Table 2). */ + if (curve_id > ECC_CURVE_DEF) { + if (wc_ecc_get_curve_size_from_id(curve_id) < WC_ECC_FIPS_GEN_MIN) { + return ECC_CURVE_OID_E; + } + } + else if (keysize < WC_ECC_FIPS_GEN_MIN) { + return ECC_CURVE_OID_E; + } +#endif + err = _ecc_make_key_ex(rng, keysize, key, curve_id, flags); #if (FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN)) && \ @@ -7893,6 +7913,17 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, return ECC_BAD_ARG_E; } +#if FIPS_VERSION3_GE(7,0,0) + /* SP 800-131A Rev. 2 Table 2: ECDSA digital signature generation with + * len(n) < 224 is disallowed, while signature verification with those + * curves remains legacy use, so only the signing path is gated. The + * curve is resolved from key->dp, never from ecc_sets[key->idx], so a + * custom-curve key (idx == ECC_CUSTOM_IDX) cannot index out of range. */ + if (key->dp->size < WC_ECC_FIPS_GEN_MIN) { + return SIG_TYPE_E; + } +#endif + #if defined(WOLFSSL_SP_MATH) if (key->idx == ECC_CUSTOM_IDX || (1 #ifndef WOLFSSL_SP_NO_256 From e8c2bf41f054e03f9538d4412964d2e68bb96035 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 2 Sep 2026 12:28:37 -0600 Subject: [PATCH 03/17] Expect the FIPS v7 1024-bit HMAC key maximum from the long RFC 4231 style test vectors and keep them covered through the wc_HmacSetKey_ex allowFlag escape (Fenrir F-9546). --- wolfcrypt/test/test.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a5558062efb..952d16f0c80 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -12403,6 +12403,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha224_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA224, (byte*)keys[i], (word32)XSTRLEN(keys[i])); +#if FIPS_VERSION3_GE(7,0,0) + /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's + * CAVP-tested key range and are rejected in approved mode; the long + * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex + * allowFlag escape. */ + if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { + if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey_ex(&hmac, WC_SHA224, (byte*)keys[i], + (word32)XSTRLEN(keys[i]), 1); + } +#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -12564,6 +12576,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha256_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA256, (byte*)keys[i], (word32)XSTRLEN(keys[i])); +#if FIPS_VERSION3_GE(7,0,0) + /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's + * CAVP-tested key range and are rejected in approved mode; the long + * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex + * allowFlag escape. */ + if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { + if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey_ex(&hmac, WC_SHA256, (byte*)keys[i], + (word32)XSTRLEN(keys[i]), 1); + } +#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_I(i), out); @@ -12728,6 +12752,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha384_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA384, (byte*)keys[i], (word32)XSTRLEN(keys[i])); +#if FIPS_VERSION3_GE(7,0,0) + /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's + * CAVP-tested key range and are rejected in approved mode; the long + * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex + * allowFlag escape. */ + if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { + if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey_ex(&hmac, WC_SHA384, (byte*)keys[i], + (word32)XSTRLEN(keys[i]), 1); + } +#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -12883,6 +12919,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha512_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA512, (byte*)keys[i], (word32)XSTRLEN(keys[i])); +#if FIPS_VERSION3_GE(7,0,0) + /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's + * CAVP-tested key range and are rejected in approved mode; the long + * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex + * allowFlag escape. */ + if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { + if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey_ex(&hmac, WC_SHA512, (byte*)keys[i], + (word32)XSTRLEN(keys[i]), 1); + } +#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -13096,6 +13144,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha3_test(void) ret = wc_HmacSetKey(&hmac, hashType[j], (byte*)key[i], (word32)XSTRLEN(key[i])); +#if FIPS_VERSION3_GE(7,0,0) + /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the + * module's CAVP-tested key range and are rejected in approved + * mode; the big-key vector stays covered through the + * wc_HmacSetKey_ex allowFlag escape. */ + if (XSTRLEN(key[i]) > HMAC_FIPS_MAX_KEY) { + if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_HmacSetKey_ex(&hmac, hashType[j], (byte*)key[i], + (word32)XSTRLEN(key[i]), 1); + } +#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); From b5de09c15a33e185fe6ef5795f396c8859005788 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 2 Sep 2026 12:28:38 -0600 Subject: [PATCH 04/17] Expect the FIPS v7 generic HKDF wrappers to refuse SHA-1 per SP 800-56C Rev. 2 Section 4 (Fenrir F-7758). --- wolfcrypt/test/test.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 952d16f0c80..eb6268dce8e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -50606,6 +50606,25 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) #endif /* !NO_SHA256 */ #ifndef NO_SHA +#if FIPS_VERSION3_GE(7,0,0) + /* SP 800-56C Rev. 2 Section 4 approves the HKDF auxiliary function only + * with an approved hash and the module's key-derivation coverage is + * SHA-256/384/512, so the generic wrappers refuse SHA-1 (see + * HkdfDigestAllowed in fips.c). */ + PRIVATE_KEY_UNLOCK(); + ret = wc_HKDF_Extract_ex(WC_SHA, NULL, 0, ikm1, (word32)sizeof(ikm1), + prk, HEAP_HINT, devId); + PRIVATE_KEY_LOCK(); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_EC(ret); + PRIVATE_KEY_UNLOCK(); + ret = wc_HKDF_Expand_ex(WC_SHA, prk, WC_SHA_DIGEST_SIZE, NULL, 0, + okm1, (word32)L, HEAP_HINT, devId); + PRIVATE_KEY_LOCK(); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_EC(ret); + ret = 0; +#else PRIVATE_KEY_UNLOCK(); #if !defined(HAVE_SELFTEST) && (!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) ret = wc_HKDF_Extract_ex(WC_SHA, NULL, 0, ikm1, (word32)sizeof(ikm1), @@ -50631,6 +50650,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) if (XMEMCMP(okm1, res1, (unsigned long)L) != 0) return WC_TEST_RET_ENC_NC; +#endif /* FIPS_VERSION3_GE(7,0,0) */ #ifndef HAVE_FIPS /* fips can't have key size under 14 bytes, salt is key too */ From 184d1885f77ef744cf505f04fab0fa03f6934c7d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 20 Oct 2026 10:00:07 -0600 Subject: [PATCH 05/17] Gate FIPS v7 wrapper expectations on !FIPS_NO_WRAPPERS The PKCS#1 v1.5 refusal, the HKDF SHA-1 refusal and the WC_FIPS_NOT_APPROVED indicator come from the fips.c wrappers, which --enable-fips=dev-no-post builds without (and without fips.h, so WC_FIPS_NOT_APPROVED is undeclared). --- wolfcrypt/test/test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index eb6268dce8e..36ad52b9eb3 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -50606,7 +50606,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) #endif /* !NO_SHA256 */ #ifndef NO_SHA -#if FIPS_VERSION3_GE(7,0,0) +#if FIPS_VERSION3_GE(7,0,0) && !defined(FIPS_NO_WRAPPERS) /* SP 800-56C Rev. 2 Section 4 approves the HKDF auxiliary function only * with an approved hash and the module's key-derivation coverage is * SHA-256/384/512, so the generic wrappers refuse SHA-1 (see @@ -50650,7 +50650,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) if (XMEMCMP(okm1, res1, (unsigned long)L) != 0) return WC_TEST_RET_ENC_NC; -#endif /* FIPS_VERSION3_GE(7,0,0) */ +#endif /* FIPS_VERSION3_GE(7,0,0) && !FIPS_NO_WRAPPERS */ #ifndef HAVE_FIPS /* fips can't have key size under 14 bytes, salt is key too */ From 51f6a14ac4b3c76bb6c7c68d822850b2d2f48032 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 20 Oct 2026 10:00:08 -0600 Subject: [PATCH 06/17] Return BAD_FUNC_ARG for a too-small FIPS v7 ECC keygen keysize Matches what the fips.c keygen wrappers return, which test_wc_ecc_sig_size_calc expects; dev-no-post builds reach the library check directly. --- wolfcrypt/src/ecc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index f2375af3804..37b32673f27 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -6548,7 +6548,8 @@ int wc_ecc_make_key_ex2(WC_RNG* rng, int keysize, ecc_key* key, int curve_id, } } else if (keysize < WC_ECC_FIPS_GEN_MIN) { - return ECC_CURVE_OID_E; + /* Same code the fips.c keygen wrappers return for this case. */ + return BAD_FUNC_ARG; } #endif From 06c40ea1b1ab26f5b016fce27b7c575f52b6e1bf Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Tue, 20 Oct 2026 10:00:08 -0600 Subject: [PATCH 07/17] Exempt the TLS PRF from the FIPS v7 HMAC key maximum wc_PRF keys HMAC with the pre-master secret, which is 256 bytes for a 2048-bit DHE group, so the 1024-bit cap broke TLS 1.2 DHE handshakes. Like HKDF and PBKDF2, the PRF's HMAC key is covered by its KDF validation. Also expect the OpenSSL-compat HMAC_Init long-key (160 byte) case to be refused under v7. --- tests/api/test_ossl_mac.c | 8 ++++++++ wolfcrypt/src/kdf.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/tests/api/test_ossl_mac.c b/tests/api/test_ossl_mac.c index 267b2da0351..7b881b4c221 100644 --- a/tests/api/test_ossl_mac.c +++ b/tests/api/test_ossl_mac.c @@ -129,6 +129,13 @@ static int test_HMAC_CTX_helper(const EVP_MD* type, unsigned char* digest, /* long key */ HMAC_CTX_init(&ctx1); +#if FIPS_VERSION3_GE(7,0,0) + /* 160 bytes is above the v7 HMAC_FIPS_MAX_KEY (1024 bits). */ + ExpectIntNE(HMAC_Init(&ctx1, (const void*)long_key, long_keySz, type), + SSL_SUCCESS); + HMAC_CTX_cleanup(&ctx1); + (void)long_keySz; +#else ExpectIntEQ(HMAC_Init(&ctx1, (const void*)long_key, long_keySz, type), SSL_SUCCESS); ExpectIntEQ(HMAC_Update(&ctx1, msg, msgSz), SSL_SUCCESS); @@ -148,6 +155,7 @@ static int test_HMAC_CTX_helper(const EVP_MD* type, unsigned char* digest, ExpectIntEQ(digestSz, digestSz2); ExpectIntEQ(XMEMCMP(digest, digest2, digestSz), 0); +#endif /* FIPS_VERSION3_GE(7,0,0) */ /* init before copy */ HMAC_CTX_init(&ctx1); diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 7e1d977e4ea..cff0c1613f2 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -167,7 +167,15 @@ int wc_PRF(byte* result, word32 resLen, const byte* secret, ret = wc_HmacInit(hmac, heap, devId); if (ret == 0) { + #if FIPS_VERSION3_GE(7,0,0) + /* The PRF secret is a (pre-)master secret, e.g. 256 bytes for a + * 2048-bit DHE group, so it is covered by the TLS KDF validation + * (SP 800-135) rather than the standalone HMAC key-length range. */ + ret = wc_HmacSetKey_ex(hmac, hash_type, secret, secLen, + FIPS_ALLOW_SHORT); + #else ret = wc_HmacSetKey(hmac, hash_type, secret, secLen); + #endif if (ret == 0) ret = wc_HmacUpdate(hmac, seed, seedLen); /* A0 = seed */ if (ret == 0) From 60df2ce9fca719410e8f5d6c297d2bbd5a18fdc6 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 21 Oct 2026 15:20:46 -0600 Subject: [PATCH 08/17] Keep the HMAC minimum key length for the FIPS v7 TLS PRF FIPS_ALLOW_SHORT lifts the HMAC_FIPS_MAX_KEY cap but also the HMAC_FIPS_MIN_KEY floor, so wc_PRF accepted secrets under 14 bytes. --- wolfcrypt/src/kdf.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index cff0c1613f2..541f3c1db10 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -170,9 +170,17 @@ int wc_PRF(byte* result, word32 resLen, const byte* secret, #if FIPS_VERSION3_GE(7,0,0) /* The PRF secret is a (pre-)master secret, e.g. 256 bytes for a * 2048-bit DHE group, so it is covered by the TLS KDF validation - * (SP 800-135) rather than the standalone HMAC key-length range. */ - ret = wc_HmacSetKey_ex(hmac, hash_type, secret, secLen, - FIPS_ALLOW_SHORT); + * (SP 800-135) rather than the standalone HMAC key-length range. + * FIPS_ALLOW_SHORT lifts that cap but also the HMAC_FIPS_MIN_KEY + * floor, so keep the floor here. */ + if (secLen < HMAC_FIPS_MIN_KEY) { + WOLFSSL_ERROR_VERBOSE(HMAC_MIN_KEYLEN_E); + ret = HMAC_MIN_KEYLEN_E; + } + else { + ret = wc_HmacSetKey_ex(hmac, hash_type, secret, secLen, + FIPS_ALLOW_SHORT); + } #else ret = wc_HmacSetKey(hmac, hash_type, secret, secLen); #endif From 6c19eaeec54ff87569956a2436b49c0e4ef66ee6 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 21 Oct 2026 15:21:06 -0600 Subject: [PATCH 09/17] Let linuxkm HMAC keys above the FIPS v7 cap through The kernel's testmgr HMAC vectors use 131-byte keys, which the HMAC_FIPS_MAX_KEY cap rejected on 6.0+ kernels. --- linuxkm/lkcapi_sha_glue.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 71056189df8..970ceb37a6e 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -1412,6 +1412,13 @@ WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, #if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(6, 0, 0) || \ !defined(WC_LINUX_CONFIG_SELFTESTS) || \ (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0))) + #if FIPS_VERSION3_GE(7, 0, 0) + /* The kernel's own HMAC self-tests use keys longer than the v7 + * HMAC_FIPS_MAX_KEY cap, so let those through. */ + if (length > HMAC_FIPS_MAX_KEY) + ret = wc_HmacSetKey_ex(&p_ctx->wc_hmac, type, key, length, 1 /* allowFlag */); + else + #endif ret = wc_HmacSetKey(&p_ctx->wc_hmac, type, key, length); #else /* kernel 5.10.x crypto manager expects FIPS-undersized keys to succeed. */ From 8b9e8c5212f409c25bd4c2356bd40214e07b21e5 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 11 Nov 2026 16:04:58 -0700 Subject: [PATCH 10/17] Apply the FIPS v7 ECDSA signing curve minimum ahead of crypto-callback and hardware dispatch The gate sat only in the software wc_ecc_sign_hash_ex(), so wc_ecc_sign_hash() could hand a sub-224-bit key to a crypto callback or hardware backend first, and the STM32 PKA wc_ecc_sign_hash_ex() had no check at all. All three entry points now share one check. --- wolfcrypt/src/ecc.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 37b32673f27..ba0faf0810f 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -7250,6 +7250,24 @@ static int wc_ecc_sign_hash_async(const byte* in, word32 inlen, byte* out, } #endif /* WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC */ +#if FIPS_VERSION3_GE(7,0,0) +/* SP 800-131A Rev. 2 Table 2: ECDSA digital signature generation with + * len(n) < 224 is disallowed, while signature verification with those + * curves remains legacy use, so only the signing paths are gated. Every + * signing entry point calls this ahead of any crypto-callback or hardware + * dispatch, so no backend signs on a too-small curve. The curve is resolved + * from key->dp, never from ecc_sets[key->idx], so a custom-curve key + * (idx == ECC_CUSTOM_IDX) cannot index out of range. A key with no curve + * set is left to the caller's own argument checks. */ +static int ecc_fips_sign_curve_allowed(const ecc_key* key) +{ + if ((key->dp != NULL) && (key->dp->size < WC_ECC_FIPS_GEN_MIN)) { + return SIG_TYPE_E; + } + return 0; +} +#endif /* FIPS_VERSION3_GE(7,0,0) */ + /** Sign a message digest in The message digest to sign @@ -7285,6 +7303,13 @@ int wc_ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, return BAD_LENGTH_E; } +#if FIPS_VERSION3_GE(7,0,0) + err = ecc_fips_sign_curve_allowed(key); + if (err != 0) { + return err; + } +#endif + #ifdef WOLF_CRYPTO_CB #ifndef WOLF_CRYPTO_CB_FIND if (key->devId != INVALID_DEVID) @@ -7516,6 +7541,12 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, return ECC_BAD_ARG_E; #endif +#if FIPS_VERSION3_GE(7,0,0) + if (ecc_fips_sign_curve_allowed(key) != 0) { + return SIG_TYPE_E; + } +#endif + return stm32_ecc_sign_hash_ex(in, inlen, rng, key, r, s); } @@ -7915,12 +7946,9 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, } #if FIPS_VERSION3_GE(7,0,0) - /* SP 800-131A Rev. 2 Table 2: ECDSA digital signature generation with - * len(n) < 224 is disallowed, while signature verification with those - * curves remains legacy use, so only the signing path is gated. The - * curve is resolved from key->dp, never from ecc_sets[key->idx], so a - * custom-curve key (idx == ECC_CUSTOM_IDX) cannot index out of range. */ - if (key->dp->size < WC_ECC_FIPS_GEN_MIN) { + /* wc_ecc_sign_hash_ex() is public, so it is gated as well as + * wc_ecc_sign_hash(). */ + if (ecc_fips_sign_curve_allowed(key) != 0) { return SIG_TYPE_E; } #endif From 1d28d810e6724ddd3b91f53a4958decac509dff7 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 11 Nov 2026 16:04:58 -0700 Subject: [PATCH 11/17] Enforce the FIPS v7 HMAC key maximum in the KCAPI wc_HmacSetKey() --- wolfcrypt/src/port/kcapi/kcapi_hmac.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wolfcrypt/src/port/kcapi/kcapi_hmac.c b/wolfcrypt/src/port/kcapi/kcapi_hmac.c index 1b763ff5316..42b6e446d55 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_hmac.c +++ b/wolfcrypt/src/port/kcapi/kcapi_hmac.c @@ -109,6 +109,11 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) #ifdef HAVE_FIPS if (length < HMAC_FIPS_MIN_KEY) return HMAC_MIN_KEYLEN_E; +#if FIPS_VERSION3_GE(7,0,0) + /* Same approved-mode maximum as wc_HmacSetKey_ex() in hmac.c. */ + if (length > HMAC_FIPS_MAX_KEY) + return BAD_LENGTH_E; +#endif #endif if (ret == 0) { From 0ee2cce7220c31ea967f6cd7b02199522099d6a5 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 11 Nov 2026 16:04:58 -0700 Subject: [PATCH 12/17] Exempt linuxkm HMAC from the FIPS v7 key maximum only when the kernel self-tests are enabled --- linuxkm/lkcapi_sha_glue.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 970ceb37a6e..f7efc3fff5c 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -1412,9 +1412,10 @@ WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, #if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(6, 0, 0) || \ !defined(WC_LINUX_CONFIG_SELFTESTS) || \ (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0))) - #if FIPS_VERSION3_GE(7, 0, 0) + #if FIPS_VERSION3_GE(7, 0, 0) && defined(WC_LINUX_CONFIG_SELFTESTS) /* The kernel's own HMAC self-tests use keys longer than the v7 - * HMAC_FIPS_MAX_KEY cap, so let those through. */ + * HMAC_FIPS_MAX_KEY cap, so let those through. Without the kernel + * self-tests nothing needs the exemption and the cap applies. */ if (length > HMAC_FIPS_MAX_KEY) ret = wc_HmacSetKey_ex(&p_ctx->wc_hmac, type, key, length, 1 /* allowFlag */); else From 1d5b6297a8bdd9f039901dfc09bb2f0a2af5d12b Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Wed, 11 Nov 2026 16:04:58 -0700 Subject: [PATCH 13/17] Test the FIPS v7 ECC keygen and signing curve gates and the wc_PRF secret length bounds --- tests/api/test_ecc.c | 26 ++++++++++++++++++++++++++ wolfcrypt/test/test.c | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index ce211b45410..08c0f569387 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -299,6 +299,15 @@ int test_wc_ecc_make_key(void) ExpectIntEQ(wc_ecc_make_key(&rng, KEY14, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#if FIPS_VERSION3_GE(7,0,0) + /* v7 refuses key generation on a named curve below 224 bits even when + * the requested key size is large enough. */ + wc_ecc_free(&key); + ExpectIntEQ(wc_ecc_init(&key), 0); + ExpectIntEQ(wc_ecc_make_key_ex(&rng, 32, &key, ECC_SECP192R1), + WC_NO_ERR_TRACE(ECC_CURVE_OID_E)); +#endif + DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_ecc_free(&key); @@ -600,6 +609,23 @@ int test_wc_ecc_signVerify_hash(void) #endif #endif /* HAVE_ECC_VERIFY */ +#if FIPS_VERSION3_GE(7,0,0) && \ + (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && \ + (ECC_MIN_KEY_SZ <= 192) + /* v7 refuses to sign on a curve below 224 bits. Key generation there is + * refused too, so import a P-192 key (d = 1, Q = G). */ + wc_ecc_free(&key); + ExpectIntEQ(wc_ecc_init(&key), 0); + ExpectIntEQ(wc_ecc_import_raw_ex(&key, + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", + "000000000000000000000000000000000000000000000001", + ECC_SECP192R1), 0); + siglen = ECC_BUFSIZE; + ExpectIntEQ(wc_ecc_sign_hash(digest, digestlen, sig, &siglen, &rng, &key), + WC_NO_ERR_TRACE(SIG_TYPE_E)); +#endif + DoExpectIntEQ(wc_FreeRng(&rng), 0); wc_ecc_free(&key); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 36ad52b9eb3..0df3eaf406c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -50991,6 +50991,30 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t prf_test(void) return WC_TEST_RET_ENC_NC; } +#if FIPS_VERSION3_GE(7,0,0) + { + /* v7 keeps the 112-bit HMAC minimum for the PRF secret but exempts + * it from the 1024-bit HMAC maximum: a (pre-)master secret is e.g. + * 256 bytes for a 2048-bit DHE group. */ + byte longSecret[256]; + + PRIVATE_KEY_UNLOCK(); + ret = wc_PRF(dig, (word32)digL, secret, HMAC_FIPS_MIN_KEY - 1, lablSd, + (word32)lblsdL, hash_type, HEAP_HINT, devId); + PRIVATE_KEY_LOCK(); + if (ret != WC_NO_ERR_TRACE(HMAC_MIN_KEYLEN_E)) + return WC_TEST_RET_ENC_EC(ret); + + XMEMSET(longSecret, 0x5a, sizeof(longSecret)); + PRIVATE_KEY_UNLOCK(); + ret = wc_PRF(dig, (word32)digL, longSecret, (word32)sizeof(longSecret), + lablSd, (word32)lblsdL, hash_type, HEAP_HINT, devId); + PRIVATE_KEY_LOCK(); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + } +#endif + return 0; } #endif /* WOLFSSL_HAVE_PRF && !NO_HMAC && WOLFSSL_SHA384 && !WOLFSSL_NO_TLS12 */ From 3363684d24145cc1a218c25f9f86cbe73d96d168 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 12 Nov 2026 09:38:53 -0700 Subject: [PATCH 14/17] Expect the FIPS v7 HKDF SHA-1 refusal only when fips.h defines WC_HAVE_FIPS_INDICATOR --- wolfcrypt/test/test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0df3eaf406c..13616cd205e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -50606,7 +50606,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) #endif /* !NO_SHA256 */ #ifndef NO_SHA -#if FIPS_VERSION3_GE(7,0,0) && !defined(FIPS_NO_WRAPPERS) +#if FIPS_VERSION3_GE(7,0,0) && defined(WC_HAVE_FIPS_INDICATOR) && \ + !defined(FIPS_NO_WRAPPERS) /* SP 800-56C Rev. 2 Section 4 approves the HKDF auxiliary function only * with an approved hash and the module's key-derivation coverage is * SHA-256/384/512, so the generic wrappers refuse SHA-1 (see @@ -50650,7 +50651,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) if (XMEMCMP(okm1, res1, (unsigned long)L) != 0) return WC_TEST_RET_ENC_NC; -#endif /* FIPS_VERSION3_GE(7,0,0) && !FIPS_NO_WRAPPERS */ +#endif /* FIPS_VERSION3_GE(7,0,0) && WC_HAVE_FIPS_INDICATOR && + * !FIPS_NO_WRAPPERS */ #ifndef HAVE_FIPS /* fips can't have key size under 14 bytes, salt is key too */ From 01fdb6aa939bff90eb9fe25dd4ba104e6d2a226d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 12 Nov 2026 10:04:53 -0700 Subject: [PATCH 15/17] Provide a KCAPI wc_HmacSetKey_ex so the key is set on the backend that runs update and final In a WOLFSSL_KCAPI_HMAC build wc_HmacSetKey_ex() was the software implementation while wc_HmacUpdate() and wc_HmacFinal() were the KCAPI ones, so a caller of the _ex form (the FIPS v7 TLS PRF, HKDF, PBKDF2) keyed one backend and then updated a KCAPI handle that was never created. The software form is now wc_HmacSetKey_ex_Software() in those builds and kcapi_hmac.c provides wc_HmacSetKey_ex(), with wc_HmacSetKey() calling it. --- wolfcrypt/src/hmac.c | 2 ++ wolfcrypt/src/port/kcapi/kcapi_hmac.c | 51 +++++++++++++++++++-------- wolfssl/wolfcrypt/hmac.h | 3 ++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index 426d512bd6e..a5e09028905 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -75,6 +75,7 @@ /* map the _Software calls used by kcapi_hmac.c */ #define wc_HmacSetKey wc_HmacSetKey_Software + #define wc_HmacSetKey_ex wc_HmacSetKey_ex_Software #define wc_HmacUpdate wc_HmacUpdate_Software #define wc_HmacFinal wc_HmacFinal_Software #endif @@ -1740,6 +1741,7 @@ int wc_HmacFinal(Hmac* hmac, byte* hash) /* unmap the _Software calls used by kcapi_hmac.c */ #undef wc_HmacSetKey + #undef wc_HmacSetKey_ex #undef wc_HmacUpdate #undef wc_HmacFinal diff --git a/wolfcrypt/src/port/kcapi/kcapi_hmac.c b/wolfcrypt/src/port/kcapi/kcapi_hmac.c index 42b6e446d55..11e74696e13 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_hmac.c +++ b/wolfcrypt/src/port/kcapi/kcapi_hmac.c @@ -92,7 +92,12 @@ void wc_HmacFree(Hmac* hmac) } } -int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) +/* KCAPI counterpart of the software wc_HmacSetKey_ex(): callers that pass + * allowFlag (the TLS PRF, HKDF, PBKDF2) then use wc_HmacUpdate() and + * wc_HmacFinal() from this file, so the key has to be set on the same + * backend. */ +int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, + int allowFlag) { int ret = 0; const char* ciphername = NULL; @@ -107,20 +112,25 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) #endif #ifdef HAVE_FIPS - if (length < HMAC_FIPS_MIN_KEY) - return HMAC_MIN_KEYLEN_E; -#if FIPS_VERSION3_GE(7,0,0) - /* Same approved-mode maximum as wc_HmacSetKey_ex() in hmac.c. */ - if (length > HMAC_FIPS_MAX_KEY) - return BAD_LENGTH_E; -#endif + if (!allowFlag) { + if (length < HMAC_FIPS_MIN_KEY) + return HMAC_MIN_KEYLEN_E; + #if FIPS_VERSION3_GE(7,0,0) + /* Same approved-mode maximum as the software wc_HmacSetKey_ex(). */ + if (length > HMAC_FIPS_MAX_KEY) + return BAD_LENGTH_E; + #endif + } +#else + (void)allowFlag; #endif if (ret == 0) { switch (type) { #ifndef NO_MD5 case WC_MD5: - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif #ifndef NO_SHA case WC_SHA: @@ -128,7 +138,8 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) ciphername = WC_NAME_HMAC_SHA1; break; #else - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif #endif #ifdef WOLFSSL_SHA224 @@ -137,7 +148,8 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) ciphername = WC_NAME_HMAC_SHA224; break; #else - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif #endif #ifndef NO_SHA256 @@ -146,7 +158,8 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) ciphername = WC_NAME_HMAC_SHA256; break; #else - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif #endif #ifdef WOLFSSL_SHA384 @@ -155,7 +168,8 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) ciphername = WC_NAME_HMAC_SHA384; break; #else - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif #endif #ifdef WOLFSSL_SHA512 @@ -164,7 +178,8 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) ciphername = WC_NAME_HMAC_SHA512; break; #else - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif #endif #ifdef WOLFSSL_SHA3 @@ -180,7 +195,8 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) #ifndef WOLFSSL_NOSHA3_512 case WC_SHA3_512: #endif - return wc_HmacSetKey_Software(hmac, type, key, length); + return wc_HmacSetKey_ex_Software(hmac, type, key, length, + allowFlag); #endif default: ret = BAD_FUNC_ARG; @@ -209,6 +225,11 @@ int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) return ret; } +int wc_HmacSetKey(Hmac* hmac, int type, const byte* key, word32 length) +{ + return wc_HmacSetKey_ex(hmac, type, key, length, 0); +} + int wc_HmacUpdate(Hmac* hmac, const byte* msg, word32 length) { int ret = 0; diff --git a/wolfssl/wolfcrypt/hmac.h b/wolfssl/wolfcrypt/hmac.h index 725bcc45d56..2c93fc53b59 100644 --- a/wolfssl/wolfcrypt/hmac.h +++ b/wolfssl/wolfcrypt/hmac.h @@ -204,6 +204,9 @@ WOLFSSL_API int wc_HmacFinal(Hmac* hmac, byte* hash); #ifdef WOLFSSL_KCAPI_HMAC WOLFSSL_API int wc_HmacSetKey_Software(Hmac* hmac, int type, const byte* key, word32 keySz); +WOLFSSL_API int wc_HmacSetKey_ex_Software(Hmac* hmac, int type, + const byte* key, word32 keySz, + int allowFlag); WOLFSSL_API int wc_HmacUpdate_Software(Hmac* hmac, const byte* in, word32 sz); WOLFSSL_API int wc_HmacFinal_Software(Hmac* hmac, byte* out); #endif From 3381929d3c1dd9ec7d1668283883d6057b6c5c42 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 12 Nov 2026 10:04:53 -0700 Subject: [PATCH 16/17] Recheck the FIPS v7 signing curve minimum after a crypto callback resolves the key's curve A key referenced by id or label has no curve until the callback fills it in, so it passed the check made before dispatch. A signature made on a sub-224-bit curve is now wiped and SIG_TYPE_E returned. --- wolfcrypt/src/ecc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index ba0faf0810f..61501b32692 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -7258,7 +7258,8 @@ static int wc_ecc_sign_hash_async(const byte* in, word32 inlen, byte* out, * dispatch, so no backend signs on a too-small curve. The curve is resolved * from key->dp, never from ecc_sets[key->idx], so a custom-curve key * (idx == ECC_CUSTOM_IDX) cannot index out of range. A key with no curve - * set is left to the caller's own argument checks. */ + * set is left to the caller's own argument checks; wc_ecc_sign_hash() + * checks again after a crypto callback has had the chance to resolve it. */ static int ecc_fips_sign_curve_allowed(const ecc_key* key) { if ((key->dp != NULL) && (key->dp->size < WC_ECC_FIPS_GEN_MIN)) { @@ -7316,8 +7317,20 @@ int wc_ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, #endif { err = wc_CryptoCb_EccSign(in, inlen, out, outlen, rng, key); - if (err != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (err != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + #if FIPS_VERSION3_GE(7,0,0) + /* A key referenced by id or label has no curve until the + * callback resolves it, so it passed the check above. Check + * again and do not release a signature made on a too-small + * curve. */ + if ((err == 0) && (ecc_fips_sign_curve_allowed(key) != 0)) { + ForceZero(out, *outlen); + *outlen = 0; + err = SIG_TYPE_E; + } + #endif return err; + } /* fall-through when unavailable */ } #endif From 81ea11b2f794ecbb52f98027eded5cc4cfea317d Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 12 Nov 2026 12:28:24 -0700 Subject: [PATCH 17/17] Drop the FIPS v7 HMAC key maximum: the CAVP harness HMAC vectors use keys up to 2048 bits The cap made wc_HmacSetKey() return BAD_LENGTH_E for the 129- and 256-byte keys in harness/vectors-v2/HMAC/req/HMAC.req, failing PRB-fips-repo-and-harness-test. Removes the cap, the PRF and linuxkm exemptions that existed only to work around it, and the tests that expected the rejection. --- linuxkm/lkcapi_sha_glue.c | 8 -- tests/api/test_ossl_mac.c | 8 -- wolfcrypt/src/hmac.c | 13 ---- wolfcrypt/src/kdf.c | 16 ---- wolfcrypt/src/port/kcapi/kcapi_hmac.c | 5 -- wolfcrypt/test/test.c | 107 -------------------------- wolfssl/wolfcrypt/hmac.h | 8 -- 7 files changed, 165 deletions(-) diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index f7efc3fff5c..71056189df8 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -1412,14 +1412,6 @@ WC_MAYBE_UNUSED static int linuxkm_hmac_setkey_common(struct crypto_shash *tfm, #if defined(HAVE_FIPS) && (FIPS_VERSION3_LT(6, 0, 0) || \ !defined(WC_LINUX_CONFIG_SELFTESTS) || \ (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0))) - #if FIPS_VERSION3_GE(7, 0, 0) && defined(WC_LINUX_CONFIG_SELFTESTS) - /* The kernel's own HMAC self-tests use keys longer than the v7 - * HMAC_FIPS_MAX_KEY cap, so let those through. Without the kernel - * self-tests nothing needs the exemption and the cap applies. */ - if (length > HMAC_FIPS_MAX_KEY) - ret = wc_HmacSetKey_ex(&p_ctx->wc_hmac, type, key, length, 1 /* allowFlag */); - else - #endif ret = wc_HmacSetKey(&p_ctx->wc_hmac, type, key, length); #else /* kernel 5.10.x crypto manager expects FIPS-undersized keys to succeed. */ diff --git a/tests/api/test_ossl_mac.c b/tests/api/test_ossl_mac.c index 7b881b4c221..267b2da0351 100644 --- a/tests/api/test_ossl_mac.c +++ b/tests/api/test_ossl_mac.c @@ -129,13 +129,6 @@ static int test_HMAC_CTX_helper(const EVP_MD* type, unsigned char* digest, /* long key */ HMAC_CTX_init(&ctx1); -#if FIPS_VERSION3_GE(7,0,0) - /* 160 bytes is above the v7 HMAC_FIPS_MAX_KEY (1024 bits). */ - ExpectIntNE(HMAC_Init(&ctx1, (const void*)long_key, long_keySz, type), - SSL_SUCCESS); - HMAC_CTX_cleanup(&ctx1); - (void)long_keySz; -#else ExpectIntEQ(HMAC_Init(&ctx1, (const void*)long_key, long_keySz, type), SSL_SUCCESS); ExpectIntEQ(HMAC_Update(&ctx1, msg, msgSz), SSL_SUCCESS); @@ -155,7 +148,6 @@ static int test_HMAC_CTX_helper(const EVP_MD* type, unsigned char* digest, ExpectIntEQ(digestSz, digestSz2); ExpectIntEQ(XMEMCMP(digest, digest2, digestSz), 0); -#endif /* FIPS_VERSION3_GE(7,0,0) */ /* init before copy */ HMAC_CTX_init(&ctx1); diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index a5e09028905..c02ceede66c 100644 --- a/wolfcrypt/src/hmac.c +++ b/wolfcrypt/src/hmac.c @@ -774,19 +774,6 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, WOLFSSL_ERROR_VERBOSE(HMAC_MIN_KEYLEN_E); return HMAC_MIN_KEYLEN_E; } -#if FIPS_VERSION3_GE(7,0,0) - /* FIPS 198-1 Section 4 (step 2) sets K0 = H(K) when the key is - * longer than the block size, so any length computes correctly, but - * the module's CAVP HMAC testing covers key lengths only up to - * 1024 bits and FIPS 140-3 IG C.B does not permit an algorithm - * implementation to be used in the approved mode outside its tested - * scope, so reject longer keys unless the caller explicitly opts out - * of the approved-mode limits with allowFlag. */ - if (length > HMAC_FIPS_MAX_KEY) { - WOLFSSL_ERROR_VERBOSE(BAD_LENGTH_E); - return BAD_LENGTH_E; - } -#endif } #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY) diff --git a/wolfcrypt/src/kdf.c b/wolfcrypt/src/kdf.c index 541f3c1db10..7e1d977e4ea 100644 --- a/wolfcrypt/src/kdf.c +++ b/wolfcrypt/src/kdf.c @@ -167,23 +167,7 @@ int wc_PRF(byte* result, word32 resLen, const byte* secret, ret = wc_HmacInit(hmac, heap, devId); if (ret == 0) { - #if FIPS_VERSION3_GE(7,0,0) - /* The PRF secret is a (pre-)master secret, e.g. 256 bytes for a - * 2048-bit DHE group, so it is covered by the TLS KDF validation - * (SP 800-135) rather than the standalone HMAC key-length range. - * FIPS_ALLOW_SHORT lifts that cap but also the HMAC_FIPS_MIN_KEY - * floor, so keep the floor here. */ - if (secLen < HMAC_FIPS_MIN_KEY) { - WOLFSSL_ERROR_VERBOSE(HMAC_MIN_KEYLEN_E); - ret = HMAC_MIN_KEYLEN_E; - } - else { - ret = wc_HmacSetKey_ex(hmac, hash_type, secret, secLen, - FIPS_ALLOW_SHORT); - } - #else ret = wc_HmacSetKey(hmac, hash_type, secret, secLen); - #endif if (ret == 0) ret = wc_HmacUpdate(hmac, seed, seedLen); /* A0 = seed */ if (ret == 0) diff --git a/wolfcrypt/src/port/kcapi/kcapi_hmac.c b/wolfcrypt/src/port/kcapi/kcapi_hmac.c index 11e74696e13..c44c2100ba0 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_hmac.c +++ b/wolfcrypt/src/port/kcapi/kcapi_hmac.c @@ -115,11 +115,6 @@ int wc_HmacSetKey_ex(Hmac* hmac, int type, const byte* key, word32 length, if (!allowFlag) { if (length < HMAC_FIPS_MIN_KEY) return HMAC_MIN_KEYLEN_E; - #if FIPS_VERSION3_GE(7,0,0) - /* Same approved-mode maximum as the software wc_HmacSetKey_ex(). */ - if (length > HMAC_FIPS_MAX_KEY) - return BAD_LENGTH_E; - #endif } #else (void)allowFlag; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 13616cd205e..d1b71b9f5ae 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -12274,30 +12274,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha_test(void) #endif } -#if FIPS_VERSION3_GE(7,0,0) - /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's - * CAVP-tested key range: wc_HmacSetKey must reject them, a key at the - * maximum must pass, and the wc_HmacSetKey_ex allowFlag escape must - * still accept the longer key as a non-approved use. */ - { - byte maxKey[HMAC_FIPS_MAX_KEY + 1]; - XMEMSET(maxKey, 0x0b, sizeof(maxKey)); - if ((ret = wc_HmacInit(&hmac, HEAP_HINT, devId)) != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey(&hmac, WC_SHA, maxKey, (word32)sizeof(maxKey)); - if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey(&hmac, WC_SHA, maxKey, - (word32)sizeof(maxKey) - 1); - if (ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey_ex(&hmac, WC_SHA, maxKey, (word32)sizeof(maxKey), - allowShortKeyWithFips); - if (ret != 0) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - wc_HmacFree(&hmac); - } -#endif out: @@ -12403,18 +12379,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha224_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA224, (byte*)keys[i], (word32)XSTRLEN(keys[i])); -#if FIPS_VERSION3_GE(7,0,0) - /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's - * CAVP-tested key range and are rejected in approved mode; the long - * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex - * allowFlag escape. */ - if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { - if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey_ex(&hmac, WC_SHA224, (byte*)keys[i], - (word32)XSTRLEN(keys[i]), 1); - } -#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -12576,18 +12540,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha256_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA256, (byte*)keys[i], (word32)XSTRLEN(keys[i])); -#if FIPS_VERSION3_GE(7,0,0) - /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's - * CAVP-tested key range and are rejected in approved mode; the long - * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex - * allowFlag escape. */ - if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { - if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey_ex(&hmac, WC_SHA256, (byte*)keys[i], - (word32)XSTRLEN(keys[i]), 1); - } -#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_I(i), out); @@ -12752,18 +12704,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha384_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA384, (byte*)keys[i], (word32)XSTRLEN(keys[i])); -#if FIPS_VERSION3_GE(7,0,0) - /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's - * CAVP-tested key range and are rejected in approved mode; the long - * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex - * allowFlag escape. */ - if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { - if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey_ex(&hmac, WC_SHA384, (byte*)keys[i], - (word32)XSTRLEN(keys[i]), 1); - } -#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -12919,18 +12859,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha512_test(void) ret = wc_HmacSetKey(&hmac, WC_SHA512, (byte*)keys[i], (word32)XSTRLEN(keys[i])); -#if FIPS_VERSION3_GE(7,0,0) - /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the module's - * CAVP-tested key range and are rejected in approved mode; the long - * RFC 4231 style vectors stay covered through the wc_HmacSetKey_ex - * allowFlag escape. */ - if (XSTRLEN(keys[i]) > HMAC_FIPS_MAX_KEY) { - if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey_ex(&hmac, WC_SHA512, (byte*)keys[i], - (word32)XSTRLEN(keys[i]), 1); - } -#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -13144,18 +13072,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha3_test(void) ret = wc_HmacSetKey(&hmac, hashType[j], (byte*)key[i], (word32)XSTRLEN(key[i])); -#if FIPS_VERSION3_GE(7,0,0) - /* Keys above HMAC_FIPS_MAX_KEY (1024 bits) are outside the - * module's CAVP-tested key range and are rejected in approved - * mode; the big-key vector stays covered through the - * wc_HmacSetKey_ex allowFlag escape. */ - if (XSTRLEN(key[i]) > HMAC_FIPS_MAX_KEY) { - if (ret != WC_NO_ERR_TRACE(BAD_LENGTH_E)) - ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - ret = wc_HmacSetKey_ex(&hmac, hashType[j], (byte*)key[i], - (word32)XSTRLEN(key[i]), 1); - } -#endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -50993,29 +50909,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t prf_test(void) return WC_TEST_RET_ENC_NC; } -#if FIPS_VERSION3_GE(7,0,0) - { - /* v7 keeps the 112-bit HMAC minimum for the PRF secret but exempts - * it from the 1024-bit HMAC maximum: a (pre-)master secret is e.g. - * 256 bytes for a 2048-bit DHE group. */ - byte longSecret[256]; - - PRIVATE_KEY_UNLOCK(); - ret = wc_PRF(dig, (word32)digL, secret, HMAC_FIPS_MIN_KEY - 1, lablSd, - (word32)lblsdL, hash_type, HEAP_HINT, devId); - PRIVATE_KEY_LOCK(); - if (ret != WC_NO_ERR_TRACE(HMAC_MIN_KEYLEN_E)) - return WC_TEST_RET_ENC_EC(ret); - - XMEMSET(longSecret, 0x5a, sizeof(longSecret)); - PRIVATE_KEY_UNLOCK(); - ret = wc_PRF(dig, (word32)digL, longSecret, (word32)sizeof(longSecret), - lablSd, (word32)lblsdL, hash_type, HEAP_HINT, devId); - PRIVATE_KEY_LOCK(); - if (ret != 0) - return WC_TEST_RET_ENC_EC(ret); - } -#endif return 0; } diff --git a/wolfssl/wolfcrypt/hmac.h b/wolfssl/wolfcrypt/hmac.h index 2c93fc53b59..0867ab41a71 100644 --- a/wolfssl/wolfcrypt/hmac.h +++ b/wolfssl/wolfcrypt/hmac.h @@ -77,14 +77,6 @@ enum { HMAC_FIPS_MIN_KEY = 14, /* 112 bit key length minimum. Note that this * minimum also applies to the salt length for * HKDF. */ -#if FIPS_VERSION3_GE(7,0,0) - HMAC_FIPS_MAX_KEY = 128, /* 1024 bit key length maximum for the approved - * mode. FIPS 198-1 Section 4 (step 2) reduces - * a key longer than the block size to - * K0 = H(K), so longer keys compute correctly, - * but key lengths above 1024 bits are outside - * the module's CAVP-tested HMAC key range. */ -#endif IPAD = 0x36, OPAD = 0x5C,