Branch-free point addition

This commit is contained in:
Pieter Wuille 2014-11-11 10:32:50 -08:00
parent ef6f677679
commit 9338dbf791
4 changed files with 230 additions and 5 deletions

View File

@ -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; k<sizeof(secp256k1_ge_t); k++)
((unsigned char*)(&add))[k] = c->prec[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);

View File

@ -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. */

View File

@ -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);

View File

@ -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();