Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/math/tfm_desc.c
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
{
fp_int t1, t2, x, y, z;
fp_digit mp;
int err, inf, x_or_y_is_zero;
int err, inf;

LTC_ARGCHK(P != NULL);
LTC_ARGCHK(Q != NULL);
Expand Down Expand Up @@ -704,6 +704,15 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
fp_mul(Q->y, &t1, &t1);
fp_montgomery_reduce(&t1, TFM_UNCONST(void *)modulus, mp);

/* Same-affine dispatch (mirror of ltc_ecc_projective_add_point): A==B means P,Q share affine x; dispatch to dbl or O before the formulas */
if (fp_cmp(&x, &t2) == FP_EQ) {
if (fp_cmp(&y, &t1) == FP_EQ) goto dbl_point;
ltc_mp.set_int(R->x, 1);
ltc_mp.set_int(R->y, 1);
ltc_mp.set_int(R->z, 0);
return CRYPT_OK;
}

/* Y = Y - T1 */
fp_sub(&y, &t1, &y);
if (fp_cmp_d(&y, 0) == FP_LT) {
Expand Down Expand Up @@ -767,7 +776,6 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
if (fp_cmp_d(&x, 0) == FP_LT) {
fp_add(&x, TFM_UNCONST(void *)modulus, &x);
}
x_or_y_is_zero = fp_cmp_d(&x, 0) == FP_EQ;

/* T2 = T2 - X */
fp_sub(&t2, &x, &t2);
Expand All @@ -792,11 +800,6 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
fp_add(&y, TFM_UNCONST(void *)modulus, &y);
}
fp_div_2(&y, &y);
x_or_y_is_zero |= fp_cmp_d(&y, 0) == LTC_MP_EQ;

if (x_or_y_is_zero) {
goto dbl_point;
}

fp_copy(&x, R->x);
fp_copy(&y, R->y);
Expand Down
14 changes: 14 additions & 0 deletions src/pk/ecc/ecc_shared_secret.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ int ecc_shared_secret(const ecc_key *private_key, const ecc_key *public_key,
return CRYPT_PK_NOT_PRIVATE;
}

/* Domain-parameter check: both keys must be on the same curve. Otherwise the scalar mul
below would silently use the private key's prime/A against a public point that lives
in a different group (scenarion from Wycheproof ecdh_*WrongCurve).
*/
if (ltc_mp_cmp(private_key->dp.prime, public_key->dp.prime) != LTC_MP_EQ ||
ltc_mp_cmp(private_key->dp.order, public_key->dp.order) != LTC_MP_EQ ||
ltc_mp_cmp(private_key->dp.A, public_key->dp.A) != LTC_MP_EQ ||
ltc_mp_cmp(private_key->dp.B, public_key->dp.B) != LTC_MP_EQ ||
ltc_mp_cmp(private_key->dp.base.x, public_key->dp.base.x) != LTC_MP_EQ ||
ltc_mp_cmp(private_key->dp.base.y, public_key->dp.base.y) != LTC_MP_EQ ||
private_key->dp.cofactor != public_key->dp.cofactor) {
return CRYPT_PK_TYPE_MISMATCH;
}

/* make new point */
result = ltc_ecc_new_point();
if (result == NULL) {
Expand Down
15 changes: 8 additions & 7 deletions src/pk/ecc/ltc_ecc_projective_add_point.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
const void *ma, const void *modulus, void *mp)
{
void *t1, *t2, *x, *y, *z;
int err, inf, x_or_y_is_zero;
int err, inf;

LTC_ARGCHK(P != NULL);
LTC_ARGCHK(Q != NULL);
Expand Down Expand Up @@ -116,6 +116,13 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
if ((err = ltc_mp_mul(Q->y, t1, t1)) != CRYPT_OK) { goto done; }
if ((err = ltc_mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; }

/* Same-affine dispatch: A==B means P,Q share affine x; dispatch to dbl or O before the formulas */
if (ltc_mp_cmp(x, t2) == LTC_MP_EQ) {
if (ltc_mp_cmp(y, t1) == LTC_MP_EQ) goto dbl_point;
err = ltc_ecc_set_point_xyz(1, 1, 0, R);
goto done;
}

/* Y = Y - T1 */
if ((err = ltc_mp_sub(y, t1, y)) != CRYPT_OK) { goto done; }
if (ltc_mp_cmp_d(y, 0) == LTC_MP_LT) {
Expand Down Expand Up @@ -179,7 +186,6 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
if (ltc_mp_cmp_d(x, 0) == LTC_MP_LT) {
if ((err = ltc_mp_add(x, modulus, x)) != CRYPT_OK) { goto done; }
}
x_or_y_is_zero = ltc_mp_cmp_d(x, 0) == LTC_MP_EQ;

/* T2 = T2 - X */
if ((err = ltc_mp_sub(t2, x, t2)) != CRYPT_OK) { goto done; }
Expand All @@ -204,11 +210,6 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
if ((err = ltc_mp_add(y, modulus, y)) != CRYPT_OK) { goto done; }
}
if ((err = ltc_mp_div_2(y, y)) != CRYPT_OK) { goto done; }
x_or_y_is_zero |= ltc_mp_cmp_d(y, 0) == LTC_MP_EQ;

if (x_or_y_is_zero) {
goto dbl_point;
}

if ((err = ltc_mp_copy(x, R->x)) != CRYPT_OK) { goto done; }
if ((err = ltc_mp_copy(y, R->y)) != CRYPT_OK) { goto done; }
Expand Down
169 changes: 169 additions & 0 deletions tests/ecc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,167 @@ static int s_ecc_issue116(void)
return CRYPT_OK;
}

/* Wycheproof ecdh_secp256r1_test.json tcId=202 */
static int s_ecc_wycheproof_p256_edgecase_dbl(void)
{
const char *priv_hex = "809c461d8b39163537ff8f5ef5b977e4cdb980e70e38a7ee0b37cc876729e9ff";
const char *pub_hex = "04dbfa466f12013255f9d57a6496c158ee7dd202a1ce4a5a53005b3564d509a0bbf2578007e857bdd082751ef2f3b4b9c38a0b87bab413d55ccb26a574f2b4be9d";
const char *exp_hex = "e2d57eeec983756c9124f885a4d118ed5b8de7d2895fd91264cf291496949a12";
const ltc_ecc_curve *cu;
ecc_key priv, pub;
unsigned char sk[32], pk[65], expected[32], out[64];
unsigned long len, olen;

DO(ecc_find_curve("SECP256R1", &cu));
len = sizeof(sk); DO(base16_decode(priv_hex, XSTRLEN(priv_hex), sk, &len));
len = sizeof(pk); DO(base16_decode(pub_hex, XSTRLEN(pub_hex), pk, &len));
len = sizeof(expected); DO(base16_decode(exp_hex, XSTRLEN(exp_hex), expected, &len));

DO(ecc_set_curve(cu, &priv));
DO(ecc_set_key(sk, sizeof(sk), PK_PRIVATE, &priv));
DO(ecc_set_curve(cu, &pub));
DO(ecc_set_key(pk, sizeof(pk), PK_PUBLIC, &pub));

olen = sizeof(out);
DO(ecc_shared_secret(&priv, &pub, out, &olen));
COMPARE_TESTVECTOR(out, olen, expected, sizeof(expected), "Wycheproof ecdh_secp256r1 EdgeCaseDoubling", 202);
ecc_free(&priv);
ecc_free(&pub);
return CRYPT_OK;
}

#if defined(LTC_PEM) && defined(LTC_DER)
/* Wycheproof ecdh_secp256r1_pem_test.json tcId=202 */
static int s_ecc_wycheproof_p256_pem_edgecase_dbl(void)
{
static const char priv_pem[] =
"-----BEGIN PRIVATE KEY-----\n"
"MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCAnEYdizkWNTf/j171\n"
"uXfkzbmA5w44p+4LN8yHZynp/w==\n"
"-----END PRIVATE KEY-----\n";
static const char pub_pem[] =
"-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2/pGbxIBMlX51XpklsFY7n3SAqHO\n"
"SlpTAFs1ZNUJoLvyV4AH6Fe90IJ1HvLztLnDiguHurQT1VzLJqV08rS+nQ==\n"
"-----END PUBLIC KEY-----\n";
const char *exp_hex = "e2d57eeec983756c9124f885a4d118ed5b8de7d2895fd91264cf291496949a12";
ltc_pka_key priv = {0}, pub = {0};
unsigned char expected[32], out[64];
unsigned long len, olen;

DO(pem_decode(priv_pem, sizeof(priv_pem) - 1, &priv, NULL));
ENSURE(priv.id == LTC_PKA_EC);
DO(pem_decode(pub_pem, sizeof(pub_pem) - 1, &pub, NULL));
ENSURE(pub.id == LTC_PKA_EC);

len = sizeof(expected);
DO(base16_decode(exp_hex, XSTRLEN(exp_hex), expected, &len));

olen = sizeof(out);
DO(ecc_shared_secret(&priv.u.ecc, &pub.u.ecc, out, &olen));
COMPARE_TESTVECTOR(out, olen, expected, sizeof(expected), "Wycheproof ecdh_secp256r1_pem EdgeCaseDoubling", 202);

pka_key_free(&priv);
pka_key_free(&pub);
return CRYPT_OK;
}

/* Wycheproof ecdh_secp256r1_pem_test.json tcId=352 (WrongOrder), tcId=359 (NoCofactor) and tcId=363 (ModifiedPrime) */
static int s_ecc_wycheproof_p256_pem_invalid_explicit(void)
{
static const char priv_pem[] =
"-----BEGIN PRIVATE KEY-----\n"
"MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCBPNBTRWJtJ9xctQ5y7\n"
"545bU1Dchd6kDNLWJ0dAxuAjnA==\n"
"-----END PRIVATE KEY-----\n";
/* tcId=352 WrongOrder (order = -secp256r1.order) */
static const char pub_pem[] =
"-----BEGIN PUBLIC KEY-----\n"
"MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAA\n"
"AAAAAAAAAAD///////////////8wRAQg/////wAAAAEAAAAAAAAAAAAAAAD/////\n"
"//////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLBEEEaxfR\n"
"8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84z\n"
"V2sxXs7LtkBoN79R9QIh/wAAAAD/////AAAAAAAAAABDGQVSWOhhewxGNT0DnNqv\n"
"AgEBA0IABBUQJkwYnD1SP/mRar1wae+mlo2Nx922RX14abU+pgzc+vt+1HhtoV0p\n"
"7lklb1Nto1daSIjBuwqVslb0p+n9dko=\n"
"-----END PUBLIC KEY-----\n";
const char *bad_secret_hex = "d003f5cc83852584061f7a8a28bcb5671ecbda096e16e7accfa8f8d311a3db7a";
ltc_pka_key priv = {0}, pub = {0};
unsigned char bad_secret[32], out[64];
unsigned long len, olen;
int err;

len = sizeof(bad_secret);
DO(base16_decode(bad_secret_hex, XSTRLEN(bad_secret_hex), bad_secret, &len));

/* private import must succeed (it's well-formed) */
DO(pem_decode(priv_pem, sizeof(priv_pem) - 1, &priv, NULL));
ENSURE(priv.id == LTC_PKA_EC);

/* acceptable: pem_decode rejects the explicit-parameter SPKI */
err = pem_decode(pub_pem, sizeof(pub_pem) - 1, &pub, NULL);
if (err != CRYPT_OK) {
pka_key_free(&priv);
return CRYPT_OK;
}
ENSURE(pub.id == LTC_PKA_EC);

/* if import did succeed, ecc_shared_secret must either fail outright or produce a value that is *not* the documented bad secret */
olen = sizeof(out);
err = ecc_shared_secret(&priv.u.ecc, &pub.u.ecc, out, &olen);
if (err == CRYPT_OK && olen == sizeof(bad_secret) && XMEMCMP(out, bad_secret, sizeof(bad_secret)) == 0) {
fprintf(stderr, "Wycheproof ecdh_secp256r1_pem invalid tcId=352\n");
pka_key_free(&priv);
pka_key_free(&pub);
return CRYPT_FAIL_TESTVECTOR;
}

pka_key_free(&priv);
pka_key_free(&pub);
return CRYPT_OK;
}
#endif /* LTC_PEM && LTC_DER */

#if defined(LTC_DER) && defined(LTC_ECC_BRAINPOOLP224R1) && defined(LTC_ECC_BRAINPOOLP224T1)
/* Wycheproof ecdh_brainpoolP224r1_test.json tcId=517 (WrongCurve) */
static int s_ecc_wycheproof_bp224_wrong_curve(void)
{
const char *priv_hex = "55a6601d488398ee537d8e745a461cfb8e60eeb7cb09088698faa6e9";
const char *pub_spki_hex = "3052301406072a8648ce3d020106092b2403030208010106033a00040ad954137b533120d3344cc8b991"
"3491791ebf145bab44f200a507d3027e41442fabed87e7a30783fd1618790511098b0d004c60b5086b1a";
const ltc_ecc_curve *bp_r1;
ecc_key priv, pub;
unsigned char sk[28], spki[256], out[64];
unsigned long len, slen, olen;
int err;

DO(ecc_find_curve("BRAINPOOLP224R1", &bp_r1));

slen = sizeof(spki);
DO(base16_decode(pub_spki_hex, XSTRLEN(pub_spki_hex), spki, &slen));

/* import public key from its own SPKI; the named-curve OID binds pub to bp224t1, but the point also lies on bp224r1 per the Wycheproof vector */
DO(ecc_import_openssl(spki, slen, &pub));

/* set up the private key on bp224r1 with the listed scalar */
len = sizeof(sk);
DO(base16_decode(priv_hex, XSTRLEN(priv_hex), sk, &len));
DO(ecc_set_curve(bp_r1, &priv));
DO(ecc_set_key(sk, len, PK_PRIVATE, &priv));

olen = sizeof(out);
err = ecc_shared_secret(&priv, &pub, out, &olen);
ecc_free(&priv);
ecc_free(&pub);

if (err == CRYPT_OK) {
fprintf(stderr, "Wycheproof ecdh_brainpoolP224r1 tcId=517\n");
return CRYPT_FAIL_TESTVECTOR;
}
return CRYPT_OK;
}
#endif

/* https://github.com/libtom/libtomcrypt/issues/108 */
static int s_ecc_issue108(void)
{
Expand Down Expand Up @@ -2272,6 +2433,14 @@ int ecc_test(void)
{
if (ltc_mp.name == NULL) return CRYPT_NOP;

DO(s_ecc_wycheproof_p256_edgecase_dbl());
#if defined(LTC_PEM) && defined(LTC_DER)
DO(s_ecc_wycheproof_p256_pem_edgecase_dbl());
DO(s_ecc_wycheproof_p256_pem_invalid_explicit());
#endif
#if defined(LTC_DER) && defined(LTC_ECC_BRAINPOOLP224R1) && defined(LTC_ECC_BRAINPOOLP224T1)
DO(s_ecc_wycheproof_bp224_wrong_curve());
#endif
DO(s_ecc_issue446());
DO(s_ecc_rfc6979());
DO(s_ecc_old_api()); /* up to 1.18 */
Expand Down
Loading