Remove secret-dependant non-constant time operation in ecmult_const.

ECMULT_CONST_TABLE_GET_GE was branching on its secret input.

Also makes secp256k1_gej_double_var implemented as a wrapper
 on secp256k1_gej_double_nonzero instead of the other way
 around.  This wasn't a constant time bug but it was fragile
 and could easily become one in the future if the double_var
 algorithm is changed.
This commit is contained in:
Gregory Maxwell 2020-01-08 14:58:28 +00:00
parent f45d897101
commit 2241ae6d14
5 changed files with 38 additions and 33 deletions

View File

@ -15,8 +15,9 @@
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ /* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ #define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
int m; \ int m; \
int abs_n = (n) * (((n) > 0) * 2 - 1); \ int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \
int idx_n = abs_n / 2; \ int abs_n = ((n) + mask) ^ mask; \
int idx_n = abs_n >> 1; \
secp256k1_fe neg_y; \ secp256k1_fe neg_y; \
VERIFY_CHECK(((n) & 1) == 1); \ VERIFY_CHECK(((n) & 1) == 1); \
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
@ -172,6 +173,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
} }
} }
#endif #endif
@ -195,7 +197,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int n; int n;
int j; int j;
for (j = 0; j < WINDOW_A - 1; ++j) { for (j = 0; j < WINDOW_A - 1; ++j) {
secp256k1_gej_double_nonzero(r, r, NULL); secp256k1_gej_double_nonzero(r, r);
} }
n = wnaf_1[i]; n = wnaf_1[i];

View File

@ -95,9 +95,8 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
/** Check whether a group element's y coordinate is a quadratic residue. */ /** Check whether a group element's y coordinate is a quadratic residue. */
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). /** Set r equal to the double of a, a cannot be infinity. Constant time. */
* a may not be zero. Constant time. */ static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a);
static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */ /** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);

View File

@ -303,7 +303,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
return secp256k1_fe_equal_var(&y2, &x3); return secp256k1_fe_equal_var(&y2, &x3);
} }
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a) {
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
* *
* Note that there is an implementation described at * Note that there is an implementation described at
@ -312,29 +312,9 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
* mainly because it requires more normalizations. * mainly because it requires more normalizations.
*/ */
secp256k1_fe t1,t2,t3,t4; secp256k1_fe t1,t2,t3,t4;
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
*
* Having said this, if this function receives a point on a sextic twist, e.g. by
* a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
* since -6 does have a cube root mod p. For this point, this function will not set
* the infinity flag even though the point doubles to infinity, and the result
* point will be gibberish (z = 0 but infinity = 0).
*/
r->infinity = a->infinity;
if (r->infinity) {
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 1);
}
return;
}
if (rzr != NULL) { VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
*rzr = a->y; r->infinity = 0;
secp256k1_fe_normalize_weak(rzr);
secp256k1_fe_mul_int(rzr, 2);
}
secp256k1_fe_mul(&r->z, &a->z, &a->y); secp256k1_fe_mul(&r->z, &a->z, &a->y);
secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */
@ -358,9 +338,32 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */
} }
static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
VERIFY_CHECK(!secp256k1_gej_is_infinity(a)); /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
secp256k1_gej_double_var(r, a, rzr); * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
*
* Having said this, if this function receives a point on a sextic twist, e.g. by
* a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
* since -6 does have a cube root mod p. For this point, this function will not set
* the infinity flag even though the point doubles to infinity, and the result
* point will be gibberish (z = 0 but infinity = 0).
*/
if (a->infinity) {
r->infinity = 1;
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 1);
}
return;
}
if (rzr != NULL) {
*rzr = a->y;
secp256k1_fe_normalize_weak(rzr);
secp256k1_fe_mul_int(rzr, 2);
}
secp256k1_gej_double_nonzero(r, a);
} }
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) {

View File

@ -142,7 +142,7 @@ void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *gr
for (i = 0; i < order; i++) { for (i = 0; i < order; i++) {
secp256k1_gej tmp; secp256k1_gej tmp;
if (i > 0) { if (i > 0) {
secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL); secp256k1_gej_double_nonzero(&tmp, &groupj[i]);
ge_equals_gej(&group[(2 * i) % order], &tmp); ge_equals_gej(&group[(2 * i) % order], &tmp);
} }
secp256k1_gej_double_var(&tmp, &groupj[i], NULL); secp256k1_gej_double_var(&tmp, &groupj[i], NULL);

View File

@ -14,6 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <limits.h>
typedef struct { typedef struct {
void (*fn)(const char *text, void* data); void (*fn)(const char *text, void* data);