diff --git a/IDE/C2000/README.md b/IDE/C2000/README.md index 48ad4b398d3..ec81d951966 100644 --- a/IDE/C2000/README.md +++ b/IDE/C2000/README.md @@ -54,7 +54,7 @@ if (wc_UnpackOctets(sig, (word32)sizeof(sig), sigPacked, (word32)sizeof(sigPacked), WC_MLDSA_65_SIG_SIZE) == 0) { ret = wc_MlDsaKey_VerifyCtxHash(key, sig, WC_MLDSA_65_SIG_SIZE, ctx, ctxLen, hash, hashLen, - WC_HASH_TYPE_SHA256, &res); + WC_HASH_TYPE_SHA512, &res); } ``` diff --git a/doc/dox_comments/header_files/ecc.h b/doc/dox_comments/header_files/ecc.h index 74b39375288..1c0d890ed06 100644 --- a/doc/dox_comments/header_files/ecc.h +++ b/doc/dox_comments/header_files/ecc.h @@ -359,6 +359,9 @@ int wc_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out, \ingroup ECC \brief Create an ECC shared secret between private key and public point. + The point is first given full public key validation on the private key's + curve with wc_ecc_check_key() (SP 800-56Ar3 5.6.2.3.3). A build without + the order check (NO_ECC_CHECK_PUBKEY_ORDER) still checks range and curve. \return MP_OKAY Indicates success. \return BAD_FUNC_ARG Error returned when any arguments are null. @@ -368,6 +371,8 @@ int wc_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out, \return MEMORY_E Error to create a new point. \return MP_VAL possible when an initialization failure occurs. \return MP_MEM possible when an initialization failure occurs. + \return IS_POINT_E, ECC_OUT_OF_RANGE_E, ECC_INF_E or MP_VAL when the point + fails validation (not on the curve, out of range, or the identity). \param private_key The private ECC key. \param point The point to use (public key). diff --git a/doc/dox_comments/header_files/wc_mldsa.h b/doc/dox_comments/header_files/wc_mldsa.h index 0cf1eb0bfd7..2279f6332c0 100644 --- a/doc/dox_comments/header_files/wc_mldsa.h +++ b/doc/dox_comments/header_files/wc_mldsa.h @@ -303,8 +303,8 @@ int wc_MlDsaKey_SignCtx(wc_MlDsaKey* key, const byte* ctx, byte ctxLen, \return 0 on success. \return BAD_FUNC_ARG if any required pointer is NULL, ctxLen is - invalid, hashAlg is not supported or the key has no private key - set. + invalid, hashAlg is not supported or is weaker than the parameter set + (FIPS 204 sec 5.4), or the key has no private key set. \return BUFFER_E if the sig buffer is too small. \param [in,out] key Pointer to a wc_MlDsaKey with the private key. @@ -482,7 +482,8 @@ int wc_MlDsaKey_VerifyCtx(wc_MlDsaKey* key, const byte* sig, word32 sigLen, \return 0 if verification completed (check res for the result). \return BAD_FUNC_ARG if any required pointer is NULL, ctxLen is - invalid, or hashAlg is unsupported. + invalid, or hashAlg is unsupported or weaker than the parameter set + (FIPS 204 sec 5.4). \param [in,out] key Pointer to a wc_MlDsaKey with the public key. \param [in] sig Signature bytes to verify. diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index f841b71421d..0f855b0ed54 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -834,6 +834,112 @@ static int ecc_shared_secret_size_bound(WC_RNG* rng, int curveId, int fieldSz) } #endif +/* wc_ecc_shared_secret_ex fully validates a raw peer point, SP 800-56Ar3 + * 5.6.2.3.3. Rejections run first: a pending async call leaves key state set. */ +int test_wc_ecc_shared_secret_ex_peer_point(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && \ + defined(HAVE_ECC_KEY_IMPORT) && !defined(WC_NO_RNG) && \ + !defined(NO_ECC256) && !defined(WOLFSSL_ATECC508A) && \ + !defined(WOLFSSL_ATECC608A) && !defined(WOLFSSL_MICROCHIP_TA100) && \ + !defined(PLUTON_CRYPTO_ECC) && !defined(WOLFSSL_CRYPTOCELL) && \ + !defined(WOLFSSL_KCAPI_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) + /* P-256 base point G, uncompressed X9.63 encoding. Not const: older + * wc_ecc_import_point_der() takes byte*. */ + byte g[] = { + 0x04, + 0x6B,0x17,0xD1,0xF2,0xE1,0x2C,0x42,0x47,0xF8,0xBC,0xE6,0xE5, + 0x63,0xA4,0x40,0xF2,0x77,0x03,0x7D,0x81,0x2D,0xEB,0x33,0xA0, + 0xF4,0xA1,0x39,0x45,0xD8,0x98,0xC2,0x96, + 0x4F,0xE3,0x42,0xE2,0xFE,0x1A,0x7F,0x9B,0x8E,0xE7,0xEB,0x4A, + 0x7C,0x0F,0x9E,0x16,0x2B,0xCE,0x33,0x57,0x6B,0x31,0x5E,0xCE, + 0xCB,0xB6,0x40,0x68,0x37,0xBF,0x51,0xF5 + }; + byte bad[sizeof(g)]; + ecc_key key; + ecc_point* peer = NULL; + WC_RNG rng; + byte out[MAX_ECC_BYTES]; + word32 outLen; + int idx = wc_ecc_get_curve_idx(ECC_SECP256R1); + int ret; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + PRIVATE_KEY_UNLOCK(); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ecc_init(&key), 0); + ret = wc_ecc_make_key_ex(&rng, 32, &key, ECC_SECP256R1); +#if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key.asyncDev, WC_ASYNC_FLAG_NONE); +#endif + ExpectIntEQ(ret, 0); +#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \ + !defined(HAVE_SELFTEST) + ExpectIntEQ(wc_ecc_set_rng(&key, &rng), 0); +#endif + ExpectNotNull(peer = wc_ecc_new_point()); + + /* Older FIPS and selftest modules keep an ecc.c without this check, and + * SE050, STM32 PKA and SILABS hardware are not covered here. */ +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \ + !defined(HAVE_SELFTEST) && !defined(WOLFSSL_SE050) && \ + !defined(WOLFSSL_STM32_PKA) && !defined(WOLFSSL_SILABS_SE_ACCEL) + /* G with one bit of y flipped is off the curve. */ + XMEMCPY(bad, g, sizeof(g)); + bad[sizeof(bad) - 1] ^= 0x01; + ExpectIntEQ(wc_ecc_import_point_der(bad, (word32)sizeof(bad), idx, peer), + 0); + outLen = sizeof(out); + ExpectIntNE(wc_ecc_shared_secret_ex(&key, peer, out, &outLen), 0); +#if defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES) + { + /* A P-384 point is not on P-256. */ + ecc_key key2; + + XMEMSET(&key2, 0, sizeof(key2)); + ExpectIntEQ(wc_ecc_init(&key2), 0); + ret = wc_ecc_make_key_ex(&rng, 48, &key2, ECC_SECP384R1); + #if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key2.asyncDev, WC_ASYNC_FLAG_NONE); + #endif + ExpectIntEQ(ret, 0); + outLen = sizeof(out); + ExpectIntNE(wc_ecc_shared_secret_ex(&key, &key2.pubkey, out, &outLen), + 0); + wc_ecc_free(&key2); + } +#endif +#ifdef WOLFSSL_PUBLIC_MP + /* G with Z = 2: ECDH would use the Jacobian point (x/4, y/8), not the + * affine (x, y) that the checks read. */ + ExpectIntEQ(wc_ecc_import_point_der(g, (word32)sizeof(g), idx, peer), 0); + ExpectIntEQ(mp_set(peer->z, 2), MP_OKAY); + outLen = sizeof(out); + ExpectIntNE(wc_ecc_shared_secret_ex(&key, peer, out, &outLen), 0); +#endif +#else + (void)bad; +#endif + + ExpectIntEQ(wc_ecc_import_point_der(g, (word32)sizeof(g), idx, peer), 0); + outLen = sizeof(out); + ret = wc_ecc_shared_secret_ex(&key, peer, out, &outLen); +#if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key.asyncDev, WC_ASYNC_FLAG_NONE); +#endif + ExpectIntEQ(ret, 0); + + wc_ecc_del_point(peer); + wc_ecc_free(&key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + PRIVATE_KEY_LOCK(); +#endif + return EXPECT_RESULT(); +} + /* * Testing wc_ecc_shared_secret() output buffer bounds at the field-size edge. */ @@ -3023,11 +3129,8 @@ int test_wc_ecc_shared_secret_ssh(void) WC_RNG rng; int ret; int keySz = KEY32; -#if FIPS_VERSION3_GE(6,0,0) - int key2Sz = KEY28; -#else - int key2Sz = KEY24; -#endif + /* ECDH needs both keys on one curve, SP 800-56A Rev 3 sec 5.7.1.2. */ + int key2Sz = KEY32; byte secret[KEY32]; word32 secretLen = (word32)keySz; diff --git a/tests/api/test_ecc.h b/tests/api/test_ecc.h index acaa4bb349b..ff9839c22e7 100644 --- a/tests/api/test_ecc.h +++ b/tests/api/test_ecc.h @@ -38,6 +38,7 @@ int test_wc_ecc_params(void); int test_wc_ecc_signVerify_hash(void); int test_wc_ecc_shared_secret(void); int test_wc_ecc_shared_secret_at_infinity(void); +int test_wc_ecc_shared_secret_ex_peer_point(void); int test_wc_ecc_shared_secret_size_bounds(void); int test_wc_ecc_export_x963(void); int test_wc_ecc_export_x963_ex(void); @@ -91,6 +92,7 @@ int test_wc_EccDecisionCoverage4(void); TEST_DECL_GROUP("ecc", test_wc_ecc_signVerify_hash), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_at_infinity), \ + TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_ex_peer_point), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_size_bounds), \ TEST_DECL_GROUP("ecc", test_wc_ecc_export_x963), \ TEST_DECL_GROUP("ecc", test_wc_ecc_export_x963_ex), \ diff --git a/tests/api/test_mldsa.c b/tests/api/test_mldsa.c index ec00aae4bc1..82f774ce0af 100644 --- a/tests/api/test_mldsa.c +++ b/tests/api/test_mldsa.c @@ -1351,6 +1351,123 @@ int test_mldsa_sign_vfy(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ + !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ + !defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_VERIFY) +/* Collision strength in bits of each pre-hash, FIPS 202 Table 4. */ +static const struct { + int hash; + int strength; +} mldsa_prehash_strengths[] = { + { WC_HASH_TYPE_SHA224, 112 }, + { WC_HASH_TYPE_SHA256, 128 }, + { WC_HASH_TYPE_SHA384, 192 }, + { WC_HASH_TYPE_SHA512, 256 }, + { WC_HASH_TYPE_SHA512_224, 112 }, + { WC_HASH_TYPE_SHA512_256, 128 }, + { WC_HASH_TYPE_SHA3_224, 112 }, + { WC_HASH_TYPE_SHA3_256, 128 }, + { WC_HASH_TYPE_SHA3_384, 192 }, + { WC_HASH_TYPE_SHA3_512, 256 }, + { WC_HASH_TYPE_SHAKE128, 128 }, + { WC_HASH_TYPE_SHAKE256, 256 }, +}; + +static int mldsa_prehash_strength_level(byte level, int lambda, WC_RNG* rng) +{ + EXPECT_DECLS; + wc_MlDsaKey* key; + byte* sig; + byte* goodSig; + word32 sigLen; + word32 goodSigLen = MLDSA_MAX_SIG_SIZE; + byte digest[WC_MAX_DIGEST_SIZE]; + int digestSz; + int hash; + int res = 0; + int i; + + key = (wc_MlDsaKey*)XMALLOC(sizeof(*key), NULL, DYNAMIC_TYPE_TMP_BUFFER); + sig = (byte*)XMALLOC(MLDSA_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); + goodSig = (byte*)XMALLOC(MLDSA_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(key); + ExpectNotNull(sig); + ExpectNotNull(goodSig); + if (key != NULL) { + XMEMSET(key, 0, sizeof(*key)); + } + XMEMSET(digest, 0x5A, sizeof(digest)); + + ExpectIntEQ(wc_MlDsaKey_Init(key, NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_MlDsaKey_SetParams(key, level), 0); + ExpectIntEQ(wc_MlDsaKey_MakeKey(key, rng), 0); + /* SHAKE256 is in every ML-DSA build and allowed at every level: a + * well-formed signature to verify. */ + ExpectIntEQ(wc_MlDsaKey_SignCtxHash(key, NULL, 0, goodSig, &goodSigLen, + digest, (word32)wc_HashGetDigestSize(WC_HASH_TYPE_SHAKE256), + WC_HASH_TYPE_SHAKE256, rng), 0); + + for (i = 0; i < (int)(sizeof(mldsa_prehash_strengths) / + sizeof(mldsa_prehash_strengths[0])); i++) { + hash = mldsa_prehash_strengths[i].hash; + digestSz = wc_HashGetDigestSize((enum wc_HashType)hash); + if (digestSz <= 0) { + continue; /* hash not compiled in */ + } + sigLen = MLDSA_MAX_SIG_SIZE; + if (mldsa_prehash_strengths[i].strength >= lambda) { + ExpectIntEQ(wc_MlDsaKey_SignCtxHash(key, NULL, 0, sig, &sigLen, + digest, (word32)digestSz, hash, rng), 0); + ExpectIntEQ(wc_MlDsaKey_VerifyCtxHash(key, sig, sigLen, NULL, 0, + digest, (word32)digestSz, hash, &res), 0); + ExpectIntEQ(res, 1); + } + else { + ExpectIntEQ(wc_MlDsaKey_SignCtxHash(key, NULL, 0, sig, &sigLen, + digest, (word32)digestSz, hash, rng), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_MlDsaKey_VerifyCtxHash(key, goodSig, goodSigLen, + NULL, 0, digest, (word32)digestSz, hash, &res), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + } + } + + wc_MlDsaKey_Free(key); + XFREE(goodSig, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return EXPECT_RESULT(); +} +#endif + +/* HashML-DSA rejects a pre-hash weaker than lambda, FIPS 204 sec 5.4. */ +int test_mldsa_prehash_strength(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ + !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ + !defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_VERIFY) + WC_RNG rng; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + ExpectIntEQ(wc_InitRng(&rng), 0); +#ifndef WOLFSSL_NO_ML_DSA_44 + ExpectIntEQ(mldsa_prehash_strength_level(WC_ML_DSA_44, 128, &rng), + TEST_SUCCESS); +#endif +#ifndef WOLFSSL_NO_ML_DSA_65 + ExpectIntEQ(mldsa_prehash_strength_level(WC_ML_DSA_65, 192, &rng), + TEST_SUCCESS); +#endif +#ifndef WOLFSSL_NO_ML_DSA_87 + ExpectIntEQ(mldsa_prehash_strength_level(WC_ML_DSA_87, 256, &rng), + TEST_SUCCESS); +#endif + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + int test_mldsa_check_key(void) { EXPECT_DECLS; diff --git a/tests/api/test_mldsa.h b/tests/api/test_mldsa.h index 82aa9cf3ac8..1ffee9e88e9 100644 --- a/tests/api/test_mldsa.h +++ b/tests/api/test_mldsa.h @@ -33,6 +33,7 @@ int test_mldsa_make_key(void); int test_mldsa_sign(void); int test_mldsa_verify(void); int test_mldsa_sign_vfy(void); +int test_mldsa_prehash_strength(void); int test_mldsa_check_key(void); int test_mldsa_public_der_decode(void); int test_mldsa_der(void); @@ -79,6 +80,7 @@ int test_wc_MlDsaKey_SetPrecompA(void); TEST_DECL_GROUP("mldsa", test_mldsa_sign), \ TEST_DECL_GROUP("mldsa", test_mldsa_verify), \ TEST_DECL_GROUP("mldsa", test_mldsa_sign_vfy), \ + TEST_DECL_GROUP("mldsa", test_mldsa_prehash_strength), \ TEST_DECL_GROUP("mldsa", test_mldsa_check_key), \ TEST_DECL_GROUP("mldsa", test_mldsa_public_der_decode), \ TEST_DECL_GROUP("mldsa", test_mldsa_der), \ diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 0cc84908060..76a79134957 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -1551,6 +1551,13 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv); !defined(WOLFSSL_KCAPI_ECC) static int _ecc_pairwise_consistency_test(ecc_key* key, WC_RNG* rng); #endif +#if defined(HAVE_ECC_DHE) && !defined(WOLFSSL_ATECC508A) && \ + !defined(WOLFSSL_ATECC608A) && !defined(WOLFSSL_MICROCHIP_TA100) && \ + !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_KCAPI_ECC) && \ + !defined(WOLF_CRYPTO_CB_ONLY_ECC) +static int ecc_shared_secret_point(ecc_key* private_key, ecc_point* point, + byte* out, word32* outlen, int checkPoint); +#endif #ifdef HAVE_COMP_KEY @@ -4883,10 +4890,13 @@ int wc_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out, if (private_key->keyIdSet) err = se050_ecc_shared_secret(private_key, public_key, out, outlen); else - err = wc_ecc_shared_secret_ex(private_key, &public_key->pubkey, out, - outlen); + err = ecc_shared_secret_point(private_key, &public_key->pubkey, out, + outlen, 0); #else - err = wc_ecc_shared_secret_ex(private_key, &public_key->pubkey, out, outlen); + /* No point check: the key got whatever its import ran, in full under + * WOLFSSL_VALIDATE_ECC_IMPORT. */ + err = ecc_shared_secret_point(private_key, &public_key->pubkey, out, outlen, + 0); #endif /* WOLFSSL_ATECC508A */ #endif /* !WOLF_CRYPTO_CB_ONLY_ECC */ @@ -5263,17 +5273,61 @@ static int wc_ecc_shared_secret_gen_async(ecc_key* private_key, #endif /* WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC */ #ifndef WOLF_CRYPTO_CB_ONLY_ECC -/** - Create an ECC shared secret between private key and public point - private_key The private ECC key (heap hint based on private key) - point The point to use (public key) - out [out] Destination of the shared secret - Conforms to EC-DH from ANSI X9.63 - outlen [in/out] The max size and resulting size of the shared secret - return MP_OKAY if successful -*/ -int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point, - byte* out, word32 *outlen) +#ifdef HAVE_ECC_CHECK_PUBKEY_ORDER +/* Full public key validation of a peer point through wc_ecc_check_key, + * SP 800-56Ar3 5.6.2.3.3. */ +static int ecc_check_peer_point(ecc_key* key, ecc_point* point) +{ + int err; + WC_DECLARE_VAR(peer, ecc_key, 1, key->heap); + + WC_ALLOC_VAR_EX(peer, ecc_key, 1, key->heap, DYNAMIC_TYPE_ECC, + return MEMORY_E); + err = wc_ecc_init_ex(peer, key->heap, INVALID_DEVID); + if (err == MP_OKAY) { + #ifdef WOLFSSL_CUSTOM_CURVES + if (key->idx == ECC_CUSTOM_IDX) + err = wc_ecc_set_custom_curve(peer, key->dp); + else + #endif + err = wc_ecc_set_curve(peer, key->dp->size, key->dp->id); + if (err == MP_OKAY) + err = wc_ecc_copy_point(point, &peer->pubkey); + if (err == MP_OKAY) { + peer->type = ECC_PUBLICKEY; + err = wc_ecc_check_key(peer); + } + wc_ecc_free(peer); + } + WC_FREE_VAR_EX(peer, key->heap, DYNAMIC_TYPE_ECC); + return err; +} +#else +/* wc_ecc_check_key checks nothing in this build: still reject the identity, + * out of range and off-curve points, SP 800-56Ar3 5.6.2.3.4. */ +static int ecc_check_peer_point(ecc_key* key, ecc_point* point) +{ + int err; + DECLARE_CURVE_SPECS(3); + + if (wc_ecc_point_is_at_infinity(point)) + return ECC_INF_E; + ALLOC_CURVE_SPECS(3, err); + if (err == MP_OKAY) { + err = wc_ecc_curve_load(key->dp, &curve, (ECC_CURVE_FIELD_PRIME | + ECC_CURVE_FIELD_AF | ECC_CURVE_FIELD_BF)); + } + if (err == MP_OKAY) + err = wc_ecc_is_point(point, curve->Af, curve->Bf, curve->prime); + wc_ecc_curve_free(curve); + FREE_CURVE_SPECS(); + return err; +} +#endif /* HAVE_ECC_CHECK_PUBKEY_ORDER */ + +/* checkPoint is 0 only for a public key that went through key import. */ +static int ecc_shared_secret_point(ecc_key* private_key, ecc_point* point, + byte* out, word32* outlen, int checkPoint) { int err; @@ -5295,6 +5349,12 @@ int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point, return ECC_BAD_ARG_E; } + if (checkPoint && private_key->state == ECC_STATE_NONE) { + err = ecc_check_peer_point(private_key, point); + if (err != MP_OKAY) + return err; + } + switch (private_key->state) { case ECC_STATE_NONE: case ECC_STATE_SHARED_SEC_GEN: @@ -5337,8 +5397,13 @@ int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point, err = BAD_STATE_E; } /* switch */ - /* if async pending then return and skip done cleanup below */ - if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) { + /* if async pending or non-blocking then return and skip done cleanup below + * so the peer point is checked once per operation, not once per call */ + if (err == WC_NO_ERR_TRACE(WC_PENDING_E) + #ifdef WC_ECC_NONBLOCK + || err == WC_NO_ERR_TRACE(MP_WOULDBLOCK) + #endif + ) { return err; } @@ -5350,6 +5415,21 @@ int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point, return err; } + +/** + Create an ECC shared secret between private key and public point + private_key The private ECC key (heap hint based on private key) + point The point to use (public key), fully validated here + out [out] Destination of the shared secret + Conforms to EC-DH from ANSI X9.63 + outlen [in/out] The max size and resulting size of the shared secret + return MP_OKAY if successful +*/ +int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point, + byte* out, word32 *outlen) +{ + return ecc_shared_secret_point(private_key, point, out, outlen, 1); +} #endif /* WOLF_CRYPTO_CB_ONLY_ECC */ #elif defined(WOLFSSL_KCAPI_ECC) int wc_ecc_shared_secret_ex(ecc_key* private_key, ecc_point* point, @@ -11455,6 +11535,13 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv) #else + /* ECDH uses the point's Z but the SP checks read only x and y, so accept + * only an affine point (Z = 1); all zero x and y still reports ECC_INF_E. */ + if (!wc_ecc_point_is_at_infinity(&key->pubkey) && + !mp_isone(key->pubkey.z)) { + return ECC_BAD_ARG_E; + } + #ifdef WOLFSSL_HAVE_SP_ECC #ifndef WOLFSSL_SP_NO_256 if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SECP256R1) { diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index edebcdf60a9..c38f856930b 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -862,6 +862,35 @@ static int mldsa_hash256_ctx_msg(wc_Shake* shake256, const byte* tr, return ret; } +/* Pre-hash must give at least lambda bits of collision strength. + * FIPS 204 sec 5.4; strengths from FIPS 202 Table 4. */ +static int mldsa_check_hash_strength(int hashAlg, word16 lambda) +{ + word16 strength; + + switch (hashAlg) { + case WC_HASH_TYPE_SHA256: + case WC_HASH_TYPE_SHA512_256: + case WC_HASH_TYPE_SHA3_256: + case WC_HASH_TYPE_SHAKE128: + strength = 128; + break; + case WC_HASH_TYPE_SHA384: + case WC_HASH_TYPE_SHA3_384: + strength = 192; + break; + case WC_HASH_TYPE_SHA512: + case WC_HASH_TYPE_SHA3_512: + case WC_HASH_TYPE_SHAKE256: + strength = 256; + break; + default: + return BAD_FUNC_ARG; + } + + return (strength >= lambda) ? 0 : BAD_FUNC_ARG; +} + /* Get the OID for the digest hash. * * @param [in] hash Hash algorithm. @@ -11723,6 +11752,9 @@ static int mldsa_sign_ctx_hash_with_seed(wc_MlDsaKey* key, if ((int)hashLen != wc_HashGetDigestSize((enum wc_HashType)hashAlg)) { ret = BAD_LENGTH_E; } + if (ret == 0) { + ret = mldsa_check_hash_strength(hashAlg, key->params->lambda); + } if (ret == 0) { XMEMCPY(seedMu, seed, MLDSA_RND_SZ); @@ -12584,6 +12616,9 @@ static int mldsa_verify_ctx_hash(wc_MlDsaKey* key, const byte* ctx, { ret = BAD_LENGTH_E; } + if (ret == 0) { + ret = mldsa_check_hash_strength(hashAlg, key->params->lambda); + } if (ret == 0) { /* Step 6: Hash public key. */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 3ca57e524ca..7df1e08cdc1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -69001,25 +69001,27 @@ static wc_test_ret_t mldsa_param_test(int param, WC_RNG* rng) if (res != 1) ERROR_OUT(WC_TEST_RET_ENC_I(res), out); -#ifndef NO_SHA256 +#ifdef WOLFSSL_SHAKE256 /* HashML-DSA: the pre-hash APIs take a digest plus its hash type, and - * reach a crypto callback with a preHashType other than NONE. */ + * reach a crypto callback with a preHashType other than NONE. SHAKE256 + * is strong enough for every parameter set, FIPS 204 sec 5.4. */ { - byte digest[WC_SHA256_DIGEST_SIZE]; + byte digest[WC_SHA3_512_DIGEST_SIZE]; - ret = wc_Sha256Hash(msg, (word32)sizeof(msg), digest); + ret = wc_Shake256Hash(msg, (word32)sizeof(msg), digest, + (word32)sizeof(digest)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); sigLen = wc_MlDsaKey_SigSize(key); ret = wc_MlDsaKey_SignCtxHash(key, NULL, 0, sig, &sigLen, digest, - (word32)sizeof(digest), WC_HASH_TYPE_SHA256, rng); + (word32)sizeof(digest), WC_HASH_TYPE_SHAKE256, rng); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); res = 0; ret = wc_MlDsaKey_VerifyCtxHash(key, sig, sigLen, NULL, 0, digest, - (word32)sizeof(digest), WC_HASH_TYPE_SHA256, &res); + (word32)sizeof(digest), WC_HASH_TYPE_SHAKE256, &res); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); if (res != 1) @@ -96451,11 +96453,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) ret = WC_TEST_RET_ENC_NC; #endif /* The pre-hash arms are reached only by the HashML-DSA round trip in - * mldsa_param_test(), so they need !NO_MAKE_KEY and SHA-256 too. */ + * mldsa_param_test(), so they need !NO_MAKE_KEY and SHAKE256 too. */ #if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) && \ - !defined(WOLFSSL_MLDSA_NO_CTX) && !defined(NO_SHA256) && \ + !defined(WOLFSSL_MLDSA_NO_CTX) && defined(WOLFSSL_SHAKE256) && \ !defined(HAVE_FIPS) if ((ret == 0) && (myCtx.mldsaSignHashCount == 0)) ret = WC_TEST_RET_ENC_NC; diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 5cd36b1a46e..900ce9c6b46 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -6258,6 +6258,11 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error WC_C_DYNAMIC_FALLBACK needs --enable-fips=dev or dev-no-post #endif +/* SP 800-38A sec 5.2: CBC input must be a whole number of blocks. */ +#if FIPS_VERSION3_GE(7,0,0) && !defined(WOLFSSL_AES_CBC_LENGTH_CHECKS) + #define WOLFSSL_AES_CBC_LENGTH_CHECKS +#endif + /* setup for opt-in DH in FIPS v7+ */ #if FIPS_VERSION3_GE(7,0,0) && !defined(HAVE_DH) && !defined(NO_DH) #define NO_DH