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/src/ecc.c b/wolfcrypt/src/ecc.c index 0c060d34517..61501b32692 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -6532,6 +6532,27 @@ 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) { + /* Same code the fips.c keygen wrappers return for this case. */ + return BAD_FUNC_ARG; + } +#endif + err = _ecc_make_key_ex(rng, keysize, key, curve_id, flags); #if (FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN)) && \ @@ -7229,6 +7250,25 @@ 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; 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)) { + return SIG_TYPE_E; + } + return 0; +} +#endif /* FIPS_VERSION3_GE(7,0,0) */ + /** Sign a message digest in The message digest to sign @@ -7264,14 +7304,33 @@ 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) #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 @@ -7495,6 +7554,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); } @@ -7893,6 +7958,14 @@ 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) + /* 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 + #if defined(WOLFSSL_SP_MATH) if (key->idx == ECC_CUSTOM_IDX || (1 #ifndef WOLFSSL_SP_NO_256 diff --git a/wolfcrypt/src/hmac.c b/wolfcrypt/src/hmac.c index cc13c5b414e..c02ceede66c 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 @@ -1727,6 +1728,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 1b763ff5316..c44c2100ba0 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,15 +112,20 @@ 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 (!allowFlag) { + if (length < HMAC_FIPS_MIN_KEY) + return HMAC_MIN_KEYLEN_E; + } +#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: @@ -123,7 +133,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 @@ -132,7 +143,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 @@ -141,7 +153,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 @@ -150,7 +163,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 @@ -159,7 +173,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 @@ -175,7 +190,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; @@ -204,6 +220,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/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c18f069631d..d1b71b9f5ae 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -12274,6 +12274,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hmac_sha_test(void) #endif } + out: wc_HmacFree(&hmac); @@ -50521,6 +50522,26 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) #endif /* !NO_SHA256 */ #ifndef NO_SHA +#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 + * 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), @@ -50546,6 +50567,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) && WC_HAVE_FIPS_INDICATOR && + * !FIPS_NO_WRAPPERS */ #ifndef HAVE_FIPS /* fips can't have key size under 14 bytes, salt is key too */ @@ -50886,6 +50909,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t prf_test(void) return WC_TEST_RET_ENC_NC; } + return 0; } #endif /* WOLFSSL_HAVE_PRF && !NO_HMAC && WOLFSSL_SHA384 && !WOLFSSL_NO_TLS12 */ diff --git a/wolfssl/wolfcrypt/hmac.h b/wolfssl/wolfcrypt/hmac.h index a874c31d3f3..0867ab41a71 100644 --- a/wolfssl/wolfcrypt/hmac.h +++ b/wolfssl/wolfcrypt/hmac.h @@ -196,6 +196,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