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.
This commit is contained in:
Pieter Wuille 2023-01-04 16:05:34 -05:00
parent 1de2a01c2b
commit 6be01036c8
5 changed files with 73 additions and 0 deletions

View File

@ -219,6 +219,19 @@ static void bench_field_sqrt(void* arg, int iters) {
CHECK(j <= iters);
}
static void bench_field_is_square_var(void* arg, int iters) {
int i, j = 0;
bench_inv *data = (bench_inv*)arg;
secp256k1_fe t = data->fe[0];
for (i = 0; i < iters; i++) {
j += secp256k1_fe_is_square_var(&t);
secp256k1_fe_add(&t, &data->fe[1]);
secp256k1_fe_normalize_var(&t);
}
CHECK(j <= iters);
}
static void bench_group_double_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
@ -371,6 +384,7 @@ int main(int argc, char **argv) {
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);
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);
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);
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);
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);
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);

View File

@ -135,4 +135,7 @@ static void secp256k1_fe_half(secp256k1_fe *r);
* magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */
static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m);
/** Determine whether a is a square (modulo p). */
static int secp256k1_fe_is_square_var(const secp256k1_fe *a);
#endif /* SECP256K1_FIELD_H */

View File

@ -1365,4 +1365,31 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
}
static int secp256k1_fe_is_square_var(const secp256k1_fe *x) {
secp256k1_fe tmp;
secp256k1_modinv32_signed30 s;
int jac, ret;
tmp = *x;
secp256k1_fe_normalize_var(&tmp);
/* secp256k1_jacobi32_maybe_var cannot deal with input 0. */
if (secp256k1_fe_is_zero(&tmp)) return 1;
secp256k1_fe_to_signed30(&s, &tmp);
jac = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe);
if (jac == 0) {
/* secp256k1_jacobi32_maybe_var failed to compute the Jacobi symbol. Fall back
* to computing a square root. This should be extremely rare with random
* input (except in VERIFY mode, where a lower iteration count is used). */
secp256k1_fe dummy;
ret = secp256k1_fe_sqrt(&dummy, &tmp);
} else {
#ifdef VERIFY
secp256k1_fe dummy;
VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1);
#endif
ret = jac >= 0;
}
return ret;
}
#endif /* SECP256K1_FIELD_REPR_IMPL_H */

View File

@ -664,4 +664,31 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
#endif
}
static int secp256k1_fe_is_square_var(const secp256k1_fe *x) {
secp256k1_fe tmp;
secp256k1_modinv64_signed62 s;
int jac, ret;
tmp = *x;
secp256k1_fe_normalize_var(&tmp);
/* secp256k1_jacobi64_maybe_var cannot deal with input 0. */
if (secp256k1_fe_is_zero(&tmp)) return 1;
secp256k1_fe_to_signed62(&s, &tmp);
jac = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe);
if (jac == 0) {
/* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back
* to computing a square root. This should be extremely rare with random
* input (except in VERIFY mode, where a lower iteration count is used). */
secp256k1_fe dummy;
ret = secp256k1_fe_sqrt(&dummy, &tmp);
} else {
#ifdef VERIFY
secp256k1_fe dummy;
VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1);
#endif
ret = jac >= 0;
}
return ret;
}
#endif /* SECP256K1_FIELD_REPR_IMPL_H */

View File

@ -3299,8 +3299,10 @@ static void run_sqrt(void) {
for (j = 0; j < COUNT; j++) {
random_fe(&x);
secp256k1_fe_sqr(&s, &x);
CHECK(secp256k1_fe_is_square_var(&s));
test_sqrt(&s, &x);
secp256k1_fe_negate(&t, &s, 1);
CHECK(!secp256k1_fe_is_square_var(&t));
test_sqrt(&t, NULL);
secp256k1_fe_mul(&t, &s, &ns);
test_sqrt(&t, NULL);