diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index efad857503f..85f125c2e6b 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -3890,6 +3890,152 @@ int test_wc_ecc_mulmod_zero(void) return EXPECT_RESULT(); } /* END test_wc_ecc_mulmod_zero */ +/* + * Testing that wc_ecc_mulmod() of the private key and the base point gives + * the public key, on every curve compiled in. This covers curves the SP code + * implements and other curves of the same size, which must not be handed to + * the SP code. + */ +int test_wc_ecc_mulmod_curves(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ECC) && !defined(WC_NO_RNG) && \ + !(defined(WOLFSSL_ATECC508A) || defined(WOLFSSL_ATECC608A) || \ + defined(WOLFSSL_MICROCHIP_TA100) || \ + defined(WOLFSSL_VALIDATE_ECC_IMPORT)) && \ + !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(HAVE_SELFTEST) && \ + !defined(HAVE_FIPS) && \ + !defined(WOLFSSL_NO_ECC_SW) + static const int curves[] = { + ECC_SECP256R1, ECC_SECP384R1, ECC_SECP521R1, ECC_SECP256K1, + ECC_BRAINPOOLP256R1, ECC_BRAINPOOLP384R1, ECC_BRAINPOOLP512R1 + }; + ecc_key key1; + ecc_key key2; + ecc_key key3; + ecc_key key4; +#if defined(WOLFSSL_PUBLIC_MP) && !defined(WOLFSSL_SP_MATH) + mp_int negY; +#endif + WC_RNG rng; + int ret; + int i; + int tested = 0; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + ExpectIntEQ(wc_InitRng(&rng), 0); + + for (i = 0; i < (int)(sizeof(curves) / sizeof(curves[0])); i++) { + const ecc_set_type* dp; + int idx = wc_ecc_get_curve_idx(curves[i]); + + if (idx < 0) { + continue; + } + dp = wc_ecc_get_curve_params(idx); + ExpectNotNull(dp); + if (dp == NULL) { + break; + } + + XMEMSET(&key1, 0, sizeof(ecc_key)); + XMEMSET(&key2, 0, sizeof(ecc_key)); + XMEMSET(&key3, 0, sizeof(ecc_key)); + XMEMSET(&key4, 0, sizeof(ecc_key)); + ExpectIntEQ(wc_ecc_init(&key1), 0); + ExpectIntEQ(wc_ecc_init(&key2), 0); + ExpectIntEQ(wc_ecc_init(&key3), 0); + ExpectIntEQ(wc_ecc_init(&key4), 0); + + ret = wc_ecc_make_key_ex(&rng, dp->size, &key1, curves[i]); + #if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key1.asyncDev, WC_ASYNC_FLAG_NONE); + #endif + ExpectIntEQ(ret, 0); + + /* As in test_wc_ecc_mulmod(): the base point, the result point, and + * the curve parameter A and the prime as public coordinates. */ + ExpectIntEQ(wc_ecc_import_raw_ex(&key2, dp->Gx, dp->Gy, NULL, + curves[i]), 0); + ExpectIntEQ(wc_ecc_import_raw_ex(&key3, dp->Gx, dp->Gy, NULL, + curves[i]), 0); + ExpectIntEQ(wc_ecc_import_raw_ex(&key4, dp->Af, dp->prime, NULL, + curves[i]), 0); + + ExpectIntEQ(wc_ecc_mulmod(wc_ecc_key_get_priv(&key1), &key2.pubkey, + &key3.pubkey, key4.pubkey.x, key4.pubkey.y, 1), 0); + ExpectIntEQ(wc_ecc_cmp_point(&key3.pubkey, &key1.pubkey), MP_EQ); + #if defined(WOLFSSL_PUBLIC_MP) && !defined(WOLFSSL_SP_MATH) + /* The same base point with z = 1 + prime, a coordinate that is not + * reduced (and wider than the curve for most primes), must give the + * same result. WOLFSSL_SP_MATH builds reject coordinates wider than + * the curve instead. */ + ExpectIntEQ(mp_add(key2.pubkey.z, key4.pubkey.y, key2.pubkey.z), + MP_OKAY); + ExpectIntEQ(wc_ecc_mulmod(wc_ecc_key_get_priv(&key1), &key2.pubkey, + &key3.pubkey, key4.pubkey.x, key4.pubkey.y, 1), 0); + ExpectIntEQ(wc_ecc_cmp_point(&key3.pubkey, &key1.pubkey), MP_EQ); + + /* With z = prime - 1 the base point is its negation, (x, -y), so the + * result is the negated public key. The SP code's FP_ECC cache + * matches tables by x and y only, so this must not leave a table for + * the negation that the affine base point then uses: multiply the + * affine point again, with the generic code's cache emptied so that + * the SP code is reached. */ + XMEMSET(&negY, 0, sizeof(negY)); + ExpectIntEQ(mp_init(&negY), MP_OKAY); + ExpectIntEQ(mp_sub_d(key4.pubkey.y, 1, key2.pubkey.z), MP_OKAY); + ExpectIntEQ(wc_ecc_mulmod(wc_ecc_key_get_priv(&key1), &key2.pubkey, + &key3.pubkey, key4.pubkey.x, key4.pubkey.y, 1), 0); + ExpectIntEQ(mp_sub(key4.pubkey.y, key1.pubkey.y, &negY), MP_OKAY); + ExpectIntEQ(mp_cmp(key3.pubkey.x, key1.pubkey.x), MP_EQ); + ExpectIntEQ(mp_cmp(key3.pubkey.y, &negY), MP_EQ); + ExpectIntEQ(mp_cmp_d(key3.pubkey.z, 1), MP_EQ); + mp_free(&negY); + ExpectIntEQ(mp_set(key2.pubkey.z, 1), MP_OKAY); + #ifdef FP_ECC + wc_ecc_fp_free(); + #endif + ExpectIntEQ(wc_ecc_mulmod(wc_ecc_key_get_priv(&key1), &key2.pubkey, + &key3.pubkey, key4.pubkey.x, key4.pubkey.y, 1), 0); + ExpectIntEQ(wc_ecc_cmp_point(&key3.pubkey, &key1.pubkey), MP_EQ); + #if !defined(WOLFSSL_SP_MATH_ALL) || defined(WOLFSSL_SP_INT_NEGATIVE) + /* A result point with negative ordinates must be given the result + * with positive ordinates. */ + mp_zero(key3.pubkey.x); + mp_zero(key3.pubkey.y); + mp_zero(key3.pubkey.z); + ExpectIntEQ(mp_sub(key3.pubkey.x, key4.pubkey.y, key3.pubkey.x), + MP_OKAY); + ExpectIntEQ(mp_sub(key3.pubkey.y, key4.pubkey.y, key3.pubkey.y), + MP_OKAY); + ExpectIntEQ(mp_sub(key3.pubkey.z, key4.pubkey.y, key3.pubkey.z), + MP_OKAY); + #ifdef FP_ECC + wc_ecc_fp_free(); + #endif + ExpectIntEQ(wc_ecc_mulmod(wc_ecc_key_get_priv(&key1), &key2.pubkey, + &key3.pubkey, key4.pubkey.x, key4.pubkey.y, 1), 0); + ExpectIntEQ(wc_ecc_cmp_point(&key3.pubkey, &key1.pubkey), MP_EQ); + #endif + #endif + tested++; + + wc_ecc_free(&key1); + wc_ecc_free(&key2); + wc_ecc_free(&key3); + wc_ecc_free(&key4); + } + ExpectIntGT(tested, 0); + + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#ifdef FP_ECC + wc_ecc_fp_free(); +#endif +#endif + return EXPECT_RESULT(); +} /* END test_wc_ecc_mulmod_curves */ + /* * Testing wc_ecc_is_valid_idx() */ diff --git a/tests/api/test_ecc.h b/tests/api/test_ecc.h index 33527118036..45258274c98 100644 --- a/tests/api/test_ecc.h +++ b/tests/api/test_ecc.h @@ -73,6 +73,7 @@ int test_wc_ecc_verify_hash_ex(void); int test_wc_ecc_mulmod(void); int test_wc_ecc_mulmod_unreduced(void); int test_wc_ecc_mulmod_zero(void); +int test_wc_ecc_mulmod_curves(void); int test_wc_ecc_is_valid_idx(void); int test_wc_ecc_get_curve_id_from_oid(void); int test_wc_ecc_sig_size_calc(void); @@ -135,6 +136,7 @@ int test_wc_ecc_make_key_id_dev(void); TEST_DECL_GROUP("ecc", test_wc_ecc_mulmod), \ TEST_DECL_GROUP("ecc", test_wc_ecc_mulmod_unreduced), \ TEST_DECL_GROUP("ecc", test_wc_ecc_mulmod_zero), \ + TEST_DECL_GROUP("ecc", test_wc_ecc_mulmod_curves), \ TEST_DECL_GROUP("ecc", test_wc_ecc_is_valid_idx), \ TEST_DECL_GROUP("ecc", test_wc_ecc_get_curve_id_from_oid), \ TEST_DECL_GROUP("ecc", test_wc_ecc_sig_size_calc), \ diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 4ad87d070cb..c6cc570face 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -3833,6 +3833,114 @@ static void ecc_key_tmp_final(ecc_key* key, void* heap) #endif /* WOLFSSL_SMALL_STACK_CACHE */ #endif /* !WOLFSSL_SP_MATH */ +#if defined(WOLFSSL_HAVE_SP_ECC) && !defined(WOLFSSL_SP_MATH) +/* Point multiplication with the SP code for callers of wc_ecc_mulmod() and + * wc_ecc_mulmod_ex() (hostapd's SAE, for example) when the curve is one the SP + * code implements. Only the modulus and a are given, so the curve is + * identified by both, as other curves of the same size may be compiled in. + * Only when mapping back to affine coordinates, as otherwise the result is + * expected in the generic code's representation. + * + * Returns NOT_COMPILED_IN when the SP code does not apply. + */ +static int ecc_sp_mulmod(const mp_int* k, ecc_point* G, ecc_point* R, + mp_int* a, mp_int* modulus, int map, void* heap) +{ + DECL_MP_INT_SIZE_DYN(t, mp_bitsused(modulus), MAX_ECC_BITS_USE); + int bits = mp_count_bits(modulus); + int curveId; + int idx; + int err; + + if (!map || a == NULL || MP_BITS_OVER_MAX(bits, MAX_ECC_BITS_USE)) { + return NOT_COMPILED_IN; + } + switch (bits) { +#ifndef WOLFSSL_SP_NO_256 + case 256: + curveId = ECC_SECP256R1; + break; +#endif +#ifdef WOLFSSL_SP_384 + case 384: + curveId = ECC_SECP384R1; + break; +#endif +#ifdef WOLFSSL_SP_521 + case 521: + curveId = ECC_SECP521R1; + break; +#endif + default: + return NOT_COMPILED_IN; + } + /* The SP code takes a non-negative scalar no longer than the curve and an + * affine point (z = 1) with coordinates already reduced modulo the prime: + * it would truncate wider values, and with FP_ECC it caches tables by x + * and y only. It writes the result's digits but not their signs, so the + * result point's ordinates must not be negative either. Leave anything + * else to the generic code, which handles them. */ + if (mp_isneg(k) || (mp_count_bits(k) > bits)) { + return NOT_COMPILED_IN; + } + if (mp_isneg(G->x) || mp_isneg(G->y) || + (mp_cmp(G->x, modulus) != MP_LT) || + (mp_cmp(G->y, modulus) != MP_LT) || + (mp_cmp_d(G->z, 1) != MP_EQ)) { + return NOT_COMPILED_IN; + } + if (mp_isneg(R->x) || mp_isneg(R->y) || mp_isneg(R->z)) { + return NOT_COMPILED_IN; + } + idx = wc_ecc_get_curve_idx(curveId); + if (idx < 0) { + return NOT_COMPILED_IN; + } + + NEW_MP_INT_SIZE(t, bits, heap, DYNAMIC_TYPE_ECC); +#ifdef MP_INT_SIZE_CHECK_NULL + if (t == NULL) { + return MEMORY_E; + } +#endif + err = INIT_MP_INT_SIZE(t, bits); + if (err == MP_OKAY) { + err = mp_read_radix(t, ecc_sets[idx].prime, MP_RADIX_HEX); + } + if ((err == MP_OKAY) && (mp_cmp(t, modulus) != MP_EQ)) { + err = NOT_COMPILED_IN; + } + if (err == MP_OKAY) { + err = mp_read_radix(t, ecc_sets[idx].Af, MP_RADIX_HEX); + } + if ((err == MP_OKAY) && (mp_cmp(t, a) != MP_EQ)) { + err = NOT_COMPILED_IN; + } + mp_clear(t); + FREE_MP_INT_SIZE(t, heap, DYNAMIC_TYPE_ECC); + if (err != MP_OKAY) { + return err; + } + + switch (bits) { +#ifndef WOLFSSL_SP_NO_256 + case 256: + return sp_ecc_mulmod_256(k, G, R, map, heap); +#endif +#ifdef WOLFSSL_SP_384 + case 384: + return sp_ecc_mulmod_384(k, G, R, map, heap); +#endif +#ifdef WOLFSSL_SP_521 + case 521: + return sp_ecc_mulmod_521(k, G, R, map, heap); +#endif + default: + return NOT_COMPILED_IN; + } +} +#endif /* WOLFSSL_HAVE_SP_ECC && !WOLFSSL_SP_MATH */ + #if !defined(WOLFSSL_SP_MATH) || !defined(FP_ECC) /** Perform a point multiplication @@ -3889,6 +3997,13 @@ int wc_ecc_mulmod_ex(const mp_int* k, ecc_point *G, ecc_point *R, mp_int* a, goto exit; } +#if defined(WOLFSSL_HAVE_SP_ECC) && !defined(WOLFSSL_SP_MATH) + err = ecc_sp_mulmod(k, G, R, a, modulus, map, heap); + if (err != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { + return err; + } +#endif + #ifdef WOLFSSL_SMALL_STACK_CACHE key = (ecc_key *)XMALLOC(sizeof(*key), heap, DYNAMIC_TYPE_ECC); if (key == NULL) {