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/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/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" \
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/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.
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/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/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 dd53173930..5a8b306fc9 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);
@@ -6872,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)];
@@ -7172,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();