diff --git a/.gitignore b/.gitignore index 55d325aeefa9c..905be987305af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ bench_inv bench_ecdh bench_ecmult +bench_schnorrsig bench_sign bench_verify -bench_schnorr_verify bench_recover bench_internal tests diff --git a/.travis.yml b/.travis.yml index 74f658f4d1c6d..cedf94b97b56d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,25 +11,27 @@ cache: - src/java/guava/ env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no JNI=no SCHNORRSIG=no - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar matrix: - SCALAR=32bit RECOVERY=yes - - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - SCALAR=64bit - - FIELD=64bit RECOVERY=yes + - FIELD=64bit RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - FIELD=64bit ENDOMORPHISM=yes - - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC - EXTRAFLAGS=CFLAGS=-O0 - BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes + - ECMULTGENPRECISION=2 + - ECMULTGENPRECISION=8 matrix: fast_finish: true include: @@ -59,10 +61,40 @@ matrix: packages: - gcc-multilib - libgmp-dev:i386 + - compiler: gcc + env: + - BIGNUM=no ENDOMORPHISM=yes ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes + - VALGRIND=yes EXTRAFLAGS="--disable-openssl-tests CPPFLAGS=-DVALGRIND" BUILD= + addons: + apt: + packages: + - valgrind + - compiler: gcc + env: # The same as above but without endomorphism. + - BIGNUM=no ENDOMORPHISM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes + - VALGRIND=yes EXTRAFLAGS="--disable-openssl-tests CPPFLAGS=-DVALGRIND" BUILD= + addons: + apt: + packages: + - valgrind + before_install: mkdir -p `dirname $GUAVA_JAR` install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi before_script: ./autogen.sh + script: - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-asm=$ASM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --with-ecmult-gen-precision=$ECMULTGENPRECISION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-module-schnorrsig=$SCHNORRSIG --enable-jni=$JNI $EXTRAFLAGS $USE_HOST + - if [ -n "$BUILD" ]; then make -j2 $BUILD; fi + - # travis_wait extends the 10 minutes without output allowed (https://docs.travis-ci.com/user/common-build-problems/#build-times-out-because-no-output-was-received) + - # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (http://valgrind.org/docs/manual/manual-core.html) + - if [ -n "$VALGRIND" ]; then + make -j2 && + travis_wait 30 valgrind --error-exitcode=42 ./tests 16 && + travis_wait 30 valgrind --error-exitcode=42 ./exhaustive_tests; + fi + +after_script: + - cat ./tests.log + - cat ./exhaustive_tests.log diff --git a/Makefile.am b/Makefile.am index 9e5b7dcce0ab9..87076d134586f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,7 @@ else JNI_LIB = endif include_HEADERS = include/secp256k1.h +include_HEADERS += include/secp256k1_preallocated.h noinst_HEADERS = noinst_HEADERS += src/scalar.h noinst_HEADERS += src/scalar_4x64.h @@ -150,11 +151,11 @@ endif endif if USE_ECMULT_STATIC_PRECOMPUTATION -CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) +CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -I$(builddir)/src gen_context_OBJECTS = gen_context.o gen_context_BIN = gen_context$(BUILD_EXEEXT) -gen_%.o: src/gen_%.c +gen_%.o: src/gen_%.c src/libsecp256k1-config.h $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@ $(gen_context_BIN): $(gen_context_OBJECTS) @@ -177,6 +178,10 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif +if ENABLE_MODULE_SCHNORRSIG +include src/modules/schnorrsig/Makefile.am.include +endif + if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif diff --git a/README.md b/README.md index 8cd344ea81232..5603d7b0ee429 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Implementation details * Extensive testing infrastructure. * Structured to facilitate review and analysis. * Intended to be portable to any system with a C89 compiler and uint64_t support. + * No use of floating types, except in benchmarks. * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") * Field operations * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). @@ -45,8 +46,10 @@ Implementation details * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. * Point multiplication for signing * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. - * Access the table with branch-free conditional moves so memory access is uniform. - * No data-dependent branches + * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains) + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * Optional runtime blinding which attempts to frustrate differential power analysis. * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. Build steps @@ -57,5 +60,19 @@ libsecp256k1 is built using autotools: $ ./autogen.sh $ ./configure $ make - $ ./tests + $ make check $ sudo make install # optional + +Exhaustive tests +----------- + + $ ./exhaustive_tests + +With valgrind, you might need to increase the max stack size: + + $ valgrind --max-stackframe=2500000 ./exhaustive_tests + +Reporting a vulnerability +------------ + +See [SECURITY.md](SECURITY.md) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..0e4d588030274 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Reporting a Vulnerability + +To report security issues send an email to secp256k1-security@bitcoincore.org (not for support). + +The following keys may be used to communicate sensitive information to developers: + +| Name | Fingerprint | +|------|-------------| +| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 | +| Andrew Poelstra | 699A 63EF C17A D3A9 A34C FFC0 7AD0 A91C 40BD 0091 | +| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 | + +You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys ""` Ensure that you put quotes around fingerprints containing spaces. diff --git a/configure.ac b/configure.ac index 3b7a328c8af0a..1048a83852105 100644 --- a/configure.ac +++ b/configure.ac @@ -85,42 +85,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], ]) AC_ARG_ENABLE(benchmark, - AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]), + AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [use_benchmark=$enableval], [use_benchmark=yes]) AC_ARG_ENABLE(coverage, - AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), [enable_coverage=$enableval], [enable_coverage=no]) AC_ARG_ENABLE(tests, - AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), + AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [use_tests=$enableval], [use_tests=yes]) AC_ARG_ENABLE(openssl_tests, - AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), + AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests [default=auto]]), [enable_openssl_tests=$enableval], [enable_openssl_tests=auto]) AC_ARG_ENABLE(experimental, - AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [use_experimental=$enableval], [use_experimental=no]) AC_ARG_ENABLE(exhaustive_tests, - AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]), + AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [use_exhaustive_tests=$enableval], [use_exhaustive_tests=yes]) AC_ARG_ENABLE(endomorphism, - AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), + AS_HELP_STRING([--enable-endomorphism],[enable endomorphism [default=no]]), [use_endomorphism=$enableval], [use_endomorphism=no]) AC_ARG_ENABLE(ecmult_static_precomputation, - AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), + AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing [default=auto]]), [use_ecmult_static_precomputation=$enableval], [use_ecmult_static_precomputation=auto]) @@ -129,34 +129,61 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) +AC_ARG_ENABLE(module_schnorrsig, + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), + [enable_module_schnorrsig=$enableval], + [enable_module_schnorrsig=no]) + AC_ARG_ENABLE(module_recovery, - AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), + AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [enable_module_recovery=$enableval], [enable_module_recovery=no]) +AC_ARG_ENABLE(external_default_callbacks, + AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), + [use_external_default_callbacks=$enableval], + [use_external_default_callbacks=no]) + AC_ARG_ENABLE(jni, - AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]), + AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni [default=no]]), [use_jni=$enableval], [use_jni=no]) AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], -[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) +[finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto]) AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], -[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto]) +[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto]) AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], -[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) - -AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] -[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) +[scalar implementation to use [default=auto]])],[req_scalar=$withval], [req_scalar=auto]) + +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto], +[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto]) + +AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto], +[window size for ecmult precomputation for verification, specified as integer in range [2..24].] +[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.] +[The table will store 2^(SIZE-2) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.] +[If the endomorphism optimization is enabled, two tables of this size are used instead of only one.] +["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]] +)], +[req_ecmult_window=$withval], [req_ecmult_window=auto]) + +AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision=2|4|8|auto], +[Precision bits to tune the precomputed table size for signing.] +[The size of the table is 32kB for 2 bits, 64kB for 4 bits, 512kB for 8 bits of precision.] +[A larger table size usually results in possible faster signing.] +["auto" is a reasonable setting for desktop machines (currently 4). [default=auto]] +)], +[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto]) AC_CHECK_TYPES([__int128]) if test x"$enable_coverage" = x"yes"; then AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) CFLAGS="$CFLAGS -O0 --coverage" - LDFLAGS="--coverage" + LDFLAGS="$LDFLAGS --coverage" else CFLAGS="$CFLAGS -O3" fi @@ -387,6 +414,44 @@ case $set_scalar in ;; esac +#set ecmult window size +if test x"$req_ecmult_window" = x"auto"; then + set_ecmult_window=15 +else + set_ecmult_window=$req_ecmult_window +fi + +error_window_size=['window size for ecmult precomputation not an integer in range [2..24] or "auto"'] +case $set_ecmult_window in +''|*[[!0-9]]*) + # no valid integer + AC_MSG_ERROR($error_window_size) + ;; +*) + if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then + # not in range + AC_MSG_ERROR($error_window_size) + fi + AC_DEFINE_UNQUOTED(ECMULT_WINDOW_SIZE, $set_ecmult_window, [Set window size for ecmult precomputation]) + ;; +esac + +#set ecmult gen precision +if test x"$req_ecmult_gen_precision" = x"auto"; then + set_ecmult_gen_precision=4 +else + set_ecmult_gen_precision=$req_ecmult_gen_precision +fi + +case $set_ecmult_gen_precision in +2|4|8) + AC_DEFINE_UNQUOTED(ECMULT_GEN_PREC_BITS, $set_ecmult_gen_precision, [Set ecmult gen precision bits]) + ;; +*) + AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"']) + ;; +esac + if test x"$use_tests" = x"yes"; then SECP_OPENSSL_CHECK if test x"$has_openssl_ec" = x"yes"; then @@ -452,6 +517,10 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi +if test x"$enable_module_schnorrsig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) +fi + if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi @@ -462,16 +531,24 @@ if test x"$use_external_asm" = x"yes"; then AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) fi +if test x"$use_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 + if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([******]) AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_schnorrsig" = x"yes"; then + AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -490,6 +567,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) @@ -504,21 +582,25 @@ AC_OUTPUT echo echo "Build Options:" -echo " with endomorphism = $use_endomorphism" -echo " with ecmult precomp = $set_precomp" -echo " with jni = $use_jni" -echo " with benchmarks = $use_benchmark" -echo " with coverage = $enable_coverage" -echo " module ecdh = $enable_module_ecdh" -echo " module recovery = $enable_module_recovery" +echo " with endomorphism = $use_endomorphism" +echo " with ecmult precomp = $set_precomp" +echo " with external callbacks = $use_external_default_callbacks" +echo " with jni = $use_jni" +echo " with benchmarks = $use_benchmark" +echo " with coverage = $enable_coverage" +echo " module ecdh = $enable_module_ecdh" +echo " module recovery = $enable_module_recovery" +echo " module schnorrsig = $enable_module_schnorrsig" echo -echo " asm = $set_asm" -echo " bignum = $set_bignum" -echo " field = $set_field" -echo " scalar = $set_scalar" +echo " asm = $set_asm" +echo " bignum = $set_bignum" +echo " field = $set_field" +echo " scalar = $set_scalar" +echo " ecmult window size = $set_ecmult_window" +echo " ecmult gen prec. bits = $set_ecmult_gen_precision" echo -echo " CC = $CC" -echo " CFLAGS = $CFLAGS" -echo " CPPFLAGS = $CPPFLAGS" -echo " LDFLAGS = $LDFLAGS" +echo " CC = $CC" +echo " CFLAGS = $CFLAGS" +echo " CPPFLAGS = $CPPFLAGS" +echo " LDFLAGS = $LDFLAGS" echo diff --git a/contrib/lax_der_parsing.c b/contrib/lax_der_parsing.c index 5b141a99481c7..e177a0562dd2d 100644 --- a/contrib/lax_der_parsing.c +++ b/contrib/lax_der_parsing.c @@ -32,7 +32,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; - if (pos + lenbyte > inputlen) { + if (lenbyte > inputlen - pos) { return 0; } pos += lenbyte; @@ -51,7 +51,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; - if (pos + lenbyte > inputlen) { + if (lenbyte > inputlen - pos) { return 0; } while (lenbyte > 0 && input[pos] == 0) { @@ -89,7 +89,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; - if (pos + lenbyte > inputlen) { + if (lenbyte > inputlen - pos) { return 0; } while (lenbyte > 0 && input[pos] == 0) { diff --git a/include/secp256k1.h b/include/secp256k1.h index 43af09c330de4..50ba0993dfaa4 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -33,9 +33,10 @@ extern "C" { * verification). * * A constructed context can safely be used from multiple threads - * simultaneously, but API call that take a non-const pointer to a context + * simultaneously, but API calls that take a non-const pointer to a context * need exclusive access to it. In particular this is the case for - * secp256k1_context_destroy and secp256k1_context_randomize. + * secp256k1_context_destroy, secp256k1_context_preallocated_destroy, + * and secp256k1_context_randomize. * * Regarding randomization, either do it once at creation time (in which case * you do not need any locking for the other calls), or use a read-write lock. @@ -163,12 +164,13 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) #define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) -/** Flags to pass to secp256k1_context_create. */ +/** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and + * secp256k1_context_preallocated_create. */ #define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) #define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) #define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) -/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */ +/** Flag to pass to secp256k1_ec_pubkey_serialize. */ #define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) #define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) @@ -186,7 +188,11 @@ typedef int (*secp256k1_nonce_function)( */ SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp; -/** Create a secp256k1 context object. +/** Create a secp256k1 context object (in dynamically allocated memory). + * + * This function uses malloc to allocate memory. It is guaranteed that malloc is + * called at most once for every call of this function. If you need to avoid dynamic + * memory allocation entirely, see the functions in secp256k1_preallocated.h. * * Returns: a newly created context object. * In: flags: which parts of the context to initialize. @@ -197,7 +203,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create( unsigned int flags ) SECP256K1_WARN_UNUSED_RESULT; -/** Copies a secp256k1 context object. +/** Copy a secp256k1 context object (into dynamically allocated memory). + * + * This function uses malloc to allocate memory. It is guaranteed that malloc is + * called at most once for every call of this function. If you need to avoid dynamic + * memory allocation entirely, see the functions in secp256k1_preallocated.h. * * Returns: a newly created context object. * Args: ctx: an existing context to copy (cannot be NULL) @@ -206,10 +216,18 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone( const secp256k1_context* ctx ) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; -/** Destroy a secp256k1 context object. +/** Destroy a secp256k1 context object (created in dynamically allocated memory). * * The context pointer may not be used afterwards. - * Args: ctx: an existing context to destroy (cannot be NULL) + * + * The context to destroy must have been created using secp256k1_context_create + * or secp256k1_context_clone. If the context has instead been created using + * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the + * behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must + * be used instead. + * + * Args: ctx: an existing context to destroy, constructed using + * secp256k1_context_create or secp256k1_context_clone */ SECP256K1_API void secp256k1_context_destroy( secp256k1_context* ctx @@ -229,11 +247,28 @@ SECP256K1_API void secp256k1_context_destroy( * to cause a crash, though its return value and output arguments are * undefined. * + * When this function has not been called (or called with fn==NULL), then the + * default handler will be used. The library provides a default handler which + * writes the message to stderr and calls abort. This default handler can be + * replaced at link time if the preprocessor macro + * USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build + * has been configured with --enable-external-default-callbacks. Then the + * following two symbols must be provided to link against: + * - void secp256k1_default_illegal_callback_fn(const char* message, void* data); + * - void secp256k1_default_error_callback_fn(const char* message, void* data); + * The library can call these default handlers even before a proper callback data + * pointer could have been set using secp256k1_context_set_illegal_callback or + * secp256k1_context_set_error_callback, e.g., when the creation of a context + * fails. In this case, the corresponding default handler will be called with + * the data pointer argument set to NULL. + * * Args: ctx: an existing context object (cannot be NULL) * In: fun: a pointer to a function to call when an illegal argument is - * passed to the API, taking a message and an opaque pointer - * (NULL restores a default handler that calls abort). + * passed to the API, taking a message and an opaque pointer. + * (NULL restores the default handler.) * data: the opaque pointer to pass to fun above. + * + * See also secp256k1_context_set_error_callback. */ SECP256K1_API void secp256k1_context_set_illegal_callback( secp256k1_context* ctx, @@ -253,9 +288,12 @@ SECP256K1_API void secp256k1_context_set_illegal_callback( * * Args: ctx: an existing context object (cannot be NULL) * In: fun: a pointer to a function to call when an internal error occurs, - * taking a message and an opaque pointer (NULL restores a default - * handler that calls abort). + * taking a message and an opaque pointer (NULL restores the + * default handler, see secp256k1_context_set_illegal_callback + * for details). * data: the opaque pointer to pass to fun above. + * + * See also secp256k1_context_set_illegal_callback. */ SECP256K1_API void secp256k1_context_set_error_callback( secp256k1_context* ctx, @@ -267,21 +305,24 @@ SECP256K1_API void secp256k1_context_set_error_callback( * * Returns: a newly created scratch space. * Args: ctx: an existing context object (cannot be NULL) - * In: max_size: maximum amount of memory to allocate + * In: size: amount of memory to be available as scratch space. Some extra + * (<100 bytes) will be allocated for extra accounting. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create( const secp256k1_context* ctx, - size_t max_size + size_t size ) SECP256K1_ARG_NONNULL(1); /** Destroy a secp256k1 scratch space. * * The pointer may not be used afterwards. - * Args: scratch: space to destroy + * Args: ctx: a secp256k1 context object. + * scratch: space to destroy */ SECP256K1_API void secp256k1_scratch_space_destroy( + const secp256k1_context* ctx, secp256k1_scratch_space* scratch -); +) SECP256K1_ARG_NONNULL(1); /** Parse a variable-length public key into the pubkey object. * @@ -482,6 +523,18 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; +/** An implementation of the nonce generation function as defined in BIP-schnorr. + * + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. If the data pointer is NULL and this function is used in + * schnorrsig_sign, it produces BIP-schnorr compliant signatures. + * When this function is used in ecdsa_sign, it generates a nonce using an + * analogue of the bip-schnorr nonce generation algorithm, but with tag + * "BIPSchnorrNULL" instead of "BIPSchnorrDerive". + * The attempt argument must be 0 or the function will fail and return 0. + */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr; + /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; @@ -605,7 +658,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. * Args: ctx: pointer to a context object initialized for validation * (cannot be NULL). - * In/Out: pubkey: pointer to a public key obkect. + * In/Out: pubkey: pointer to a public key object. * In: tweak: pointer to a 32-byte tweak. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( @@ -636,7 +689,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( * contexts not initialized for signing; then it will have no effect and return 1. * * You should call this after secp256k1_context_create or - * secp256k1_context_clone, and may call this repeatedly afterwards. + * secp256k1_context_clone (and secp256k1_context_preallocated_create or + * secp256k1_context_clone, resp.), and you may call this repeatedly afterwards. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( secp256k1_context* ctx, @@ -659,6 +713,170 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( size_t n ) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Opaque data structure that holds a parsed and valid "x-only" public key. + * An x-only pubkey encodes a point whose Y coordinate is square. It is + * serialized using only its X coordinate (32 bytes). See bip-schnorr for more + * information about x-only pubkeys. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_xonly_pubkey_serialize and + * secp256k1_xonly_pubkey_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_xonly_pubkey; + +/** Parse a 32-byte public key into a xonly_pubkey object. + * + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * + * Args: ctx: a secp256k1 context object. + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is undefined. + * In: input32: pointer to a serialized xonly public key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey* pubkey, + const unsigned char *input32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + + +/** Serialize an xonly_pubkey object into a 32-byte sequence. + * + * Returns: 1 always. + * + * Args: ctx: a secp256k1 context object. + * Out: output32: a pointer to a 32-byte array to place the serialized key in. + * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an + * initialized public key. + */ +SECP256K1_API int secp256k1_xonly_pubkey_serialize( + const secp256k1_context* ctx, + unsigned char *output32, + const secp256k1_xonly_pubkey* pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute the xonly public key for a secret key. Same as ec_pubkey_create, but + * for xonly public keys. + * + * Returns: 1 if secret was valid, public key stores + * 0 if secret was invalid, try again + * + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: pubkey: pointer to the created xonly public key (cannot be NULL) + * In: seckey: pointer to a 32-byte private key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_create( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey *pubkey, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey. + * + * Returns: 1 if the public key was successfully converted + * 0 otherwise + * + * Args: ctx: pointer to a context object + * Out: xonly_pubkey: pointer to an x-only public key object for placing the + * converted public key (cannot be NULL) + * is_negated: pointer to an integer that will be set to 1 if the point + * encoded by `xonly_pubkey` is the negation of `pubkey` + * and set to 0 otherwise. (can be NULL) + * In: pubkey: pointer to a public key that is converted (cannot be + * NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey *xonly_pubkey, + int *is_negated, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Tweak the secret key of an x-only pubkey by adding a tweak to it. The public + * key of the resulting secret key will be the same as the output of + * secp256k1_xonly_pubkey_tweak_add called with the same tweak and corresponding + * input public key. + * + * If the public key corresponds to a point with square Y, tweak32 is added to + * the seckey (modulo the group order). Otherwise, tweak32 is added to the + * negation of the seckey (modulo the group order). + * + * Returns: 1 if the tweak was successfully added to seckey + * 0 if the tweak was out of range or the resulting secret key would be + * invalid (only when the tweak is the complement of the secret key) or + * seckey is 0. + * + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * In/Out: seckey: pointer to a 32-byte secret key + * In: tweak32: pointer to a 32-byte tweak + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_seckey_tweak_add( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak an x-only public key (in place) by adding tweak times the generator to it. + * + * Because the resulting point may have a non-square Y coordinate, it may not + * be representable by an x-only pubkey. Instead, `output_pubkey` will be set + * to the negation of that point. Therefore this function outputs `is_negated` + * which is required for `xonly_pubkey_tweak_test`. + * + * Returns: 1 if tweak times the generator was successfully added to pubkey + * 0 if the tweak was out of range or the resulting public key would be + * invalid (only when the tweak is the complement of the corresponding + * private key). + * + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL) + * In/Out: pubkey: pointer to an x-only public key object to apply the tweak to + * (cannot be NULL) + * Out: is_negated: pointer to an integer that will be set to 1 if + * `output_pubkey` is the negation of the point that resulted + * from adding the tweak. (cannot be NULL) + * In: tweak32: pointer to a 32-byte tweak (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey *pubkey, + int *is_negated, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Tests that output_pubkey and is_negated are the result of calling + * secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32. Note + * that this alone does _not_ verify that output_pubkey is a commitment. If the + * tweak is not chosen in a specific way, the output_pubkey can easily be the + * result of a different internal_pubkey and tweak. + * + * Returns: 1 if output_pubkey is the result of tweaking the internal_pubkey with + * tweak32 + * 0 otherwise + * + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL) + * In: output_pubkey: pointer to a public key object (cannot be NULL) + * is_negated: 1 if `output_pubkey is the negation of the point that + * resulted from adding the tweak and 0 otherwise. + * internal_pubkey: pointer to an x-only public key object to apply the + * tweak to (cannot be NULL) + * tweak32: pointer to a 32-byte tweak (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_test( + const secp256k1_context* ctx, + const secp256k1_xonly_pubkey *output_pubkey, + int is_negated, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + #ifdef __cplusplus } #endif diff --git a/include/secp256k1_preallocated.h b/include/secp256k1_preallocated.h new file mode 100644 index 0000000000000..a9ae15d5ae8d8 --- /dev/null +++ b/include/secp256k1_preallocated.h @@ -0,0 +1,128 @@ +#ifndef SECP256K1_PREALLOCATED_H +#define SECP256K1_PREALLOCATED_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* The module provided by this header file is intended for settings in which it + * is not possible or desirable to rely on dynamic memory allocation. It provides + * functions for creating, cloning, and destroying secp256k1 context objects in a + * contiguous fixed-size block of memory provided by the caller. + * + * Context objects created by functions in this module can be used like contexts + * objects created by functions in secp256k1.h, i.e., they can be passed to any + * API function that expects a context object (see secp256k1.h for details). The + * only exception is that context objects created by functions in this module + * must be destroyed using secp256k1_context_preallocated_destroy (in this + * module) instead of secp256k1_context_destroy (in secp256k1.h). + * + * It is guaranteed that functions in this module will not call malloc or its + * friends realloc, calloc, and free. + */ + +/** Determine the memory size of a secp256k1 context object to be created in + * caller-provided memory. + * + * The purpose of this function is to determine how much memory must be provided + * to secp256k1_context_preallocated_create. + * + * Returns: the required size of the caller-provided memory block + * In: flags: which parts of the context to initialize. + */ +SECP256K1_API size_t secp256k1_context_preallocated_size( + unsigned int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Create a secp256k1 context object in caller-provided memory. + * + * The caller must provide a pointer to a rewritable contiguous block of memory + * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably + * aligned to hold an object of any type. + * + * The block of memory is exclusively owned by the created context object during + * the lifetime of this context object, which begins with the call to this + * function and ends when a call to secp256k1_context_preallocated_destroy + * (which destroys the context object again) returns. During the lifetime of the + * context object, the caller is obligated not to access this block of memory, + * i.e., the caller may not read or write the memory, e.g., by copying the memory + * contents to a different location or trying to create a second context object + * in the memory. In simpler words, the prealloc pointer (or any pointer derived + * from it) should not be used during the lifetime of the context object. + * + * Returns: a newly created context object. + * In: prealloc: a pointer to a rewritable contiguous block of memory of + * size at least secp256k1_context_preallocated_size(flags) + * bytes, as detailed above (cannot be NULL) + * flags: which parts of the context to initialize. + * + * See also secp256k1_context_randomize (in secp256k1.h) + * and secp256k1_context_preallocated_destroy. + */ +SECP256K1_API secp256k1_context* secp256k1_context_preallocated_create( + void* prealloc, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Determine the memory size of a secp256k1 context object to be copied into + * caller-provided memory. + * + * Returns: the required size of the caller-provided memory block. + * In: ctx: an existing context to copy (cannot be NULL) + */ +SECP256K1_API size_t secp256k1_context_preallocated_clone_size( + const secp256k1_context* ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Copy a secp256k1 context object into caller-provided memory. + * + * The caller must provide a pointer to a rewritable contiguous block of memory + * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably + * aligned to hold an object of any type. + * + * The block of memory is exclusively owned by the created context object during + * the lifetime of this context object, see the description of + * secp256k1_context_preallocated_create for details. + * + * Returns: a newly created context object. + * Args: ctx: an existing context to copy (cannot be NULL) + * In: prealloc: a pointer to a rewritable contiguous block of memory of + * size at least secp256k1_context_preallocated_size(flags) + * bytes, as detailed above (cannot be NULL) + */ +SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone( + const secp256k1_context* ctx, + void* prealloc +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object that has been created in + * caller-provided memory. + * + * The context pointer may not be used afterwards. + * + * The context to destroy must have been created using + * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone. + * If the context has instead been created using secp256k1_context_create or + * secp256k1_context_clone, the behaviour is undefined. In that case, + * secp256k1_context_destroy must be used instead. + * + * If required, it is the responsibility of the caller to deallocate the block + * of memory properly after this function returns, e.g., by calling free on the + * preallocated pointer given to secp256k1_context_preallocated_create or + * secp256k1_context_preallocated_clone. + * + * Args: ctx: an existing context to destroy, constructed using + * secp256k1_context_preallocated_create or + * secp256k1_context_preallocated_clone (cannot be NULL) + */ +SECP256K1_API void secp256k1_context_preallocated_destroy( + secp256k1_context* ctx +); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_PREALLOCATED_H */ diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h new file mode 100644 index 0000000000000..ad1f22da87430 --- /dev/null +++ b/include/secp256k1_schnorrsig.h @@ -0,0 +1,125 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements a variant of Schnorr signatures compliant with + * BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki). + */ + +/** Opaque data structure that holds a parsed Schnorr signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the `secp256k1_schnorrsig_serialize` and + * `secp256k1_schnorrsig_parse` functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_schnorrsig; + +/** Serialize a Schnorr signature. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: out64: pointer to a 64-byte array to store the serialized signature + * In: sig: pointer to the signature + * + * See secp256k1_schnorrsig_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_schnorrsig_serialize( + const secp256k1_context* ctx, + unsigned char *out64, + const secp256k1_schnorrsig* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a Schnorr signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in64: pointer to the 64-byte signature to be parsed + * + * The signature is serialized in the form R||s, where R is a 32-byte public + * key (X coordinate only; the Y coordinate is considered to be the unique + * Y coordinate satisfying the curve equation that is square) + * and s is a 32-byte big-endian scalar. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_schnorrsig_parse( + const secp256k1_context* ctx, + secp256k1_schnorrsig* sig, + const unsigned char *in64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Create a Schnorr signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to the returned signature (cannot be NULL) + * In: msg32: the 32-byte message being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context* ctx, + secp256k1_schnorrsig *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify a Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message being verified (cannot be NULL) + * pubkey: pointer to an x-only public key to verify with (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( + const secp256k1_context* ctx, + const secp256k1_schnorrsig *sig, + const unsigned char *msg32, + const secp256k1_xonly_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verifies a set of Schnorr signatures. + * + * Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0. + * + * Args: ctx: a secp256k1 context object, initialized for verification. + * scratch: scratch space used for the multiexponentiation + * In: sig: array of pointers to signatures, or NULL if there are no signatures + * msg32: array of pointers to messages, or NULL if there are no signatures + * pk: array of pointers to x-only public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays. Must be below the + * minimum of 2^31 and SIZE_MAX/2. Must be 0 if above arrays are NULL. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_schnorrsig *const *sig, + const unsigned char *const *msg32, + const secp256k1_xonly_pubkey *const *pk, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/asm/field_10x26_arm.s b/src/asm/field_10x26_arm.s index 5a9cc3ffcfdaf..9a5bd06721778 100644 --- a/src/asm/field_10x26_arm.s +++ b/src/asm/field_10x26_arm.s @@ -16,15 +16,9 @@ Note: */ .syntax unified - .arch armv7-a @ eabi attributes - see readelf -A - .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes - .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no - .eabi_attribute 10, 0 @ Tag_FP_arch = none .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP - .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed - .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 .text @ Field constants diff --git a/src/basic-config.h b/src/basic-config.h index fc588061ca40c..e9be39d4ca4d4 100644 --- a/src/basic-config.h +++ b/src/basic-config.h @@ -10,7 +10,10 @@ #ifdef USE_BASIC_CONFIG #undef USE_ASM_X86_64 +#undef USE_ECMULT_STATIC_PRECOMPUTATION #undef USE_ENDOMORPHISM +#undef USE_EXTERNAL_ASM +#undef USE_EXTERNAL_DEFAULT_CALLBACKS #undef USE_FIELD_10X26 #undef USE_FIELD_5X52 #undef USE_FIELD_INV_BUILTIN @@ -21,12 +24,14 @@ #undef USE_SCALAR_8X32 #undef USE_SCALAR_INV_BUILTIN #undef USE_SCALAR_INV_NUM +#undef ECMULT_WINDOW_SIZE #define USE_NUM_NONE 1 #define USE_FIELD_INV_BUILTIN 1 #define USE_SCALAR_INV_BUILTIN 1 #define USE_FIELD_10X26 1 #define USE_SCALAR_8X32 1 +#define ECMULT_WINDOW_SIZE 15 #endif /* USE_BASIC_CONFIG */ diff --git a/src/bench_ecmult.c b/src/bench_ecmult.c index 6d0ed1f4364e4..7b5d185dce606 100644 --- a/src/bench_ecmult.c +++ b/src/bench_ecmult.c @@ -64,7 +64,7 @@ static void bench_ecmult(void* arg) { size_t iter; for (iter = 0; iter < iters; ++iter) { - data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g); + data->ecmult_multi(&data->ctx->error_callback, &data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g); data->offset1 = (data->offset1 + count) % POINTS; data->offset2 = (data->offset2 + count - 1) % POINTS; } @@ -154,7 +154,7 @@ int main(int argc, char **argv) { } else if(have_flag(argc, argv, "simple")) { printf("Using simple algorithm:\n"); data.ecmult_multi = secp256k1_ecmult_multi_var; - secp256k1_scratch_space_destroy(data.scratch); + secp256k1_scratch_space_destroy(data.ctx, data.scratch); data.scratch = NULL; } else { fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]); @@ -193,10 +193,10 @@ int main(int argc, char **argv) { run_test(&data, i << p, 1); } } - secp256k1_context_destroy(data.ctx); if (data.scratch != NULL) { - secp256k1_scratch_space_destroy(data.scratch); + secp256k1_scratch_space_destroy(data.ctx, data.scratch); } + secp256k1_context_destroy(data.ctx); free(data.scalars); free(data.pubkeys); free(data.seckeys); diff --git a/src/bench_internal.c b/src/bench_internal.c index 9071724331e1e..9159c0a7c3a95 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -57,12 +57,13 @@ void bench_setup(void* arg) { } void bench_scalar_add(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } + CHECK(j <= 2000000); } void bench_scalar_negate(void* arg) { @@ -94,35 +95,37 @@ void bench_scalar_mul(void* arg) { #ifdef USE_ENDOMORPHISM void bench_scalar_split(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_scalar l, r; - secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + secp256k1_scalar_split_lambda(&data->scalar_x, &data->scalar_y, &data->scalar_x); + j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } + CHECK(j <= 20000); } #endif void bench_scalar_inverse(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000; i++) { secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } + CHECK(j <= 2000); } void bench_scalar_inverse_var(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000; i++) { secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } + CHECK(j <= 2000); } void bench_field_normalize(void* arg) { @@ -182,15 +185,16 @@ void bench_field_inverse_var(void* arg) { } void bench_field_sqrt(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; secp256k1_fe t; for (i = 0; i < 20000; i++) { t = data->fe_x; - secp256k1_fe_sqrt(&data->fe_x, &t); + j += secp256k1_fe_sqrt(&data->fe_x, &t); secp256k1_fe_add(&data->fe_x, &data->fe_y); } + CHECK(j <= 20000); } void bench_group_double_var(void* arg) { @@ -230,32 +234,37 @@ void bench_group_add_affine_var(void* arg) { } void bench_group_jacobi_var(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_gej_has_quad_y_var(&data->gej_x); + j += secp256k1_gej_has_quad_y_var(&data->gej_x); } + CHECK(j == 20000); } void bench_ecmult_wnaf(void* arg) { - int i; + int i, bits = 0, overflow = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); + overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } + CHECK(overflow >= 0); + CHECK(bits <= 256*20000); } void bench_wnaf_const(void* arg) { - int i; + int i, bits = 0, overflow = 0; bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { - secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + bits += secp256k1_wnaf_const(data->wnaf, &data->scalar_x, WINDOW_A, 256); + overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } + CHECK(overflow >= 0); + CHECK(bits <= 256*20000); } @@ -312,7 +321,7 @@ void bench_context_sign(void* arg) { #ifndef USE_NUM_NONE void bench_num_jacobi(void* arg) { - int i; + int i, j = 0; bench_inv *data = (bench_inv*)arg; secp256k1_num nx, norder; @@ -321,8 +330,9 @@ void bench_num_jacobi(void* arg) { secp256k1_scalar_get_num(&norder, &data->scalar_y); for (i = 0; i < 200000; i++) { - secp256k1_num_jacobi(&nx, &norder); + j += secp256k1_num_jacobi(&nx, &norder); } + CHECK(j <= 200000); } #endif diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c new file mode 100644 index 0000000000000..b36499f851a32 --- /dev/null +++ b/src/bench_schnorrsig.c @@ -0,0 +1,127 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "util.h" +#include "bench.h" + +#define MAX_SIGS (32768) + +typedef struct { + secp256k1_context *ctx; + secp256k1_scratch_space *scratch; + size_t n; + const unsigned char **pk; + const secp256k1_schnorrsig **sigs; + const unsigned char **msgs; +} bench_schnorrsig_data; + +void bench_schnorrsig_sign(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i; + unsigned char sk[32] = "benchmarkexample secrettemplate"; + unsigned char msg[32] = "benchmarkexamplemessagetemplate"; + secp256k1_schnorrsig sig; + + for (i = 0; i < 1000; i++) { + msg[0] = i; + msg[1] = i >> 8; + sk[0] = i; + sk[1] = i >> 8; + CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, msg, sk, NULL, NULL)); + } +} + +void bench_schnorrsig_verify(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i; + + for (i = 0; i < 1000; i++) { + secp256k1_xonly_pubkey pk; + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1); + CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk)); + } +} + +void bench_schnorrsig_verify_n(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i, j; + const secp256k1_xonly_pubkey **pk = (const secp256k1_xonly_pubkey **)malloc(data->n * sizeof(*pk)); + + CHECK(pk != NULL); + for (j = 0; j < MAX_SIGS/data->n; j++) { + for (i = 0; i < data->n; i++) { + secp256k1_xonly_pubkey *pk_nonconst = (secp256k1_xonly_pubkey *)malloc(sizeof(*pk_nonconst)); + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, pk_nonconst, data->pk[i]) == 1); + pk[i] = pk_nonconst; + } + CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n)); + for (i = 0; i < data->n; i++) { + free((void *)pk[i]); + } + } + free(pk); +} + +int main(void) { + size_t i; + bench_schnorrsig_data data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024); + data.pk = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *)); + data.msgs = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *)); + data.sigs = (const secp256k1_schnorrsig **)malloc(MAX_SIGS * sizeof(secp256k1_schnorrsig *)); + + for (i = 0; i < MAX_SIGS; i++) { + unsigned char sk[32]; + unsigned char *msg = (unsigned char *)malloc(32); + secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig)); + unsigned char *pk_char = (unsigned char *)malloc(32); + secp256k1_xonly_pubkey pk; + msg[0] = sk[0] = i; + msg[1] = sk[1] = i >> 8; + msg[2] = sk[2] = i >> 16; + msg[3] = sk[3] = i >> 24; + memset(&msg[4], 'm', 28); + memset(&sk[4], 's', 28); + + data.pk[i] = pk_char; + data.msgs[i] = msg; + data.sigs[i] = sig; + + CHECK(secp256k1_xonly_pubkey_create(data.ctx, &pk, sk)); + CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1); + CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, sk, NULL, NULL)); + } + + run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, 1000); + run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, 1000); + for (i = 1; i <= MAX_SIGS; i *= 2) { + char name[64]; + sprintf(name, "schnorrsig_batch_verify_%d", (int) i); + + data.n = i; + run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, MAX_SIGS); + } + + for (i = 0; i < MAX_SIGS; i++) { + free((void *)data.pk[i]); + free((void *)data.msgs[i]); + free((void *)data.sigs[i]); + } + free(data.pk); + free(data.msgs); + free(data.sigs); + + secp256k1_scratch_space_destroy(data.ctx, data.scratch); + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/ecdsa_impl.h b/src/ecdsa_impl.h index c3400042d8393..eb099c87dc83f 100644 --- a/src/ecdsa_impl.h +++ b/src/ecdsa_impl.h @@ -46,68 +46,73 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL ); -static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { - int lenleft, b1; - size_t ret = 0; +static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) { + size_t lenleft; + unsigned char b1; + VERIFY_CHECK(len != NULL); + *len = 0; if (*sigp >= sigend) { - return -1; + return 0; } b1 = *((*sigp)++); if (b1 == 0xFF) { /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ - return -1; + return 0; } if ((b1 & 0x80) == 0) { /* X.690-0207 8.1.3.4 short form length octets */ - return b1; + *len = b1; + return 1; } if (b1 == 0x80) { /* Indefinite length is not allowed in DER. */ - return -1; + return 0; } /* X.690-207 8.1.3.5 long form length octets */ - lenleft = b1 & 0x7F; - if (lenleft > sigend - *sigp) { - return -1; + lenleft = b1 & 0x7F; /* lenleft is at least 1 */ + if (lenleft > (size_t)(sigend - *sigp)) { + return 0; } if (**sigp == 0) { /* Not the shortest possible length encoding. */ - return -1; + return 0; } - if ((size_t)lenleft > sizeof(size_t)) { + if (lenleft > sizeof(size_t)) { /* The resulting length would exceed the range of a size_t, so * certainly longer than the passed array size. */ - return -1; + return 0; } while (lenleft > 0) { - ret = (ret << 8) | **sigp; - if (ret + lenleft > (size_t)(sigend - *sigp)) { - /* Result exceeds the length of the passed array. */ - return -1; - } + *len = (*len << 8) | **sigp; (*sigp)++; lenleft--; } - if (ret < 128) { + if (*len > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. */ + return 0; + } + if (*len < 128) { /* Not the shortest possible length encoding. */ - return -1; + return 0; } - return ret; + return 1; } static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { int overflow = 0; unsigned char ra[32] = {0}; - int rlen; + size_t rlen; if (*sig == sigend || **sig != 0x02) { /* Not a primitive integer (X.690-0207 8.3.1). */ return 0; } (*sig)++; - rlen = secp256k1_der_read_len(sig, sigend); - if (rlen <= 0 || (*sig) + rlen > sigend) { + if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) { + return 0; + } + if (rlen == 0 || *sig + rlen > sigend) { /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ return 0; } @@ -123,8 +128,11 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char /* Negative. */ overflow = 1; } - while (rlen > 0 && **sig == 0) { - /* Skip leading zero bytes */ + /* There is at most one leading zero byte: + * if there were two leading zero bytes, we would have failed and returned 0 + * because of excessive 0x00 padding already. */ + if (rlen > 0 && **sig == 0) { + /* Skip leading zero byte */ rlen--; (*sig)++; } @@ -144,18 +152,16 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { const unsigned char *sigend = sig + size; - int rlen; + size_t rlen; if (sig == sigend || *(sig++) != 0x30) { /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ return 0; } - rlen = secp256k1_der_read_len(&sig, sigend); - if (rlen < 0 || sig + rlen > sigend) { - /* Tuple exceeds bounds */ + if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) { return 0; } - if (sig + rlen != sigend) { - /* Garbage after tuple. */ + if (rlen != (size_t)(sigend - sig)) { + /* Tuple exceeds bounds or garage after tuple. */ return 0; } diff --git a/src/ecmult.h b/src/ecmult.h index 3d75a960f4247..c9b198239d889 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -20,10 +20,10 @@ typedef struct { #endif } secp256k1_ecmult_context; +static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); -static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, - const secp256k1_ecmult_context *src, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc); +static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src); static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); @@ -43,6 +43,6 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge * 0 if there is not enough scratch space for a single point or * callback returns 0 */ -static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); +static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); #endif /* SECP256K1_ECMULT_H */ diff --git a/src/ecmult_const.h b/src/ecmult_const.h index d4804b8b68faa..03bb33257d532 100644 --- a/src/ecmult_const.h +++ b/src/ecmult_const.h @@ -10,8 +10,11 @@ #include "scalar.h" #include "group.h" -/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus - * one because we internally sometimes add 2 to the number during the WNAF conversion. */ +/** + * Multiply: R = q*A (in constant-time) + * Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus + * one because we internally sometimes add 2 to the number during the WNAF conversion. + */ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); #endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h index 8411752eb069f..aaa576ada4cc2 100644 --- a/src/ecmult_const_impl.h +++ b/src/ecmult_const_impl.h @@ -48,7 +48,7 @@ * * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 */ -static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) { +static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) { int global_sign; int skew = 0; int word = 0; @@ -59,8 +59,12 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) int flip; int bit; - secp256k1_scalar neg_s; + secp256k1_scalar s; int not_neg_one; + + VERIFY_CHECK(w > 0); + VERIFY_CHECK(size > 0); + /* Note that we cannot handle even numbers by negating them to be odd, as is * done in other implementations, since if our scalars were specified to have * width < 256 for performance reasons, their negations would have width 256 @@ -75,12 +79,13 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) * {1, 2} we want to add to the scalar when ensuring that it's odd. Further * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and * we need to special-case it in this logic. */ - flip = secp256k1_scalar_is_high(&s); + flip = secp256k1_scalar_is_high(scalar); /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ - bit = flip ^ !secp256k1_scalar_is_even(&s); + bit = flip ^ !secp256k1_scalar_is_even(scalar); /* We check for negative one, since adding 2 to it will cause an overflow */ - secp256k1_scalar_negate(&neg_s, &s); - not_neg_one = !secp256k1_scalar_is_one(&neg_s); + secp256k1_scalar_negate(&s, scalar); + not_neg_one = !secp256k1_scalar_is_one(&s); + s = *scalar; secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects * that we added two to it and flipped it. In fact for -1 these operations are @@ -93,7 +98,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) /* 4 */ u_last = secp256k1_scalar_shr_int(&s, w); - while (word * w < size) { + do { int sign; int even; @@ -109,7 +114,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) wnaf[word++] = u_last * global_sign; u_last = u; - } + } while (word * w < size); wnaf[word] = u * global_sign; VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); @@ -132,7 +137,6 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; int i; - secp256k1_scalar sc = *scalar; /* build wnaf representation for q. */ int rsize = size; @@ -140,13 +144,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons if (size > 128) { rsize = 128; /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); - skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128); - skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128); + secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar); + skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128); + skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128); } else #endif { - skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size); + skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size); #ifdef USE_ENDOMORPHISM skew_lam = 0; #endif diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index 7564b7015f0b7..30815e5aa10e7 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -10,28 +10,35 @@ #include "scalar.h" #include "group.h" +#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8 +# error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8." +#endif +#define ECMULT_GEN_PREC_B ECMULT_GEN_PREC_BITS +#define ECMULT_GEN_PREC_G (1 << ECMULT_GEN_PREC_B) +#define ECMULT_GEN_PREC_N (256 / ECMULT_GEN_PREC_B) + typedef struct { /* For accelerating the computation of a*G: * To harden against timing attacks, use the following mechanism: - * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63. - * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where: - * * U_i = U * 2^i (for i=0..62) - * * U_i = U * (1-2^63) (for i=63) - * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0. - * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is - * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63). + * * Break up the multiplicand into groups of PREC_B bits, called n_0, n_1, n_2, ..., n_(PREC_N-1). + * * Compute sum(n_i * (PREC_G)^i * G + U_i, i=0 ... PREC_N-1), where: + * * U_i = U * 2^i, for i=0 ... PREC_N-2 + * * U_i = U * (1-2^(PREC_N-1)), for i=PREC_N-1 + * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0 ... PREC_N-1) = 0. + * For each i, and each of the PREC_G possible values of n_i, (n_i * (PREC_G)^i * G + U_i) is + * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0 ... PREC_N-1). * None of the resulting prec group elements have a known scalar, and neither do any of * the intermediate sums while computing a*G. */ - secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ + secp256k1_ge_storage (*prec)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G]; /* prec[j][i] = (PREC_G)^j * i * G + U_i */ secp256k1_scalar blind; secp256k1_gej initial; } secp256k1_ecmult_gen_context; +static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb); -static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, - const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc); +static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src); static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index d64505dc00107..a1b96393939e2 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_ECMULT_GEN_IMPL_H #define SECP256K1_ECMULT_GEN_IMPL_H +#include "util.h" #include "scalar.h" #include "group.h" #include "ecmult_gen.h" @@ -14,23 +15,32 @@ #ifdef USE_ECMULT_STATIC_PRECOMPUTATION #include "ecmult_static_context.h" #endif + +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = ROUND_TO_ALIGN(sizeof(*((secp256k1_ecmult_gen_context*) NULL)->prec)); +#else + static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = 0; +#endif + static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { ctx->prec = NULL; } -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) { #ifndef USE_ECMULT_STATIC_PRECOMPUTATION - secp256k1_ge prec[1024]; + secp256k1_ge prec[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; secp256k1_gej gj; secp256k1_gej nums_gej; int i, j; + size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; + void* const base = *prealloc; #endif if (ctx->prec != NULL) { return; } #ifndef USE_ECMULT_STATIC_PRECOMPUTATION - ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); + ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])manual_alloc(prealloc, prealloc_size, base, prealloc_size); /* get the generator */ secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); @@ -54,39 +64,39 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx /* compute prec. */ { - secp256k1_gej precj[1024]; /* Jacobian versions of prec. */ + secp256k1_gej precj[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; /* Jacobian versions of prec. */ secp256k1_gej gbase; secp256k1_gej numsbase; - gbase = gj; /* 16^j * G */ + gbase = gj; /* PREC_G^j * G */ numsbase = nums_gej; /* 2^j * nums. */ - for (j = 0; j < 64; j++) { - /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ - precj[j*16] = numsbase; - for (i = 1; i < 16; i++) { - secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); + for (j = 0; j < ECMULT_GEN_PREC_N; j++) { + /* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */ + precj[j*ECMULT_GEN_PREC_G] = numsbase; + for (i = 1; i < ECMULT_GEN_PREC_G; i++) { + secp256k1_gej_add_var(&precj[j*ECMULT_GEN_PREC_G + i], &precj[j*ECMULT_GEN_PREC_G + i - 1], &gbase, NULL); } - /* Multiply gbase by 16. */ - for (i = 0; i < 4; i++) { + /* Multiply gbase by PREC_G. */ + for (i = 0; i < ECMULT_GEN_PREC_B; i++) { secp256k1_gej_double_var(&gbase, &gbase, NULL); } /* Multiply numbase by 2. */ secp256k1_gej_double_var(&numsbase, &numsbase, NULL); - if (j == 62) { + if (j == ECMULT_GEN_PREC_N - 2) { /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ secp256k1_gej_neg(&numsbase, &numsbase); secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); } } - secp256k1_ge_set_all_gej_var(prec, precj, 1024); + secp256k1_ge_set_all_gej_var(prec, precj, ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G); } - for (j = 0; j < 64; j++) { - for (i = 0; i < 16; i++) { - secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); + for (j = 0; j < ECMULT_GEN_PREC_N; j++) { + for (i = 0; i < ECMULT_GEN_PREC_G; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*ECMULT_GEN_PREC_G + i]); } } #else - (void)cb; - ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; + (void)prealloc; + ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])secp256k1_ecmult_static_context; #endif secp256k1_ecmult_gen_blind(ctx, NULL); } @@ -95,27 +105,18 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont return ctx->prec != NULL; } -static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, - const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) { - if (src->prec == NULL) { - dst->prec = NULL; - } else { +static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context *src) { #ifndef USE_ECMULT_STATIC_PRECOMPUTATION - dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); - memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + if (src->prec != NULL) { + /* We cast to void* first to suppress a -Wcast-align warning. */ + dst->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src)); + } #else - (void)cb; - dst->prec = src->prec; + (void)dst, (void)src; #endif - dst->initial = src->initial; - dst->blind = src->blind; - } } static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - free(ctx->prec); -#endif secp256k1_scalar_clear(&ctx->blind); secp256k1_gej_clear(&ctx->initial); ctx->prec = NULL; @@ -132,9 +133,9 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ secp256k1_scalar_add(&gnb, gn, &ctx->blind); add.infinity = 0; - for (j = 0; j < 64; j++) { - bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4); - for (i = 0; i < 16; i++) { + for (j = 0; j < ECMULT_GEN_PREC_N; j++) { + bits = secp256k1_scalar_get_bits(&gnb, j * ECMULT_GEN_PREC_B, ECMULT_GEN_PREC_B); + for (i = 0; i < ECMULT_GEN_PREC_G; i++) { /** This uses a conditional move to avoid any secret data in array indexes. * _Any_ use of secret indexes has been demonstrated to result in timing * sidechannels, even when the cache-line access patterns are uniform. @@ -186,7 +187,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const do { secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); retry = !secp256k1_fe_set_b32(&s, nonce32); - retry |= secp256k1_fe_is_zero(&s); + retry = retry || secp256k1_fe_is_zero(&s); } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ /* Randomize the projection to defend against multiplier sidechannels. */ secp256k1_gej_rescale(&ctx->initial, &s); @@ -195,7 +196,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); secp256k1_scalar_set_b32(&b, nonce32, &retry); /* A blinding value of 0 works, but would undermine the projection hardening. */ - retry |= secp256k1_scalar_is_zero(&b); + retry = retry || secp256k1_scalar_is_zero(&b); } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ secp256k1_rfc6979_hmac_sha256_finalize(&rng); memset(nonce32, 0, 32); diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 1986914a4fd5d..f03fa9469d73b 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -10,6 +10,7 @@ #include #include +#include "util.h" #include "group.h" #include "scalar.h" #include "ecmult.h" @@ -30,16 +31,32 @@ # endif #else /* optimal for 128-bit and 256-bit exponents. */ -#define WINDOW_A 5 -/** larger numbers may result in slightly better performance, at the cost of - exponentially larger precomputed tables. */ -#ifdef USE_ENDOMORPHISM -/** Two tables for window size 15: 1.375 MiB. */ -#define WINDOW_G 15 -#else -/** One table for window size 16: 1.375 MiB. */ -#define WINDOW_G 16 +# define WINDOW_A 5 +/** Larger values for ECMULT_WINDOW_SIZE result in possibly better + * performance at the cost of an exponentially larger precomputed + * table. The exact table size is + * (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes, + * where sizeof(secp256k1_ge_storage) is typically 64 bytes but can + * be larger due to platform-specific padding and alignment. + * If the endomorphism optimization is enabled (USE_ENDOMORMPHSIM) + * two tables of this size are used instead of only one. + */ +# define WINDOW_G ECMULT_WINDOW_SIZE #endif + +/* Noone will ever need more than a window size of 24. The code might + * be correct for larger values of ECMULT_WINDOW_SIZE but this is not + * not tested. + * + * The following limitations are known, and there are probably more: + * If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect + * because the size of the memory object that we allocate (in bytes) + * will not fit in a size_t. + * If WINDOW_G > 31 and int has 32 bits, then the code is incorrect + * because certain expressions will overflow. + */ +#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24 +# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24]. #endif #ifdef USE_ENDOMORPHISM @@ -121,7 +138,7 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec * It only operates on tables sized for WINDOW_A wnaf multiples. * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its * resulting point set to actually affine points, and stores those in pre. - * It operates on tables of any size, but uses heap-allocated temporaries. + * It operates on tables of any size. * * To compute a*P + b*G, we compute a table for P using the first function, * and for G using the second (which requires an inverse, but it only needs to @@ -294,6 +311,13 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp25 } \ } while(0) +static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE = + ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)) +#ifdef USE_ENDOMORPHISM + + ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)) +#endif + ; + static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { ctx->pre_g = NULL; #ifdef USE_ENDOMORPHISM @@ -301,8 +325,10 @@ static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { #endif } -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) { secp256k1_gej gj; + void* const base = *prealloc; + size_t const prealloc_size = SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; if (ctx->pre_g != NULL) { return; @@ -311,7 +337,12 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const /* get the generator */ secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); - ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + { + size_t size = sizeof((*ctx->pre_g)[0]) * ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)); + /* check for overflow */ + VERIFY_CHECK(size / sizeof((*ctx->pre_g)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G))); + ctx->pre_g = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size); + } /* precompute the tables with odd multiples */ secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj); @@ -321,7 +352,10 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_gej g_128j; int i; - ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + size_t size = sizeof((*ctx->pre_g_128)[0]) * ((size_t) ECMULT_TABLE_SIZE(WINDOW_G)); + /* check for overflow */ + VERIFY_CHECK(size / sizeof((*ctx->pre_g_128)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G))); + ctx->pre_g_128 = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size); /* calculate 2^128*generator */ g_128j = gj; @@ -333,22 +367,14 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const #endif } -static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, - const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { - if (src->pre_g == NULL) { - dst->pre_g = NULL; - } else { - size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); - dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); - memcpy(dst->pre_g, src->pre_g, size); +static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) { + if (src->pre_g != NULL) { + /* We cast to void* first to suppress a -Wcast-align warning. */ + dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src)); } #ifdef USE_ENDOMORPHISM - if (src->pre_g_128 == NULL) { - dst->pre_g_128 = NULL; - } else { - size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); - dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); - memcpy(dst->pre_g_128, src->pre_g_128, size); + if (src->pre_g_128 != NULL) { + dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src)); } #endif } @@ -358,10 +384,6 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx } static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { - free(ctx->pre_g); -#ifdef USE_ENDOMORPHISM - free(ctx->pre_g_128); -#endif secp256k1_ecmult_context_init(ctx); } @@ -373,7 +395,7 @@ static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { * than the number of bits in the (absolute value) of the input. */ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { - secp256k1_scalar s = *a; + secp256k1_scalar s; int last_set_bit = -1; int bit = 0; int sign = 1; @@ -386,6 +408,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, memset(wnaf, 0, len * sizeof(wnaf[0])); + s = *a; if (secp256k1_scalar_get_bits(&s, 255, 1)) { secp256k1_scalar_negate(&s, &s); sign = -1; @@ -418,7 +441,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, CHECK(carry == 0); while (bit < 256) { CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); - } + } #endif return last_set_bit + 1; } @@ -626,52 +649,55 @@ static size_t secp256k1_strauss_scratch_size(size_t n_points) { return n_points*point_size; } -static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { +static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { secp256k1_gej* points; secp256k1_scalar* scalars; struct secp256k1_strauss_state state; size_t i; + const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); secp256k1_gej_set_infinity(r); if (inp_g_sc == NULL && n_points == 0) { return 1; } - if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) { - return 0; - } - points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej)); - scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar)); - state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); - state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); + points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej)); + scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); + state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); + state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); #ifdef USE_ENDOMORPHISM - state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A); #else - state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); #endif - state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); + state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); + + if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } for (i = 0; i < n_points; i++) { secp256k1_ge point; if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) { - secp256k1_scratch_deallocate_frame(scratch); + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } secp256k1_gej_set_ge(&points[i], &point); } secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc); - secp256k1_scratch_deallocate_frame(scratch); + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 1; } /* Wrapper for secp256k1_ecmult_multi_func interface */ -static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { - return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); +static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + return secp256k1_ecmult_strauss_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); } -static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) { - return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); +static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) { + return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); } /** Convert a number to WNAF notation. @@ -963,7 +989,8 @@ static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_windo return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size; } -static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { +static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { + const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); /* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch * sizes. The reason for +1 is that we add the G scalar to the list of * other scalars. */ @@ -988,15 +1015,21 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, } bucket_window = secp256k1_pippenger_bucket_window(n_points); - if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) { + points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points)); + scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars)); + state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space)); + if (points == NULL || scalars == NULL || state_space == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + + state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps)); + state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); + buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } - points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points)); - scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars)); - state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space)); - state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps)); - state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); - buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, sizeof(*buckets) << bucket_window); if (inp_g_sc != NULL) { scalars[0] = *inp_g_sc; @@ -1010,7 +1043,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, while (point_idx < n_points) { if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) { - secp256k1_scratch_deallocate_frame(scratch); + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } idx++; @@ -1034,13 +1067,13 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, for(i = 0; i < 1<= ECMULT_PIPPENGER_THRESHOLD) { f = secp256k1_ecmult_pippenger_batch; } else { - if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(scratch), n)) { - return 0; + if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) { + return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n); } f = secp256k1_ecmult_strauss_batch; } @@ -1169,7 +1204,7 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2 size_t nbp = n < n_batch_points ? n : n_batch_points; size_t offset = n_batch_points*i; secp256k1_gej tmp; - if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { + if (!f(error_callback, ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { return 0; } secp256k1_gej_add_var(r, r, &tmp, NULL); diff --git a/src/gen_context.c b/src/gen_context.c index 87d296ebf0e2c..539f574bfd0c6 100644 --- a/src/gen_context.c +++ b/src/gen_context.c @@ -4,10 +4,16 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ +// Autotools creates libsecp256k1-config.h, of which ECMULT_GEN_PREC_BITS is needed. +// ifndef guard so downstream users can define their own if they do not use autotools. +#if !defined(ECMULT_GEN_PREC_BITS) +#include "libsecp256k1-config.h" +#endif #define USE_BASIC_CONFIG 1 - #include "basic-config.h" + #include "include/secp256k1.h" +#include "util.h" #include "field_impl.h" #include "scalar_impl.h" #include "group_impl.h" @@ -26,6 +32,7 @@ static const secp256k1_callback default_error_callback = { int main(int argc, char **argv) { secp256k1_ecmult_gen_context ctx; + void *prealloc, *base; int inner; int outer; FILE* fp; @@ -38,26 +45,31 @@ int main(int argc, char **argv) { fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n"); return -1; } - + fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); fprintf(fp, "#include \"src/group.h\"\n"); fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); - fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); + fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G); + fprintf(fp, " #error configuration mismatch, invalid ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G. Try deleting ecmult_static_context.h before the build.\n"); + fprintf(fp, "#endif\n"); + fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G] = {\n"); + base = checked_malloc(&default_error_callback, SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE); + prealloc = base; secp256k1_ecmult_gen_context_init(&ctx); - secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); - for(outer = 0; outer != 64; outer++) { + secp256k1_ecmult_gen_context_build(&ctx, &prealloc); + for(outer = 0; outer != ECMULT_GEN_PREC_N; outer++) { fprintf(fp,"{\n"); - for(inner = 0; inner != 16; inner++) { + for(inner = 0; inner != ECMULT_GEN_PREC_G; inner++) { fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); - if (inner != 15) { + if (inner != ECMULT_GEN_PREC_G - 1) { fprintf(fp,",\n"); } else { fprintf(fp,"\n"); } } - if (outer != 63) { + if (outer != ECMULT_GEN_PREC_N - 1) { fprintf(fp,"},\n"); } else { fprintf(fp,"}\n"); @@ -65,10 +77,11 @@ int main(int argc, char **argv) { } fprintf(fp,"};\n"); secp256k1_ecmult_gen_context_clear(&ctx); - + free(base); + fprintf(fp, "#undef SC\n"); fprintf(fp, "#endif\n"); fclose(fp); - + return 0; } diff --git a/src/group.h b/src/group.h index 8e122ab429c56..2f1c1323921ac 100644 --- a/src/group.h +++ b/src/group.h @@ -53,6 +53,11 @@ static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); * for Y. Return value indicates whether the result is valid. */ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); +/* Converts group element into its "absolute" value. That means it is kept as + * is if it has a square Y and otherwise negated. is_negated is set to 1 in + * the former case and to 0 in the latter case. */ +static void secp256k1_ge_absolute(secp256k1_ge *r, int *is_negated); + /** Check whether a group element is the point at infinity. */ static int secp256k1_ge_is_infinity(const secp256k1_ge *a); diff --git a/src/group_impl.h b/src/group_impl.h index 9b93c39e92f5c..fef5f890928d1 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -241,6 +241,18 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int o } +static void secp256k1_ge_absolute(secp256k1_ge *r, int *is_negated) { + if (is_negated != NULL) { + *is_negated = 0; + } + if (!secp256k1_fe_is_quad_var(&r->y)) { + secp256k1_ge_neg(r, r); + if (is_negated != NULL) { + *is_negated = 1; + } + } +} + static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { r->infinity = a->infinity; r->x = a->x; diff --git a/src/hash_impl.h b/src/hash_impl.h index 009f26beba939..1ab05cae1d354 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -131,7 +131,8 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) { size_t bufsize = hash->bytes & 0x3F; hash->bytes += len; - while (bufsize + len >= 64) { + VERIFY_CHECK(hash->bytes >= len); + while (len >= 64 - bufsize) { /* Fill the buffer, and process it. */ size_t chunk_len = 64 - bufsize; memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len); @@ -162,6 +163,19 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out memcpy(out32, (const unsigned char*)out, 32); } +/* Initializes a sha256 struct and writes the 64 byte string + * SHA256(tag)||SHA256(tag) into it. */ +static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) { + unsigned char buf[32]; + secp256k1_sha256_initialize(hash); + secp256k1_sha256_write(hash, tag, taglen); + secp256k1_sha256_finalize(hash, buf); + + secp256k1_sha256_initialize(hash); + secp256k1_sha256_write(hash, buf, 32); + secp256k1_sha256_write(hash, buf, 32); +} + static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { size_t n; unsigned char rkey[64]; diff --git a/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/java/org/bitcoin/NativeSecp256k1Test.java index d766a1029ce38..710d9f0bbfef3 100644 --- a/src/java/org/bitcoin/NativeSecp256k1Test.java +++ b/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -3,7 +3,6 @@ import com.google.common.io.BaseEncoding; import java.util.Arrays; import java.math.BigInteger; -import javax.xml.bind.DatatypeConverter; import static org.bitcoin.NativeSecp256k1Util.*; /** @@ -70,7 +69,7 @@ public static void testPubKeyCreatePos() throws AssertFailException{ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); byte[] resultArr = NativeSecp256k1.computePubkey( sec); - String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String pubkeyString = BaseEncoding.base16().encode(resultArr); assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos"); } @@ -81,7 +80,7 @@ public static void testPubKeyCreateNeg() throws AssertFailException{ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); byte[] resultArr = NativeSecp256k1.computePubkey( sec); - String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String pubkeyString = BaseEncoding.base16().encode(resultArr); assertEquals( pubkeyString, "" , "testPubKeyCreateNeg"); } @@ -94,7 +93,7 @@ public static void testSignPos() throws AssertFailException{ byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); byte[] resultArr = NativeSecp256k1.sign(data, sec); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String sigString = BaseEncoding.base16().encode(resultArr); assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); } @@ -106,7 +105,7 @@ public static void testSignNeg() throws AssertFailException{ byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); byte[] resultArr = NativeSecp256k1.sign(data, sec); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String sigString = BaseEncoding.base16().encode(resultArr); assertEquals( sigString, "" , "testSignNeg"); } @@ -118,7 +117,7 @@ public static void testPrivKeyTweakAdd_1() throws AssertFailException { byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String sigString = BaseEncoding.base16().encode(resultArr); assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1"); } @@ -130,7 +129,7 @@ public static void testPrivKeyTweakMul_1() throws AssertFailException { byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String sigString = BaseEncoding.base16().encode(resultArr); assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1"); } @@ -142,7 +141,7 @@ public static void testPrivKeyTweakAdd_2() throws AssertFailException { byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String sigString = BaseEncoding.base16().encode(resultArr); assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2"); } @@ -154,7 +153,7 @@ public static void testPrivKeyTweakMul_2() throws AssertFailException { byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String sigString = BaseEncoding.base16().encode(resultArr); assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2"); } @@ -173,7 +172,7 @@ public static void testCreateECDHSecret() throws AssertFailException{ byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub); - String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + String ecdhString = BaseEncoding.base16().encode(resultArr); assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret"); } diff --git a/src/java/org_bitcoin_NativeSecp256k1.c b/src/java/org_bitcoin_NativeSecp256k1.c index b50970b4f24c8..b590256867c67 100644 --- a/src/java/org_bitcoin_NativeSecp256k1.c +++ b/src/java/org_bitcoin_NativeSecp256k1.c @@ -81,15 +81,15 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e jbyteArray sigArray, intsByteArray; unsigned char intsarray[2]; - secp256k1_ecdsa_signature sig[72]; + secp256k1_ecdsa_signature sig; - int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL); + int ret = secp256k1_ecdsa_sign(ctx, &sig, data, secKey, NULL, NULL); unsigned char outputSer[72]; size_t outputLen = 72; if( ret ) { - int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2; + int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, &sig ); (void)ret2; } intsarray[0] = outputLen; diff --git a/src/modules/recovery/main_impl.h b/src/modules/recovery/main_impl.h index 2f6691c5a1309..ed356e53a5c8f 100755 --- a/src/modules/recovery/main_impl.h +++ b/src/modules/recovery/main_impl.h @@ -147,7 +147,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd break; } secp256k1_scalar_set_b32(&non, nonce32, &overflow); - if (!secp256k1_scalar_is_zero(&non) && !overflow) { + if (!overflow && !secp256k1_scalar_is_zero(&non)) { if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) { break; } diff --git a/src/modules/recovery/tests_impl.h b/src/modules/recovery/tests_impl.h index 5c9bbe86101c9..38a533a755fc9 100644 --- a/src/modules/recovery/tests_impl.h +++ b/src/modules/recovery/tests_impl.h @@ -215,7 +215,7 @@ void test_ecdsa_recovery_edge_cases(void) { }; const unsigned char sig64[64] = { /* Generated by signing the above message with nonce 'This is the nonce we will use...' - * and secret key 0 (which is not valid), resulting in recid 0. */ + * and secret key 0 (which is not valid), resulting in recid 1. */ 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include new file mode 100644 index 0000000000000..a82bafe43fda6 --- /dev/null +++ b/src/modules/schnorrsig/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_schnorrsig.h +noinst_HEADERS += src/modules/schnorrsig/main_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_schnorrsig +bench_schnorrsig_SOURCES = src/bench_schnorrsig.c +bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h new file mode 100644 index 0000000000000..d3bd5fe12644c --- /dev/null +++ b/src/modules/schnorrsig/main_impl.h @@ -0,0 +1,358 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ +#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "hash.h" + +int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out64 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out64, sig->data, 64); + return 1; +} + +int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in64 != NULL); + memcpy(sig->data, in64, 64); + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIPSchnorr")||SHA256("BIPSchnorr"). */ +static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x048d9a59ul; + sha->s[1] = 0xfe39fb05ul; + sha->s[2] = 0x28479648ul; + sha->s[3] = 0xe4a660f9ul; + sha->s[4] = 0x814b9e66ul; + sha->s[5] = 0x0469e801ul; + sha->s[6] = 0x83909280ul; + sha->s[7] = 0xb329e454ul; + sha->bytes = 64; +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { + secp256k1_scalar x; + secp256k1_scalar e; + secp256k1_scalar k; + secp256k1_gej pkj; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_ge r; + secp256k1_sha256 sha; + int overflow; + unsigned char buf[32]; + unsigned char seckey_tmp[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_bipschnorr; + } + secp256k1_scalar_set_b32(&x, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (overflow || secp256k1_scalar_is_zero(&x)) { + memset(sig, 0, sizeof(*sig)); + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); + secp256k1_ge_set_gej(&pk, &pkj); + + /* Because we are signing for a x-only pubkey, the secret key is negated + * before signing if the point corresponding to the secret key does not + * have a square Y. */ + if (!secp256k1_fe_is_quad_var(&pk.y)) { + secp256k1_scalar_negate(&x, &x); + } + + secp256k1_scalar_get_b32(seckey_tmp, &x); + if (!noncefp(buf, msg32, seckey_tmp, (unsigned char *) "BIPSchnorrDerive", (void*)ndata, 0)) { + memset(sig, 0, sizeof(*sig)); + memset(seckey_tmp, 0, sizeof(seckey_tmp)); + secp256k1_scalar_clear(&x); + return 0; + } + memset(seckey_tmp, 0, sizeof(seckey_tmp)); + + secp256k1_scalar_set_b32(&k, buf, NULL); + if (secp256k1_scalar_is_zero(&k)) { + memset(sig, 0, sizeof(*sig)); + secp256k1_scalar_clear(&x); + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + + if (!secp256k1_fe_is_quad_var(&r.y)) { + secp256k1_scalar_negate(&k, &k); + } + secp256k1_fe_normalize(&r.x); + secp256k1_fe_get_b32(&sig->data[0], &r.x); + + /* tagged hash(r.x, pk.x, msg32) */ + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_fe_normalize(&pk.x); + secp256k1_fe_get_b32(buf, &pk.x); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(&e, buf, NULL); + secp256k1_scalar_mul(&e, &e, &x); + secp256k1_scalar_add(&e, &e, &k); + + secp256k1_scalar_get_b32(&sig->data[32], &e); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&x); + + return 1; +} + +int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_gej pkj; + secp256k1_fe rx; + secp256k1_sha256 sha; + unsigned char buf[32]; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig->data[32], &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_fe_get_b32(buf, &pk.x); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(&e, buf, NULL); + + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + secp256k1_gej_set_ge(&pkj, &pk); + secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); + + return secp256k1_gej_has_quad_y_var(&rj) /* fails if rj is infinity */ + && secp256k1_gej_eq_x_var(&rx, &rj); +} + +/* Data that is used by the batch verification ecmult callback */ +typedef struct { + const secp256k1_context *ctx; + /* Seed for the random number generator */ + unsigned char chacha_seed[32]; + /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching + * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and + * the PRNG is called at every odd indexed schnorrsig to fill the cache. */ + secp256k1_scalar randomizer_cache[2]; + /* Signature, message, public key tuples to verify */ + const secp256k1_schnorrsig *const *sig; + const unsigned char *const *msg32; + const secp256k1_xonly_pubkey *const *pk; + size_t n_sigs; +} secp256k1_schnorrsig_verify_ecmult_context; + +/* Callback function which is called by ecmult_multi in order to convert the ecmult_context + * consisting of signature, message and public key tuples into scalars and points. */ +static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data; + + if (idx % 4 == 2) { + /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4 + * consecutive tuples before we need to call the RNG for new randomizers: + * (-randomizer_cache[0], R1) + * (-randomizer_cache[0]*e1, P1) + * (-randomizer_cache[1], R2) + * (-randomizer_cache[1]*e2, P2) */ + secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4); + } + + /* R */ + if (idx % 2 == 0) { + secp256k1_fe rx; + *sc = ecmult_context->randomizer_cache[(idx / 2) % 2]; + if (!secp256k1_fe_set_b32(&rx, &ecmult_context->sig[idx / 2]->data[0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(pt, &rx)) { + return 0; + } + /* eP */ + } else { + unsigned char buf[32]; + secp256k1_sha256 sha; + + /* xonly_pubkey_load is guaranteed not to fail because + * verify_batch_init_randomizer calls secp256k1_ec_pubkey_serialize + * which only works if loading the pubkey into a group element + * succeeds.*/ + VERIFY_CHECK(secp256k1_xonly_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])); + + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2]->data[0], 32); + secp256k1_fe_get_b32(buf, &pt->x); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + secp256k1_sha256_write(&sha, ecmult_context->msg32[idx / 2], 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(sc, buf, NULL); + secp256k1_scalar_mul(sc, sc, &ecmult_context->randomizer_cache[(idx / 2) % 2]); + } + return 1; +} + +/** Helper function for batch verification. Hashes signature verification data into the + * randomization seed and initializes ecmult_context. + * + * Returns 1 if the randomizer was successfully initialized. + * + * Args: ctx: a secp256k1 context object + * Out: ecmult_context: context for batch_ecmult_callback + * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a + * seed for the randomizer PRNG + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_init_randomizer(const secp256k1_context *ctx, secp256k1_schnorrsig_verify_ecmult_context *ecmult_context, secp256k1_sha256 *sha, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_xonly_pubkey *const *pk, size_t n_sigs) { + size_t i; + + if (n_sigs > 0) { + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + } + + for (i = 0; i < n_sigs; i++) { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256_write(sha, sig[i]->data, 64); + secp256k1_sha256_write(sha, msg32[i], 32); + /* We use compressed serialization here. If we would use + * xonly_pubkey serialization and a user would wrongly memcpy + * normal secp256k1_pubkeys into xonly_pubkeys then the randomizer + * would be the same for two different pubkeys. */ + if (!secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, (const secp256k1_pubkey *) pk[i], SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(sha, buf, buflen); + } + ecmult_context->ctx = ctx; + ecmult_context->sig = sig; + ecmult_context->msg32 = msg32; + ecmult_context->pk = pk; + ecmult_context->n_sigs = n_sigs; + + return 1; +} + +/** Helper function for batch verification. Sums the s part of all signatures multiplied by their + * randomizer. + * + * Returns 1 if s is successfully summed. + * + * In/Out: s: the s part of the input sigs is added to this s argument + * In: chacha_seed: PRNG seed for computing randomizers + * sig: array of signatures, or NULL if there are no signatures + * n_sigs: number of signatures in above array (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *chacha_seed, const secp256k1_schnorrsig *const *sig, size_t n_sigs) { + secp256k1_scalar randomizer_cache[2]; + size_t i; + + secp256k1_scalar_set_int(&randomizer_cache[0], 1); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + if (i % 2 == 1) { + secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2); + } + + secp256k1_scalar_set_b32(&term, &sig[i]->data[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_mul(&term, &term, &randomizer_cache[i % 2]); + secp256k1_scalar_add(s, s, &term); + } + return 1; +} + +/* schnorrsig batch verification. + * Seeds a random number generator with the inputs and derives a random number ai for every + * signature i. Fails if y-coordinate of any R is not a quadratic residue or if + * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */ +int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_scratch *scratch, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_xonly_pubkey *const *pk, size_t n_sigs) { + secp256k1_schnorrsig_verify_ecmult_context ecmult_context; + secp256k1_sha256 sha; + secp256k1_scalar s; + secp256k1_gej rj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because + * the number of points given to ecmult_multi is 2*n_sigs. */ + ARG_CHECK(n_sigs <= SIZE_MAX / 2); + /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit + * and 64-bit platforms. */ + ARG_CHECK(n_sigs < ((uint32_t)1 << 31)); + + secp256k1_sha256_initialize(&sha); + if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) { + return 0; + } + secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed); + secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1); + + secp256k1_scalar_clear(&s); + if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) { + return 0; + } + secp256k1_scalar_negate(&s, &s); + + return secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs) + && secp256k1_gej_is_infinity(&rj); +} + +#endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h new file mode 100644 index 0000000000000..b33d03c83e521 --- /dev/null +++ b/src/modules/schnorrsig/tests_impl.h @@ -0,0 +1,745 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ +#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ + +#include "secp256k1_schnorrsig.h" + +void test_schnorrsig_serialize(void) { + secp256k1_schnorrsig sig; + unsigned char in[64]; + unsigned char out[64]; + + memset(in, 0x12, 64); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, in)); + CHECK(secp256k1_schnorrsig_serialize(ctx, out, &sig)); + CHECK(memcmp(in, out, 64) == 0); +} + +void test_schnorrsig_api(secp256k1_scratch_space *scratch) { + unsigned char sk1[32]; + unsigned char sk2[32]; + unsigned char sk3[32]; + unsigned char msg[32]; + unsigned char sig64[64]; + secp256k1_xonly_pubkey pk[3]; + secp256k1_xonly_pubkey zero_pk; + secp256k1_schnorrsig sig; + const secp256k1_schnorrsig *sigptr = &sig; + const unsigned char *msgptr = msg; + const secp256k1_xonly_pubkey *pkptr = &pk[0]; + const secp256k1_xonly_pubkey *zeroptr = &zero_pk; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_rand256(sk1); + secp256k1_rand256(sk2); + secp256k1_rand256(sk3); + secp256k1_rand256(msg); + CHECK(secp256k1_xonly_pubkey_create(ctx, &pk[0], sk1) == 1); + CHECK(secp256k1_xonly_pubkey_create(ctx, &pk[1], sk2) == 1); + CHECK(secp256k1_xonly_pubkey_create(ctx, &pk[2], sk3) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_schnorrsig_sign(none, &sig, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, msg, sk1, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, sk1, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, msg, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_schnorrsig_serialize(none, NULL, &sig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_serialize(none, sig64, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, &sig, sig64) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, NULL, sig64) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); + CHECK(ecount == 4); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify(sign, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, NULL, &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &zero_pk) == 0); + CHECK(ecount == 6); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_batch(none, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_batch(sign, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, NULL, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, NULL, NULL, 0) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (uint32_t)1 << 31) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &zeroptr, 1) == 0); + CHECK(ecount == 9); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +/* Checks that hash initialized by secp256k1_musig_sha256_tagged has the + * expected state. */ +void test_schnorrsig_sha256_tagged(void) { + char tag[10] = "BIPSchnorr"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_schnorrsig_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); +} + +/* Helper function for schnorrsig_bip_vectors + * Signs the message and checks that it's the same as expected_sig. */ +void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *msg, const unsigned char *expected_sig) { + secp256k1_schnorrsig sig; + unsigned char serialized_sig[64]; + secp256k1_xonly_pubkey pk; + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); + CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); + + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); +} + +/* Helper function for schnorrsig_bip_vectors + * Checks that both verify and verify_batch return the same value as expected. */ +void test_schnorrsig_bip_vectors_check_verify(secp256k1_scratch_space *scratch, const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig_serialized, int expected) { + const unsigned char *msg_arr[1]; + const secp256k1_schnorrsig *sig_arr[1]; + const secp256k1_xonly_pubkey *pk_arr[1]; + secp256k1_xonly_pubkey pk; + secp256k1_schnorrsig sig; + + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized)); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, sig_serialized)); + + sig_arr[0] = &sig; + msg_arr[0] = msg32; + pk_arr[0] = &pk; + + CHECK(expected == secp256k1_schnorrsig_verify(ctx, &sig, msg32, &pk)); + CHECK(expected == secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); +} + +/* Test vectors according to BIP-schnorr + * (https://github.com/sipa/bips/blob/775cb2fd903cbfe459081fda49ada744ef3139be/bip-schnorr/test-vectors.csv). + */ +void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { + { + /* Test vector 0 */ + const unsigned char sk[32] = { + 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, 0x01 + }; + const unsigned char pk[32] = { + 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, + 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, + 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, + 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 + }; + const unsigned char msg[32] = { + 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 + }; + const unsigned char sig[64] = { + 0x52, 0x8F, 0x74, 0x57, 0x93, 0xE8, 0x47, 0x2C, + 0x03, 0x29, 0x74, 0x2A, 0x46, 0x3F, 0x59, 0xE5, + 0x8F, 0x3A, 0x3F, 0x1A, 0x4A, 0xC0, 0x9C, 0x28, + 0xF6, 0xF8, 0x51, 0x4D, 0x4D, 0x03, 0x22, 0xA2, + 0x58, 0xBD, 0x08, 0x39, 0x8F, 0x82, 0xCF, 0x67, + 0xB8, 0x12, 0xAB, 0x2C, 0x77, 0x17, 0xCE, 0x56, + 0x6F, 0x87, 0x7C, 0x2F, 0x87, 0x95, 0xC8, 0x46, + 0x14, 0x69, 0x78, 0xE8, 0xF0, 0x47, 0x82, 0xAE + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, msg, sig); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); + } + { + /* Test vector 1 */ + const unsigned char sk[32] = { + 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, + 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, + 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, + 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF + }; + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x66, 0x7C, 0x2F, 0x77, 0x8E, 0x06, 0x16, 0xE6, + 0x11, 0xBD, 0x0C, 0x14, 0xB8, 0xA6, 0x00, 0xC5, + 0x88, 0x45, 0x51, 0x70, 0x1A, 0x94, 0x9E, 0xF0, + 0xEB, 0xFD, 0x72, 0xD4, 0x52, 0xD6, 0x4E, 0x84, + 0x41, 0x60, 0xBC, 0xFC, 0x3F, 0x46, 0x6E, 0xCB, + 0x8F, 0xAC, 0xD1, 0x9A, 0xDE, 0x57, 0xD8, 0x69, + 0x9D, 0x74, 0xE7, 0x20, 0x7D, 0x78, 0xC6, 0xAE, + 0xDC, 0x37, 0x99, 0xB5, 0x2A, 0x8E, 0x05, 0x98 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, msg, sig); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); + } + { + /* Test vector 2 */ + const unsigned char sk[32] = { + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC9 + }; + const unsigned char pk[32] = { + 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, + 0x12, 0x1F, 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, + 0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9, + 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 + }; + const unsigned char msg[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig[64] = { + 0x2D, 0x94, 0x1B, 0x38, 0xE3, 0x26, 0x24, 0xBF, + 0x0A, 0xC7, 0x66, 0x9C, 0x09, 0x71, 0xB9, 0x90, + 0x99, 0x4A, 0xF6, 0xF9, 0xB1, 0x84, 0x26, 0xBF, + 0x4F, 0x4E, 0x7E, 0xC1, 0x0E, 0x6C, 0xDF, 0x38, + 0x6C, 0xF6, 0x46, 0xC6, 0xDD, 0xAF, 0xCF, 0xA7, + 0xF1, 0x99, 0x3E, 0xEB, 0x2E, 0x4D, 0x66, 0x41, + 0x6A, 0xEA, 0xD1, 0xDD, 0xAE, 0x2F, 0x22, 0xD6, + 0x3C, 0xAD, 0x90, 0x14, 0x12, 0xD1, 0x16, 0xC6 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, msg, sig); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); + } + { + /* Test vector 3 */ + const unsigned char sk[32] = { + 0x0B, 0x43, 0x2B, 0x26, 0x77, 0x93, 0x73, 0x81, + 0xAE, 0xF0, 0x5B, 0xB0, 0x2A, 0x66, 0xEC, 0xD0, + 0x12, 0x77, 0x30, 0x62, 0xCF, 0x3F, 0xA2, 0x54, + 0x9E, 0x44, 0xF5, 0x8E, 0xD2, 0x40, 0x17, 0x10 + }; + const unsigned char pk[32] = { + 0x25, 0xD1, 0xDF, 0xF9, 0x51, 0x05, 0xF5, 0x25, + 0x3C, 0x40, 0x22, 0xF6, 0x28, 0xA9, 0x96, 0xAD, + 0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A, + 0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17 + }; + const unsigned char msg[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char sig[64] = { + 0x8B, 0xD2, 0xC1, 0x16, 0x04, 0xB0, 0xA8, 0x7A, + 0x44, 0x3F, 0xCC, 0x2E, 0x5D, 0x90, 0xE5, 0x32, + 0x8F, 0x93, 0x41, 0x61, 0xB1, 0x88, 0x64, 0xFB, + 0x48, 0xCE, 0x10, 0xCB, 0x59, 0xB4, 0x5F, 0xB9, + 0xB5, 0xB2, 0xA0, 0xF1, 0x29, 0xBD, 0x88, 0xF5, + 0xBD, 0xC0, 0x5D, 0x5C, 0x21, 0xE5, 0xC5, 0x71, + 0x76, 0xB9, 0x13, 0x00, 0x23, 0x35, 0x78, 0x4F, + 0x97, 0x77, 0xA2, 0x4B, 0xD3, 0x17, 0xCD, 0x36 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, msg, sig); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); + } + { + /* Test vector 4 */ + const unsigned char pk[32] = { + 0xD6, 0x9C, 0x35, 0x09, 0xBB, 0x99, 0xE4, 0x12, + 0xE6, 0x8B, 0x0F, 0xE8, 0x54, 0x4E, 0x72, 0x83, + 0x7D, 0xFA, 0x30, 0x74, 0x6D, 0x8B, 0xE2, 0xAA, + 0x65, 0x97, 0x5F, 0x29, 0xD2, 0x2D, 0xC7, 0xB9 + }; + const unsigned char msg[32] = { + 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, + 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, + 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, + 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, + 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, + 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, + 0xEE, 0x37, 0x4A, 0xC7, 0xFA, 0xE9, 0x27, 0xD3, + 0x34, 0xCC, 0xB1, 0x90, 0xF6, 0xFB, 0x8F, 0xD2, + 0x7A, 0x2D, 0xDC, 0x63, 0x9C, 0xCE, 0xE4, 0x6D, + 0x43, 0xF1, 0x13, 0xA4, 0x03, 0x5A, 0x2C, 0x7F + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); + } + { + /* Test vector 5 */ + const unsigned char pk[32] = { + 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, 0x50, + 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, 0x21, + 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, 0x87, + 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, 0x34 + }; + secp256k1_xonly_pubkey pk_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk)); + } + { + /* Test vector 6 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, + 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, + 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, + 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9, + 0x93, 0x55, 0x54, 0xD1, 0xAA, 0x5F, 0x03, 0x74, + 0xE5, 0xCD, 0xAA, 0xCB, 0x39, 0x25, 0x03, 0x5C, + 0x7C, 0x16, 0x9B, 0x27, 0xC4, 0x42, 0x6D, 0xF0, + 0xA6, 0xB1, 0x9A, 0xF3, 0xBA, 0xEA, 0xB1, 0x38 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 7 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x10, 0xAC, 0x49, 0xA6, 0xA2, 0xEB, 0xF6, 0x04, + 0x18, 0x9C, 0x5F, 0x40, 0xFC, 0x75, 0xAF, 0x2D, + 0x42, 0xD7, 0x7D, 0xE9, 0xA2, 0x78, 0x27, 0x09, + 0xB1, 0xEB, 0x4E, 0xAF, 0x1C, 0xFE, 0x91, 0x08, + 0xD7, 0x00, 0x3B, 0x70, 0x3A, 0x34, 0x99, 0xD5, + 0xE2, 0x95, 0x29, 0xD3, 0x9B, 0xA0, 0x40, 0xA4, + 0x49, 0x55, 0x12, 0x71, 0x40, 0xF8, 0x1A, 0x8A, + 0x89, 0xA9, 0x6F, 0x99, 0x2A, 0xC0, 0xFE, 0x79 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 8 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x66, 0x7C, 0x2F, 0x77, 0x8E, 0x06, 0x16, 0xE6, + 0x11, 0xBD, 0x0C, 0x14, 0xB8, 0xA6, 0x00, 0xC5, + 0x88, 0x45, 0x51, 0x70, 0x1A, 0x94, 0x9E, 0xF0, + 0xEB, 0xFD, 0x72, 0xD4, 0x52, 0xD6, 0x4E, 0x84, + 0xBE, 0x9F, 0x43, 0x03, 0xC0, 0xB9, 0x91, 0x34, + 0x70, 0x53, 0x2E, 0x65, 0x21, 0xA8, 0x27, 0x95, + 0x1D, 0x39, 0xF5, 0xC6, 0x31, 0xCF, 0xD9, 0x8C, + 0xE3, 0x9A, 0xC4, 0xD7, 0xA5, 0xA8, 0x3B, 0xA9 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 9 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 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, + 0x99, 0xD2, 0xF0, 0xEB, 0xC2, 0x99, 0x68, 0x08, + 0x20, 0x86, 0x33, 0xCD, 0x99, 0x26, 0xBF, 0x7E, + 0xC3, 0xDA, 0xB7, 0x3D, 0xAA, 0xD3, 0x6E, 0x85, + 0xB3, 0x04, 0x0A, 0x69, 0x8E, 0x6D, 0x1C, 0xE0 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 10 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 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, 0x01, + 0x24, 0xE8, 0x1D, 0x89, 0xF0, 0x13, 0x04, 0x69, + 0x5C, 0xE9, 0x43, 0xF7, 0xD5, 0xEB, 0xD0, 0x0E, + 0xF7, 0x26, 0xA0, 0x86, 0x4B, 0x4F, 0xF3, 0x38, + 0x95, 0xB4, 0xE8, 0x6B, 0xEA, 0xDC, 0x54, 0x56 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 11 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x41, 0x60, 0xBC, 0xFC, 0x3F, 0x46, 0x6E, 0xCB, + 0x8F, 0xAC, 0xD1, 0x9A, 0xDE, 0x57, 0xD8, 0x69, + 0x9D, 0x74, 0xE7, 0x20, 0x7D, 0x78, 0xC6, 0xAE, + 0xDC, 0x37, 0x99, 0xB5, 0x2A, 0x8E, 0x05, 0x98 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 12 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, + 0x41, 0x60, 0xBC, 0xFC, 0x3F, 0x46, 0x6E, 0xCB, + 0x8F, 0xAC, 0xD1, 0x9A, 0xDE, 0x57, 0xD8, 0x69, + 0x9D, 0x74, 0xE7, 0x20, 0x7D, 0x78, 0xC6, 0xAE, + 0xDC, 0x37, 0x99, 0xB5, 0x2A, 0x8E, 0x05, 0x98 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 13 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x14, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x66, 0x7C, 0x2F, 0x77, 0x8E, 0x06, 0x16, 0xE6, + 0x11, 0xBD, 0x0C, 0x14, 0xB8, 0xA6, 0x00, 0xC5, + 0x88, 0x45, 0x51, 0x70, 0x1A, 0x94, 0x9E, 0xF0, + 0xEB, 0xFD, 0x72, 0xD4, 0x52, 0xD6, 0x4E, 0x84, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, + 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); + } + { + /* Test vector 14 */ + const unsigned char pk[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 + }; + secp256k1_xonly_pubkey pk_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk)); + } +} + +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + + memset(nonce32, 0, 32); + return 1; +} + +void test_schnorrsig_sign(void) { + unsigned char sk[32]; + const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + secp256k1_schnorrsig sig; + unsigned char zeros64[64]; + + + memset(sk, 23, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 1); + + memset(zeros64, 0, 64); + /* Overflowing secret key */ + memset(sk, 0xFF, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 0); + CHECK(memcmp(&sig, zeros64, sizeof(sig)) == 0); + + /* Zeroed secret key */ + memset(sk, 0, sizeof(sk)); + memset(&sig, 1, sizeof(sig)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 0); + CHECK(memcmp(&sig, zeros64, sizeof(sig)) == 0); + + memset(sk, 23, sizeof(sk)); + memset(&sig, 1, sizeof(sig)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, nonce_function_failing, NULL) == 0); + CHECK(memcmp(&sig, zeros64, sizeof(sig)) == 0); + memset(&sig, 1, sizeof(sig)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, nonce_function_0, NULL) == 0); + CHECK(memcmp(&sig, zeros64, sizeof(sig)) == 0); +} + +#define N_SIGS 200 +/* Creates N_SIGS valid signatures and verifies them with verify and verify_batch. Then flips some + * bits and checks that verification now fails. */ +void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { + const unsigned char sk[32] = "shhhhhhhh! this key is a secret."; + unsigned char msg[N_SIGS][32]; + secp256k1_schnorrsig sig[N_SIGS]; + size_t i; + const secp256k1_schnorrsig *sig_arr[N_SIGS]; + const unsigned char *msg_arr[N_SIGS]; + const secp256k1_xonly_pubkey *pk_arr[N_SIGS]; + secp256k1_xonly_pubkey pk; + + CHECK(secp256k1_xonly_pubkey_create(ctx, &pk, sk)); + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, NULL, NULL, NULL, 0)); + + for (i = 0; i < N_SIGS; i++) { + secp256k1_rand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], msg[i], sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[i], msg[i], &pk)); + sig_arr[i] = &sig[i]; + msg_arr[i] = msg[i]; + pk_arr[i] = &pk; + } + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 2)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, N_SIGS)); + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch fail */ + size_t sig_idx = secp256k1_rand_int(4); + size_t byte_idx = secp256k1_rand_int(32); + unsigned char xorbyte = secp256k1_rand_int(254)+1; + sig[sig_idx].data[byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + msg[sig_idx][byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + msg[sig_idx][byte_idx] ^= xorbyte; + + /* Check that above bitflips have been reversed correctly */ + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + } +} +#undef N_SIGS + +void test_schnorrsig_taproot(void) { + unsigned char sk[32]; + secp256k1_xonly_pubkey internal_pk; + unsigned char internal_pk_bytes[32]; + secp256k1_xonly_pubkey output_pk; + unsigned char output_pk_bytes[32]; + unsigned char tweak[32]; + int is_negated; + unsigned char msg[32]; + secp256k1_schnorrsig sig; + + /* Create output key */ + secp256k1_rand256(sk); + CHECK(secp256k1_xonly_pubkey_create(ctx, &internal_pk, sk) == 1); + memset(tweak, 1, sizeof(tweak)); + /* Copy internal_pk because tweak_add changes the public key in place */ + output_pk = internal_pk; + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &is_negated, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk) == 1); + + /* Key spend */ + secp256k1_rand256(msg); + CHECK(secp256k1_xonly_seckey_tweak_add(ctx, sk, tweak) == 1); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 1); + /* Verify key spend */ + CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &output_pk) == 1); + + /* Script spend */ + CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1); + /* Verify script spend */ + CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &output_pk, is_negated, &internal_pk, tweak) == 1); +} + +void run_schnorrsig_tests(void) { + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + test_schnorrsig_serialize(); + test_schnorrsig_api(scratch); + test_schnorrsig_sha256_tagged(); + test_schnorrsig_bip_vectors(scratch); + test_schnorrsig_sign(); + test_schnorrsig_sign_verify(scratch); + test_schnorrsig_taproot(); + + secp256k1_scratch_space_destroy(ctx, scratch); +} + +#endif diff --git a/src/scalar.h b/src/scalar.h index 59304cb66e905..640693a5e32ed 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -103,4 +103,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); +/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index d378335d996f3..268cc68d60fc9 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -7,6 +7,9 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "scalar.h" +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) #define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) @@ -946,4 +949,91 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#else +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[3] = (((uint64_t) x0) << 32) | x1; + r1->d[2] = (((uint64_t) x2) << 32) | x3; + r1->d[1] = (((uint64_t) x4) << 32) | x5; + r1->d[0] = (((uint64_t) x6) << 32) | x7; + r2->d[3] = (((uint64_t) x8) << 32) | x9; + r2->d[2] = (((uint64_t) x10) << 32) | x11; + r2->d[1] = (((uint64_t) x12) << 32) | x13; + r2->d[0] = (((uint64_t) x14) << 32) | x15; + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 4f9ed61feaecc..a9a934c43c483 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) #define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) @@ -718,4 +720,99 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#else +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[7] = x0; + r1->d[6] = x1; + r1->d[5] = x2; + r1->d[4] = x3; + r1->d[3] = x4; + r1->d[2] = x5; + r1->d[1] = x6; + r1->d[0] = x7; + r2->d[7] = x8; + r2->d[6] = x9; + r2->d[5] = x10; + r2->d[4] = x11; + r2->d[3] = x12; + r2->d[2] = x13; + r2->d[1] = x14; + r2->d[0] = x15; + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_impl.h b/src/scalar_impl.h index fa790570ff837..6b336d9d1a66d 100644 --- a/src/scalar_impl.h +++ b/src/scalar_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_SCALAR_IMPL_H #define SECP256K1_SCALAR_IMPL_H -#include "group.h" #include "scalar.h" +#include "util.h" #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index c80e70c5a2ad2..0e796a7b62069 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -38,8 +38,11 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { if (flag && bit < 32) - *r += (1 << bit); + *r += ((uint32_t)1 << bit); #ifdef VERIFY + VERIFY_CHECK(bit < 32); + /* Verify that adding (1 << bit) will not overflow any in-range scalar *r by overflowing the underlying uint32_t. */ + VERIFY_CHECK(((uint32_t)1 << bit) - 1 <= UINT32_MAX - EXHAUSTIVE_TEST_ORDER); VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); #endif } @@ -111,4 +114,9 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } +SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { + *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; + *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scratch.h b/src/scratch.h index fef377af0d942..77b35d126bbe6 100644 --- a/src/scratch.h +++ b/src/scratch.h @@ -7,33 +7,36 @@ #ifndef _SECP256K1_SCRATCH_ #define _SECP256K1_SCRATCH_ -#define SECP256K1_SCRATCH_MAX_FRAMES 5 - /* The typedef is used internally; the struct name is used in the public API * (where it is exposed as a different typedef) */ typedef struct secp256k1_scratch_space_struct { - void *data[SECP256K1_SCRATCH_MAX_FRAMES]; - size_t offset[SECP256K1_SCRATCH_MAX_FRAMES]; - size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES]; - size_t frame; + /** guard against interpreting this object as other types */ + unsigned char magic[8]; + /** actual allocated data */ + void *data; + /** amount that has been allocated (i.e. `data + offset` is the next + * available pointer) */ + size_t alloc_size; + /** maximum size available to allocate */ size_t max_size; - const secp256k1_callback* error_callback; } secp256k1_scratch; static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size); -static void secp256k1_scratch_destroy(secp256k1_scratch* scratch); +static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch); -/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */ -static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects); +/** Returns an opaque object used to "checkpoint" a scratch space. Used + * with `secp256k1_scratch_apply_checkpoint` to undo allocations. */ +static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch); -/** Deallocates a stack frame */ -static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch); +/** Applies a check point received from `secp256k1_scratch_checkpoint`, + * undoing all allocations since that point. */ +static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint); /** Returns the maximum allocation the scratch space will allow */ -static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects); +static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t n_objects); /** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */ -static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n); +static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n); #endif diff --git a/src/scratch_impl.h b/src/scratch_impl.h index abed713b21d2a..4cee70000147b 100644 --- a/src/scratch_impl.h +++ b/src/scratch_impl.h @@ -7,78 +7,80 @@ #ifndef _SECP256K1_SCRATCH_IMPL_H_ #define _SECP256K1_SCRATCH_IMPL_H_ +#include "util.h" #include "scratch.h" -/* Using 16 bytes alignment because common architectures never have alignment - * requirements above 8 for any of the types we care about. In addition we - * leave some room because currently we don't care about a few bytes. - * TODO: Determine this at configure time. */ -#define ALIGNMENT 16 - -static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) { - secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret)); +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) { + const size_t base_alloc = ((sizeof(secp256k1_scratch) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + void *alloc = checked_malloc(error_callback, base_alloc + size); + secp256k1_scratch* ret = (secp256k1_scratch *)alloc; if (ret != NULL) { memset(ret, 0, sizeof(*ret)); - ret->max_size = max_size; - ret->error_callback = error_callback; + memcpy(ret->magic, "scratch", 8); + ret->data = (void *) ((char *) alloc + base_alloc); + ret->max_size = size; } return ret; } -static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) { +static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) { if (scratch != NULL) { - VERIFY_CHECK(scratch->frame == 0); + VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ + if (memcmp(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return; + } + memset(scratch->magic, 0, sizeof(scratch->magic)); free(scratch); } } -static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) { - size_t i = 0; - size_t allocated = 0; - for (i = 0; i < scratch->frame; i++) { - allocated += scratch->frame_size[i]; - } - if (scratch->max_size - allocated <= objects * ALIGNMENT) { +static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) { + if (memcmp(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); return 0; } - return scratch->max_size - allocated - objects * ALIGNMENT; + return scratch->alloc_size; } -static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) { - VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES); - - if (n <= secp256k1_scratch_max_allocation(scratch, objects)) { - n += objects * ALIGNMENT; - scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n); - if (scratch->data[scratch->frame] == NULL) { - return 0; - } - scratch->frame_size[scratch->frame] = n; - scratch->offset[scratch->frame] = 0; - scratch->frame++; - return 1; - } else { - return 0; +static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) { + if (memcmp(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return; + } + if (checkpoint > scratch->alloc_size) { + secp256k1_callback_call(error_callback, "invalid checkpoint"); + return; } + scratch->alloc_size = checkpoint; } -static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) { - VERIFY_CHECK(scratch->frame > 0); - scratch->frame -= 1; - free(scratch->data[scratch->frame]); +static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) { + if (memcmp(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return 0; + } + if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) { + return 0; + } + return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1); } -static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) { +static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) { void *ret; - size_t frame = scratch->frame - 1; - size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + size = ROUND_TO_ALIGN(size); + + if (memcmp(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return NULL; + } - if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) { + if (size > scratch->max_size - scratch->alloc_size) { return NULL; } - ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]); + ret = (void *) ((char *) scratch->data + scratch->alloc_size); memset(ret, 0, size); - scratch->offset[frame] += size; + scratch->alloc_size += size; return ret; } diff --git a/src/secp256k1.c b/src/secp256k1.c index 15981f46e21af..61d958ce6932d 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -5,6 +5,7 @@ **********************************************************************/ #include "include/secp256k1.h" +#include "include/secp256k1_preallocated.h" #include "util.h" #include "num_impl.h" @@ -26,28 +27,39 @@ } \ } while(0) -static void default_illegal_callback_fn(const char* str, void* data) { +#define ARG_CHECK_NO_RETURN(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + } \ +} while(0) + +#ifndef USE_EXTERNAL_DEFAULT_CALLBACKS +#include +#include +static void secp256k1_default_illegal_callback_fn(const char* str, void* data) { (void)data; fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); abort(); } - -static const secp256k1_callback default_illegal_callback = { - default_illegal_callback_fn, - NULL -}; - -static void default_error_callback_fn(const char* str, void* data) { +static void secp256k1_default_error_callback_fn(const char* str, void* data) { (void)data; fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); abort(); } +#else +void secp256k1_default_illegal_callback_fn(const char* str, void* data); +void secp256k1_default_error_callback_fn(const char* str, void* data); +#endif -static const secp256k1_callback default_error_callback = { - default_error_callback_fn, +static const secp256k1_callback default_illegal_callback = { + secp256k1_default_illegal_callback_fn, NULL }; +static const secp256k1_callback default_error_callback = { + secp256k1_default_error_callback_fn, + NULL +}; struct secp256k1_context_struct { secp256k1_ecmult_context ecmult_ctx; @@ -59,20 +71,55 @@ struct secp256k1_context_struct { static const secp256k1_context secp256k1_context_no_precomp_ = { { 0 }, { 0 }, - { default_illegal_callback_fn, 0 }, - { default_error_callback_fn, 0 } + { secp256k1_default_illegal_callback_fn, 0 }, + { secp256k1_default_error_callback_fn, 0 } }; const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_; -secp256k1_context* secp256k1_context_create(unsigned int flags) { - secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); +size_t secp256k1_context_preallocated_size(unsigned int flags) { + size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context)); + + if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { + secp256k1_callback_call(&default_illegal_callback, + "Invalid flags"); + return 0; + } + + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { + ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; + } + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { + ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; + } + return ret; +} + +size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) { + size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context)); + VERIFY_CHECK(ctx != NULL); + if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) { + ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE; + } + if (secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)) { + ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE; + } + return ret; +} + +secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) { + void* const base = prealloc; + size_t prealloc_size; + secp256k1_context* ret; + + VERIFY_CHECK(prealloc != NULL); + prealloc_size = secp256k1_context_preallocated_size(flags); + ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size); ret->illegal_callback = default_illegal_callback; ret->error_callback = default_error_callback; if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { secp256k1_callback_call(&ret->illegal_callback, "Invalid flags"); - free(ret); return NULL; } @@ -80,47 +127,79 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) { secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { - secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc); } if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { - secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); + secp256k1_ecmult_context_build(&ret->ecmult_ctx, &prealloc); } + return (secp256k1_context*) ret; +} + +secp256k1_context* secp256k1_context_create(unsigned int flags) { + size_t const prealloc_size = secp256k1_context_preallocated_size(flags); + secp256k1_context* ctx = (secp256k1_context*)checked_malloc(&default_error_callback, prealloc_size); + if (EXPECT(secp256k1_context_preallocated_create(ctx, flags) == NULL, 0)) { + free(ctx); + return NULL; + } + + return ctx; +} + +secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* ctx, void* prealloc) { + size_t prealloc_size; + secp256k1_context* ret; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(prealloc != NULL); + + prealloc_size = secp256k1_context_preallocated_clone_size(ctx); + ret = (secp256k1_context*)prealloc; + memcpy(ret, ctx, prealloc_size); + secp256k1_ecmult_gen_context_finalize_memcpy(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx); + secp256k1_ecmult_context_finalize_memcpy(&ret->ecmult_ctx, &ctx->ecmult_ctx); return ret; } secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { - secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context)); - ret->illegal_callback = ctx->illegal_callback; - ret->error_callback = ctx->error_callback; - secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); - secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); + secp256k1_context* ret; + size_t prealloc_size; + + VERIFY_CHECK(ctx != NULL); + prealloc_size = secp256k1_context_preallocated_clone_size(ctx); + ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size); + ret = secp256k1_context_preallocated_clone(ctx, ret); return ret; } -void secp256k1_context_destroy(secp256k1_context* ctx) { - CHECK(ctx != secp256k1_context_no_precomp); +void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) { + ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp); if (ctx != NULL) { secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + } +} +void secp256k1_context_destroy(secp256k1_context* ctx) { + if (ctx != NULL) { + secp256k1_context_preallocated_destroy(ctx); free(ctx); } } void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { - CHECK(ctx != secp256k1_context_no_precomp); + ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp); if (fun == NULL) { - fun = default_illegal_callback_fn; + fun = secp256k1_default_illegal_callback_fn; } ctx->illegal_callback.fn = fun; ctx->illegal_callback.data = data; } void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { - CHECK(ctx != secp256k1_context_no_precomp); + ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp); if (fun == NULL) { - fun = default_error_callback_fn; + fun = secp256k1_default_error_callback_fn; } ctx->error_callback.fn = fun; ctx->error_callback.data = data; @@ -131,8 +210,9 @@ secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* return secp256k1_scratch_create(&ctx->error_callback, max_size); } -void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) { - secp256k1_scratch_destroy(scratch); +void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scratch_space* scratch) { + VERIFY_CHECK(ctx != NULL); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); } static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { @@ -333,6 +413,54 @@ static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *off *offset += len; } +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIPSchnorrDerive")||SHA256("BIPSchnorrDerive"). */ +static void secp256k1_nonce_function_bipschnorr_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x1cd78ec3ul; + sha->s[1] = 0xc4425f87ul; + sha->s[2] = 0xb4f1a9f1ul; + sha->s[3] = 0xa16abd8dul; + sha->s[4] = 0x5a6dea72ul; + sha->s[5] = 0xd28469e3ul; + sha->s[6] = 0x17119b2eul; + sha->s[7] = 0x7bd19a16ul; + sha->bytes = 64; +} + +/* This nonce function is described in BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) */ +static int nonce_function_bipschnorr(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + secp256k1_sha256 sha; + + if (counter != 0) { + return 0; + } + /* Tag the hash with algo16 which is important to avoid nonce reuse across + * algorithms. If the this nonce function is used in BIP-schnorr signing as + * defined in the spec, an optimized tagging implementation is used. */ + if (algo16 != NULL) { + if (memcmp(algo16, "BIPSchnorrDerive", 16) == 0) { + secp256k1_nonce_function_bipschnorr_sha256_tagged(&sha); + } else { + secp256k1_sha256_initialize_tagged(&sha, algo16, 16); + } + } else { + /* If algo16 is NULL use a 14-bytes tag to rule out collisions with any + * non-NULL algo16 */ + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) "BIPSchnorrNULL", 14); + } + + /* Hash x||msg using the tagged hash as per the spec */ + secp256k1_sha256_write(&sha, key32, 32); + secp256k1_sha256_write(&sha, msg32, 32); + if (data != NULL) { + secp256k1_sha256_write(&sha, data, 32); + } + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; unsigned int offset = 0; @@ -363,6 +491,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m return 1; } +const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr = nonce_function_bipschnorr; const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; @@ -438,7 +567,7 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p ARG_CHECK(seckey != NULL); secp256k1_scalar_set_b32(&sec, seckey, &overflow); - ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec)); + ret = !overflow && !secp256k1_scalar_is_zero(&sec); if (ret) { secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); secp256k1_ge_set_gej(&p, &pj); @@ -457,6 +586,7 @@ int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *sec secp256k1_scalar_negate(&sec, &sec); secp256k1_scalar_get_b32(seckey, &sec); + secp256k1_scalar_clear(&sec); return 1; } @@ -600,10 +730,145 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * return 1; } +/* Converts the point encoded by a secp256k1_pubkey into its "absolute" value. + * That means it is kept as is if it has a square Y and otherwise negated. + * is_negated is set to 1 in the former case and to 0 in the latter case. */ +static void secp256k1_ec_pubkey_absolute(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, int *is_negated) { + secp256k1_ge ge; + secp256k1_pubkey_load(ctx, &ge, pubkey); + secp256k1_ge_absolute(&ge, is_negated); + secp256k1_pubkey_save(pubkey, &ge); +} + + +static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_xonly_pubkey* pubkey) { + return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey); +} + +static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey* pubkey, secp256k1_ge* ge) { + secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge); +} + +int secp256k1_xonly_pubkey_create(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *seckey) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(seckey != NULL); + + if (!secp256k1_ec_pubkey_create(ctx, (secp256k1_pubkey *) pubkey, seckey)) { + return 0; + } + secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) pubkey, NULL); + return 1; +} + +int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey* pubkey, const unsigned char *input32) { + secp256k1_ge Q; + secp256k1_fe x; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input32 != NULL); + + if (!secp256k1_fe_set_b32(&x, input32)) { + return 0; + } + if (!secp256k1_ge_set_xquad(&Q, &x)) { + return 0; + } + secp256k1_xonly_pubkey_save(pubkey, &Q); + secp256k1_ge_clear(&Q); + return 1; +} + +int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey* pubkey) { + secp256k1_ge Q; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output32 != NULL); + memset(output32, 0, 32); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &Q, pubkey)) { + return 0; + } + secp256k1_fe_get_b32(output32, &Q.x); + return 1; +} + +int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *is_negated, const secp256k1_pubkey *pubkey) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(xonly_pubkey != NULL); + ARG_CHECK(pubkey != NULL); + + memcpy(xonly_pubkey, pubkey, sizeof(*xonly_pubkey)); + + secp256k1_ec_pubkey_absolute(ctx, (secp256k1_pubkey *) xonly_pubkey, is_negated); + return 1; +} + +int secp256k1_xonly_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey32, const unsigned char *tweak32) { + secp256k1_ge ge; + secp256k1_pubkey pubkey; + secp256k1_scalar sec; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(tweak32 != NULL); + + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, seckey32)) { + return 0; + } + secp256k1_pubkey_load(ctx, &ge, &pubkey); + if (!secp256k1_fe_is_quad_var(&ge.y)) { + /* Overflow can be ignored because ec_pubkey_create would already fail */ + secp256k1_scalar_set_b32(&sec, seckey32, NULL); + secp256k1_scalar_negate(&sec, &sec); + secp256k1_scalar_get_b32(seckey32, &sec); + secp256k1_scalar_clear(&sec); + } + + return secp256k1_ec_privkey_tweak_add(ctx, seckey32, tweak32); +} + +int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *is_negated, const unsigned char *tweak32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(is_negated != NULL); + ARG_CHECK(tweak32 != NULL); + + if(!secp256k1_ec_pubkey_tweak_add(ctx, (secp256k1_pubkey *) pubkey, tweak32)) { + return 0; + } + return secp256k1_xonly_pubkey_from_pubkey(ctx, pubkey, is_negated, (secp256k1_pubkey *) pubkey); +} + +int secp256k1_xonly_pubkey_tweak_test(const secp256k1_context* ctx, const secp256k1_xonly_pubkey *output_pubkey, int is_negated, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_xonly_pubkey pk_expected; + int is_negated_expected; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(internal_pubkey != NULL); + ARG_CHECK(output_pubkey != NULL); + ARG_CHECK(tweak32 != NULL); + + pk_expected = *internal_pubkey; + if (!secp256k1_xonly_pubkey_tweak_add(ctx, &pk_expected, &is_negated_expected, tweak32)) { + return 0; + } + return memcmp(&pk_expected, output_pubkey, sizeof(pk_expected)) == 0 + && is_negated_expected == is_negated; +} + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index f1c4db929a776..3099ac4370069 100644 --- a/src/tests.c +++ b/src/tests.c @@ -16,6 +16,7 @@ #include "secp256k1.c" #include "include/secp256k1.h" +#include "include/secp256k1_preallocated.h" #include "testrand_impl.h" #ifdef ENABLE_OPENSSL_TESTS @@ -82,7 +83,9 @@ void random_field_element_magnitude(secp256k1_fe *fe) { secp256k1_fe_negate(&zero, &zero, 0); secp256k1_fe_mul_int(&zero, n - 1); secp256k1_fe_add(fe, &zero); - VERIFY_CHECK(fe->magnitude == n); +#ifdef VERIFY + CHECK(fe->magnitude == n); +#endif } void random_group_element_test(secp256k1_ge *ge) { @@ -137,23 +140,47 @@ void random_scalar_order(secp256k1_scalar *num) { } while(1); } -void run_context_tests(void) { +void run_context_tests(int use_prealloc) { secp256k1_pubkey pubkey; secp256k1_pubkey zero_pubkey; secp256k1_ecdsa_signature sig; unsigned char ctmp[32]; int32_t ecount; int32_t ecount2; - secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *none; + secp256k1_context *sign; + secp256k1_context *vrfy; + secp256k1_context *both; + void *none_prealloc = NULL; + void *sign_prealloc = NULL; + void *vrfy_prealloc = NULL; + void *both_prealloc = NULL; secp256k1_gej pubj; secp256k1_ge pub; secp256k1_scalar msg, key, nonce; secp256k1_scalar sigr, sigs; + if (use_prealloc) { + none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); + vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); + both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); + CHECK(none_prealloc != NULL); + CHECK(sign_prealloc != NULL); + CHECK(vrfy_prealloc != NULL); + CHECK(both_prealloc != NULL); + none = secp256k1_context_preallocated_create(none_prealloc, SECP256K1_CONTEXT_NONE); + sign = secp256k1_context_preallocated_create(sign_prealloc, SECP256K1_CONTEXT_SIGN); + vrfy = secp256k1_context_preallocated_create(vrfy_prealloc, SECP256K1_CONTEXT_VERIFY); + both = secp256k1_context_preallocated_create(both_prealloc, SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + } else { + none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + } + memset(&zero_pubkey, 0, sizeof(zero_pubkey)); ecount = 0; @@ -163,14 +190,57 @@ void run_context_tests(void) { secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL); CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + /* check if sizes for cloning are consistent */ + CHECK(secp256k1_context_preallocated_clone_size(none) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(secp256k1_context_preallocated_clone_size(sign) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); + CHECK(secp256k1_context_preallocated_clone_size(vrfy) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); + CHECK(secp256k1_context_preallocated_clone_size(both) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); + /*** clone and destroy all of them to make sure cloning was complete ***/ { secp256k1_context *ctx_tmp; - ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); + if (use_prealloc) { + /* clone into a non-preallocated context and then again into a new preallocated one. */ + ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp); + free(none_prealloc); none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(none_prealloc != NULL); + ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, none_prealloc); secp256k1_context_destroy(ctx_tmp); + + ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp); + free(sign_prealloc); sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(sign_prealloc != NULL); + ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, sign_prealloc); secp256k1_context_destroy(ctx_tmp); + + ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp); + free(vrfy_prealloc); vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(vrfy_prealloc != NULL); + ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, vrfy_prealloc); secp256k1_context_destroy(ctx_tmp); + + ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp); + free(both_prealloc); both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(both_prealloc != NULL); + ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, both_prealloc); secp256k1_context_destroy(ctx_tmp); + } else { + /* clone into a preallocated context and then again into a new non-preallocated one. */ + void *prealloc_tmp; + + prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(prealloc_tmp != NULL); + ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, prealloc_tmp); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp); + free(prealloc_tmp); + + prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(prealloc_tmp != NULL); + ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, prealloc_tmp); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp); + free(prealloc_tmp); + + prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL); + ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, prealloc_tmp); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp); + free(prealloc_tmp); + + prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL); + ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, prealloc_tmp); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp); + free(prealloc_tmp); + } } /* Verify that the error callback makes it across the clone. */ @@ -229,10 +299,6 @@ void run_context_tests(void) { secp256k1_context_set_illegal_callback(vrfy, NULL, NULL); secp256k1_context_set_illegal_callback(sign, NULL, NULL); - /* This shouldn't leak memory, due to already-set tests. */ - secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL); - secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL); - /* obtain a working nonce */ do { random_scalar_order_test(&nonce); @@ -247,49 +313,95 @@ void run_context_tests(void) { CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); /* cleanup */ - secp256k1_context_destroy(none); - secp256k1_context_destroy(sign); - secp256k1_context_destroy(vrfy); - secp256k1_context_destroy(both); + if (use_prealloc) { + secp256k1_context_preallocated_destroy(none); + secp256k1_context_preallocated_destroy(sign); + secp256k1_context_preallocated_destroy(vrfy); + secp256k1_context_preallocated_destroy(both); + free(none_prealloc); + free(sign_prealloc); + free(vrfy_prealloc); + free(both_prealloc); + } else { + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); + } /* Defined as no-op. */ secp256k1_context_destroy(NULL); + secp256k1_context_preallocated_destroy(NULL); + } void run_scratch_tests(void) { + const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + int32_t ecount = 0; + size_t checkpoint; + size_t checkpoint_2; secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_scratch_space *scratch; + secp256k1_scratch_space local_scratch; /* Test public API */ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); scratch = secp256k1_scratch_space_create(none, 1000); CHECK(scratch != NULL); CHECK(ecount == 0); /* Test internal API */ - CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); - CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000); - - /* Allocating 500 bytes with no frame fails */ - CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); - CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); - - /* ...but pushing a new stack frame does affect the max allocation */ - CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1)); - CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */ - CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL); - CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); - - CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1)); + CHECK(scratch->alloc_size == 0); + CHECK(scratch->alloc_size % ALIGNMENT == 0); + + /* Allocating 500 bytes succeeds */ + checkpoint = secp256k1_scratch_checkpoint(&none->error_callback, scratch); + CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); + CHECK(scratch->alloc_size != 0); + CHECK(scratch->alloc_size % ALIGNMENT == 0); + + /* Allocating another 500 bytes fails */ + CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); + CHECK(scratch->alloc_size != 0); + CHECK(scratch->alloc_size % ALIGNMENT == 0); + + /* ...but it succeeds once we apply the checkpoint to undo it */ + secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint); + CHECK(scratch->alloc_size == 0); + CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000); + CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL); + CHECK(scratch->alloc_size != 0); + + /* try to apply a bad checkpoint */ + checkpoint_2 = secp256k1_scratch_checkpoint(&none->error_callback, scratch); + secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint); + CHECK(ecount == 0); + secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */ + CHECK(ecount == 1); + secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */ + CHECK(ecount == 2); - /* ...and this effect is undone by popping the frame */ - secp256k1_scratch_deallocate_frame(scratch); - CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); - CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); + /* try to use badly initialized scratch space */ + secp256k1_scratch_space_destroy(none, scratch); + memset(&local_scratch, 0, sizeof(local_scratch)); + scratch = &local_scratch; + CHECK(!secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0)); + CHECK(ecount == 3); + CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL); + CHECK(ecount == 4); + secp256k1_scratch_space_destroy(none, scratch); + CHECK(ecount == 5); /* cleanup */ - secp256k1_scratch_space_destroy(scratch); + secp256k1_scratch_space_destroy(none, NULL); /* no-op */ secp256k1_context_destroy(none); } @@ -331,6 +443,60 @@ void run_sha256_tests(void) { } } +/* Tests for the equality of two sha256 structs. This function only produces a + * correct result if an integer multiple of 64 many bytes have been written + * into the hash functions. */ +void test_sha256_eq(secp256k1_sha256 *sha1, secp256k1_sha256 *sha2) { + unsigned char buf[32] = { 0 }; + unsigned char buf2[32]; + + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + /* Compare the struct excluding the the buffer, because it may be + * uninitialized or already included in the state. */ + CHECK(sha1->bytes == sha2->bytes); + CHECK(memcmp(sha1->s, sha2->s, sizeof(sha1->s)) == 0); + + /* Compare the output */ + secp256k1_sha256_write(sha1, buf, 32); + secp256k1_sha256_write(sha2, buf, 32); + secp256k1_sha256_finalize(sha1, buf); + secp256k1_sha256_finalize(sha2, buf2); + CHECK(memcmp(buf, buf2, 32) == 0); +} + +void run_nonce_function_bipschnorr_tests(void) { + char tag[16] = "BIPSchnorrDerive"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char nonces[3][32]; + unsigned char msg[32]; + unsigned char key[32]; + + /* Check that hash initialized by + * secp256k1_nonce_function_bipschnorr_sha256_tagged has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_nonce_function_bipschnorr_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that different choices of the algo16 argument result in different + * hashes. */ + memset(msg, 0, sizeof(msg)); + memset(key, 1, sizeof(key)); + CHECK(nonce_function_bipschnorr(nonces[0], msg, key, (unsigned char *) "BIPSchnorrDerive", NULL, 0)); + CHECK(nonce_function_bipschnorr(nonces[1], msg, key, NULL, NULL, 0)); + CHECK(memcmp(nonces[1], nonces[0], sizeof(nonces[1])) != 0); + CHECK(nonce_function_bipschnorr(nonces[2], msg, key, (unsigned char *) "something16chars", NULL, 0)); + CHECK(memcmp(nonces[2], nonces[0], sizeof(nonces[2])) != 0); + CHECK(memcmp(nonces[2], nonces[1], sizeof(nonces[2])) != 0); + + /* Check that counter != 0 makes nonce function fail. */ + CHECK(nonce_function_bipschnorr(nonces[0], msg, key, NULL, NULL, 0) == 1); + CHECK(nonce_function_bipschnorr(nonces[0], msg, key, NULL, NULL, 1) == 0); +} + void run_hmac_sha256_tests(void) { static const char *keys[6] = { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", @@ -965,12 +1131,122 @@ void scalar_test(void) { } +void scalar_chacha_tests(void) { + /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A + * Note that scalar_set_b32 and scalar_get_b32 represent integers + * underlying the scalar in big-endian format. */ + unsigned char expected1[64] = { + 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, + 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, + 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, + 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, + 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, + 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, + 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, + 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 + }; + unsigned char expected2[64] = { + 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, + 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, + 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, + 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, + 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, + 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, + 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, + 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b + }; + unsigned char seed3[32] = { + 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, 0x01 + }; + unsigned char expected3[64] = { + 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, + 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, + 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, + 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, + 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, + 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, + 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, + 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 + }; + unsigned char seed4[32] = { + 0x00, 0xff, 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 + }; + unsigned char expected4[64] = { + 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, + 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, + 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, + 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, + 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, + 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, + 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, + 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e + }; + unsigned char seed5[32] = { + 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, + 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, + 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, + 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 + }; + unsigned char expected5[64] = { + 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, + 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, + 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, + 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, + 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, + 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, + 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, + 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 + }; + + secp256k1_scalar exp_r1, exp_r2; + secp256k1_scalar r1, r2; + unsigned char seed0[32] = { 0 }; + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); + secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); + secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); + secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); +} + void run_scalar_tests(void) { int i; for (i = 0; i < 128 * count; i++) { scalar_test(); } + scalar_chacha_tests(); + { /* (-1)+1 should be zero. */ secp256k1_scalar s, o; @@ -1709,24 +1985,32 @@ void run_field_misc(void) { /* Test fe conditional move; z is not normalized here. */ q = x; secp256k1_fe_cmov(&x, &z, 0); - VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude); +#ifdef VERIFY + CHECK(!x.normalized && x.magnitude == z.magnitude); +#endif secp256k1_fe_cmov(&x, &x, 1); CHECK(fe_memcmp(&x, &z) != 0); CHECK(fe_memcmp(&x, &q) == 0); secp256k1_fe_cmov(&q, &z, 1); - VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); +#ifdef VERIFY + CHECK(!q.normalized && q.magnitude == z.magnitude); +#endif CHECK(fe_memcmp(&q, &z) == 0); secp256k1_fe_normalize_var(&x); secp256k1_fe_normalize_var(&z); CHECK(!secp256k1_fe_equal_var(&x, &z)); secp256k1_fe_normalize_var(&q); secp256k1_fe_cmov(&q, &z, (i&1)); - VERIFY_CHECK(q.normalized && q.magnitude == 1); +#ifdef VERIFY + CHECK(q.normalized && q.magnitude == 1); +#endif for (j = 0; j < 6; j++) { secp256k1_fe_negate(&z, &z, j+1); secp256k1_fe_normalize_var(&q); secp256k1_fe_cmov(&q, &z, (j&1)); - VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); +#ifdef VERIFY + CHECK(!q.normalized && q.magnitude == (j+2)); +#endif } secp256k1_fe_normalize_var(&z); /* Test storage conversion and conditional moves. */ @@ -2120,7 +2404,7 @@ void test_ge(void) { /* Test batch gej -> ge conversion with many infinities. */ for (i = 0; i < 4 * runs + 1; i++) { random_group_element_test(&ge[i]); - /* randomly set half the points to infinitiy */ + /* randomly set half the points to infinity */ if(secp256k1_fe_is_odd(&ge[i].x)) { secp256k1_ge_set_infinity(&ge[i]); } @@ -2572,14 +2856,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_gej r; secp256k1_gej r2; ecmult_multi_data data; - secp256k1_scratch *scratch_empty; data.sc = sc; data.pt = pt; secp256k1_scalar_set_int(&szero, 0); /* No points to multiply */ - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); /* Check 1- and 2-point multiplies against ecmult */ for (ncount = 0; ncount < count; ncount++) { @@ -2595,36 +2878,31 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e /* only G scalar */ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); secp256k1_gej_neg(&r2, &r2); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); /* 1-point */ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); secp256k1_gej_neg(&r2, &r2); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); - /* Try to multiply 1 point, but scratch space is empty */ - scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0); - CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1)); - secp256k1_scratch_destroy(scratch_empty); - /* Try to multiply 1 point, but callback returns false */ - CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); + CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); /* 2-point */ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); secp256k1_gej_neg(&r2, &r2); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); /* 2-point with G scalar */ secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); secp256k1_gej_neg(&r2, &r2); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); @@ -2641,7 +2919,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e random_scalar_order(&sc[i]); secp256k1_ge_set_infinity(&pt[i]); } - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -2651,7 +2929,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e pt[i] = ptg; secp256k1_scalar_set_int(&sc[i], 0); } - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -2664,7 +2942,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e pt[2 * i + 1] = ptg; } - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); random_scalar_order(&sc[0]); @@ -2677,7 +2955,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]); } - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -2692,7 +2970,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_scalar_negate(&sc[i], &sc[i]); } - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -2711,7 +2989,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e } secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); secp256k1_gej_neg(&r2, &r2); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); @@ -2734,7 +3012,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_gej_set_ge(&p0j, &pt[0]); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); secp256k1_gej_neg(&r2, &r2); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); @@ -2747,13 +3025,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e } secp256k1_scalar_clear(&sc[0]); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); secp256k1_scalar_clear(&sc[1]); secp256k1_scalar_clear(&sc[2]); secp256k1_scalar_clear(&sc[3]); secp256k1_scalar_clear(&sc[4]); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); CHECK(secp256k1_gej_is_infinity(&r)); /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */ @@ -2798,7 +3076,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e secp256k1_scalar_add(&tmp1, &tmp1, &tmp2); secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero); - CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); + CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); secp256k1_gej_neg(&expected, &expected); secp256k1_gej_add_var(&actual, &actual, &expected, NULL); CHECK(secp256k1_gej_is_infinity(&actual)); @@ -2809,6 +3087,24 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e } } +void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { + secp256k1_scalar szero; + secp256k1_scalar sc[32]; + secp256k1_ge pt[32]; + secp256k1_gej r; + ecmult_multi_data data; + secp256k1_scratch *scratch_empty; + + data.sc = sc; + data.pt = pt; + secp256k1_scalar_set_int(&szero, 0); + + /* Try to multiply 1 point, but scratch space is empty.*/ + scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0); + CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1)); + secp256k1_scratch_destroy(&ctx->error_callback, scratch_empty); +} + void test_secp256k1_pippenger_bucket_window_inv(void) { int i; @@ -2839,17 +3135,27 @@ void test_ecmult_multi_pippenger_max_points(void) { int bucket_window = 0; for(; scratch_size < max_size; scratch_size+=256) { + size_t i; + size_t total_alloc; + size_t checkpoint; scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size); CHECK(scratch != NULL); - n_points_supported = secp256k1_pippenger_max_points(scratch); + checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch); if (n_points_supported == 0) { - secp256k1_scratch_destroy(scratch); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); continue; } bucket_window = secp256k1_pippenger_bucket_window(n_points_supported); - CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS)); - secp256k1_scratch_deallocate_frame(scratch); - secp256k1_scratch_destroy(scratch); + /* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */ + total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window); + for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) { + CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1)); + total_alloc--; + } + CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc)); + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); } CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW); } @@ -2932,19 +3238,25 @@ void test_ecmult_multi_batching(void) { } data.sc = sc; data.pt = pt; + secp256k1_gej_neg(&r2, &r2); - /* Test with empty scratch space */ + /* Test with empty scratch space. It should compute the correct result using + * ecmult_mult_simple algorithm which doesn't require a scratch space. */ scratch = secp256k1_scratch_create(&ctx->error_callback, 0); - CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1)); - secp256k1_scratch_destroy(scratch); + CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); /* Test with space for 1 point in pippenger. That's not enough because - * ecmult_multi selects strauss which requires more memory. */ + * ecmult_multi selects strauss which requires more memory. It should + * therefore select the simple algorithm. */ scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); - CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1)); - secp256k1_scratch_destroy(scratch); + CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); - secp256k1_gej_neg(&r2, &r2); for(i = 1; i <= n_points; i++) { if (i > ECMULT_PIPPENGER_THRESHOLD) { int bucket_window = secp256k1_pippenger_bucket_window(i); @@ -2954,10 +3266,10 @@ void test_ecmult_multi_batching(void) { size_t scratch_size = secp256k1_strauss_scratch_size(i); scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); } - CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); secp256k1_gej_add_var(&r, &r, &r2, NULL); CHECK(secp256k1_gej_is_infinity(&r)); - secp256k1_scratch_destroy(scratch); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); } free(sc); free(pt); @@ -2972,13 +3284,15 @@ void run_ecmult_multi_tests(void) { test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); test_ecmult_multi(NULL, secp256k1_ecmult_multi_var); test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single); + test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single); test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single); - secp256k1_scratch_destroy(scratch); + test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); /* Run test_ecmult_multi with space for exactly one point */ scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); - secp256k1_scratch_destroy(scratch); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); test_ecmult_multi_batch_size_helper(); test_ecmult_multi_batching(); @@ -3050,7 +3364,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) { } bits = 128; #endif - skew = secp256k1_wnaf_const(wnaf, num, w, bits); + skew = secp256k1_wnaf_const(wnaf, &num, w, bits); for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) { secp256k1_scalar t; @@ -3954,6 +4268,258 @@ void run_eckey_edge_case_test(void) { secp256k1_context_set_illegal_callback(ctx, NULL, NULL); } +void test_xonly_pubkey(void) { + unsigned char sk[32] = { 0 }; + unsigned char ones32[32]; + unsigned char zeros64[64] = { 0 }; + secp256k1_pubkey xy_pk; + secp256k1_xonly_pubkey xonly_pk; + secp256k1_xonly_pubkey xonly_pk_tmp; + secp256k1_ge pk1; + secp256k1_ge pk2; + secp256k1_fe y; + int is_negated; + unsigned char buf32[32]; + + /* sk = 0 should fail */ + CHECK(secp256k1_xonly_pubkey_create(ctx, &xonly_pk, sk) == 0); + + /* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */ + sk[0] = 3; + CHECK(secp256k1_ec_pubkey_create(ctx, &xy_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_create(ctx, &xonly_pk, sk) == 1); + CHECK(memcmp(&xonly_pk, &xy_pk, sizeof(xonly_pk)) == 0); + /* Test result of from_pubkey */ + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk_tmp, &is_negated, &xy_pk) == 1); + CHECK(memcmp(&xonly_pk_tmp, &xonly_pk, sizeof(xonly_pk)) == 0); + CHECK(is_negated == 0); + + /* Choose a secret key such that pubkey and xonly_pubkey are each others + * negation. */ + sk[0] = 6; + CHECK(secp256k1_ec_pubkey_create(ctx, &xy_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_create(ctx, &xonly_pk, sk) == 1); + CHECK(memcmp(&xonly_pk, &xy_pk, sizeof(xonly_pk)) != 0); + secp256k1_pubkey_load(ctx, &pk1, &xy_pk); + secp256k1_pubkey_load(ctx, &pk2, (secp256k1_pubkey *) &xonly_pk); + CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1); + secp256k1_fe_negate(&y, &pk2.y, 1); + CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1); + /* Test result of from_pubkey */ + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk_tmp, &is_negated, &xy_pk) == 1); + CHECK(memcmp(&xonly_pk_tmp, &xonly_pk, sizeof(xonly_pk)) == 0); + CHECK(is_negated == 1); + + /* Serialization and parse roundtrip */ + CHECK(secp256k1_xonly_pubkey_create(ctx, &xonly_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, buf32) == 1); + CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0); + + /* Can't parse a byte string that's not a valid X coordinate */ + memset(ones32, 0xFF, sizeof(ones32)); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, ones32) == 0); + CHECK(memcmp(&xonly_pk_tmp, zeros64, sizeof(xonly_pk_tmp)) == 0); +} + +void test_xonly_pubkey_api(void) { + secp256k1_xonly_pubkey pk; + secp256k1_xonly_pubkey pk_tweaked; + secp256k1_pubkey xy_pk; + unsigned char sk[32]; + unsigned char xy_sk[32]; + unsigned char buf32[32]; + unsigned char ones32[32]; + unsigned char zeros32[32] = { 0 }; + unsigned char tweak[32]; + int is_negated; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + + secp256k1_rand256(sk); + memset(ones32, 0xFF, 32); + secp256k1_rand256(tweak); + secp256k1_rand256(xy_sk); + CHECK(secp256k1_ec_pubkey_create(sign, &xy_pk, xy_sk) == 1); + + ecount = 0; + CHECK(secp256k1_xonly_pubkey_create(none, &pk, sk) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_create(sign, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_create(vrfy, &pk, sk) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_pubkey_create(sign, NULL, sk) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_xonly_pubkey_create(sign, &pk, NULL) == 0); + CHECK(ecount == 4); + + CHECK(secp256k1_xonly_pubkey_create(sign, &pk, sk) == 1); + ecount = 0; + CHECK(secp256k1_xonly_pubkey_serialize(none, NULL, &pk) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, NULL) == 0); + CHECK(memcmp(buf32, zeros32, 32) == 0); + CHECK(ecount == 2); + { + /* A pubkey filled with 0s will fail to serialize due to pubkey_load + * special casing. */ + secp256k1_xonly_pubkey pk_tmp; + memset(&pk_tmp, 0, sizeof(pk_tmp)); + CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &pk_tmp) == 0); + } + /* pubkey_load called illegal callback */ + CHECK(ecount == 3); + CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &pk) == 1); + + ecount = 0; + CHECK(secp256k1_xonly_pubkey_parse(none, NULL, buf32) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_parse(none, &pk, NULL) == 0); + CHECK(ecount == 2); + /* Invalid field element */ + CHECK(secp256k1_xonly_pubkey_parse(none, &pk, ones32) == 0); + CHECK(ecount == 2); + /* There's no point with x-coordinate 0 on secp256k1 */ + CHECK(secp256k1_xonly_pubkey_parse(none, &pk, zeros32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_pubkey_parse(none, &pk, buf32) == 1); + + ecount = 0; + CHECK(secp256k1_xonly_seckey_tweak_add(none, sk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_seckey_tweak_add(sign, sk, tweak) == 1); + CHECK(secp256k1_xonly_seckey_tweak_add(vrfy, sk, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_seckey_tweak_add(sign, NULL, tweak) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_xonly_seckey_tweak_add(sign, sk, NULL) == 0); + CHECK(ecount == 4); + + ecount = 0; + CHECK(secp256k1_xonly_pubkey_tweak_add(none, &pk, &is_negated, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(sign, &pk, &is_negated, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &pk, &is_negated, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, NULL, &is_negated, tweak) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &pk, NULL, tweak) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &pk, &is_negated, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + pk_tweaked = pk; + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &pk_tweaked, &is_negated, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_test(none, &pk_tweaked, is_negated, &pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_tweak_test(sign, &pk_tweaked, is_negated, &pk, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_pubkey_tweak_test(vrfy, &pk_tweaked, is_negated, &pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_test(vrfy, NULL, is_negated, &pk, tweak) == 0); + CHECK(ecount == 3); + /* invalid is_negated value */ + CHECK(secp256k1_xonly_pubkey_tweak_test(vrfy, &pk_tweaked, 2, &pk, tweak) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_xonly_pubkey_tweak_test(vrfy, &pk_tweaked, is_negated, NULL, tweak) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_xonly_pubkey_tweak_test(vrfy, &pk_tweaked, is_negated, &pk, NULL) == 0); + CHECK(ecount == 5); + + + ecount = 0; + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &pk, &is_negated, &xy_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(sign, &pk, &is_negated, &xy_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(vrfy, &pk, &is_negated, &xy_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, NULL, &is_negated, &xy_pk) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &pk, NULL, &xy_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &pk, &is_negated, NULL) == 0); + CHECK(ecount == 2); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + +void test_xonly_pubkey_tweak(void) { + unsigned char zeros64[64] = { 0 }; + unsigned char overflows[32]; + unsigned char sk[32]; + secp256k1_xonly_pubkey internal_pk; + secp256k1_xonly_pubkey output_pk; + secp256k1_pubkey xy_pk; + int is_negated; + unsigned char tweak[32]; + + memset(overflows, 0xff, sizeof(overflows)); + secp256k1_rand256(sk); + CHECK(secp256k1_xonly_pubkey_create(ctx, &internal_pk, sk) == 1); + + memset(tweak, 1, sizeof(tweak)); + output_pk = internal_pk; + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &is_negated, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &output_pk, is_negated, &internal_pk, tweak) == 1); + /* Using privkey_tweak_add gives the same result */ + CHECK(secp256k1_xonly_seckey_tweak_add(ctx, sk, tweak) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &xy_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_pk, &is_negated, &xy_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &output_pk, is_negated, &internal_pk, tweak) == 1); + + /* Wrong is_negated */ + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &output_pk, !is_negated, &internal_pk, tweak) == 0); + /* Wrong public key */ + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &internal_pk, is_negated, &internal_pk, tweak) == 0); + + /* Overflowing secret key not allowed */ + CHECK(secp256k1_xonly_seckey_tweak_add(ctx, overflows, tweak) == 0); + + /* Overflowing tweak not allowed */ + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &output_pk, is_negated, &internal_pk, overflows) == 0); + CHECK(secp256k1_xonly_seckey_tweak_add(ctx, sk, overflows) == 0); + CHECK(memcmp(sk, zeros64, sizeof(sk)) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &is_negated, overflows) == 0); + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); +} + +/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1 + * additional pubkeys by calling tweak_add. Then verifies every tweak starting + * from the last pubkey. */ +#define N_PUBKEYS 32 +void test_xonly_pubkey_tweak_recursive(void) { + unsigned char sk[32]; + secp256k1_xonly_pubkey pk[N_PUBKEYS]; + int is_negated[N_PUBKEYS]; + unsigned char tweak[N_PUBKEYS - 1][32]; + int i; + + secp256k1_rand256(sk); + CHECK(secp256k1_xonly_pubkey_create(ctx, &pk[0], sk) == 1); + /* Add tweaks */ + for (i = 0; i < N_PUBKEYS - 1; i++) { + memset(tweak[i], i + 1, sizeof(tweak[i])); + pk[i + 1] = pk[i]; + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &is_negated[i + 1], tweak[i]) == 1); + } + + /* Verify tweaks */ + for (i = N_PUBKEYS - 1; i > 0; i--) { + CHECK(secp256k1_xonly_pubkey_tweak_test(ctx, &pk[i], is_negated[i], &pk[i - 1], tweak[i - 1]) == 1); + } +} +#undef N_PUBKEYS + void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { secp256k1_scalar nonce; do { @@ -4315,7 +4881,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ if (valid_der) { ret |= (!roundtrips_der_lax) << 12; ret |= (len_der != len_der_lax) << 13; - ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14; + ret |= ((len_der != len_der_lax) || (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0)) << 14; } ret |= (roundtrips_der != roundtrips_der_lax) << 15; if (parsed_der) { @@ -4356,7 +4922,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ ret |= (roundtrips_der != roundtrips_openssl) << 7; if (roundtrips_openssl) { ret |= (len_der != (size_t)len_openssl) << 8; - ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9; + ret |= ((len_der != (size_t)len_openssl) || (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0)) << 9; } #endif return ret; @@ -5012,6 +5578,10 @@ void run_ecdsa_openssl(void) { # include "modules/ecdh/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/tests_impl.h" #endif @@ -5019,6 +5589,15 @@ void run_ecdsa_openssl(void) { int main(int argc, char **argv) { unsigned char seed16[16] = {0}; unsigned char run32[32] = {0}; + + /* Disable buffering for stdout to improve reliability of getting + * diagnostic information. Happens right at the start of main because + * setbuf must be used before any other operation on the stream. */ + setbuf(stdout, NULL); + /* Also disable buffering for stderr because it's not guaranteed that it's + * unbuffered on all systems. */ + setbuf(stderr, NULL); + /* find iteration count */ if (argc > 1) { count = strtol(argv[1], NULL, 0); @@ -5030,7 +5609,7 @@ int main(int argc, char **argv) { const char* ch = argv[2]; while (pos < 16 && ch[0] != 0 && ch[1] != 0) { unsigned short sh; - if (sscanf(ch, "%2hx", &sh)) { + if ((sscanf(ch, "%2hx", &sh)) == 1) { seed16[pos] = sh; } else { break; @@ -5062,7 +5641,8 @@ int main(int argc, char **argv) { printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); /* initialize */ - run_context_tests(); + run_context_tests(0); + run_context_tests(1); run_scratch_tests(); ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); if (secp256k1_rand_bits(1)) { @@ -5119,11 +5699,23 @@ int main(int argc, char **argv) { /* EC key edge cases */ run_eckey_edge_case_test(); + /* xonly key test cases */ + test_xonly_pubkey(); + test_xonly_pubkey_api(); + test_xonly_pubkey_tweak(); + test_xonly_pubkey_tweak_recursive(); + #ifdef ENABLE_MODULE_ECDH /* ecdh tests */ run_ecdh_tests(); #endif + run_nonce_function_bipschnorr_tests(); +#ifdef ENABLE_MODULE_SCHNORRSIG + /* Schnorrsig tests */ + run_schnorrsig_tests(); +#endif + /* ecdsa tests */ run_random_pubkeys(); run_ecdsa_der_parse(); diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c index ab9779b02fc54..b44e357cb69c7 100644 --- a/src/tests_exhaustive.c +++ b/src/tests_exhaustive.c @@ -212,14 +212,14 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ data.pt[0] = group[x]; data.pt[1] = group[y]; - secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2); + secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2); ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp); } } } } } - secp256k1_scratch_destroy(scratch); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); } void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { diff --git a/src/util.h b/src/util.h index e1f5b764527d9..9deb61bc59d15 100644 --- a/src/util.h +++ b/src/util.h @@ -84,6 +84,47 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void return ret; } +#if defined(__BIGGEST_ALIGNMENT__) +#define ALIGNMENT __BIGGEST_ALIGNMENT__ +#else +/* Using 16 bytes alignment because common architectures never have alignment + * requirements above 8 for any of the types we care about. In addition we + * leave some room because currently we don't care about a few bytes. */ +#define ALIGNMENT 16 +#endif + +#define ROUND_TO_ALIGN(size) (((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT) + +/* Assume there is a contiguous memory object with bounds [base, base + max_size) + * of which the memory range [base, *prealloc_ptr) is already allocated for usage, + * where *prealloc_ptr is an aligned pointer. In that setting, this functions + * reserves the subobject [*prealloc_ptr, *prealloc_ptr + alloc_size) of + * alloc_size bytes by increasing *prealloc_ptr accordingly, taking into account + * alignment requirements. + * + * The function returns an aligned pointer to the newly allocated subobject. + * + * This is useful for manual memory management: if we're simply given a block + * [base, base + max_size), the caller can use this function to allocate memory + * in this block and keep track of the current allocation state with *prealloc_ptr. + * + * It is VERIFY_CHECKed that there is enough space left in the memory object and + * *prealloc_ptr is aligned relative to base. + */ +static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_size, void* base, size_t max_size) { + size_t aligned_alloc_size = ROUND_TO_ALIGN(alloc_size); + void* ret; + VERIFY_CHECK(prealloc_ptr != NULL); + VERIFY_CHECK(*prealloc_ptr != NULL); + VERIFY_CHECK(base != NULL); + VERIFY_CHECK((unsigned char*)*prealloc_ptr >= (unsigned char*)base); + VERIFY_CHECK(((unsigned char*)*prealloc_ptr - (unsigned char*)base) % ALIGNMENT == 0); + VERIFY_CHECK((unsigned char*)*prealloc_ptr - (unsigned char*)base + aligned_alloc_size <= max_size); + ret = *prealloc_ptr; + *((unsigned char**)prealloc_ptr) += aligned_alloc_size; + return ret; +} + /* Macro for restrict, when available and not in a VERIFY build. */ #if defined(SECP256K1_BUILD) && defined(VERIFY) # define SECP256K1_RESTRICT