diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index 2dcbdfba8d2..56d3a3fb7d6 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -530,6 +530,12 @@ "configure": ["--enable-cryptonly", "--disable-dh", "--enable-eccsi", "--enable-sakke", "--enable-rsavfy", "--enable-sp-math", "--enable-sp", "--disable-examples"]}, +{"name": "eccsi-sp-no-p256", + "comment": "ECCSI on SP-ECC without SP P-256.", + "configure": ["--enable-eccsi", "--enable-sp=ec384", "--enable-ecccustcurves=all"]}, +{"name": "eccsi-sp-custcurves", + "comment": "ECCSI on SP P-256 with the custom curves.", + "configure": ["--enable-eccsi", "--enable-sp", "--enable-ecccustcurves=all"]}, {"name": "cryptonly-no-asn-rsa", "minutes": 0.2, "check": false, "comment": "RSA with ASN.1 disabled. Build only, since wolfcrypt test and benchmark do not support this combination.", "configure": ["--enable-cryptonly", "--disable-asn", "--enable-lowresource", diff --git a/tests/api/test_eccsi.c b/tests/api/test_eccsi.c index df09a80e2b2..da0102f0e67 100644 --- a/tests/api/test_eccsi.c +++ b/tests/api/test_eccsi.c @@ -485,11 +485,7 @@ int test_wc_Eccsi_DecisionCoverage(void) #endif /* --- wc_ValidateEccsiPvt() (CLIENT) --- - * eccsi.c ~line 1579: (key==NULL) | (pvt==NULL) || (valid==NULL) - * (first two operands are combined with a bitwise '|', not '&&'/'||', - * so both are always evaluated; MC/DC's baseline+single-flip pattern - * still applies since the result is the same 0/1 boolean algebra.) - */ + * eccsi.c: (key==NULL) || (pvt==NULL) || (valid==NULL) */ ExpectIntEQ(wc_ValidateEccsiPvt(NULL, pvt, &valid), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); ExpectIntEQ(wc_ValidateEccsiPvt(&keyPub, NULL, &valid), @@ -860,3 +856,170 @@ int test_wc_Eccsi_FeatureCoverage(void) #endif return EXPECT_RESULT(); } + +/* Reuse one KMS key object across key generations and signatures. */ +int test_wc_Eccsi_KeyReuse(void) +{ + EXPECT_DECLS; +#ifdef WOLFCRYPT_HAVE_ECCSI + EccsiKey key; + WC_RNG rng; + mp_int ssk; + ecc_point* pvt = NULL; + ecc_point* badPvt = NULL; + char mail[] = "test@wolfssl.com"; + byte* id = (byte*)mail; + word32 idSz; + int valid; + int verified; + int round; + byte hash[WC_MAX_DIGEST_SIZE]; + byte hashSz = 0; + byte sig[32 * 4 + 1]; + word32 sigSz; + byte msg[1] = { 0x00 }; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&ssk, 0, sizeof(ssk)); + idSz = (word32)XSTRLEN(mail); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectNotNull(pvt = wc_ecc_new_point()); + ExpectNotNull(badPvt = wc_ecc_new_point()); + ExpectIntEQ(mp_init(&ssk), MP_OKAY); + ExpectIntEQ(wc_InitEccsiKey(&key, HEAP_HINT, INVALID_DEVID), 0); + + /* The second round regenerates the KPAK after a verify used it. */ + for (round = 0; round < 2; round++) { + verified = 0; + ExpectIntEQ(wc_MakeEccsiKey(&key, &rng), 0); + if (round > 0) { + /* The identity hash set in the first round is for the old KPAK. */ + sigSz = sizeof(sig); + ExpectIntEQ(wc_SignEccsiHash(&key, &rng, WC_HASH_TYPE_SHA256, msg, + (word32)sizeof(msg), sig, &sigSz), + WC_NO_ERR_TRACE(BAD_STATE_E)); + } + ExpectIntEQ(wc_MakeEccsiPair(&key, &rng, WC_HASH_TYPE_SHA256, id, + idSz, &ssk, pvt), 0); + valid = 0; + ExpectIntEQ(wc_ValidateEccsiPvt(&key, pvt, &valid), 0); + ExpectIntEQ(valid, 1); + ExpectIntEQ(wc_HashEccsiId(&key, WC_HASH_TYPE_SHA256, id, idSz, pvt, + hash, &hashSz), 0); + ExpectIntEQ(wc_SetEccsiHash(&key, hash, hashSz), 0); + ExpectIntEQ(wc_SetEccsiPair(&key, &ssk, pvt), 0); + sigSz = sizeof(sig); + ExpectIntEQ(wc_SignEccsiHash(&key, &rng, WC_HASH_TYPE_SHA256, msg, + (word32)sizeof(msg), sig, &sigSz), 0); + valid = 0; + ExpectIntEQ(wc_ValidateEccsiPvt(&key, pvt, &valid), 0); + ExpectIntEQ(valid, 1); + ExpectIntEQ(wc_VerifyEccsiHash(&key, WC_HASH_TYPE_SHA256, msg, + (word32)sizeof(msg), sig, sigSz, &verified), 0); + ExpectIntEQ(verified, 1); + valid = 0; + ExpectIntEQ(wc_ValidateEccsiPvt(&key, pvt, &valid), 0); + ExpectIntEQ(valid, 1); + } + + /* A PVT off the curve or at infinity is rejected. */ + ExpectIntEQ(wc_ecc_copy_point(pvt, badPvt), 0); + ExpectIntEQ(mp_add_d(badPvt->y, 1, badPvt->y), MP_OKAY); + valid = 1; + ExpectIntEQ(wc_ValidateEccsiPvt(&key, badPvt, &valid), 0); + ExpectIntEQ(valid, 0); + ExpectIntEQ(mp_set(badPvt->x, 0), MP_OKAY); + ExpectIntEQ(mp_set(badPvt->y, 0), MP_OKAY); + valid = 1; + ExpectIntEQ(wc_ValidateEccsiPvt(&key, badPvt, &valid), 0); + ExpectIntEQ(valid, 0); + + if (badPvt != NULL) { + wc_ecc_del_point(badPvt); + } + if (pvt != NULL) { + wc_ecc_del_point(pvt); + } + mp_free(&ssk); + wc_FreeRng(&rng); + wc_FreeEccsiKey(&key); +#endif + return EXPECT_RESULT(); +} + +/* Sign and verify on each 256-bit curve compiled in. */ +int test_wc_Eccsi_Curves(void) +{ + EXPECT_DECLS; +#ifdef WOLFCRYPT_HAVE_ECCSI + static const int curves[] = { + ECC_SECP256R1, + #ifdef HAVE_ECC_BRAINPOOL + ECC_BRAINPOOLP256R1, + #endif + #ifdef HAVE_ECC_KOBLITZ + ECC_SECP256K1, + #endif + }; + EccsiKey key; + WC_RNG rng; + mp_int ssk; + ecc_point* pvt = NULL; + char mail[] = "test@wolfssl.com"; + byte* id = (byte*)mail; + word32 idSz; + int valid; + int verified; + int i; + byte hash[WC_MAX_DIGEST_SIZE]; + byte hashSz = 0; + byte sig[32 * 4 + 1]; + word32 sigSz; + byte msg[1] = { 0x00 }; + + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&ssk, 0, sizeof(ssk)); + idSz = (word32)XSTRLEN(mail); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectNotNull(pvt = wc_ecc_new_point()); + ExpectIntEQ(mp_init(&ssk), MP_OKAY); + + for (i = 0; i < (int)(sizeof(curves) / sizeof(curves[0])); i++) { + verified = 0; + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_InitEccsiKey_ex(&key, 32, curves[i], HEAP_HINT, + INVALID_DEVID), 0); + ExpectIntEQ(wc_MakeEccsiKey(&key, &rng), 0); + ExpectIntEQ(wc_MakeEccsiPair(&key, &rng, WC_HASH_TYPE_SHA256, id, + idSz, &ssk, pvt), 0); + valid = 0; + ExpectIntEQ(wc_ValidateEccsiPair(&key, WC_HASH_TYPE_SHA256, id, idSz, + &ssk, pvt, &valid), 0); + ExpectIntEQ(valid, 1); + valid = 0; + ExpectIntEQ(wc_ValidateEccsiPvt(&key, pvt, &valid), 0); + ExpectIntEQ(valid, 1); + ExpectIntEQ(wc_HashEccsiId(&key, WC_HASH_TYPE_SHA256, id, idSz, pvt, + hash, &hashSz), 0); + ExpectIntEQ(wc_SetEccsiHash(&key, hash, hashSz), 0); + ExpectIntEQ(wc_SetEccsiPair(&key, &ssk, pvt), 0); + sigSz = sizeof(sig); + ExpectIntEQ(wc_SignEccsiHash(&key, &rng, WC_HASH_TYPE_SHA256, msg, + (word32)sizeof(msg), sig, &sigSz), 0); + ExpectIntEQ(wc_VerifyEccsiHash(&key, WC_HASH_TYPE_SHA256, msg, + (word32)sizeof(msg), sig, sigSz, &verified), 0); + ExpectIntEQ(verified, 1); + wc_FreeEccsiKey(&key); + } + + if (pvt != NULL) { + wc_ecc_del_point(pvt); + } + mp_free(&ssk); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_eccsi.h b/tests/api/test_eccsi.h index 80ec3257944..01a0eb6d65f 100644 --- a/tests/api/test_eccsi.h +++ b/tests/api/test_eccsi.h @@ -26,9 +26,13 @@ int test_wc_Eccsi_DecisionCoverage(void); int test_wc_Eccsi_FeatureCoverage(void); +int test_wc_Eccsi_KeyReuse(void); +int test_wc_Eccsi_Curves(void); #define TEST_ECCSI_DECLS \ TEST_DECL_GROUP("eccsi", test_wc_Eccsi_DecisionCoverage), \ - TEST_DECL_GROUP("eccsi", test_wc_Eccsi_FeatureCoverage) + TEST_DECL_GROUP("eccsi", test_wc_Eccsi_FeatureCoverage), \ + TEST_DECL_GROUP("eccsi", test_wc_Eccsi_KeyReuse), \ + TEST_DECL_GROUP("eccsi", test_wc_Eccsi_Curves) #endif /* WOLFCRYPT_TEST_ECCSI_H */ diff --git a/wolfcrypt/src/eccsi.c b/wolfcrypt/src/eccsi.c index 95cab2236cf..32a8ed5efad 100644 --- a/wolfcrypt/src/eccsi.c +++ b/wolfcrypt/src/eccsi.c @@ -306,80 +306,6 @@ static int eccsi_encode_base(EccsiKey* key, byte* data, word32* dataSz) return err; } -#ifndef WOLFSSL_HAVE_SP_ECC -/* - * Convert the KPAK to montgomery form. - * - * The KPAK is needed in Montgomery form for verification. - * - * @param [in] key ECCSI key. - * @return 0 on success. - * @return MP_MEM or MEMORY_E when dynamic memory allocation fails. - * @return Other -ve value when an internal operation fails. - */ -static int eccsi_kpak_to_mont(EccsiKey* key) -{ - int err = 0; - ecc_point* kpak = &key->ecc.pubkey; - mp_int* mu = &key->tmp; - mp_int* prime = &key->params.prime; - - if (!key->kpakMont) { - err = mp_montgomery_calc_normalization(mu, prime); - if (err == 0) { - err = mp_mulmod(kpak->x, mu, prime, kpak->x); - } - if (err == 0) { - err = mp_mulmod(kpak->y, mu, prime, kpak->y); - } - if (err == 0) { - err = mp_mulmod(kpak->z, mu, prime, kpak->z); - } - if (err == 0) { - key->kpakMont = 1; - } - } - - return err; -} -#endif - -/* - * Convert the KPAK from montgomery form. - * - * The KPAK is needed in Montgomery form for verification. - * - * @param [in] key ECCSI key. - * @return 0 on success. - * @return MP_MEM or MEMORY_E when dynamic memory allocation fails. - * @return Other -ve value when an internal operation fails. - */ -static int eccsi_kpak_from_mont(EccsiKey* key) -{ - int err = 0; - ecc_point* kpak = &key->ecc.pubkey; - mp_digit mp; - mp_int* prime = &key->params.prime; - - if (key->kpakMont) { - err = mp_montgomery_setup(prime, &mp); - if (err == 0) { - err = mp_montgomery_reduce(kpak->x, prime, mp); - } - if (err == 0) { - err = mp_montgomery_reduce(kpak->y, prime, mp); - } - if (err == 0) { - err = mp_montgomery_reduce(kpak->z, prime, mp); - } - if (err == 0) { - key->kpakMont = 0; - } - } - - return err; -} - /* * Compute HS = hash( G | KPAK | ID | PVT ) * @@ -416,9 +342,6 @@ static int eccsi_compute_hs(EccsiKey* key, enum wc_HashType hashType, if (err == 0) { err = wc_HashUpdate(&key->hash, hashType, key->data, dataSz); } - if (err == 0) { - err = eccsi_kpak_from_mont(key); - } if (err == 0) { dataSz = sizeof(key->data); /* KPAK - public key */ @@ -470,6 +393,7 @@ static int eccsi_compute_hs(EccsiKey* key, enum wc_HashType hashType, * * Creates a random private key and multiplies it by the base point to calculate * the public key. + * Clears the identity hash, as it was computed with the previous KPAK. * * @param [in] key ECCSI key. * @param [in] rng Random number generator. @@ -487,6 +411,7 @@ int wc_MakeEccsiKey(EccsiKey* key, WC_RNG* rng) } if (err == 0) { + key->idHashSz = 0; err = wc_ecc_make_key_ex(rng, key->ecc.dp->size, &key->ecc, key->ecc.dp->id); #ifdef WOLFSSL_ASYNC_CRYPT @@ -677,9 +602,6 @@ int wc_ExportEccsiKey(EccsiKey* key, byte* data, word32* sz) *sz = (word32)(key->ecc.dp->size * 3); } } - if (err == 0) { - err = eccsi_kpak_from_mont(key); - } if (err == 0) { /* Encode key */ err = eccsi_encode_key(key, data); @@ -747,8 +669,6 @@ int wc_ImportEccsiKey(EccsiKey* key, const byte* data, word32 sz) } if (err == 0) { - key->kpakMont = 0; - /* Decode key */ err = eccsi_decode_key(key, data); } @@ -879,9 +799,6 @@ int wc_ExportEccsiPublicKey(EccsiKey* key, byte* data, word32* sz, int raw) err = BAD_STATE_E; } - if ((err == 0) && (data != NULL)) { - err = eccsi_kpak_from_mont(key); - } if (err == 0) { /* Write out public key. */ err = eccsi_encode_point(&key->ecc.pubkey, (word32)key->ecc.dp->size, @@ -1342,8 +1259,6 @@ int wc_ImportEccsiPublicKey(EccsiKey* key, const byte* data, word32 sz, } if (err == 0) { - key->kpakMont = 0; - /* Read the public key. */ err = eccsi_decode_point(&key->ecc.pubkey, (word32)key->ecc.dp->size, data, sz); @@ -1363,7 +1278,7 @@ int wc_ImportEccsiPublicKey(EccsiKey* key, const byte* data, word32 sz, * * @param [in] key ECCSI key. * @param [in] n MP integer representing scalar to multiply by. - * @param [in] a ECC point to add. + * @param [in] a ECC point to add, in Montgomery form. * @param [out] res ECC point representation of the resulting point. * @param [in] mp Montgomery reduction multiplier. * @param [in] map 0 indicates to leave in projective representation. @@ -1453,7 +1368,7 @@ static int eccsi_mulmod_point(EccsiKey* key, const mp_int* n, ecc_point* point, * @param [in] key ECCSI key. * @param [in] n MP integer representing scalar to multiply by. * @param [in] point ECC point representation of a point on the curve. - * @param [in] a ECC point to add. + * @param [in] a ECC point to add, in normal form. * @param [out] res ECC point representation of the resulting point. * @param [in] mp Montgomery reduction multiplier. * @param [in] map 0 indicates to leave in projective representation. @@ -1465,32 +1380,64 @@ static int eccsi_mulmod_point(EccsiKey* key, const mp_int* n, ecc_point* point, static int eccsi_mulmod_point_add(EccsiKey* key, const mp_int* n, ecc_point* point, ecc_point* a, ecc_point* res, mp_digit mp, int map) { -#if defined(WOLFSSL_HAVE_SP_ECC) && !defined(WOLFSSL_SP_NO_256) - int err = WC_NO_ERR_TRACE(NOT_COMPILED_IN); + int err = 0; +#if defined(WOLFSSL_HAVE_SP_ECC) && !defined(WOLFSSL_SP_NO_256) if ((key->ecc.idx != ECC_CUSTOM_IDX) && (ecc_sets[key->ecc.idx].id == ECC_SECP256R1)) { err = sp_ecc_mulmod_add_256(n, point, a, 0, res, map, key->heap); } + else +#endif +#ifndef WOLFSSL_SP_MATH + { + EccsiKeyParams* params = &key->params; + ecc_point* montA; - (void)mp; - - return err; -#else - int err; - EccsiKeyParams* params = &key->params; - - err = wc_ecc_mulmod(n, point, res, ¶ms->a, ¶ms->prime, 0); - if (err == 0) { - err = ecc_projective_add_point(res, a, res, &key->params.a, - ¶ms->prime, mp); + montA = wc_ecc_new_point_h(key->heap); + if (montA == NULL) { + err = MEMORY_E; + } + if (err == 0) { + err = wc_ecc_mulmod(n, point, res, ¶ms->a, ¶ms->prime, 0); + } + if (err == 0) { + err = mp_montgomery_calc_normalization(montA->z, ¶ms->prime); + } + if (err == 0) { + err = mp_mulmod(a->x, montA->z, ¶ms->prime, montA->x); + } + if (err == 0) { + err = mp_mulmod(a->y, montA->z, ¶ms->prime, montA->y); + } + if (err == 0) { + err = mp_mulmod(a->z, montA->z, ¶ms->prime, montA->z); + } + if (err == 0) { + err = ecc_projective_add_point(res, montA, res, ¶ms->a, + ¶ms->prime, mp); + } + if ((err == 0) && map) { + err = ecc_map(res, ¶ms->prime, mp); + } + if (montA != NULL) { + wc_ecc_del_point_h(montA, key->heap); + } } - if ((err == 0) && map) { - err = ecc_map(res, ¶ms->prime, mp); +#else + { + err = NOT_COMPILED_IN; } + (void)key; + (void)n; + (void)point; + (void)a; + (void)res; + (void)mp; + (void)map; +#endif return err; -#endif } /** @@ -1562,7 +1509,6 @@ int wc_ValidateEccsiPair(EccsiKey* key, enum wc_HashType hashType, /* Step 2: Compute HS = hash( G | KPAK | ID | PVT ) */ if (err == 0) { hashSz = (byte)sizeof(key->data); - /* Converts KPAK from mont. */ err = eccsi_compute_hs(key, hashType, id, idSz, pvt, key->data, &hashSz); } @@ -1615,7 +1561,7 @@ int wc_ValidateEccsiPvt(EccsiKey* key, const ecc_point* pvt, int* valid) { int err = 0; - if ((key == NULL)| (pvt == NULL) || (valid == NULL)) { + if ((key == NULL) || (pvt == NULL) || (valid == NULL)) { err = BAD_FUNC_ARG; } @@ -1627,6 +1573,7 @@ int wc_ValidateEccsiPvt(EccsiKey* key, const ecc_point* pvt, int* valid) err = wc_ecc_copy_point(pvt, &key->pubkey.pubkey); } if (err == 0) { + key->pubkey.type = ECC_PUBLICKEY; *valid = (wc_ecc_check_key(&key->pubkey) == 0); } @@ -2143,15 +2090,7 @@ static int eccsi_calc_y(EccsiKey* key, ecc_point* pvt, mp_digit mp, int err; mp_int* hs = &key->tmp; -#ifndef WOLFSSL_HAVE_SP_ECC - err = eccsi_kpak_to_mont(key); - /* Need KPAK in montgomery form. */ - if (err == 0) { - err = mp_read_unsigned_bin(hs, key->idHash, key->idHashSz); - } -#else err = mp_read_unsigned_bin(hs, key->idHash, key->idHashSz); -#endif if (err == 0) { /* [HS]PVT + KPAK */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 75f2d90b683..1775ac4718e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -82406,12 +82406,12 @@ static wc_test_ret_t eccsi_sign_verify_test(EccsiKey* priv, EccsiKey* pub, WC_RN if (!verified) return WC_TEST_RET_ENC_NC; - /* Check that the KPAK is converted from montgomery form. */ + /* Check that the key exports and imports after verifying. */ ret = eccsi_imp_exp_key_test(priv); if (ret != 0) return ret; - /* Check that KPAK can converted to Montgomery form again. */ + /* Check that verifying still works afterwards. */ ret = wc_VerifyEccsiHash(priv, WC_HASH_TYPE_SHA256, msg, msgSz, sig, sigSz, &verified); if (ret != 0) @@ -82419,7 +82419,7 @@ static wc_test_ret_t eccsi_sign_verify_test(EccsiKey* priv, EccsiKey* pub, WC_RN if (!verified) return WC_TEST_RET_ENC_NC; - /* Check that the KPAK is converted from montgomery form. */ + /* Check that the pair still validates after verifying. */ ret = wc_ValidateEccsiPair(pub, WC_HASH_TYPE_SHA256, id, idSz, ssk, pvt, &valid); if (ret != 0) @@ -82427,7 +82427,7 @@ static wc_test_ret_t eccsi_sign_verify_test(EccsiKey* priv, EccsiKey* pub, WC_RN if (!valid) return WC_TEST_RET_ENC_NC; - /* Check that KPAK can converted to Montgomery form again. */ + /* Check that verifying still works afterwards. */ ret = wc_VerifyEccsiHash(priv, WC_HASH_TYPE_SHA256, msg, msgSz, sig, sigSz, &verified); if (ret != 0) @@ -82435,7 +82435,7 @@ static wc_test_ret_t eccsi_sign_verify_test(EccsiKey* priv, EccsiKey* pub, WC_RN if (!verified) return WC_TEST_RET_ENC_NC; - /* Check that the KPAK is converted from montgomery form. */ + /* Check that the public key exports and imports after verifying. */ ret = eccsi_imp_exp_pubkey_test(priv, pub); if (ret != 0) return ret; diff --git a/wolfssl/wolfcrypt/eccsi.h b/wolfssl/wolfcrypt/eccsi.h index 7583320d981..3e3545b7c55 100644 --- a/wolfssl/wolfcrypt/eccsi.h +++ b/wolfssl/wolfcrypt/eccsi.h @@ -103,8 +103,6 @@ typedef struct EccsiKey { #endif /** Heap hint for dynamic memory allocation. */ void* heap; - /** Bit indicates KPAK (public key) is in montgomery form. */ - WC_BITFIELD kpakMont:1; } EccsiKey; #ifdef __cplusplus