Skip to content

Commit 09ca4f3

Browse files
committed
secp256k1_fe_sqrt checks for success
- secp256k1_fe_sqrt now checks that the value it calculated is actually a square root. - Add return values to secp256k1_fe_sqrt and secp256k1_ge_set_xo. - Callers of secp256k1_ge_set_xo can use return value instead of explicit validity checks - Add random value tests for secp256k1_fe_sqrt
1 parent 78fb796 commit 09ca4f3

File tree

6 files changed

+84
-21
lines changed

6 files changed

+84
-21
lines changed

src/ecdsa_impl.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ int static secp256k1_ecdsa_pubkey_parse(secp256k1_ge_t *elem, const unsigned cha
2525
if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) {
2626
secp256k1_fe_t x;
2727
secp256k1_fe_set_b32(&x, pub+1);
28-
secp256k1_ge_set_xo(elem, &x, pub[0] == 0x03);
28+
return secp256k1_ge_set_xo(elem, &x, pub[0] == 0x03);
2929
} else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) {
3030
secp256k1_fe_t x, y;
3131
secp256k1_fe_set_b32(&x, pub+1);
3232
secp256k1_fe_set_b32(&y, pub+33);
3333
secp256k1_ge_set_xy(elem, &x, &y);
3434
if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07))
3535
return 0;
36+
return secp256k1_ge_is_valid(elem);
3637
} else {
3738
return 0;
3839
}
39-
return secp256k1_ge_is_valid(elem);
4040
}
4141

