diff --git a/src/math/fp/ltc_ecc_fp_mulmod.c b/src/math/fp/ltc_ecc_fp_mulmod.c index fa956252..bbc83f34 100644 --- a/src/math/fp/ltc_ecc_fp_mulmod.c +++ b/src/math/fp/ltc_ecc_fp_mulmod.c @@ -1224,6 +1224,11 @@ int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *a, void *modulu int idx, err; void *mp, *mu; + if (ltc_mp_iszero(k) == LTC_MP_YES) { + /* 0 * G is the point at infinity */ + return ltc_ecc_set_point_xyz(1, 1, 0, R); + } + mp = NULL; mu = NULL; LTC_MUTEX_LOCK(<c_ecc_fp_lock); diff --git a/src/pk/ecc/ltc_ecc_mulmod.c b/src/pk/ecc/ltc_ecc_mulmod.c index 73e75569..230aad03 100644 --- a/src/pk/ecc/ltc_ecc_mulmod.c +++ b/src/pk/ecc/ltc_ecc_mulmod.c @@ -42,6 +42,10 @@ int ltc_ecc_mulmod(const void *k, const ecc_point *G, ecc_point *R, const void * /* return the point at infinity */ return ltc_ecc_set_point_xyz(1, 1, 0, R); } + if (ltc_mp_iszero(k) == LTC_MP_YES) { + /* 0 * G is the point at infinity */ + return ltc_ecc_set_point_xyz(1, 1, 0, R); + } /* init montgomery reduction */ if ((err = ltc_mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto error; } diff --git a/src/pk/ecc/ltc_ecc_mulmod_timing.c b/src/pk/ecc/ltc_ecc_mulmod_timing.c index 04456af1..b263d37b 100644 --- a/src/pk/ecc/ltc_ecc_mulmod_timing.c +++ b/src/pk/ecc/ltc_ecc_mulmod_timing.c @@ -40,6 +40,10 @@ int ltc_ecc_mulmod(const void *k, const ecc_point *G, ecc_point *R, const void * /* return the point at infinity */ return ltc_ecc_set_point_xyz(1, 1, 0, R); } + if (ltc_mp_iszero(k) == LTC_MP_YES) { + /* 0 * G is the point at infinity */ + return ltc_ecc_set_point_xyz(1, 1, 0, R); + } /* init montgomery reduction */ if ((err = ltc_mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto error; } diff --git a/tests/ecc_test.c b/tests/ecc_test.c index 60c679ea..014d039a 100644 --- a/tests/ecc_test.c +++ b/tests/ecc_test.c @@ -245,6 +245,41 @@ static int s_ecc_issue630(void) return 0; } +/* https://github.com/libtom/libtomcrypt/issues/116 + verifies that ECDSA verification succeeds for an all-zero hash when the non-Shamir fallback is used + i.e. when u1 == 0 and the code path computes 0 * G + u2 * Q via two separate point multiplications + */ +static int s_ecc_issue116(void) +{ + const ltc_ecc_curve *dp; + ecc_key key; + int err, stat; + unsigned long siglen; + unsigned char hash[20]; + unsigned char sig[128]; + ltc_ecc_sig_opts sig_opts = { .type = LTC_ECCSIG_RFC7518, .prng = &yarrow_prng, .wprng = find_prng("yarrow") }; + int (*saved_mul2add)(const ecc_point *A, void *kA, const ecc_point *B, void *kB, ecc_point *C, const void *ma, const void *modulus); + + XMEMSET(hash, 0, sizeof(hash)); + + DO(ecc_find_curve("SECP256R1", &dp)); + DO(ecc_make_key_ex(&yarrow_prng, find_prng("yarrow"), &key, dp)); + + siglen = sizeof(sig); + DO(ecc_sign_hash_v2(hash, sizeof(hash), sig, &siglen, &sig_opts, &key)); + + saved_mul2add = ltc_mp.ecc_mul2add; + ltc_mp.ecc_mul2add = NULL; + stat = 0; + err = ecc_verify_hash_v2(sig, siglen, hash, sizeof(hash), &(ltc_ecc_sig_opts){ .type = LTC_ECCSIG_RFC7518 }, &stat, &key); + ltc_mp.ecc_mul2add = saved_mul2add; + ecc_free(&key); + + if (err != CRYPT_OK) return err; + if (stat != 1) return CRYPT_FAIL_TESTVECTOR; + return CRYPT_OK; +} + /* https://github.com/libtom/libtomcrypt/issues/108 */ static int s_ecc_issue108(void) { @@ -2246,6 +2281,7 @@ int ecc_test(void) DO(s_ecc_issue108()); DO(s_ecc_issue443_447()); DO(s_ecc_issue630()); + DO(s_ecc_issue116()); #ifdef LTC_ECC_SHAMIR DO(s_ecc_test_shamir()); DO(s_ecc_test_recovery());