From e5ae1e0c4d9febc02bf834161938ffcdcb1d8d6f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 11 Sep 2021 10:39:00 -0400 Subject: [PATCH 1/4] Native jacobi symbol algorithm This introduces variants of the divsteps-based GCD algorithm used for modular inverses to compute Jacobi symbols. Changes compared to the normal vartime divsteps: * Only positive matrices are used, guaranteeing that f and g remain positive. * An additional jac variable is updated to track sign changes during matrix computation. * There is (so far) no proof that this algorithm terminates within reasonable amount of time for every input, but experimentally it appears to almost always need less than 900 iterations. To account for that, only a bounded number of iterations is performed (1500), after which failure is returned. The field logic then falls back to using square roots to determining the result. * The algorithm converges to f=g=gcd(f0,g0) rather than g=0. To keep this test simple, the end condition is f=1, which won't be reached if started with g=0. That case is dealt with specially. --- src/bench_internal.c | 12 +++ src/field.h | 3 + src/field_10x26_impl.h | 28 +++++++ src/field_5x52_impl.h | 28 +++++++ src/modinv32.h | 4 + src/modinv32_impl.h | 174 +++++++++++++++++++++++++++++++++++++---- src/modinv64.h | 4 + src/modinv64_impl.h | 155 +++++++++++++++++++++++++++++++++++- src/tests.c | 46 ++++++++++- 9 files changed, 435 insertions(+), 19 deletions(-) diff --git a/src/bench_internal.c b/src/bench_internal.c index 7eb3af28d7..27af24b1a0 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -218,6 +218,17 @@ void bench_field_sqrt(void* arg, int iters) { CHECK(j <= iters); } +void bench_field_jacobi_var(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + j += secp256k1_fe_jacobi_var(&data->fe[0]); + secp256k1_fe_add(&data->fe[0], &data->fe[1]); + } + CHECK(j <= iters); +} + void bench_group_double_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -379,6 +390,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, "jacobi")) run_benchmark("field_jacobi_var", bench_field_jacobi_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); diff --git a/src/field.h b/src/field.h index 2584a494ee..c9bafeb481 100644 --- a/src/field.h +++ b/src/field.h @@ -139,4 +139,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); +/** Compute the Jacobi symbol of a / p. 0 if a=0; 1 if a square; -1 if a non-square. */ +static int secp256k1_fe_jacobi_var(const secp256k1_fe *a); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h index 21742bf6eb..61a86190c5 100644 --- a/src/field_10x26_impl.h +++ b/src/field_10x26_impl.h @@ -1364,4 +1364,32 @@ 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_jacobi_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv32_signed30 s; + int ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed30(&s, &tmp); + ret = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (ret == -2) { + /* 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. */ + secp256k1_fe dummy; + ret = 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1; +#ifdef VERIFY + } else { + secp256k1_fe dummy; + if (secp256k1_fe_is_zero(&tmp)) { + VERIFY_CHECK(ret == 0); + } else { + VERIFY_CHECK(ret == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); + } +#endif + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index 6bd202f587..26e89123a0 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -667,4 +667,32 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { #endif } +static int secp256k1_fe_jacobi_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv64_signed62 s; + int ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed62(&s, &tmp); + ret = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (ret == -2) { + /* 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. */ + secp256k1_fe dummy; + ret = 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1; +#ifdef VERIFY + } else { + secp256k1_fe dummy; + if (secp256k1_fe_is_zero(&tmp)) { + VERIFY_CHECK(ret == 0); + } else { + VERIFY_CHECK(ret == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); + } +#endif + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/modinv32.h b/src/modinv32.h index 0efdda9ab5..263bda20b8 100644 --- a/src/modinv32.h +++ b/src/modinv32.h @@ -39,4 +39,8 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 /* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */ static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); +/* Compute the Jacobi symbol for (x | modinfo->modulus). Either x must be 0, or x must be coprime with + * modulus. All limbs of x must be non-negative. Returns -2 if the result cannot be computed. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); + #endif /* SECP256K1_MODINV32_H */ diff --git a/src/modinv32_impl.h b/src/modinv32_impl.h index 661c5fc04c..93bc576675 100644 --- a/src/modinv32_impl.h +++ b/src/modinv32_impl.h @@ -232,6 +232,21 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ return zeta; } +/* inv256[i] = -(2*i+1)^-1 (mod 256) */ +static const uint8_t secp256k1_modinv32_inv256[128] = { + 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, + 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, + 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, + 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, + 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, + 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, + 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, + 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, + 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, + 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, + 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 +}; + /* Compute the transition matrix and eta for 30 divsteps (variable time). * * Input: eta: initial eta @@ -243,21 +258,6 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ * Implements the divsteps_n_matrix_var function from the explanation. */ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { - /* inv256[i] = -(2*i+1)^-1 (mod 256) */ - static const uint8_t inv256[128] = { - 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, - 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, - 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, - 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, - 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, - 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, - 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, - 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, - 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, - 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, - 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 - }; - /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */ uint32_t u = 1, v = 0, q = 0, r = 1; uint32_t f = f0, g = g0, m; @@ -297,7 +297,7 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint VERIFY_CHECK(limit > 0 && limit <= 30); m = (UINT32_MAX >> (32 - limit)) & 255U; /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ - w = (g * inv256[(f >> 1) & 127]) & m; + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; /* Do so. */ g += f * w; q += u * w; @@ -317,6 +317,83 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint return eta; } +/* Compute the transition matrix and eta for 30 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^32 rather than 2^30, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + */ +static int32_t secp256k1_modinv32_posdivsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t, int *jacp) { + /* Transformation matrix. */ + uint32_t u = 1, v = 0, q = 0, r = 1; + uint32_t f = f0, g = g0, m; + uint16_t w; + int i = 30, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 30 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint32_t tmp; + eta = -eta; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + } + /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more + * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 + * can be done as its sign will flip once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ + VERIFY_CHECK(limit > 0 && limit <= 30); + m = (UINT32_MAX >> (32 - limit)) & 255U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; + /* Do so. */ + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int32_t)u; + t->v = (int32_t)v; + t->q = (int32_t)q; + t->r = (int32_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 30 of them will have determinant 2^30 or -2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30 || + (int64_t)t->u * t->r - (int64_t)t->v * t->q == -(((int64_t)1) << 30)); + *jacp = jac; + return eta; +} + /* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps. * * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range @@ -584,4 +661,69 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 *x = d; } +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1, or x must be 0. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; + int j, len = 9; + int32_t eta = -1; /* eta = -delta; delta is initially 1 */ + int32_t cond, fn, gn; + int jac = 0; + int count; + + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0 && g.v[5] >= 0 && g.v[6] >= 0 && g.v[7] >= 0 && g.v[8] >= 0); + + /* The loop below does not converge for input g=0. Deal with this case specifically. */ + if (!(g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4] | g.v[5] | g.v[6] | g.v[7] | g.v[8])) return 0; + + /* Do up to 50 iterations of 30 posdivsteps (up to 1500 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (750, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY + for (count = 0; count < 25; ++count) { +#else + for (count = 0; count < 50; ++count) { +#endif + /* Compute transition matrix and new eta after 30 posdivsteps. */ + secp256k1_modinv32_trans2x2 t; + eta = secp256k1_modinv32_posdivsteps_30_var(eta, f.v[0] | ((uint32_t)f.v[1] << 30), g.v[0] | ((uint32_t)g.v[1] << 30), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int32_t)len - 2) >> 31; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1500 iterations. Return -2, indicating unknown result. */ + return -2; +} + #endif /* SECP256K1_MODINV32_IMPL_H */ diff --git a/src/modinv64.h b/src/modinv64.h index da506dfa9f..e432fcbe8d 100644 --- a/src/modinv64.h +++ b/src/modinv64.h @@ -43,4 +43,8 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 /* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */ static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); +/* Compute the Jacobi symbol for (x | modinfo->modulus). Either x must be 0, or x must be coprime with + * modulus. All limbs of x must be non-negative. Returns -2 if the result cannot be computed. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); + #endif /* SECP256K1_MODINV64_H */ diff --git a/src/modinv64_impl.h b/src/modinv64_impl.h index 0743a9c821..2d0d33d777 100644 --- a/src/modinv64_impl.h +++ b/src/modinv64_impl.h @@ -256,7 +256,7 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint tmp = v; v = r; r = -tmp; /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled * out (as we'd be done before that point), and no more than eta+1 can be done as its - * will flip again once that happens. */ + * sign will flip again once that happens. */ limit = ((int)eta + 1) > i ? i : ((int)eta + 1); VERIFY_CHECK(limit > 0 && limit <= 62); /* m is a mask for the bottom min(limit, 6) bits. */ @@ -294,6 +294,94 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint return eta; } +/* Compute the transition matrix and eta for 62 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^64 rather than 2^62, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + */ +static int64_t secp256k1_modinv64_posdivsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t, int *jacp) { + /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */ + uint64_t u = 1, v = 0, q = 0, r = 1; + uint64_t f = f0, g = g0, m; + uint32_t w; + int i = 62, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 62 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint64_t tmp; + eta = -eta; + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled + * out (as we'd be done before that point), and no more than eta+1 can be done as its + * sign will flip again once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 6) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 63U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6) + * bits. */ + w = (f * g * (f * f - 2)) & m; + } else { + /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as + * eta tends to be smaller here. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 4) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 15U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4) + * bits. */ + w = f + (((f + 1) & 4) << 1); + w = (-w * g) & m; + } + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 62 of them will have determinant 2^62 or -2^62. */ + VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 62 || + (int128_t)t->u * t->r - (int128_t)t->v * t->q == -(((int128_t)1) << 62)); + *jacp = jac; + return eta; +} + /* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62. * * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range @@ -590,4 +678,69 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 *x = d; } +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1, or x must be 0. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; + int j, len = 5; + int64_t eta = -1; /* eta = -delta; delta is initially 1 */ + int64_t cond, fn, gn; + int jac = 0; + int count; + + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0); + + /* The loop below does not converge for input g=0. Deal with this case specifically. */ + if (!(g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4])) return 0; + + /* Do up to 25 iterations of 62 posdivsteps (up to 1550 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (744, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY + for (count = 0; count < 12; ++count) { +#else + for (count = 0; count < 25; ++count) { +#endif + /* Compute transition matrix and new eta after 62 posdivsteps. */ + secp256k1_modinv64_trans2x2 t; + eta = secp256k1_modinv64_posdivsteps_62_var(eta, f.v[0] | ((uint64_t)f.v[1] << 62), g.v[0] | ((uint64_t)g.v[1] << 62), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int64_t)len - 2) >> 63; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1550 iterations. Return -2, indicating unknown result. */ + return -2; +} + #endif /* SECP256K1_MODINV64_IMPL_H */ diff --git a/src/tests.c b/src/tests.c index dd53173930..b30e02f70e 100644 --- a/src/tests.c +++ b/src/tests.c @@ -942,12 +942,32 @@ void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod uint16_to_signed30(&x, in); nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4] | x.v[5] | x.v[6] | x.v[7] | x.v[8]) != 0; uint16_to_signed30(&m.modulus, mod); - mutate_sign_signed30(&m.modulus); /* compute 1/modulus mod 2^30 */ m.modulus_inv30 = modinv2p64(m.modulus.v[0]) & 0x3fffffff; CHECK(((m.modulus_inv30 * m.modulus.v[0]) & 0x3fffffff) == 1); + /* Test secp256k1_jacobi32_maybe_var. */ + { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed30(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 0 or 1 (or uncomputable). */ + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == -2 || jac == nonzero); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed30(&x, sqr); + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == -2 || jac == (1 - (mod[0] & 2)) * nonzero); + } + + uint16_to_signed30(&x, in); + mutate_sign_signed30(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m); @@ -1015,12 +1035,32 @@ void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod uint16_to_signed62(&x, in); nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4]) != 0; uint16_to_signed62(&m.modulus, mod); - mutate_sign_signed62(&m.modulus); /* compute 1/modulus mod 2^62 */ m.modulus_inv62 = modinv2p64(m.modulus.v[0]) & M62; CHECK(((m.modulus_inv62 * m.modulus.v[0]) & M62) == 1); + /* Test secp256k1_jacobi64_maybe_var. */ + { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed62(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 0 or 1 (or uncomputable). */ + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == -2 || jac == nonzero); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed62(&x, sqr); + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == -2 || jac == (1 - (mod[0] & 2)) * nonzero); + } + + uint16_to_signed62(&x, in); + mutate_sign_signed62(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m); @@ -2854,8 +2894,10 @@ void run_sqrt(void) { for (j = 0; j < count; j++) { random_fe(&x); secp256k1_fe_sqr(&s, &x); + CHECK(secp256k1_fe_jacobi_var(&s) == 1); test_sqrt(&s, &x); secp256k1_fe_negate(&t, &s, 1); + CHECK(secp256k1_fe_jacobi_var(&t) == -1); test_sqrt(&t, NULL); secp256k1_fe_mul(&t, &s, &ns); test_sqrt(&t, NULL); From 7e1bbef3641ff5bb0593a53f452577bf96d7d2dc Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Wed, 10 Nov 2021 10:37:44 -0800 Subject: [PATCH 2/4] doc: Describe Jacobi calculation in safegcd_implementation.md --- doc/safegcd_implementation.md | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/doc/safegcd_implementation.md b/doc/safegcd_implementation.md index 063aa8efae..c1cdd0cfe1 100644 --- a/doc/safegcd_implementation.md +++ b/doc/safegcd_implementation.md @@ -1,7 +1,7 @@ # The safegcd implementation in libsecp256k1 explained -This document explains the modular inverse implementation in the `src/modinv*.h` files. It is based -on the paper +This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files. +It is based on the paper ["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd) by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version. @@ -769,3 +769,30 @@ def modinv_var(M, Mi, x): d, e = update_de(d, e, t, M, Mi) return normalize(f, d, Mi) ``` + +## 8. From GCDs to Jacobi symbol + +We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we make corresponding updates to *j* using [properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties). In particular, we update *j* whenever we divide *g* by *2* or swap *f* and *g*; these updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied very quickly. Overall, this calculation is slightly simpler than the one for modular inverse because we no longer need to keep track of *d* and *e*. + +However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative values. We resolve this by using the following modified steps: + +```python + # Before + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g - f) // 2 + + # After + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g + f) // 2 +``` + +The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm will converge. The justification for posdivsteps is completely empirical: in practice, it appears that the vast majority of inputs converge to *f=g=gcd(f0, g0)* in a number of steps proportional to their logarithm. + +Note that: +- We require inputs to satisfy *gcd(x, M) = 1*. +- We need to update the termination condition from *g=0* to *f=1*. +- We deal with the case where *g=0* on input specially. + +We account for the possibility of nonconvergence by only performing a bounded number of posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not yet been found. + +The optimizations in sections 3-7 above are described in the context of the original divsteps, but in the C implementation we also adapt most of them (not including "avoiding modulus operations", since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate Jacobi symbols for secret data) to the posdivsteps version. From fcd4abfeeda73dd54cacf980487f4ac1d899e73c Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 2 Sep 2021 19:00:43 -0400 Subject: [PATCH 3/4] Elligator Squared module This adds a module with an implementation of the Elligator Squared algorithm for encoding/decoding public keys in uniformly random byte arrays. --- Makefile.am | 4 + configure.ac | 11 + include/secp256k1_ellsq.h | 78 +++++++ src/bench.c | 9 + src/modules/ellsq/Makefile.am.include | 4 + src/modules/ellsq/bench_impl.h | 67 ++++++ src/modules/ellsq/main_impl.h | 305 ++++++++++++++++++++++++++ src/modules/ellsq/tests_impl.h | 166 ++++++++++++++ src/secp256k1.c | 4 + src/tests.c | 8 + 10 files changed, 656 insertions(+) create mode 100644 include/secp256k1_ellsq.h create mode 100644 src/modules/ellsq/Makefile.am.include create mode 100644 src/modules/ellsq/bench_impl.h create mode 100644 src/modules/ellsq/main_impl.h create mode 100644 src/modules/ellsq/tests_impl.h diff --git a/Makefile.am b/Makefile.am index 51c5960301..07122d6366 100644 --- a/Makefile.am +++ b/Makefile.am @@ -228,3 +228,7 @@ endif if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif + +if ENABLE_MODULE_ELLSQ +include src/modules/ellsq/Makefile.am.include +endif diff --git a/configure.ac b/configure.ac index 2db59a8ff3..d14497f24f 100644 --- a/configure.ac +++ b/configure.ac @@ -156,6 +156,11 @@ AC_ARG_ENABLE(module_schnorrsig, AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])]) +AC_ARG_ENABLE(module_ellsq, + AS_HELP_STRING([--enable-module-ellsq],[enable Elligator^2 module (experimental)]), + [enable_module_ellsq=$enableval], + [enable_module_ellsq=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -352,6 +357,10 @@ if test x"$enable_module_extrakeys" = x"yes"; then AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) fi +if test x"$enable_module_ellsq" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ELLSQ, 1, [Define this symbol to enable the Elligator^2 module]) +fi + if test x"$enable_external_default_callbacks" = x"yes"; then AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used]) fi @@ -391,6 +400,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ELLSQ], [test x"$enable_module_ellsq" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) @@ -411,6 +421,7 @@ echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" +echo " module ellsq = $enable_module_ellsq" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/include/secp256k1_ellsq.h b/include/secp256k1_ellsq.h new file mode 100644 index 0000000000..e01937e352 --- /dev/null +++ b/include/secp256k1_ellsq.h @@ -0,0 +1,78 @@ +#ifndef SECP256K1_ELLSQ_H +#define SECP256K1_ELLSQ_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This module provides an implementation of the Elligator Squared encoding + * for secp256k1 public keys. Given a uniformly random public key, this + * produces a 64-byte encoding that is indistinguishable from uniformly + * random bytes. + * + * Elligator Squared is described in https://eprint.iacr.org/2014/043.pdf by + * Mehdi Tibouchi. The mapping function used is described in + * https://www.di.ens.fr/~fouque/pub/latincrypt12.pdf by Fouque and Tibouchi. + * + * Let f be the function from field elements to curve points, defined as + * follows: + * f(t): + * - Let c = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852 + * - Let x1 = (c - 1)/2 - c*t^2 / (t^2 + 8) (mod p) + * - Let x2 = (-c - 1)/2 + c*t^2 / (t^2 + 8) (mod p) + * - Let x3 = 1 - (t^2 + 8)^2 / (3*t^2) (mod p) + * - Let x be the first of [x1,x2,x3] that is an X coordinate on the curve + * (at least one of them is, for any field element t). + * - Let y be the the corresponding Y coordinate to x, with the same parity + * as t (even if t is even, odd if t is odd). + * - Return the curve point with coordinates (x, y). + * + * Then an Elligator Squared encoding of P consists of the 32-byte big-endian + * encodings of field elements u1 and u2 concatenated, where f(u1)+f(u2) = P. + * The encoding algorithm is described in the paper, and effectively picks a + * uniformly random pair (u1,u2) among those which encode P. + * + * To make the encoding able to deal with all inputs, if f(u1)+f(u2) is the + * point at infinity, the decoding is defined to be f(u1) instead. + */ + +/* Construct a 64-byte Elligator Squared encoding of a given pubkey. + * + * Returns: 1 when pubkey is valid. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to be filled + * In: rnd32: pointer to 32 bytes of entropy (must be unpredictable) + * pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellsq_encode( + const secp256k1_context* ctx, + unsigned char *ell64, + const unsigned char *rnd32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decode a 64-bytes Elligator Squared encoded public key. + * + * Returns: always 1 + * Args: ctx: pointer to a context object + * Out: pubkey: pointer to a secp256k1_pubkey that will be filled + * In: ell64: pointer to a 64-byte array to decode + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellsq_decode( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *ell64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ELLSQ_H */ diff --git a/src/bench.c b/src/bench.c index d5937b763f..9ce5b51437 100644 --- a/src/bench.c +++ b/src/bench.c @@ -133,6 +133,10 @@ static void bench_sign_run(void* arg, int iters) { # include "modules/schnorrsig/bench_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSQ +# include "modules/ellsq/bench_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_pubkey pubkey; @@ -230,5 +234,10 @@ int main(int argc, char** argv) { run_schnorrsig_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_ELLSQ + /* Elligator squared signature benchmarks */ + run_ellsq_bench(iters, argc, argv); +#endif + return 0; } diff --git a/src/modules/ellsq/Makefile.am.include b/src/modules/ellsq/Makefile.am.include new file mode 100644 index 0000000000..313dae9526 --- /dev/null +++ b/src/modules/ellsq/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_ellsq.h +noinst_HEADERS += src/modules/ellsq/bench_impl.h +noinst_HEADERS += src/modules/ellsq/main_impl.h +noinst_HEADERS += src/modules/ellsq/tests_impl.h diff --git a/src/modules/ellsq/bench_impl.h b/src/modules/ellsq/bench_impl.h new file mode 100644 index 0000000000..fd71aa6e58 --- /dev/null +++ b/src/modules/ellsq/bench_impl.h @@ -0,0 +1,67 @@ +/*********************************************************************** + * Copyright (c) 2021 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSQ_BENCH_H +#define SECP256K1_MODULE_ELLSQ_BENCH_H + +#include "../include/secp256k1_ellsq.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char rnd64[64]; +} bench_ellsq_data; + +static void bench_ellsq_setup(void* arg) { + bench_ellsq_data *data = (bench_ellsq_data*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x37, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); +} + +static void bench_ellsq_encode(void* arg, int iters) { + int i; + bench_ellsq_data *data = (bench_ellsq_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[29] ^= 145; + CHECK(secp256k1_ellsq_encode(data->ctx, data->rnd64, data->rnd64 + 16, &data->point) == 1); + } +} + +static void bench_ellsq_decode(void* arg, int iters) { + int i; + secp256k1_pubkey out; + bench_ellsq_data *data = (bench_ellsq_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[13] ^= 247; + data->rnd64[47] ^= 113; + CHECK(secp256k1_ellsq_decode(data->ctx, &out, data->rnd64) == 1); + memcpy(data->rnd64, &out.data, 64); + } +} + +void run_ellsq_bench(int iters, int argc, char** argv) { + bench_ellsq_data data; + int d = argc == 1; + + /* create a context with no capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); + memset(data.rnd64, 11, sizeof(data.rnd64)); + + if (d || have_flag(argc, argv, "ellsq") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellsq_encode")) run_benchmark("ellsq_encode", bench_ellsq_encode, bench_ellsq_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellsq") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellsq_decode")) run_benchmark("ellsq_decode", bench_ellsq_decode, bench_ellsq_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif /* SECP256K1_MODULE_ELLSQ_BENCH_H */ diff --git a/src/modules/ellsq/main_impl.h b/src/modules/ellsq/main_impl.h new file mode 100644 index 0000000000..81b2b9c774 --- /dev/null +++ b/src/modules/ellsq/main_impl.h @@ -0,0 +1,305 @@ +/*********************************************************************** + * Copyright (c) 2021 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSQ_MAIN_H +#define SECP256K1_MODULE_ELLSQ_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ellsq.h" +#include "../../hash.h" + +/* c1 = the square root of -3 ((-3)**((p+1)/4)). */ +static const secp256k1_fe secp256k1_ellsq_c1 = SECP256K1_FE_CONST(0x0a2d2ba9, 0x3507f1df, 0x233770c2, 0xa797962c, 0xc61f6d15, 0xda14ecd4, 0x7d8d27ae, 0x1cd5f852); +/* c2 = (c1-1)/2 (a cube root of 1). */ +static const secp256k1_fe secp256k1_ellsq_c2 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40); +/* c3 = (-c1-1)/2 (another cube root of 1). */ +static const secp256k1_fe secp256k1_ellsq_c3 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ee); +/* c4 = 16*(c1-1) */ +static const secp256k1_fe secp256k1_ellsq_c4 = SECP256K1_FE_CONST(0xa2d2ba93, 0x507f1df2, 0x33770c2a, 0x797962cc, 0x61f6d15d, 0xa14ecd47, 0xd8d27ae1, 0xcd5f8510); +/* c5 = 1/2 */ +static const secp256k1_fe secp256k1_ellsq_c5 = SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe18); + +/* Given a field element u, compute a group element out. This functions results in a + * non-infinity point on the curve for every possible input u. It implements the function + * f defined in secp256k1_ellsq.h. + */ +static void secp256k1_ellsq_fe_to_ge_var(secp256k1_ge* out, const secp256k1_fe* u) { + secp256k1_fe t0, t1, t2, t3, t4, x, y; + int ret; + secp256k1_fe_sqr(&t0, u); + secp256k1_fe_set_int(&t1, 8); + secp256k1_fe_add(&t1, &t0); + secp256k1_fe_mul(&t3, &t0, &secp256k1_ellsq_c1); + secp256k1_fe_negate(&t3, &t3, 1); + secp256k1_fe_mul(&t2, &t1, &secp256k1_ellsq_c2); + secp256k1_fe_add(&t2, &t3); + secp256k1_fe_sqr(&t4, &t1); + secp256k1_fe_sqr(&t4, &t4); + secp256k1_fe_mul_int(&t4, 7); + secp256k1_fe_sqr(&t3, &t2); + secp256k1_fe_mul(&t3, &t3, &t2); + secp256k1_fe_mul(&t3, &t3, &t1); + secp256k1_fe_add(&t3, &t4); + if (secp256k1_fe_jacobi_var(&t3) >= 0) { + ret = secp256k1_fe_sqrt(&t4, &t3); + VERIFY_CHECK(ret); + secp256k1_fe_inv_var(&t1, &t1); + secp256k1_fe_mul(&x, &t1, &t2); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_mul(&y, &t1, &t4); + } else { + secp256k1_fe_add(&t2, &t1); + secp256k1_fe_negate(&t2, &t2, 5); + secp256k1_fe_sqr(&t3, &t2); + secp256k1_fe_mul(&t3, &t3, &t2); + secp256k1_fe_mul(&t3, &t3, &t1); + secp256k1_fe_add(&t3, &t4); + if (secp256k1_fe_jacobi_var(&t3) >= 0) { + ret = secp256k1_fe_sqrt(&t4, &t3); + VERIFY_CHECK(ret); + secp256k1_fe_inv_var(&t1, &t1); + secp256k1_fe_mul(&x, &t1, &t2); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_mul(&y, &t1, &t4); + } else { + secp256k1_fe_mul_int(&t0, 3); + secp256k1_fe_inv_var(&t0, &t0); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_mul(&t0, &t0, &t1); + secp256k1_fe_negate(&t0, &t0, 1); + secp256k1_fe_set_int(&x, 1); + secp256k1_fe_add(&x, &t0); + secp256k1_fe_sqr(&t0, &x); + secp256k1_fe_mul(&t0, &t0, &x); + secp256k1_fe_set_int(&t1, 7); + secp256k1_fe_add(&t1, &t0); + ret = secp256k1_fe_sqrt(&y, &t1); + VERIFY_CHECK(ret); + } + } + t0 = *u; + secp256k1_fe_normalize_var(&y); + secp256k1_fe_normalize_var(&t0); + if (secp256k1_fe_is_odd(&y) != secp256k1_fe_is_odd(&t0)) secp256k1_fe_negate(&y, &y, 1); + secp256k1_ge_set_xy(out, &x, &y); +} + +/* Given a point on the curve p, and an integer branch value i in [0,4), compute a + * field element out which secp256k1_ellsq_fe_to_ge_var would map back to p, or + * fail. Combining all non-failing outs for a given p, over all values of i, + * results in the set of all preimages of p under secp256k1_ellsq_fe_to_ge_var. No + * two (p, i) inputs map to the same out, if successful. + * + * i=0 will compute a preimage that maps to p using the "x1" above. + * i=1 will compute a preimage that maps to p using the "x2" above. + * i=2 and i=3 will compute a preimage that maps to using the "x3" above. + * + * All of them will fail if no preimage under the respective x formula exists. + * When i>0, the function will fail when the would-be preimage maps to the curve + * using a lowered-numbered x (so i=1 fails when its x1 lands on the curve, and + * i=2 and i=3 fail when its x1 or x2 land on the curve). In addition, failure is + * returned when a lowered-value i would result in the same preimage. + */ +static int secp256k1_ellsq_ge_to_fe_var(secp256k1_fe* out, const secp256k1_ge* p, int i) { + int ret; + secp256k1_fe t0, t1, t2, t3, u, x = p->x, y = p->y; + secp256k1_fe_normalize_var(&x); + secp256k1_fe_normalize_var(&y); + VERIFY_CHECK(i >= 0); + VERIFY_CHECK(i < 4); + if (i < 2) { + t0 = x; + secp256k1_fe_mul_int(&t0, 2); + secp256k1_fe_set_int(&t1, 1); + secp256k1_fe_add(&t0, &t1); + secp256k1_fe_negate(&t1, &t0, 3); + secp256k1_fe_add(&t1, &secp256k1_ellsq_c1); + secp256k1_fe_add(&t0, &secp256k1_ellsq_c1); + secp256k1_fe_mul(&t2, &t0, &t1); + secp256k1_fe_mul_int(&t2, 8); + if (secp256k1_fe_jacobi_var(&t2) < 0) return 0; + if (i == 0) { + if (secp256k1_fe_normalizes_to_zero_var(&t0)) return 0; + if (secp256k1_fe_normalizes_to_zero_var(&t1) && secp256k1_fe_is_odd(&y)) return 0; + ret = secp256k1_fe_sqrt(&t1, &t2); + VERIFY_CHECK(ret); + secp256k1_fe_inv_var(&t0, &t0); + secp256k1_fe_mul(&u, &t0, &t1); + } else { /* i == 1 */ + secp256k1_fe_set_int(&t0, 1); + secp256k1_fe_add(&t0, &x); + secp256k1_fe_negate(&t0, &t0, 2); + secp256k1_fe_sqr(&t3, &t0); + secp256k1_fe_mul(&t0, &t0, &t3); + secp256k1_fe_set_int(&t3, 7); + secp256k1_fe_add(&t0, &t3); + if (secp256k1_fe_jacobi_var(&t0) >= 0) return 0; + ret = secp256k1_fe_sqrt(&t0, &t2); + VERIFY_CHECK(ret); + secp256k1_fe_inv_var(&t1, &t1); + secp256k1_fe_mul(&u, &t0, &t1); + } + } else { + t0 = x; + secp256k1_fe_mul_int(&t0, 6); + secp256k1_fe_set_int(&t1, 26); + secp256k1_fe_add(&t0, &t1); + secp256k1_fe_sqr(&t1, &t0); + secp256k1_fe_set_int(&t2, 1024); + secp256k1_fe_negate(&t2, &t2, 1); + secp256k1_fe_add(&t2, &t1); + if (secp256k1_fe_jacobi_var(&t2) < 0) return 0; + ret = secp256k1_fe_sqrt(&t1, &t2); + VERIFY_CHECK(ret); + if (i == 3) { + if (secp256k1_fe_normalizes_to_zero_var(&t1)) return 0; + secp256k1_fe_negate(&t1, &t1, 1); + } + secp256k1_fe_negate(&t0, &t0, 7); + secp256k1_fe_add(&t0, &t1); + if (secp256k1_fe_jacobi_var(&t0) < 0) return 0; + secp256k1_fe_set_int(&t1, 32); + secp256k1_fe_normalize_weak(&t0); + secp256k1_fe_add(&t1, &t0); + secp256k1_fe_mul(&t2, &t0, &secp256k1_ellsq_c3); + secp256k1_fe_add(&t2, &secp256k1_ellsq_c4); + secp256k1_fe_sqr(&t3, &t2); + secp256k1_fe_mul(&t3, &t3, &t2); + secp256k1_fe_mul(&t3, &t3, &t1); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_mul_int(&t1, 7); + secp256k1_fe_add(&t3, &t1); + if (secp256k1_fe_jacobi_var(&t3) >= 0) return 0; + ret = secp256k1_fe_sqrt(&u, &t0); + VERIFY_CHECK(ret); + secp256k1_fe_mul(&u, &u, &secp256k1_ellsq_c5); + } + secp256k1_fe_normalize_var(&u); + if (secp256k1_fe_is_odd(&u) != secp256k1_fe_is_odd(&y)) { + secp256k1_fe_negate(&u, &u, 1); + } + *out = u; + return 1; +} + +int secp256k1_ellsq_encode(const secp256k1_context* ctx, unsigned char *ell64, const unsigned char *rnd32, const secp256k1_pubkey *pubkey) { + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + ARG_CHECK(rnd32 != NULL); + ARG_CHECK(pubkey != NULL); + + if (secp256k1_pubkey_load(ctx, &p, pubkey)) { + uint32_t cnt = 0; + /* Field elements and branch values are extracted from + * SHA256("secp256k1_ellsq_encode\x00" + uint32{cnt} + rnd32 + X + byte{Y & 1}) + * for consecutive values of cnt. cnt==0 is first used to populate a pool of + * 128 2-bit branch values. The 128 cnt values that follow are used to + * generate field elements u1. cnt==129 (and multiples thereof) are used to + * repopulate the pool and start over, if that were ever necessary. */ + unsigned char hashdata[23 + 4 + 32 + 32 + 1] = "secp256k1_ellsq_encode"; + /* Pool of 2-bit branch values. */ + unsigned char branch_hash[32]; + /* Number of 2-bit values in branch_hash left. */ + int branches_left = 0; + /* Fill up hashdata, excluding i. */ + memcpy(hashdata + 23 + 4, rnd32, 32); + secp256k1_fe_get_b32(hashdata + 23 + 4 + 32, &p.x); + hashdata[4 + 23 + 32 + 32] = secp256k1_fe_is_odd(&p.y); + while (1) { + int branch; + secp256k1_fe u1, u2; + secp256k1_ge q; + secp256k1_gej qj; + /* If the pool of branch values is empty, populate it. */ + if (branches_left == 0) { + secp256k1_sha256 hash; + hashdata[23 + 0] = cnt; + hashdata[23 + 1] = cnt >> 8; + hashdata[23 + 2] = cnt >> 16; + hashdata[23 + 3] = cnt >> 24; + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, hashdata, sizeof(hashdata)); + secp256k1_sha256_finalize(&hash, branch_hash); + ++cnt; + branches_left = 128; + } + /* Take a 2-bit branch value from the branch pool. */ + --branches_left; + branch = (branch_hash[(127 - branches_left) >> 2] >> (((127 - branches_left) & 3) << 1)) & 3; + /* Compute a new u1 value by hashing (a potential first 32 bytes of the output). */ + { + secp256k1_sha256 hash; + hashdata[23 + 0] = cnt; + hashdata[23 + 1] = cnt >> 8; + hashdata[23 + 2] = cnt >> 16; + hashdata[23 + 3] = cnt >> 24; + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, hashdata, sizeof(hashdata)); + secp256k1_sha256_finalize(&hash, ell64); + ++cnt; + } + if (!secp256k1_fe_set_b32(&u1, ell64)) continue; + /* Compute the remainder Q to encode in the last 32 bytes of the output. */ + secp256k1_ellsq_fe_to_ge_var(&q, &u1); + secp256k1_ge_neg(&q, &q); + secp256k1_gej_set_ge(&qj, &q); + secp256k1_gej_add_ge_var(&qj, &qj, &p, NULL); + if (!secp256k1_gej_is_infinity(&qj)) { + secp256k1_ge_set_gej_var(&q, &qj); + } else { + /* If Q=P-f(u1) is infinity, it means we're trying to encode P=f(u1). + * While no u2 exists such that f(u2)=Q in that case, it is still + * possible to encode P due to the special rule that if f(u1)+f(u2) + * is infinity, the result of decoding is f(u1). + * In other words, we're not trying to reach f(u2)=P-f(u1), but + * f(u2)=-f(u1) instead. -f(u1) is exactly what the variable q + * already holds at this point. Note that if u1 is generated using + * a secure hash function, the probability of reaching this branch + * is negligible. */ + } + /* Try to find a u2 value which encodes Q. */ + if (secp256k1_ellsq_ge_to_fe_var(&u2, &q, branch)) { + /* If that succeeds, store it in the output. */ + secp256k1_fe_normalize_var(&u2); + secp256k1_fe_get_b32(ell64 + 32, &u2); + break; + } + } + memset(hashdata, 0, sizeof(hashdata)); + return 1; + } + /* Only returned in case the provided pubkey is invalid. */ + return 0; +} + +int secp256k1_ellsq_decode(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *ell64) { + secp256k1_fe f1, f2; + secp256k1_ge p1, p2; + secp256k1_gej acc; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(ell64 != NULL); + + secp256k1_fe_set_b32(&f1, ell64); + secp256k1_fe_set_b32(&f2, ell64 + 32); + secp256k1_ellsq_fe_to_ge_var(&p1, &f1); + secp256k1_ellsq_fe_to_ge_var(&p2, &f2); + secp256k1_gej_set_ge(&acc, &p1); + secp256k1_gej_add_ge_var(&acc, &acc, &p2, NULL); + if (!secp256k1_gej_is_infinity(&acc)) { + secp256k1_ge_set_gej_var(&p1, &acc); + } else { + /* f(u1)+f(u2) is infinity. In that case the decoding is defined to be + * equal to f(u1) instead. f(u1) is already stored in the p1 variable + * at this point. */ + } + secp256k1_pubkey_save(pubkey, &p1); + return 1; +} + +#endif diff --git a/src/modules/ellsq/tests_impl.h b/src/modules/ellsq/tests_impl.h new file mode 100644 index 0000000000..9b74663cf4 --- /dev/null +++ b/src/modules/ellsq/tests_impl.h @@ -0,0 +1,166 @@ +/*********************************************************************** + * Copyright (c) 2021 Pieter Wuile * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSQ_TESTS_H +#define SECP256K1_MODULE_ELLSQ_TESTS_H + +#include "../../../include/secp256k1_ellsq.h" + +struct ellsq_test { + secp256k1_ge point; + int enc_bitmap; + secp256k1_fe encs[4]; +}; + +/* Set of (point, encodings) test vectors, selected to maximize branch coverage. + * Created using an independent implementation. */ +static const struct ellsq_test ellsq_tests[] = { + {SECP256K1_GE_CONST(0xc27fb7a3, 0x283a7d3e, 0xc9f96421, 0x545ef6f5, 0x8ace7b71, 0x06c8a1b9, 0x07c0ae8a, 0x7598159c, 0xe05a060e, 0x839ef79f, 0xc0c1267c, 0xa17880c9, 0x584cdd34, 0xc05f9695, 0x55482207, 0xe6851f2a), 15, {SECP256K1_FE_CONST(0xc0ad127a, 0xa36824d6, 0x5b1f5be7, 0x4de1aa25, 0xbc4d5cbe, 0xcee15462, 0x0a12682a, 0xfc87df98), SECP256K1_FE_CONST(0xd40fd5bc, 0x51992484, 0x8f13273b, 0x1d857cba, 0x42d45e78, 0x9eaa4e47, 0xf458b83a, 0xbd5f8d1c), SECP256K1_FE_CONST(0xde636141, 0x7deb440b, 0x3a305924, 0x43635cf9, 0xcf42f9b5, 0xf5b891c1, 0x1e119f09, 0x71b570ac), SECP256K1_FE_CONST(0xd55135ce, 0x41bb4d05, 0x5b3757f4, 0xaf1d6537, 0x137376d7, 0x5270caae, 0xda68382d, 0x25d00708)}}, + {SECP256K1_GE_CONST(0x3f5ada4e, 0x8f646ec9, 0x10ffc1a2, 0xb74d94bb, 0xb1860631, 0xa3c2a349, 0xeddf55ca, 0xfd49cce9, 0x28ad9d8d, 0x77d9cd87, 0xf80aaa34, 0x8e9ad1b4, 0x40353d7a, 0x6e717714, 0x60425319, 0x38f530c3), 15, {SECP256K1_FE_CONST(0xac42348f, 0x1b356822, 0x5bb7d4c0, 0x0feab37e, 0xa5fb7fbb, 0x0cc3879d, 0xc74e2dda, 0xf9a393bf), SECP256K1_FE_CONST(0xda7a45b2, 0x6c87dcb6, 0x4a934c1d, 0xc841d250, 0xf98af5f0, 0x511be2a3, 0x82d17bab, 0xe1e4a533), SECP256K1_FE_CONST(0xc3d9b9a6, 0x570ca9c8, 0xa640fc75, 0x945850b2, 0xcc86b6d6, 0x399b4496, 0x4288d76d, 0x832a32d7), SECP256K1_FE_CONST(0xbf5ebc2f, 0x4060abe7, 0x884a1fa7, 0xcc0883cb, 0x97535c5a, 0x31dc6df4, 0xc6968e9d, 0x8554f3b1)}}, + {SECP256K1_GE_CONST(0xf5f74fab, 0x3ebbbcfd, 0xdcaef6cc, 0xd14eb934, 0xf9435a4e, 0x4a1ed2d8, 0x75352c47, 0x306d6c2f, 0xea6a5b2a, 0xe109897d, 0x046e1504, 0xf7a382d6, 0x1eb49a8a, 0xae8852ef, 0x48e29466, 0x194d9e66), 12, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0xe8362df2, 0x38e0405b, 0x49218747, 0x74f9ebca, 0x36dfe21b, 0x1a49ae2d, 0x0fa23fd4, 0x11a262a6), SECP256K1_FE_CONST(0x9e453426, 0xac973155, 0x19d11d63, 0xc3bb27ee, 0x89a7ec85, 0x5661dce4, 0xe428f6cc, 0x0be059cc)}}, + {SECP256K1_GE_CONST(0x977694f6, 0x6f0a3005, 0x2c638916, 0x61432fa0, 0x605528a7, 0xad87d829, 0x5c9eb9a3, 0x973c6fed, 0x16515f14, 0x00186fec, 0x67f6314c, 0x8a9e2d43, 0x3d2020e9, 0x38f86465, 0x39f749a1, 0x51a793ed), 12, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x8f091a42, 0xce496be8, 0x877d43fc, 0x2f2b2927, 0x42c9c1fb, 0x0dfe570b, 0x9c9fbd3e, 0x04afa709), SECP256K1_FE_CONST(0xb5930cf1, 0x4db355a5, 0xa92b9f78, 0x9390b59a, 0x013c8e27, 0x7c41ddd6, 0xd8221622, 0x93d39141)}}, + {SECP256K1_GE_CONST(0x9c970ce9, 0x39e8a4ec, 0x70237f33, 0xad858370, 0xc9d30e8a, 0xadaac257, 0x546d1e16, 0xf374973b, 0x95755fab, 0x1bcae32e, 0xc811c63f, 0xb1e56da8, 0x97a1e140, 0xb1aae97e, 0x0b6ae6c5, 0x3879f51c), 13, {SECP256K1_FE_CONST(0xa7424f55, 0x60b58ceb, 0xbb9a6ee1, 0x5fc41b18, 0xf282b2cd, 0xd9e2fb4d, 0x02626c1a, 0xc0a89ec4), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0xd7016e9b, 0x94db9b4c, 0x5bc61c87, 0xaf3b3c9c, 0x72707e5e, 0x48332958, 0xce5371bf, 0xd501a006), SECP256K1_FE_CONST(0xe95cd3a1, 0x2cff74bd, 0x6761a782, 0x61f73f0d, 0x755a80f6, 0x39ccd117, 0x136f9963, 0xf422b82a)}}, + {SECP256K1_GE_CONST(0x48206211, 0x5e6fc771, 0x738b4859, 0x4da66901, 0xa0a8c36e, 0xa61122b7, 0x745cf5fe, 0xec932b64, 0x01c9e1a1, 0x59effb22, 0x4442c868, 0x9119fd26, 0x8cdca070, 0x7edbefb6, 0xea81d5f6, 0x86333768), 13, {SECP256K1_FE_CONST(0xf1047fb9, 0x4cfa6dcd, 0x202e1acc, 0xa85afc88, 0x46381925, 0x7adf32aa, 0x25e19e52, 0xbf3cadd8), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x94dc1b2b, 0x6a24bbdb, 0x36afab1a, 0x6e036e7f, 0xdf1ded22, 0x915bf761, 0x97e5e5a5, 0xc6261582), SECP256K1_FE_CONST(0x8dd664ba, 0x47061bac, 0x0c99d727, 0xac2ade9f, 0xf8d33aff, 0x995a7a28, 0x97f2968c, 0x558ef724)}}, + {SECP256K1_GE_CONST(0x47e54d7b, 0x86025d30, 0x248b18e6, 0xc6b2b128, 0x3f8eb11e, 0x60d11cad, 0xf59884ea, 0x56939f5a, 0xb618d932, 0x6110c200, 0xcbed144f, 0xc6376800, 0xd8ba0de1, 0xd87fa02d, 0x17d1d58d, 0x9652c498), 3, {SECP256K1_FE_CONST(0x8797d6a9, 0xe3614b34, 0x80e43cb6, 0x936cd932, 0xbe4eee02, 0x1e47e067, 0x2d1d9f2f, 0xd0148558), SECP256K1_FE_CONST(0xb19c75d0, 0xb4856c81, 0xb467f8f5, 0xb9f8d849, 0x0e5296f0, 0x4c60d639, 0x6f772b7f, 0x427c5d38), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x8fa5ffb5, 0x597068f6, 0x06785a63, 0x1f74cd6f, 0x8b16e94b, 0xe6cee831, 0x2970e0ec, 0xa9ecda52, 0x6c4f0efe, 0xf1d0eef2, 0xe3281b13, 0x4f29289f, 0x0a9d7b4d, 0xb3118c5f, 0x1d2d1da4, 0x75569ebf), 3, {SECP256K1_FE_CONST(0xe66995d0, 0x9cfdddda, 0xadf4b4ec, 0xc00270ed, 0xaeaacf01, 0x2db38d37, 0xe4143baf, 0x0ae7dfa3), SECP256K1_FE_CONST(0xfe0d264e, 0x3121942c, 0xd5126e26, 0x0766f36c, 0x3a08a689, 0x4e8ec172, 0xf3fdb252, 0x70def1ad), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x016a682d, 0x1df4f869, 0xb32c48b0, 0xa9b442a1, 0x493949fb, 0x85d951d1, 0x21c1143b, 0xd3d5c1af, 0x38d33fe5, 0xd3f9b4b9, 0x82e37dff, 0x7561428d, 0x47ef4ddf, 0x654bd959, 0x51b04e90, 0xa3be50e7), 0, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x1ec42424, 0xb4d2226f, 0x83f94258, 0xc737d0da, 0xf93a4eb1, 0x1d9b9e3f, 0xd500d5b9, 0xc3aa7c71, 0x84975819, 0xb703da77, 0xca98bd3c, 0xd9bbdc7a, 0xf1dbc7b5, 0x85c590eb, 0xcbd417fd, 0x739ad572), 1, {SECP256K1_FE_CONST(0x945faa12, 0x7e8bf378, 0x63581bfb, 0xde084bf7, 0x63caee39, 0x1449c610, 0xc2074f86, 0xff1bf16c), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x69ee52b2, 0x88dfb06a, 0x449d3db8, 0x7602e094, 0xb4f131e3, 0xf6a4b249, 0xdc0a76ff, 0xdebe989a, 0x3922f1a4, 0xdd208f94, 0xcbac1c5d, 0x34a9278d, 0x84310781, 0x84ff4430, 0x31a14018, 0x95ffd9e6), 1, {SECP256K1_FE_CONST(0xdc1e4760, 0x15bda784, 0xa1b9527b, 0x0357786a, 0xdf2a8028, 0x03957837, 0xe10cff92, 0x5ef4ca7e), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ee, 0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), 0, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b, 0x3ac01550, 0x68185039, 0x6068aaf0, 0xc3f24144, 0x9a267956, 0x698833d4, 0x80c03dc5, 0x678b67cf), 4, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0xbde70df5, 0x1939b94c, 0x9c24979f, 0xa7dd04eb, 0xd9b3572d, 0xa7802290, 0x438af2a6, 0x81895441), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x9ddddd8e, 0xc1814a29, 0x3fcca202, 0xebfbe14e, 0x5d808dda, 0x142eee64, 0xc6108381, 0xe99e5cff, 0xb5072d55, 0x37223f39, 0x3e4176d2, 0xcfd93c86, 0x82ca2c22, 0xcd25ec40, 0x877296bd, 0xbb7c08f6), 3, {SECP256K1_FE_CONST(0xadd34f27, 0xc5f90171, 0x75186c23, 0xd14f6ef2, 0xaa182896, 0x77d5373a, 0xd6c31e9f, 0xf6358ae8), SECP256K1_FE_CONST(0xf5ee8614, 0x1916fe03, 0x945d028b, 0xbc354c4a, 0x09f6d6ab, 0x1468ab9a, 0xd8742075, 0x1543c2a2), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x1ee7e9a7, 0xfcd56edf, 0xabf3712e, 0x72cc24a3, 0x0a476f5a, 0x97f77825, 0xf0308620, 0x162f31ad, 0x77bfc7de, 0xc2401a39, 0x8c5e8675, 0x417c8a7b, 0x632f5d64, 0x2f1a5059, 0x9a830b8c, 0x7981f636), 3, {SECP256K1_FE_CONST(0xb3a8d9e7, 0x368af258, 0x3785be92, 0x2ad54dfb, 0x47329513, 0x6ade2d18, 0x2f931cd6, 0x54f35d02), SECP256K1_FE_CONST(0xe1d420e5, 0xfab5c26d, 0xf4294b2b, 0x0c19eb9a, 0x188409bf, 0x48a3741f, 0x31f72acc, 0x6ea93418), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x9e24d0a5, 0xd5014164, 0x987f86bb, 0x1709305a, 0x6fd352a0, 0xa3478fae, 0x3f85e594, 0x21d72a80, 0x3729c39b, 0xbbb26d97, 0xa4ec6bf7, 0xcb4e6453, 0x058e448e, 0x7530b028, 0xd1ae345e, 0x35608d3c), 0, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x15f2f1a4, 0x339f5f2a, 0x313b9501, 0x5cad8124, 0xd054a171, 0xac2f31cf, 0x529dda7c, 0xfb6a38b4, 0xfe1d0fa5, 0x95b4f7d3, 0x63e82c29, 0x0095189f, 0x5f2be99c, 0x880be4fc, 0x9742a31b, 0x40041eda), 1, {SECP256K1_FE_CONST(0xc1c3ed27, 0x17ffabfd, 0x01132f5e, 0x54dd73c3, 0x475297e0, 0xfdbff814, 0xdc9456b8, 0x4a57b698), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xd383134d, 0x721cf055, 0x143570e7, 0x82bb323d, 0x5c542a61, 0xe455823e, 0xd60b940f, 0x86826d54, 0x5a88e50b, 0x3f59874e, 0x84dab4a2, 0x07d34623, 0xd836c376, 0xc68dded3, 0xc095a716, 0xf563e4fc), 1, {SECP256K1_FE_CONST(0xe4d2660c, 0x1d50d031, 0x97f5e610, 0x4d9c2066, 0x01f6c791, 0xadb52178, 0xe2bd6c88, 0xe89cf012), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40, 0xbde70df5, 0x1939b94c, 0x9c24979f, 0xa7dd04eb, 0xd9b3572d, 0xa7802290, 0x438af2a6, 0x81895441), 0, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0xbde70df5, 0x1939b94c, 0x9c24979f, 0xa7dd04eb, 0xd9b3572d, 0xa7802290, 0x438af2a6, 0x81895441), 3, {SECP256K1_FE_CONST(0xd3779b57, 0x3cb17828, 0xac118cff, 0x74412ab5, 0xb84c86f8, 0xa92f48b8, 0xefcbe4c7, 0x0a675631), SECP256K1_FE_CONST(0xea6f729d, 0xdc884123, 0xf0130aa0, 0x339bda36, 0x2166d034, 0xfe50d9d7, 0x53bf0dde, 0x7721fa3f), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x99a70224, 0xc3062c32, 0x6c45d3c6, 0x46a545e9, 0xb152b75b, 0xee868378, 0x07e47951, 0x9e5b600d, 0x95b6675a, 0x10845b66, 0x37ff96e8, 0xe67f2a75, 0xbbf0f764, 0xc56d26c5, 0x4b2db5eb, 0xb026d7de), 3, {SECP256K1_FE_CONST(0xb74a9552, 0xc5b9b6ed, 0x575d380f, 0xec3df8ed, 0xdb524ed1, 0x80b13607, 0x81e2eec6, 0x7ad06c04), SECP256K1_FE_CONST(0xbb702282, 0x4194fbe4, 0x4a74c4f4, 0xabd01ee3, 0xdac8f4cb, 0x5a0e3a67, 0xd2276039, 0xdd4aac1a), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xb5902433, 0x3110b310, 0x8625f254, 0x47665c1e, 0xbf10c6a6, 0xbbe9f018, 0xc421f4b0, 0xdcb5a993, 0x43bae2cd, 0xaae9c002, 0xe57ac99a, 0x17926e22, 0x76a66728, 0xf92b11bb, 0x7dc953b9, 0xea6d49b7), 3, {SECP256K1_FE_CONST(0xd5a57c1b, 0x71916606, 0xbfb235f0, 0xce8d880d, 0xe9109a01, 0xb86d58c8, 0x2852b211, 0x0e55ee0f), SECP256K1_FE_CONST(0xca6cf74b, 0x128e1d79, 0x75482bfd, 0xc9e81416, 0x71a5c3e7, 0xe2af854b, 0x23707630, 0x97ba917b), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xb526749e, 0x35fa04ef, 0x5d20b1d6, 0xcda6f57e, 0x2f3c10c9, 0x85098901, 0xc390da79, 0x31769e34, 0x182093b3, 0xce5883a2, 0x7b834af6, 0x18547fd1, 0x6017cee0, 0x4e9398da, 0x6aaaed2b, 0x87ca0e7c), 0, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x901c52d6, 0xa39718c7, 0x255e94e3, 0x3189cbeb, 0x41f2fa97, 0x95279076, 0xbecd6678, 0x99684c17, 0xf988a838, 0x156cc39f, 0x2182bbc5, 0xf7e4f707, 0x9cf75bfb, 0x58638cff, 0x5b201fd3, 0xcf499fc0), 1, {SECP256K1_FE_CONST(0xa356db31, 0x44b754a3, 0xdafdf2a9, 0x0767b65a, 0xbaea92ca, 0x56c69c3a, 0x31a4ff5b, 0xd7914d9c), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x957d4fd4, 0x4a10f38a, 0x0d0e1e46, 0x2656dd2e, 0x7f2b6b8c, 0x9545ee02, 0x903f28b0, 0x8f9a57e7, 0x3f4bf4de, 0x3731bea3, 0x291627e3, 0x9daa7dac, 0xcdcd4e13, 0xb2418482, 0x488730b7, 0xa7a816b7), 1, {SECP256K1_FE_CONST(0xa761cd3a, 0x58385878, 0x300c6963, 0xe918b545, 0x99eb0254, 0x550f6254, 0xe414628c, 0x2f431bbd), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x0a7b6db2, 0x56f01aea, 0x797a0798, 0x5eaf98d6, 0x64486f82, 0x723758bf, 0x1a5f7b00, 0xb74887e1, 0xe07ce7ec, 0x5f24b6da, 0x199329ff, 0x674788c4, 0x1b7312d4, 0xbb63672f, 0x81ebbfd3, 0x6d962235), 15, {SECP256K1_FE_CONST(0x442e15e3, 0xac31958b, 0x7acdb8b9, 0x7977b6d0, 0x533b1ef0, 0x5094f496, 0x126a04d0, 0xc6d6c327), SECP256K1_FE_CONST(0xc565d637, 0x6c8f4fa4, 0xa22ab5d4, 0xe1c87f5d, 0x6f9beb27, 0x7764a77f, 0x8ebe3796, 0xaa82cba5), SECP256K1_FE_CONST(0x2082a3b7, 0x04d3729c, 0x71a73a0c, 0xd745c7ce, 0x7a7c5e26, 0x77c688e2, 0x772806d1, 0xdd1a849f), SECP256K1_FE_CONST(0xc00c8cc3, 0x5ea8122e, 0xf17b0a8e, 0xc69218d1, 0x8cb45a3f, 0x0227a2c5, 0x68fbd9f9, 0xc6d6d141)}}, + {SECP256K1_GE_CONST(0x770ed6cb, 0xf6d2156b, 0x362523eb, 0xc2908f68, 0x65ab182c, 0x43468bc8, 0x69d6754e, 0x68dc71a0, 0x2a378713, 0x10223129, 0xbaba56c2, 0x0dc4a1e9, 0x634dba32, 0xa034d21f, 0x3104176b, 0x870c9916), 15, {SECP256K1_FE_CONST(0x092c79ab, 0xbbafd66d, 0x58c56208, 0x7ba5c385, 0x9fed6c1b, 0x5f8005af, 0x0087cedb, 0xec7dc084), SECP256K1_FE_CONST(0x3f5c280a, 0x60802515, 0x16dfd84a, 0x4488df47, 0x96198d5f, 0xbce0be21, 0x1ab0ee7d, 0xa456e73e), SECP256K1_FE_CONST(0x94057b6b, 0xd54b13b2, 0xe2b9d322, 0x687569f5, 0xdd16727d, 0x3d912ba3, 0xeb8aa33d, 0x36c15108), SECP256K1_FE_CONST(0x098360ae, 0xcf93979e, 0x7cd6df39, 0x6e8fe2f3, 0x18fa1da3, 0x9efa707a, 0xeab95cd8, 0xcd5dca2a)}}, + {SECP256K1_GE_CONST(0xab01575c, 0x0604c63e, 0xe77d3153, 0x4a5bcfa2, 0x0ce66c9d, 0xf47d6054, 0xb822bfd8, 0x6934f8ec, 0xce488d85, 0xd0875b40, 0x4fb92b6e, 0x8068602a, 0x670ac4f8, 0xd76b78b6, 0xc246b713, 0x595e226b), 12, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x4ab21b18, 0x1009aa48, 0xb8ba5eb9, 0xd373919b, 0xcfcb36a6, 0xf34961b2, 0xc859f5a8, 0x6da8ba41), SECP256K1_FE_CONST(0x9a11c401, 0x9a4ba9fc, 0xf4698a71, 0x2d85c8e4, 0x70028e02, 0x545ef049, 0xf9f3083d, 0x187c5b41)}}, + {SECP256K1_GE_CONST(0x6084cfdd, 0xf8d9736e, 0xa90100eb, 0xdb43338f, 0x65e2ab43, 0xef35a799, 0x926e6ce3, 0x2a89ae17, 0x753998b5, 0x9eaae7a3, 0xdcab34d9, 0xa15dbc71, 0xe539cdff, 0xdcf05927, 0x0eb27c86, 0xab6b62a4), 12, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x18b1f707, 0x3fca3167, 0x04e1b3b8, 0x8cc8ff5a, 0x702d79bc, 0x756e4dea, 0x2ff948cc, 0xdb43a9f4), SECP256K1_FE_CONST(0xca02e589, 0x89eb16d1, 0x520463d2, 0x435745cf, 0x6e69fa52, 0x6b5c7adc, 0x57cea2b3, 0xf5a6441c)}}, + {SECP256K1_GE_CONST(0xc9fbac00, 0x9d8eda5d, 0x25c9aabb, 0x2b6794bc, 0x9a801afd, 0x17adef78, 0x78c65392, 0x04eb0f82, 0x95ed9e51, 0x898b903e, 0xe689e6ed, 0xff2b54bf, 0xed5c2da1, 0x69e2bdd0, 0x415a392e, 0x16b3de2b), 13, {SECP256K1_FE_CONST(0x21a95220, 0x8577e3f0, 0xcc5b4b17, 0xf5e434b2, 0x2bbdbaaa, 0x51cd2659, 0xe37880a6, 0xa25aa7dd), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0xcfd1ca13, 0x2f8d3eae, 0x73a97895, 0x01d2c82a, 0x6f057566, 0x7949fab9, 0x267bc1e8, 0xef9bf5bd), SECP256K1_FE_CONST(0x9cde02aa, 0x3acd2596, 0xdbea4b82, 0xf9f47ad1, 0x994ad567, 0x3c0d4fb2, 0xe8a3dca5, 0xe8e067fb)}}, + {SECP256K1_GE_CONST(0x8dcb38d9, 0x0059d4f1, 0x270455af, 0x6f3dd40e, 0x8d671a34, 0xa1fad81d, 0x2470db8a, 0x13b18f76, 0x603ed5be, 0x7bc3e67f, 0x439067da, 0x29949bcb, 0xd3c96c9f, 0x94da4231, 0x3c9c0feb, 0xe5cdf560), 13, {SECP256K1_FE_CONST(0x3ef4008a, 0x8a190a3f, 0x5c97d211, 0x3bb539e1, 0xf4261a78, 0xf7cd85c4, 0xfd254837, 0xeaacd020), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x0fe7af4f, 0xcab80199, 0x37ee0026, 0x47d55d97, 0x575474b3, 0x4b9cc1bb, 0x133f4261, 0x017124a0), SECP256K1_FE_CONST(0x09dcec3b, 0x93c4ab42, 0x91d01dce, 0xccc19525, 0xc801add3, 0x77170c2c, 0x919f5488, 0xf41d6d3e)}}, + {SECP256K1_GE_CONST(0xf69dfe44, 0x890d2b09, 0x4b749a56, 0xf680e851, 0x50c47c4c, 0xd51e7796, 0x3fec4e6a, 0x09dcd0a1, 0xfb5d321c, 0x1e243b63, 0x6dfb71f3, 0xcf0e8a01, 0x2e52b22c, 0x905cec6d, 0x2f6ae32a, 0x6a4eb7be), 3, {SECP256K1_FE_CONST(0xa17fd528, 0x7276cbf6, 0xc168dcde, 0xb32aba14, 0xe1aeae2f, 0xe7f5bea5, 0xa87d384e, 0xe8046aac), SECP256K1_FE_CONST(0x637f176a, 0xf2fe854a, 0x968ab19b, 0xee010554, 0x313e3eff, 0xc6ff8cb4, 0xcb538a6d, 0xbaedd954), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x043a0631, 0x871a3f67, 0xac03c5f8, 0x406b69a0, 0xdc14bd5b, 0x23e55f27, 0xa5d4462b, 0x0f0a2d23, 0x247b9bcc, 0x0019091c, 0x31eb4b03, 0xe731a0b5, 0xa9b33f75, 0xad9e5e63, 0x39286573, 0xa6439d88), 3, {SECP256K1_FE_CONST(0xd65add13, 0xad3044d9, 0x2ebcd0e6, 0xd42853d8, 0xe5733ff6, 0x5297f544, 0x09a3ce89, 0xfdaffbdc), SECP256K1_FE_CONST(0x7281ad3c, 0x85de3870, 0x84f64e14, 0x42b37154, 0xeab39453, 0x8b1c0753, 0x4b303ae7, 0x37f3973e), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x8855508a, 0xade16ec5, 0x73d21e6a, 0x485dfd0a, 0x7624085c, 0x1a14b5ec, 0xdd6485de, 0x0c6839a4, 0xe50aaeba, 0xa0ceceec, 0xa1bce62e, 0x5f0fac4b, 0xe78ab03a, 0x7b2deaa6, 0xe5c17e88, 0x98e277e9), 1, {SECP256K1_FE_CONST(0x4e96da73, 0xae14fc85, 0x25eccb2d, 0xf4416924, 0x8a7fd269, 0xa065e065, 0x04d315e6, 0x63666b03), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xc66327bc, 0x5b0b8b90, 0x37adfd63, 0xc2a9f192, 0x2ce2144a, 0xa513b390, 0xd48bc387, 0xae3ebff6, 0x17a1ca89, 0x64eb0b41, 0x162894e6, 0x4fb4112b, 0x638f96ec, 0xe0c6f30d, 0xef7616fe, 0x0e78386a), 1, {SECP256K1_FE_CONST(0x56e8e17e, 0xfaf989d6, 0xa7efb81d, 0x5a602393, 0x6814930e, 0xbc3f6fdf, 0x72ebf472, 0x69ba4c9a), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b, 0xc53feaaf, 0x97e7afc6, 0x9f97550f, 0x3c0dbebb, 0x65d986a9, 0x9677cc2b, 0x7f3fc239, 0x98749460), 4, {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x9d709c02, 0x74604cb6, 0x3b531fea, 0x35932e2e, 0xc965f4bf, 0x5913e577, 0xff31080b, 0x67727a2e, 0xf2b0b821, 0xa24081a9, 0xd0ca84d9, 0x303068cf, 0x7ea32788, 0x05926b0a, 0xb90b9af7, 0x498efbd5), 3, {SECP256K1_FE_CONST(0xb06abefa, 0x192a6498, 0xbce368ff, 0xacc843fb, 0xb39f8117, 0xa56a1870, 0xf57197ef, 0xd9312f6d), SECP256K1_FE_CONST(0x1263d142, 0xaac9cfc5, 0x64c56650, 0x0fa4a62f, 0x38e727fb, 0xb4dbeaf2, 0x6fdf7d05, 0xfd022c71), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xb4955dcb, 0x4daaa784, 0x9b421c14, 0x53ec8945, 0xd685d554, 0xf41103f8, 0x12cbfb2f, 0x54a4539b, 0x354d18e4, 0xb1cee7a3, 0xf98b0651, 0xf5544091, 0xe8a00656, 0x0c74750d, 0xaadf460e, 0xc3f620ea), 3, {SECP256K1_FE_CONST(0x6281a8a7, 0x0a3b5745, 0xb897ce4f, 0x58305fb0, 0xd6a0f8ab, 0xa6c5ba18, 0xed278ce1, 0x50f7911c), SECP256K1_FE_CONST(0x5f95a708, 0x2d2f6d69, 0xf7ff9b74, 0x2b88063c, 0x39a3003b, 0xb03f333c, 0x7e3d7c5e, 0xd861fb04), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x6ad63dfd, 0xcd231967, 0xff2508f4, 0x75896976, 0xf8728e40, 0xdd7a2acc, 0x6b5ced37, 0xcada8291, 0xf93e5181, 0x8f5329b8, 0xd520a9af, 0xd72938e1, 0x2e3f8be6, 0x421d2bce, 0x89d7b14e, 0x25bf5336), 1, {SECP256K1_FE_CONST(0x0f050318, 0x622f79f1, 0x5a2b23d9, 0xf76329b7, 0x8e195f1a, 0x4651aae0, 0x65d58bcd, 0xdfa4d3b6), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xa91b7f2a, 0xb93de821, 0xabeec175, 0x0258e4d4, 0xf5f09831, 0xb0a11dda, 0x47e89ddf, 0x6944d819, 0x22eb9bf6, 0x4a517df2, 0xc27d1c55, 0x1df07609, 0x166fc995, 0xe2b39fee, 0x0473ea46, 0xed14efc1), 1, {SECP256K1_FE_CONST(0x7a01651a, 0x81a7f09e, 0x2733cf34, 0x9e6472a1, 0x18c16780, 0x6f5c880f, 0x534b89a6, 0x52be06a7), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), 3, {SECP256K1_FE_CONST(0x2c8864a8, 0xc34e87d7, 0x53ee7300, 0x8bbed54a, 0x47b37907, 0x56d0b747, 0x10341b37, 0xf598a5fe), SECP256K1_FE_CONST(0x15908d62, 0x2377bedc, 0x0fecf55f, 0xcc6425c9, 0xde992fcb, 0x01af2628, 0xac40f220, 0x88de01f0), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xa64de96a, 0x6254cefc, 0xffbeaf89, 0x8f2c228a, 0xf6d405f3, 0xbcc6a4cc, 0xe068312a, 0xf7ccf8e1, 0x8f9b3a1b, 0x2d146ea9, 0x54bfc5e2, 0xcdfe861c, 0xcbed8431, 0xc741c5f9, 0xd32f16a3, 0x073ea496), 3, {SECP256K1_FE_CONST(0x4591d33d, 0x1a133a87, 0x94689b1b, 0x0ca445b7, 0x8ada3bce, 0xc2e812b0, 0x8315e2b1, 0x07940ad4), SECP256K1_FE_CONST(0xa763d217, 0x6027d40e, 0x8a8ff34b, 0xd9c639b7, 0x3e2ea045, 0x92274fdc, 0xfa4051c6, 0x6d93a1b6), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x49a0dc06, 0x8c3f117a, 0xefdc842d, 0x3d358153, 0xf677f04c, 0x6dabc9c9, 0x1b09d452, 0xfef27b66, 0x7b944da4, 0x8a175dbc, 0x444ead8d, 0xb82eff66, 0xb081a8aa, 0xe6453fed, 0x2bca9720, 0xb44dd6e5), 3, {SECP256K1_FE_CONST(0x7bf1e2b1, 0x720c1c44, 0x0db64687, 0xf16439fa, 0x41b39833, 0x8095f24e, 0xbeec0cfa, 0x88750dc9), SECP256K1_FE_CONST(0xdc97e26d, 0x3137445d, 0x6c1269b6, 0x1a765501, 0x0c19c36a, 0x2e361066, 0xe31e2bb1, 0x0403470b), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0xd09a4047, 0xf158fe52, 0xf96c661d, 0x02c68657, 0xc4c976ea, 0x96ea85ef, 0x46d6985b, 0xd540756b, 0xe793bfaa, 0xe9300f18, 0xe6f9b55a, 0xae263223, 0x68b61d51, 0xae5022ef, 0xe266c72d, 0x574178bc), 1, {SECP256K1_FE_CONST(0x7e6175fd, 0xfbb9fb4f, 0xaf6e2b92, 0x5ef86c4a, 0x444d819a, 0xaa82dbee, 0x545d3d9b, 0x296375be), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}}, + {SECP256K1_GE_CONST(0x34986625, 0x04b73c7c, 0x8cecb6c3, 0x3cd493bd, 0xfc190e0f, 0x87d913d7, 0xff9ad42e, 0x222bfe95, 0x245b3a61, 0xb8d46997, 0xf14f2fea, 0x28748996, 0x91eb3254, 0x2b9907d6, 0x5eb9d21d, 0x42454021), 1, {SECP256K1_FE_CONST(0x7f556282, 0xc3dd9d26, 0x3390d6bb, 0xddada698, 0xab8fd7c7, 0xd1a06498, 0xf42b3043, 0x7c8361ad), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000)}} +}; + +struct ellsq_enc_test { + unsigned char ell64[64]; + unsigned char pubkey[33]; +}; + +/* Set of (ell64, point) test vectors, selected to maximize branch coverage. + * Created using an independent implementation. */ +static const struct ellsq_enc_test ellsq_enc_tests[] = { + {{0x54,0xca,0xd2,0x27,0xb2,0xc9,0x8d,0x5f,0x7c,0x78,0x8c,0xfc,0x3d,0xaf,0xd6,0x52,0xf5,0x8f,0x69,0xcf,0xef,0x63,0x2b,0x82,0x2b,0x35,0xd0,0xb0,0xe2,0x4f,0xc0,0x3a,0xd2,0x8c,0xa1,0x4b,0x6f,0x62,0xd4,0x53,0x79,0xc5,0x3f,0x70,0xee,0x40,0x5c,0xa9,0x2c,0xe7,0xb6,0xf9,0x70,0x83,0x13,0x05,0xf2,0x7d,0xc4,0x1e,0xb6,0x9d,0xe0,0x6e}, {0x02,0x11,0x62,0x89,0x03,0x32,0x88,0x91,0xae,0x09,0xd1,0x08,0xd8,0x92,0x43,0xe4,0x7e,0x10,0x9f,0xe7,0xb8,0xbb,0x1e,0x2d,0xf1,0xa3,0xae,0x9b,0x0e,0x78,0x08,0x54,0x9c}}, + {{0xfb,0xe6,0xce,0xab,0x4c,0x5f,0xdf,0xa5,0xfb,0xee,0x8f,0x3d,0x09,0xa2,0xf7,0x23,0x53,0xe7,0x4e,0x5a,0x9c,0xd4,0xab,0x8e,0x6a,0x34,0xd4,0x95,0x23,0xa7,0xd1,0xa2,0xc4,0x50,0xb7,0x45,0xda,0xb1,0xaf,0xa9,0x95,0x4b,0x3a,0x35,0x75,0xe4,0xe8,0xe2,0xdb,0x3d,0xa5,0xcd,0x4d,0x56,0x48,0xea,0xd0,0x0a,0x60,0xb4,0xcd,0xfe,0x84,0xb3}, {0x02,0xc0,0x4c,0x84,0x85,0xf9,0x8d,0x56,0x6c,0x79,0xbf,0x33,0xa7,0x0c,0xb2,0x32,0x54,0x9e,0x3d,0xe1,0xc3,0xe3,0x01,0xe3,0x57,0x1c,0x83,0x68,0x97,0xf0,0x7c,0x5d,0x12}}, + {{0x71,0x7e,0x63,0xd7,0x71,0xdb,0xda,0x67,0x67,0xd5,0x8f,0x26,0xab,0x5f,0x54,0x9b,0xd2,0xd1,0x8a,0xcf,0x59,0xff,0x50,0x77,0x5f,0x4e,0xb5,0x0a,0xc0,0x17,0x4d,0xf1,0x7d,0xd0,0x34,0xc8,0xed,0x08,0x11,0x61,0x5e,0x3e,0xbb,0x36,0xf8,0xf3,0x3e,0x09,0x23,0x8e,0x4d,0xa8,0xf5,0x01,0x9d,0x37,0x00,0x78,0x4f,0x37,0xc1,0x53,0x53,0x94}, {0x02,0x72,0x81,0x15,0x0c,0xeb,0xc3,0xd7,0xb3,0xbb,0xb9,0x92,0xf5,0x81,0xbb,0xcb,0x9e,0x30,0x4f,0x87,0x44,0xf0,0x19,0x98,0xa7,0x1f,0x5d,0xe1,0x14,0xf8,0x22,0x91,0xc4}}, + {{0x01,0xf0,0xbf,0xe4,0xf9,0xbd,0xee,0x52,0x5e,0xb7,0x7c,0x8e,0x35,0x1e,0x1f,0x88,0x3f,0xb9,0xcd,0x37,0x7e,0xf7,0xc5,0xbd,0xde,0xe4,0xf6,0x60,0x64,0x43,0x90,0xf5,0x95,0x3e,0x7d,0x2b,0x6c,0xde,0x36,0x90,0x3e,0xa1,0x34,0x4b,0x0d,0x16,0x33,0x5c,0xc5,0x11,0x5d,0xaa,0x97,0x7c,0x3c,0x2b,0xf9,0x31,0xac,0xde,0x2f,0xf5,0x78,0x9a}, {0x02,0x10,0x44,0x9d,0x7e,0xa0,0x62,0x3e,0x80,0xa5,0x87,0x01,0x9f,0xa5,0x11,0xaf,0xd3,0x94,0xb2,0x55,0xb0,0x8f,0x91,0xb5,0xf7,0x48,0x2a,0xe9,0xd1,0xa1,0xa7,0xfb,0x7c}}, + {{0x82,0xd5,0x87,0x1e,0x18,0x37,0x66,0xbd,0x22,0xe1,0x13,0xa8,0x52,0x79,0xaa,0x61,0x7e,0x6b,0x9f,0x73,0x52,0x2c,0xd4,0x6b,0x90,0x59,0xba,0x51,0x97,0xfa,0x56,0x44,0xaf,0x90,0x41,0x89,0x30,0x98,0x7d,0xb7,0xab,0x4a,0x84,0x0c,0x72,0x64,0x1b,0x58,0xb3,0x66,0xe5,0x7c,0x92,0x8c,0x98,0x3a,0x47,0x37,0x82,0x00,0x3c,0x36,0x10,0xab}, {0x03,0xc8,0xb2,0x62,0xf9,0x31,0x69,0x43,0x75,0x51,0x48,0x3b,0x8a,0x61,0x19,0x83,0x82,0xe3,0x11,0x41,0xaf,0x61,0xbf,0x36,0x10,0x0b,0xd0,0x68,0x46,0x5d,0xdd,0xa8,0x40}}, + {{0xda,0x82,0x53,0xb4,0x3b,0x5a,0xc2,0x3b,0x42,0x36,0x07,0xe9,0x18,0xab,0x5c,0xaa,0x5d,0x7d,0x34,0x3d,0x77,0xa3,0x99,0x6a,0x42,0xeb,0x33,0x2a,0x3b,0x55,0x1d,0x8c,0xda,0x6c,0xb6,0xf9,0x57,0x4c,0xe3,0x60,0x91,0x2c,0xf4,0x5b,0x90,0x9a,0x96,0x2e,0x4d,0xed,0x63,0xae,0x5a,0xac,0xb0,0xab,0x23,0x29,0x45,0xb1,0x01,0xf7,0x2b,0x62}, {0x02,0xe7,0x28,0x34,0x1d,0xf6,0x93,0x48,0x71,0xb3,0x94,0xbb,0x4f,0xb2,0x8b,0xd8,0xd2,0xdf,0x39,0x92,0x55,0xb0,0x30,0x02,0xed,0x6f,0xc3,0x8f,0x28,0xcf,0xbf,0x53,0x56}}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x03,0x1b,0x41,0x2e,0x7a,0x96,0x6d,0x2c,0x24,0x3d,0xbc,0x5b,0x18,0xb7,0xf9,0xba,0xf1,0x85,0xbc,0xfe,0x41,0x38,0x96,0x04,0x79,0x64,0x1a,0xb1,0xe6,0x3b,0x38,0x1e,0x11}}, + {{0xdc,0x30,0x98,0xe4,0x00,0x61,0x83,0x30,0xf3,0x8b,0x19,0xe9,0x20,0x0a,0xdf,0x7f,0xfb,0x96,0x84,0x3f,0xa8,0x3c,0x49,0x1c,0xf6,0x7f,0x34,0xa7,0x90,0xbb,0xcf,0xe1,0x23,0xdc,0x30,0x07,0xa4,0xfd,0x13,0x3a,0x39,0x44,0x0b,0x06,0x03,0x1e,0x9e,0x2c,0x38,0x8e,0x41,0x47,0xaf,0x0e,0x82,0xbe,0xda,0x6d,0x56,0x4b,0xf8,0xcc,0x37,0xb1}, {0x02,0x5b,0x74,0x48,0x15,0x22,0xd4,0xc2,0x9f,0x2e,0x6a,0x2f,0x11,0x7f,0x9e,0x39,0xf9,0xab,0x01,0xb1,0xe9,0xf2,0xc3,0x4c,0x68,0xbe,0x8f,0x53,0x1b,0xe0,0x1f,0x6e,0xa7}}, + {{0x35,0xd7,0x0a,0x71,0x2c,0xc0,0x85,0x7f,0x8d,0xb1,0xbc,0x55,0x6a,0x6c,0x4e,0xf8,0x66,0x24,0xfd,0x0a,0x47,0x7f,0x96,0x7e,0xed,0xc0,0x32,0xfc,0xda,0xac,0xe7,0x96,0xc6,0x73,0xc5,0x43,0xd0,0x07,0x34,0x32,0x07,0x85,0x5b,0xeb,0xad,0x85,0xe9,0x4b,0xca,0xc7,0x78,0x2b,0x11,0x57,0x9a,0x70,0xdc,0x88,0xe2,0xa4,0x8d,0x9d,0xf2,0xd4}, {0x02,0xdb,0x21,0xb4,0x8f,0xe9,0xf9,0x95,0x08,0x3a,0x1f,0x9c,0x1f,0x3f,0x4b,0x31,0x1d,0x2c,0x43,0xa1,0x28,0xdb,0xb3,0xa4,0xd4,0x78,0x41,0xe4,0xff,0x5d,0xd0,0x2e,0x61}}, + {{0x5f,0xb8,0x07,0xce,0x10,0x0c,0x90,0xd2,0x83,0x7c,0xcf,0xc9,0x4d,0x8f,0x8b,0xa5,0xd3,0x5c,0xd3,0xd6,0xfa,0xfc,0xd2,0xf4,0x1f,0x24,0x5b,0x59,0x6e,0x36,0x00,0x57,0xa0,0x47,0xf8,0x31,0xef,0xf3,0x6f,0x2d,0x7c,0x83,0x30,0x36,0xb2,0x70,0x74,0x5a,0x2c,0xa3,0x2c,0x29,0x05,0x03,0x2d,0x0b,0xe0,0xdb,0xa4,0xa5,0x91,0xc9,0xfb,0xd8}, {0x03,0x41,0x58,0x28,0x65,0x43,0x5e,0xe9,0xc8,0xc9,0x27,0xc3,0x49,0xbd,0x3e,0x43,0x7b,0xce,0x2b,0x5c,0xfc,0xd0,0xc4,0x17,0x77,0xc3,0x4c,0x71,0xc6,0x7b,0x14,0x06,0x93}}, + {{0x1e,0x76,0x57,0x72,0xbf,0x72,0xde,0xb8,0x81,0x54,0x16,0xbd,0x54,0x45,0xdd,0x75,0x50,0xcd,0x86,0x7a,0xa2,0x5a,0xc6,0x3f,0x6f,0xd9,0xaf,0xd3,0x2f,0x92,0x1c,0xc8,0x8a,0x06,0x1a,0xb5,0xf6,0x98,0x1b,0x55,0x92,0x1b,0x90,0x5b,0x6f,0x4f,0x3d,0xf4,0x82,0x5d,0x79,0x72,0xd6,0x99,0xe3,0xb4,0x21,0x4e,0x40,0x44,0xcf,0xbe,0x65,0x34}, {0x03,0x90,0xd2,0x94,0x30,0x92,0xec,0x7e,0xd8,0xff,0x5a,0xf7,0x04,0x43,0x2d,0x0d,0xbe,0xb0,0x33,0x7c,0xbf,0x58,0x22,0x87,0x18,0x32,0x76,0x38,0x68,0x1f,0x70,0xd7,0xf0}}, + {{0x86,0xef,0x92,0xfd,0x28,0x09,0x85,0x4f,0x74,0xf7,0x5a,0xeb,0xbe,0xa1,0x8a,0xee,0xc0,0xee,0xdd,0x4e,0x81,0x92,0xc8,0x8c,0xd7,0xcf,0xf5,0xdf,0xc0,0x8a,0x57,0xdc,0x32,0x73,0xbf,0x6f,0x39,0x2d,0xee,0x48,0x4a,0x72,0x2c,0x3d,0xb0,0x0c,0x0e,0xfb,0x40,0xd5,0x1e,0x8a,0x72,0xfc,0xfb,0x78,0x3f,0xa7,0xeb,0xd4,0x30,0x82,0xdb,0x71}, {0x02,0x31,0x74,0x79,0x29,0x80,0x2d,0x79,0x76,0x02,0x26,0x71,0xb2,0xf7,0x5a,0xc0,0x31,0x18,0x56,0xb3,0x84,0xf4,0xb9,0xa8,0x00,0x0d,0x44,0xa2,0xab,0xc5,0x90,0x3a,0xd4}} +}; + +void run_ellsq_tests(void) { + int i = 0; + /* Verify that secp256k1_ellsq_fe_to_ge_var maps everything to curve points for random inputs. */ + for (i = 0; i < 1000*count; i++) { + int j, matches = 0; + secp256k1_fe t, f; + secp256k1_ge g; + random_field_element_test(&t); + secp256k1_ellsq_fe_to_ge_var(&g, &t); + CHECK(secp256k1_ge_is_valid_var(&g)); + /* t should appear exactly once in preimages */ + for (j = 0; j < 4; j++) { + matches += secp256k1_ellsq_ge_to_fe_var(&f, &g, j) && check_fe_equal(&f, &t); + } + CHECK(matches == 1); + } + /* Verify that secp256k1_ellsq_ge_to_fe_var + fe_to_ge_var roundtrips for random inputs. */ + for (i = 0; i < 1000*count; i++) { + int j, k, nf = 0; + secp256k1_ge g; + secp256k1_fe fs[4]; + random_group_element_test(&g); + for (j = 0; j < 4; j++) { + if (secp256k1_ellsq_ge_to_fe_var(&fs[nf], &g, j)) { + secp256k1_ge g2; + secp256k1_ellsq_fe_to_ge_var(&g2, &fs[nf]); + ge_equals_ge(&g, &g2); + /* Check that all preimages are distinct. */ + for (k = 0; k < nf; ++k) CHECK(!check_fe_equal(&fs[nf], &fs[k])); + ++nf; + } + } + } + /* Verify precomputed test cases. */ + for (i = 0; i < (int)sizeof(ellsq_tests) / (int)sizeof(ellsq_tests[0]); ++i) { + int j; + const struct ellsq_test *test = ellsq_tests + i; + for (j = 0; j < 4; j++) { + secp256k1_fe f; + int ret; + ret = secp256k1_ellsq_ge_to_fe_var(&f, &test->point, j); + CHECK(ret == ((test->enc_bitmap >> j) & 1)); + if (ret) { + secp256k1_ge g; + CHECK(check_fe_equal(&f, &test->encs[j])); + secp256k1_ellsq_fe_to_ge_var(&g, &f); + ge_equals_ge(&test->point, &g); + } + } + } + /* Verify that secp256k1_ellsq_encode + decode roundtrips. */ + for (i = 0; i < 1000*count; i++) { + unsigned char rnd32[32]; + unsigned char ell64[64]; + secp256k1_ge g, g2; + secp256k1_pubkey pubkey, pubkey2; + random_group_element_test(&g); + secp256k1_pubkey_save(&pubkey, &g); + secp256k1_testrand256(rnd32); + secp256k1_ellsq_encode(ctx, ell64, rnd32, &pubkey); + secp256k1_ellsq_decode(ctx, &pubkey2, ell64); + secp256k1_pubkey_load(ctx, &g2, &pubkey2); + ge_equals_ge(&g, &g2); + } + /* Verify decoding of precomputed test cases. */ + for (i = 0; i < (int)sizeof(ellsq_enc_tests) / (int)sizeof(ellsq_enc_tests[0]); ++i) { + secp256k1_pubkey pubkey; + unsigned char pk33[33]; + size_t size = 33; + secp256k1_ellsq_decode(ctx, &pubkey, ellsq_enc_tests[i].ell64); + secp256k1_ec_pubkey_serialize(ctx, pk33, &size, &pubkey, SECP256K1_EC_COMPRESSED); + CHECK(size == 33); + CHECK(secp256k1_memcmp_var(&pk33, ellsq_enc_tests[i].pubkey, size) == 0); + } +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 8f34c35283..270490f207 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -765,3 +765,7 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/main_impl.h" #endif + +#ifdef ENABLE_MODULE_ELLSQ +# include "modules/ellsq/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index b30e02f70e..5a8b306fc9 100644 --- a/src/tests.c +++ b/src/tests.c @@ -6914,6 +6914,10 @@ void run_ecdsa_edge_cases(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSQ +# include "modules/ellsq/tests_impl.h" +#endif + void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7214,6 +7218,10 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_ELLSQ + run_ellsq_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_byteorder_tests(); From d4cbedc023ab921631723a37ea8052d48b1c4537 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 28 Sep 2021 15:09:50 -0400 Subject: [PATCH 4/4] Add ellsq testing to CI --- .cirrus.yml | 15 ++++++++++++--- ci/cirrus.sh | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index a2e7f36d1f..5adf579c21 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -18,6 +18,7 @@ env: ECDH: no RECOVERY: no SCHNORRSIG: no + ELLSQ: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -67,12 +68,12 @@ task: << : *LINUX_CONTAINER matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, ELLSQ: yes} - env: {WIDEMUL: int128} - - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes, ELLSQ: yes} - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes} - env: {WIDEMUL: int128, ASM: x86_64} - - env: { RECOVERY: yes, SCHNORRSIG: yes} + - env: { RECOVERY: yes, SCHNORRSIG: yes, ELLSQ: yes} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} - env: {CFLAGS: -O0, CTIMETEST: no} @@ -94,6 +95,7 @@ task: env: HOST: i686-linux-gnu ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes matrix: @@ -176,6 +178,7 @@ task: HOST: s390x-linux-gnu WITH_VALGRIND: no ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes CTIMETEST: no @@ -195,6 +198,7 @@ task: HOST: arm-linux-gnueabihf WITH_VALGRIND: no ECDH: yes + ELLSQ: tes RECOVERY: yes SCHNORRSIG: yes CTIMETEST: no @@ -215,6 +219,7 @@ task: HOST: aarch64-linux-gnu WITH_VALGRIND: no ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes CTIMETEST: no @@ -232,6 +237,7 @@ task: HOST: powerpc64le-linux-gnu WITH_VALGRIND: no ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes CTIMETEST: no @@ -249,6 +255,7 @@ task: HOST: x86_64-w64-mingw32 WITH_VALGRIND: no ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes CTIMETEST: no @@ -262,6 +269,7 @@ task: << : *LINUX_CONTAINER env: ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes CTIMETEST: no @@ -311,6 +319,7 @@ task: MAKEFLAGS: -j4 CC=g++ CFLAGS=-fpermissive\ -g WERROR_CFLAGS: ECDH: yes + ELLSQ: yes RECOVERY: yes SCHNORRSIG: yes << : *MERGE_BASE diff --git a/ci/cirrus.sh b/ci/cirrus.sh index b85f012d3f..02515c2677 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -18,6 +18,7 @@ valgrind --version || true --with-ecmult-window="$ECMULTWINDOW" \ --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ellsq="$ELLSQ" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-examples="$EXAMPLES" \ --with-valgrind="$WITH_VALGRIND" \