diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 976569c..3b568ab 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -109,10 +109,7 @@ void static secp256k1_ecmult_gen(secp256k1_gej_t *r, const secp256k1_scalar_t *g bits = secp256k1_scalar_get_bits(gn, j * 4, 4); for (int k=0; kprec[j][k][bits]; - // Note that the next line uses a variable-time addition function, which - // is fine, as the inputs are blinded (they have no known corresponding - // private key). - secp256k1_gej_add_ge_var(r, r, &add); + secp256k1_gej_add_ge(r, r, &add); } bits = 0; secp256k1_ge_clear(&add); diff --git a/src/group.h b/src/group.h index fde1e13..f594e39 100644 --- a/src/group.h +++ b/src/group.h @@ -96,8 +96,12 @@ void static secp256k1_gej_double_var(secp256k1_gej_t *r, const secp256k1_gej_t * /** Set r equal to the sum of a and b. */ void static secp256k1_gej_add_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_gej_t *b); +/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */ +void static secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b); + /** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient - than secp256k1_gej_add. */ + than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time + guarantee, and b is allowed to be infinity. */ void static secp256k1_gej_add_ge_var(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b); /** Get a hex representation of a point. *rlen will be overwritten with the real length. */ diff --git a/src/group_impl.h b/src/group_impl.h index 446ddf9..e62bd9a 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -108,6 +108,9 @@ void static secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge_t r[len], cons void static secp256k1_gej_set_infinity(secp256k1_gej_t *r) { r->infinity = 1; + secp256k1_fe_set_int(&r->x, 0); + secp256k1_fe_set_int(&r->y, 0); + secp256k1_fe_set_int(&r->z, 0); } void static secp256k1_gej_set_xy(secp256k1_gej_t *r, const secp256k1_fe_t *x, const secp256k1_fe_t *y) { @@ -322,6 +325,79 @@ void static secp256k1_gej_add_ge_var(secp256k1_gej_t *r, const secp256k1_gej_t * secp256k1_fe_add(&r->y, &h3); } +void static secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b) { + VERIFY_CHECK(!b->infinity); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + + // In: + // Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. + // In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. + // we find as solution for a unified addition/doubling formula: + // lambda = ((x1 + x2)^2 - x1 * x2 + a) / (y1 + y2), with a = 0 for secp256k1's curve equation. + // x3 = lambda^2 - (x1 + x2) + // 2*y3 = lambda * (x1 + x2 - 2 * x3) - (y1 + y2). + // + // Substituting x_i = Xi / Zi^2 and yi = Yi / Zi^3, for i=1,2,3, gives: + // U1 = X1*Z2^2, U2 = X2*Z1^2 + // S1 = X1*Z2^3, S2 = X2*Z2^3 + // Z = Z1*Z2 + // T = U1+U2 + // M = S1+S2 + // Q = T*M^2 + // R = T^2-U1*U2 + // X3 = 4*(R^2-Q) + // Y3 = 4*(R*(3*Q-2*R^2)-M^4) + // Z3 = 2*M*Z + // (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) + + secp256k1_fe_t zz; secp256k1_fe_sqr(&zz, &a->z); // z = Z1^2 + secp256k1_fe_t u1 = a->x; secp256k1_fe_normalize(&u1); // u1 = U1 = X1*Z2^2 (1) + secp256k1_fe_t u2; secp256k1_fe_mul(&u2, &b->x, &zz); // u2 = U2 = X2*Z1^2 (1) + secp256k1_fe_t s1 = a->y; secp256k1_fe_normalize(&s1); // s1 = S1 = Y1*Z2^3 (1) + secp256k1_fe_t s2; secp256k1_fe_mul(&s2, &b->y, &zz); // s2 = Y2*Z2^2 (1) + secp256k1_fe_mul(&s2, &s2, &a->z); // s2 = S2 = Y2*Z1^3 (1) + secp256k1_fe_t z = a->z; // z = Z = Z1*Z2 (8) + secp256k1_fe_t t = u1; secp256k1_fe_add(&t, &u2); // t = T = U1+U2 (2) + secp256k1_fe_t m = s1; secp256k1_fe_add(&m, &s2); // m = M = S1+S2 (2) + secp256k1_fe_t n; secp256k1_fe_sqr(&n, &m); // n = M^2 (1) + secp256k1_fe_t q; secp256k1_fe_mul(&q, &n, &t); // q = Q = T*M^2 (1) + secp256k1_fe_sqr(&n, &n); // n = M^4 (1) + secp256k1_fe_negate(&n, &n, 1); // n = -M^4 (2) + secp256k1_fe_t rr; secp256k1_fe_sqr(&rr, &t); // rr = T^2 (1) + secp256k1_fe_mul(&t, &u1, &u2); secp256k1_fe_negate(&t, &t, 1); // t = -U1*U2 (2) + secp256k1_fe_add(&rr, &t); // rr = R = T^2-U1*U2 (3) + secp256k1_fe_sqr(&t, &rr); // t = R^2 (1) + secp256k1_fe_mul(&r->z, &m, &z); // r->z = M*Z (1) + secp256k1_fe_normalize(&r->z); + int infinity = secp256k1_fe_is_zero(&r->z) * (1 - a->infinity); + secp256k1_fe_mul_int(&r->z, 2 * (1 - a->infinity)); // r->z = Z3 = 2*M*Z (2) + r->x = t; // r->x = R^2 (1) + r->y = q; secp256k1_fe_mul_int(&r->y, 3); // r->y = 3*Q (3) + secp256k1_fe_negate(&q, &q, 1); // q = -Q (2) + secp256k1_fe_negate(&t, &t, 1); // t = -R^2 (2) + secp256k1_fe_mul_int(&t, 2); // t = -2*R^2 (4) + secp256k1_fe_add(&r->x, &q); // r->x = R^2-Q (3) + secp256k1_fe_normalize(&r->x); + secp256k1_fe_add(&r->y, &t); // r->y = 3*Q-2*R^2 + secp256k1_fe_mul(&r->y, &r->y, &rr); // r->y = R*(3*Q-2*R^2) (1) + secp256k1_fe_add(&r->y, &n); // r->y = R*(3*Q-2*R^2)-M^4 (3) + secp256k1_fe_normalize(&r->y); + secp256k1_fe_mul_int(&r->x, 4 * (1 - a->infinity)); // r->x = X3 = 4*(R^2-Q) + secp256k1_fe_mul_int(&r->y, 4 * (1 - a->infinity)); // r->y = Y3 = 4*R*(3*Q-2*R^2)-4*M^4 (4) + + // In case a->infinity == 1, the above code results in r->x, r->y, and r->z all equal to 0. + // Add b->x to x, b->y to y, and 1 to z in that case. + t = b->x; secp256k1_fe_mul_int(&t, a->infinity); + secp256k1_fe_add(&r->x, &t); + t = b->y; secp256k1_fe_mul_int(&t, a->infinity); + secp256k1_fe_add(&r->y, &t); + secp256k1_fe_set_int(&t, a->infinity); + secp256k1_fe_add(&r->z, &t); + r->infinity = infinity; +} + + + void static secp256k1_gej_get_hex(char *r, int *rlen, const secp256k1_gej_t *a) { secp256k1_gej_t c = *a; secp256k1_ge_t t; secp256k1_ge_set_gej(&t, &c); diff --git a/src/tests.c b/src/tests.c index 593380a..e23e0e2 100644 --- a/src/tests.c +++ b/src/tests.c @@ -28,6 +28,51 @@ void random_num_negate(secp256k1_num_t *num) { secp256k1_num_negate(num); } +void random_field_element_test(secp256k1_fe_t *fe) { + do { + unsigned char b32[32]; + secp256k1_rand256_test(b32); + secp256k1_num_t num; + secp256k1_num_set_bin(&num, b32, 32); + if (secp256k1_num_cmp(&num, &secp256k1_fe_consts->p) >= 0) + continue; + secp256k1_fe_set_b32(fe, b32); + break; + } while(1); +} + +void random_field_element_magnitude(secp256k1_fe_t *fe) { + secp256k1_fe_normalize(fe); + int n = secp256k1_rand32() % 4; + for (int i = 0; i < n; i++) { + secp256k1_fe_negate(fe, fe, 1 + 2*i); + secp256k1_fe_negate(fe, fe, 2 + 2*i); + } +} + +void random_group_element_test(secp256k1_ge_t *ge) { + secp256k1_fe_t fe; + do { + random_field_element_test(&fe); + if (secp256k1_ge_set_xo(ge, &fe, secp256k1_rand32() & 1)) + break; + } while(1); +} + +void random_group_element_jacobian_test(secp256k1_gej_t *gej, const secp256k1_ge_t *ge) { + do { + random_field_element_test(&gej->z); + if (!secp256k1_fe_is_zero(&gej->z)) { + break; + } + } while(1); + secp256k1_fe_t z2; secp256k1_fe_sqr(&z2, &gej->z); + secp256k1_fe_t z3; secp256k1_fe_mul(&z3, &z2, &gej->z); + secp256k1_fe_mul(&gej->x, &ge->x, &z2); + secp256k1_fe_mul(&gej->y, &ge->y, &z3); + gej->infinity = ge->infinity; +} + void random_num_order_test(secp256k1_num_t *num) { do { unsigned char b32[32]; @@ -546,6 +591,106 @@ void run_sqrt() { } } +/***** GROUP TESTS *****/ + +int ge_equals_ge(const secp256k1_ge_t *a, const secp256k1_ge_t *b) { + if (a->infinity && b->infinity) + return 1; + return check_fe_equal(&a->x, &b->x) && check_fe_equal(&a->y, &b->y); +} + +void ge_equals_gej(const secp256k1_ge_t *a, const secp256k1_gej_t *b) { + secp256k1_ge_t bb; + secp256k1_gej_t bj = *b; + secp256k1_ge_set_gej_var(&bb, &bj); + CHECK(ge_equals_ge(a, &bb)); +} + +void gej_equals_gej(const secp256k1_gej_t *a, const secp256k1_gej_t *b) { + secp256k1_ge_t aa, bb; + secp256k1_gej_t aj = *a, bj = *b; + secp256k1_ge_set_gej_var(&aa, &aj); + secp256k1_ge_set_gej_var(&bb, &bj); + CHECK(ge_equals_ge(&aa, &bb)); +} + +void test_ge() { + secp256k1_ge_t a, b, i, n; + random_group_element_test(&a); + random_group_element_test(&b); + n = a; + secp256k1_fe_normalize(&a.y); + secp256k1_fe_negate(&n.y, &a.y, 1); + secp256k1_ge_set_infinity(&i); + random_field_element_magnitude(&a.x); + random_field_element_magnitude(&a.y); + random_field_element_magnitude(&b.x); + random_field_element_magnitude(&b.y); + random_field_element_magnitude(&n.x); + random_field_element_magnitude(&n.y); + + secp256k1_gej_t aj, bj, ij, nj; + random_group_element_jacobian_test(&aj, &a); + random_group_element_jacobian_test(&bj, &b); + secp256k1_gej_set_infinity(&ij); + random_group_element_jacobian_test(&nj, &n); + random_field_element_magnitude(&aj.x); + random_field_element_magnitude(&aj.y); + random_field_element_magnitude(&aj.z); + random_field_element_magnitude(&bj.x); + random_field_element_magnitude(&bj.y); + random_field_element_magnitude(&bj.z); + random_field_element_magnitude(&nj.x); + random_field_element_magnitude(&nj.y); + random_field_element_magnitude(&nj.z); + + // gej + gej adds + secp256k1_gej_t aaj; secp256k1_gej_add_var(&aaj, &aj, &aj); + secp256k1_gej_t abj; secp256k1_gej_add_var(&abj, &aj, &bj); + secp256k1_gej_t aij; secp256k1_gej_add_var(&aij, &aj, &ij); + secp256k1_gej_t anj; secp256k1_gej_add_var(&anj, &aj, &nj); + secp256k1_gej_t iaj; secp256k1_gej_add_var(&iaj, &ij, &aj); + secp256k1_gej_t iij; secp256k1_gej_add_var(&iij, &ij, &ij); + + // gej + ge adds + secp256k1_gej_t aa; secp256k1_gej_add_ge_var(&aa, &aj, &a); + secp256k1_gej_t ab; secp256k1_gej_add_ge_var(&ab, &aj, &b); + secp256k1_gej_t ai; secp256k1_gej_add_ge_var(&ai, &aj, &i); + secp256k1_gej_t an; secp256k1_gej_add_ge_var(&an, &aj, &n); + secp256k1_gej_t ia; secp256k1_gej_add_ge_var(&ia, &ij, &a); + secp256k1_gej_t ii; secp256k1_gej_add_ge_var(&ii, &ij, &i); + + // const gej + ge adds + secp256k1_gej_t aac; secp256k1_gej_add_ge(&aac, &aj, &a); + secp256k1_gej_t abc; secp256k1_gej_add_ge(&abc, &aj, &b); + secp256k1_gej_t anc; secp256k1_gej_add_ge(&anc, &aj, &n); + secp256k1_gej_t iac; secp256k1_gej_add_ge(&iac, &ij, &a); + + CHECK(secp256k1_gej_is_infinity(&an)); + CHECK(secp256k1_gej_is_infinity(&anj)); + CHECK(secp256k1_gej_is_infinity(&anc)); + gej_equals_gej(&aa, &aaj); + gej_equals_gej(&aa, &aac); + gej_equals_gej(&ab, &abj); + gej_equals_gej(&ab, &abc); + gej_equals_gej(&an, &anj); + gej_equals_gej(&an, &anc); + gej_equals_gej(&ia, &iaj); + gej_equals_gej(&ai, &aij); + gej_equals_gej(&ii, &iij); + ge_equals_gej(&a, &ai); + ge_equals_gej(&a, &ai); + ge_equals_gej(&a, &iaj); + ge_equals_gej(&a, &iaj); + ge_equals_gej(&a, &iac); +} + +void run_ge() { + for (int i = 0; i < 2000*count; i++) { + test_ge(); + } +} + /***** ECMULT TESTS *****/ void run_ecmult_chain() { @@ -879,6 +1024,9 @@ int main(int argc, char **argv) { run_sqr(); run_sqrt(); + // group tests + run_ge(); + // ecmult tests run_wnaf(); run_point_times_order();