4242
int static secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size) {
@@ -134,8 +134,7 @@ int static secp256k1_ecdsa_sig_recover(const secp256k1_ecdsa_sig_t *sig, secp256
134134
secp256k1_fe_t fx;
135135
secp256k1_fe_set_b32(&fx, brx);
136136
secp256k1_ge_t x;
137-
secp256k1_ge_set_xo(&x, &fx, recid & 1);
138-
if (!secp256k1_ge_is_valid(&x))
137+
if (!secp256k1_ge_set_xo(&x, &fx, recid & 1))
139138
return 0;
140139
secp256k1_gej_t xj;
141140
secp256k1_gej_set_ge(&xj, &x);

src/field.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,10 @@ void static secp256k1_fe_mul(secp256k1_fe_t *r, const secp256k1_fe_t *a, const s
8282
* The output magnitude is 1 (but not guaranteed to be normalized). */
8383
void static secp256k1_fe_sqr(secp256k1_fe_t *r, const secp256k1_fe_t *a);
8484

85-
/** Sets a field element to be the (modular) square root of another. Requires the inputs' magnitude to
86-
* be at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */
87-
void static secp256k1_fe_sqrt(secp256k1_fe_t *r, const secp256k1_fe_t *a);
85+
/** Sets a field element to be the (modular) square root (if any exist) of another. Requires the
86+
* input's magnitude to be at most 8. The output magnitude is 1 (but not guaranteed to be
87+
* normalized). Return value indicates whether a square root was found. */
88+
int static secp256k1_fe_sqrt(secp256k1_fe_t *r, const secp256k1_fe_t *a);
8889

8990
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be
9091
* at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */

src/field_impl.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void static secp256k1_fe_set_hex(secp256k1_fe_t *r, const char *a, int alen) {
6262
secp256k1_fe_set_b32(r, tmp);
6363
}
6464

65-
void static secp256k1_fe_sqrt(secp256k1_fe_t *r, const secp256k1_fe_t *a) {
65+
int static secp256k1_fe_sqrt(secp256k1_fe_t *r, const secp256k1_fe_t *a) {
6666

6767
// The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
6868
// { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
@@ -121,6 +121,14 @@ void static secp256k1_fe_sqrt(secp256k1_fe_t *r, const secp256k1_fe_t *a) {
121121
secp256k1_fe_mul(&t1, &t1, &x2);
122122
secp256k1_fe_sqr(&t1, &t1);
123123
secp256k1_fe_sqr(r, &t1);
124+
125+
// Check that a square root was actually calculated
126+
127+
secp256k1_fe_sqr(&t1, r);
128+
secp256k1_fe_negate(&t1, &t1, 1);
129+
secp256k1_fe_add(&t1, a);
130+
secp256k1_fe_normalize(&t1);
131+
return secp256k1_fe_is_zero(&t1);
124132
}
125133

126134
void static secp256k1_fe_inv(secp256k1_fe_t *r, const secp256k1_fe_t *a) {

src/group.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ void static secp256k1_ge_set_infinity(secp256k1_ge_t *r);
4848
/** Set a group element equal to the point with given X and Y coordinates */
4949
void static secp256k1_ge_set_xy(secp256k1_ge_t *r, const secp256k1_fe_t *x, const secp256k1_fe_t *y);
5050

51-
/** Set a group element (jacobian) equal to the point with given X coordinate, and given oddness for Y.
52-
The result is not guaranteed to be valid. */
53-
void static secp256k1_ge_set_xo(secp256k1_ge_t *r, const secp256k1_fe_t *x, int odd);
51+
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
52+
* for Y. Return value indicates whether the result is valid. */
53+
int static secp256k1_ge_set_xo(secp256k1_ge_t *r, const secp256k1_fe_t *x, int odd);
5454

5555
/** Check whether a group element is the point at infinity. */
5656
int static secp256k1_ge_is_infinity(const secp256k1_ge_t *a);
@@ -91,7 +91,7 @@ void static secp256k1_gej_double(secp256k1_gej_t *r, const secp256k1_gej_t *a);
9191
/** Set r equal to the sum of a and b. */
9292
void static secp256k1_gej_add(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_gej_t *b);
9393

94-
/** Set r equal to the sum of a and b (with b given in jacobian coordinates). This is more efficient
94+
/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient
9595
than secp256k1_gej_add. */
9696
void static secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_ge_t *b);
9797

src/group_impl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,19 @@ void static secp256k1_gej_set_xy(secp256k1_gej_t *r, const secp256k1_fe_t *x, co
7777
secp256k1_fe_set_int(&r->z, 1);
7878
}
7979

80-
void static secp256k1_ge_set_xo(secp256k1_ge_t *r, const secp256k1_fe_t *x, int odd) {
80+
int static secp256k1_ge_set_xo(secp256k1_ge_t *r, const secp256k1_fe_t *x, int odd) {
8181
r->x = *x;
8282
secp256k1_fe_t x2; secp256k1_fe_sqr(&x2, x);
8383
secp256k1_fe_t x3; secp256k1_fe_mul(&x3, x, &x2);
8484
r->infinity = 0;
8585
secp256k1_fe_t c; secp256k1_fe_set_int(&c, 7);
8686
secp256k1_fe_add(&c, &x3);
87-
secp256k1_fe_sqrt(&r->y, &c);
87+
if (!secp256k1_fe_sqrt(&r->y, &c))
88+
return 0;
8889
secp256k1_fe_normalize(&r->y);
8990
if (secp256k1_fe_is_odd(&r->y) != odd)
9091
secp256k1_fe_negate(&r->y, &r->y, 1);
92+
return 1;
9193
}
9294

9395
void static secp256k1_gej_set_ge(secp256k1_gej_t *r, const secp256k1_ge_t *a) {

src/tests.c

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,54 @@ void run_num_smalltests() {
209209
run_num_int();
210210
}
211211

212+
/***** FIELD TESTS *****/
213+
214+
void random_fe(secp256k1_fe_t *x) {
215+
unsigned char bin[32];
216+
secp256k1_rand256(bin);
217+
secp256k1_fe_set_b32(x, bin);
218+
}
219+
220+
void random_fe_non_square(secp256k1_fe_t *ns) {
221+
secp256k1_fe_t r;
222+
int tries = 100;
223+
while (--tries >= 0) {
224+
random_fe(ns);
225+
if (!secp256k1_fe_sqrt(&r, ns))
226+
break;
227+
}
228+
// 2^-100 probability of spurious failure here
229+
assert(tries >= 0);
230+
}
231+
232+
void test_sqrt(const secp256k1_fe_t *a, const secp256k1_fe_t *k) {
233+
secp256k1_fe_t r1, r2;
234+
int v = secp256k1_fe_sqrt(&r1, a);
235+
assert((v == 0) == (k == NULL));
236+
237+
if (k != NULL) {
238+
// Check that the returned root is +/- the given known answer
239+
secp256k1_fe_negate(&r2, &r1, 1);
240+
secp256k1_fe_add(&r1, k); secp256k1_fe_add(&r2, k);
241+
secp256k1_fe_normalize(&r1); secp256k1_fe_normalize(&r2);
242+
assert(secp256k1_fe_is_zero(&r1) || secp256k1_fe_is_zero(&r2));
243+
}
244+
}
245+
246+
void run_sqrt() {
247+
secp256k1_fe_t ns, x, s, t;
248+
random_fe_non_square(&ns);
249+
for (int i=0; i<10*count; i++) {
250+
random_fe(&x);
251+
secp256k1_fe_sqr(&s, &x);
252+
test_sqrt(&s, &x);
253+
secp256k1_fe_mul(&t, &s, &ns);
254+
test_sqrt(&t, NULL);
255+
}
256+
}
257+
258+
/***** ECMULT TESTS *****/
259+
212260
void run_ecmult_chain() {
213261
// random starting point A (on the curve)
214262
secp256k1_fe_t ax; secp256k1_fe_set_hex(&ax, "8b30bbe9ae2a990696b22f670709dff3727fd8bc04d3362c6c7bf458e2846004", 64);
@@ -275,10 +323,7 @@ void run_ecmult_chain() {
275323
}
276324

277325
void test_point_times_order(const secp256k1_gej_t *point) {
278-
// either the point is not on the curve, or multiplying it by the order results in O
279-
if (!secp256k1_gej_is_valid(point))
280-
return;
281-
326+
// multiplying a point by the order results in O
282327
const secp256k1_num_t *order = &secp256k1_ge_consts->order;
283328
secp256k1_num_t zero;
284329
secp256k1_num_init(&zero);
@@ -292,9 +337,14 @@ void test_point_times_order(const secp256k1_gej_t *point) {
292337
void run_point_times_order() {
293338
secp256k1_fe_t x; secp256k1_fe_set_hex(&x, "02", 2);
294339
for (int i=0; i<500; i++) {
295-
secp256k1_ge_t p; secp256k1_ge_set_xo(&p, &x, 1);
296-
secp256k1_gej_t j; secp256k1_gej_set_ge(&j, &p);
297-
test_point_times_order(&j);
340+
secp256k1_ge_t p;
341+
if (secp256k1_ge_set_xo(&p, &x, 1)) {
342+
assert(secp256k1_ge_is_valid(&p));
343+
secp256k1_gej_t j;
344+
secp256k1_gej_set_ge(&j, &p);
345+
assert(secp256k1_gej_is_valid(&j));
346+
test_point_times_order(&j);
347+
}
298348
secp256k1_fe_sqr(&x, &x);
299349
}
300350
char c[65]; int cl=65;
@@ -451,6 +501,9 @@ int main(int argc, char **argv) {
451501
// num tests
452502
run_num_smalltests();
453503

504+
// field tests
505+
run_sqrt();
506+
454507
// ecmult tests
455508
run_wnaf();
456509
run_point_times_order();

0 commit comments

Comments
 (0)