Skip to content

Commit 6be0103

Browse files
committed
Add secp256k1_fe_is_square_var function
The implementation calls the secp256k1_modinvNN_jacobi_var code, falling back to computing a square root in the (extremely rare) case it failed converge.
1 parent 1de2a01 commit 6be0103

File tree

5 files changed

+73
-0
lines changed

5 files changed

+73
-0
lines changed

src/bench_internal.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,19 @@ static void bench_field_sqrt(void* arg, int iters) {
219219
CHECK(j <= iters);
220220
}
221221

222+
static void bench_field_is_square_var(void* arg, int iters) {
223+
int i, j = 0;
224+
bench_inv *data = (bench_inv*)arg;
225+
secp256k1_fe t = data->fe[0];
226+
227+
for (i = 0; i < iters; i++) {
228+
j += secp256k1_fe_is_square_var(&t);
229+
secp256k1_fe_add(&t, &data->fe[1]);
230+
secp256k1_fe_normalize_var(&t);
231+
}
232+
CHECK(j <= iters);
233+
}
234+
222235
static void bench_group_double_var(void* arg, int iters) {
223236
int i;
224237
bench_inv *data = (bench_inv*)arg;
@@ -371,6 +384,7 @@ int main(int argc, char **argv) {
371384
if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10);
372385
if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters);
373386
if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters);
387+
if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "issquare")) run_benchmark("field_is_square_var", bench_field_is_square_var, bench_setup, NULL, &data, 10, iters);
374388
if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters);
375389

376390
if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10);

src/field.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,7 @@ static void secp256k1_fe_half(secp256k1_fe *r);
135135
* magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */
136136
static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m);
137137

138+
/** Determine whether a is a square (modulo p). */
139+
static int secp256k1_fe_is_square_var(const secp256k1_fe *a);
140+
138141
#endif /* SECP256K1_FIELD_H */

src/field_10x26_impl.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,4 +1365,31 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
13651365
VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
13661366
}
13671367

1368+
static int secp256k1_fe_is_square_var(const secp256k1_fe *x) {
1369+
secp256k1_fe tmp;
1370+
secp256k1_modinv32_signed30 s;
1371+
int jac, ret;
1372+
1373+
tmp = *x;
1374+
secp256k1_fe_normalize_var(&tmp);
1375+
/* secp256k1_jacobi32_maybe_var cannot deal with input 0. */
1376+
if (secp256k1_fe_is_zero(&tmp)) return 1;
1377+
secp256k1_fe_to_signed30(&s, &tmp);
1378+
jac = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe);
1379+
if (jac == 0) {
1380+
/* secp256k1_jacobi32_maybe_var failed to compute the Jacobi symbol. Fall back
1381+
* to computing a square root. This should be extremely rare with random
1382+
* input (except in VERIFY mode, where a lower iteration count is used). */
1383+
secp256k1_fe dummy;
1384+
ret = secp256k1_fe_sqrt(&dummy, &tmp);
1385+
} else {
1386+
#ifdef VERIFY
1387+
secp256k1_fe dummy;
1388+
VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1);
1389+
#endif
1390+
ret = jac >= 0;
1391+
}
1392+
return ret;
1393+
}
1394+
13681395
#endif /* SECP256K1_FIELD_REPR_IMPL_H */

src/field_5x52_impl.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,4 +664,31 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
664664
#endif
665665
}
666666

667+
static int secp256k1_fe_is_square_var(const secp256k1_fe *x) {
668+
secp256k1_fe tmp;
669+
secp256k1_modinv64_signed62 s;
670+
int jac, ret;
671+
672+
tmp = *x;
673+
secp256k1_fe_normalize_var(&tmp);
674+
/* secp256k1_jacobi64_maybe_var cannot deal with input 0. */
675+
if (secp256k1_fe_is_zero(&tmp)) return 1;
676+
secp256k1_fe_to_signed62(&s, &tmp);
677+
jac = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe);
678+
if (jac == 0) {
679+
/* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back
680+
* to computing a square root. This should be extremely rare with random
681+
* input (except in VERIFY mode, where a lower iteration count is used). */
682+
secp256k1_fe dummy;
683+
ret = secp256k1_fe_sqrt(&dummy, &tmp);
684+
} else {
685+
#ifdef VERIFY
686+
secp256k1_fe dummy;
687+
VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1);
688+
#endif
689+
ret = jac >= 0;
690+
}
691+
return ret;
692+
}
693+
667694
#endif /* SECP256K1_FIELD_REPR_IMPL_H */

src/tests.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,8 +3299,10 @@ static void run_sqrt(void) {
32993299
for (j = 0; j < COUNT; j++) {
33003300
random_fe(&x);
33013301
secp256k1_fe_sqr(&s, &x);
3302+
CHECK(secp256k1_fe_is_square_var(&s));
33023303
test_sqrt(&s, &x);
33033304
secp256k1_fe_negate(&t, &s, 1);
3305+
CHECK(!secp256k1_fe_is_square_var(&t));
33043306
test_sqrt(&t, NULL);
33053307
secp256k1_fe_mul(&t, &s, &ns);
33063308
test_sqrt(&t, NULL);

0 commit comments

Comments
 (0)