From d5f970e6479c9cef435aee9b266badd8fb088aaa Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Sun, 25 Feb 2024 22:14:12 +0100 Subject: [PATCH] Squashed 'src/secp256k1/' changes from efe85c70a2..282757398c 282757398c WIP: Silent merge conflicts 42dc5a9494 batch: Generate graphs for batch verification speed up fd9f58842f batch, extrakeys: Add benchmark for batch verify and `tweak_add_check` 94df19cd26 batch: Add tests for `batch_add_*` APIs 32d3f93683 batch,ecmult: Add tests for core batch APIs and `strauss_batch` refactor 30d5b37526 batch: Add API usage example 014c1501e2 batch: Add `batch_add_*` APIs 57f1c10a48 batch, ecmult: Add `batch_verify` API and refactor `strauss_batch` 871ade3aac batch: Add `create` and `destroy` APIs 960b6dafa4 batch: Initialize an experimental batch module 0653a25d50 Merge bitcoin-core/secp256k1#1486: ci: Update cache action 94a14d5290 ci: Update cache action 2483627299 Merge bitcoin-core/secp256k1#1483: cmake: Recommend native CMake commands in README 5ad3aa3dcd Merge bitcoin-core/secp256k1#1484: tests: Drop redundant _scalar_check_overflow calls 51df2d9ab3 tests: Drop redundant _scalar_check_overflow calls 3777e3f36a cmake: Recommend native CMake commands in README e4af41c61b Merge bitcoin-core/secp256k1#1249: cmake: Add `SECP256K1_LATE_CFLAGS` configure option 3bf4d68fc0 Merge bitcoin-core/secp256k1#1482: build: Clean up handling of module dependencies e6822678ea build: Error if required module explicitly off 89ec583ccf build: Clean up handling of module dependencies 44378867a0 Merge bitcoin-core/secp256k1#1468: v0.4.1 release aftermath a9db9f2d75 Merge bitcoin-core/secp256k1#1480: Get rid of untested sizeof(secp256k1_ge_storage) == 64 code path 74b7c3b53e Merge bitcoin-core/secp256k1#1476: include: make docs more consistent b37fdb28ce check-abi: Minor UI improvements ad5f589a94 check-abi: Default to HEAD for new version 9fb7e2f156 release process: Style and formatting nits ba5d72d626 assumptions: Use new STATIC_ASSERT macro e53c2d9ffc Require that sizeof(secp256k1_ge_storage) == 64 d0ba2abbff util: Add STATIC_ASSERT macro da7bc1b803 include: in doc, remove article in front of "pointer" aa3dd5280b include: make doc about ctx more consistent e3f690015a include: remove obvious "cannot be NULL" doc d373bf6d08 Merge bitcoin-core/secp256k1#1474: tests: restore scalar_mul test 79e094517c Merge bitcoin-core/secp256k1#1473: Fix typos 3dbfb48946 tests: restore scalar_mul test d77170a88d Fix typos e7053d065b release process: Add email step 429d21dc79 release process: Run sanity checks on release PR 42f8c51402 cmake: Add `SECP256K1_LATE_CFLAGS` configure option git-subtree-dir: src/secp256k1 git-subtree-split: 282757398c85c747addee74c5e410ab0b050f4ac --- .../install-homebrew-valgrind/action.yml | 2 +- .gitignore | 1 + CMakeLists.txt | 41 ++- CONTRIBUTING.md | 2 +- Makefile.am | 15 + README.md | 7 +- ci/cirrus.sh | 82 +++++ cmake/AllTargetsCompileOptions.cmake | 12 + configure.ac | 43 ++- contrib/lax_der_parsing.h | 4 +- doc/release-process.md | 72 ++-- doc/speedup-batch.md | 15 + doc/speedup-batch/.gitignore | 1 + doc/speedup-batch/Makefile | 23 ++ doc/speedup-batch/bench.sh | 13 + doc/speedup-batch/bench_output.txt | 137 ++++++++ doc/speedup-batch/bench_output.txt.log | 127 +++++++ doc/speedup-batch/plot.gp | 41 +++ .../schnorrsig-speedup-batch.png | Bin 0 -> 8192 bytes .../tweakcheck-speedup-batch.png | Bin 0 -> 8639 bytes examples/batch.c | 181 ++++++++++ include/secp256k1.h | 66 ++-- include/secp256k1_batch.h | 110 ++++++ include/secp256k1_ecdh.h | 2 +- include/secp256k1_ellswift.h | 4 +- include/secp256k1_extrakeys.h | 10 +- include/secp256k1_preallocated.h | 14 +- include/secp256k1_recovery.h | 20 +- include/secp256k1_schnorrsig.h | 4 +- include/secp256k1_schnorrsig_batch.h | 42 +++ include/secp256k1_tweak_check_batch.h | 50 +++ src/assumptions.h | 100 +++--- src/bench.c | 40 ++- src/bench.h | 4 +- src/ecmult_impl.h | 73 ++-- src/modules/batch/Makefile.am.include | 3 + src/modules/batch/main_impl.h | 206 ++++++++++++ src/modules/batch/tests_impl.h | 210 ++++++++++++ src/modules/extrakeys/Makefile.am.include | 7 + src/modules/extrakeys/batch_add_impl.h | 151 +++++++++ src/modules/extrakeys/batch_add_tests_impl.h | 165 +++++++++ src/modules/extrakeys/bench_impl.h | 139 ++++++++ src/modules/schnorrsig/Makefile.am.include | 7 + src/modules/schnorrsig/batch_add_impl.h | 158 +++++++++ src/modules/schnorrsig/batch_add_tests_impl.h | 313 ++++++++++++++++++ src/modules/schnorrsig/bench_impl.h | 49 ++- src/modules/schnorrsig/tests_impl.h | 89 ++++- src/scalar_impl.h | 4 +- src/secp256k1.c | 49 ++- src/tests.c | 183 +++++++--- src/util.h | 16 +- tools/check-abi.sh | 27 +- 52 files changed, 2847 insertions(+), 287 deletions(-) create mode 100755 ci/cirrus.sh create mode 100644 cmake/AllTargetsCompileOptions.cmake create mode 100644 doc/speedup-batch.md create mode 100644 doc/speedup-batch/.gitignore create mode 100644 doc/speedup-batch/Makefile create mode 100755 doc/speedup-batch/bench.sh create mode 100644 doc/speedup-batch/bench_output.txt create mode 100644 doc/speedup-batch/bench_output.txt.log create mode 100644 doc/speedup-batch/plot.gp create mode 100644 doc/speedup-batch/schnorrsig-speedup-batch.png create mode 100644 doc/speedup-batch/tweakcheck-speedup-batch.png create mode 100644 examples/batch.c create mode 100644 include/secp256k1_batch.h create mode 100644 include/secp256k1_schnorrsig_batch.h create mode 100644 include/secp256k1_tweak_check_batch.h create mode 100644 src/modules/batch/Makefile.am.include create mode 100644 src/modules/batch/main_impl.h create mode 100644 src/modules/batch/tests_impl.h create mode 100644 src/modules/extrakeys/batch_add_impl.h create mode 100644 src/modules/extrakeys/batch_add_tests_impl.h create mode 100644 src/modules/extrakeys/bench_impl.h create mode 100644 src/modules/schnorrsig/batch_add_impl.h create mode 100644 src/modules/schnorrsig/batch_add_tests_impl.h diff --git a/.github/actions/install-homebrew-valgrind/action.yml b/.github/actions/install-homebrew-valgrind/action.yml index 094ff891f71f7..ce10eb2686cd4 100644 --- a/.github/actions/install-homebrew-valgrind/action.yml +++ b/.github/actions/install-homebrew-valgrind/action.yml @@ -16,7 +16,7 @@ runs: cat valgrind_fingerprint shell: bash - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: cache with: path: ${{ env.CI_HOMEBREW_CELLAR_VALGRIND }} diff --git a/.gitignore b/.gitignore index 574902b8b5e45..8e50c65049d64 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ ctime_tests ecdh_example ecdsa_example schnorr_example +batch_example *.exe *.so *.a diff --git a/CMakeLists.txt b/CMakeLists.txt index cf0dc3ba93ff3..9ef7defe51cba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,29 +51,40 @@ endif() option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL}) -option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON) -if(SECP256K1_ENABLE_MODULE_ECDH) - add_compile_definitions(ENABLE_MODULE_ECDH=1) -endif() +## Modules +# We declare all options before processing them, to make sure we can express +# dependendencies while processing. +option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON) option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF) -if(SECP256K1_ENABLE_MODULE_RECOVERY) - add_compile_definitions(ENABLE_MODULE_RECOVERY=1) -endif() - option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) +option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) + +# Processing must be done in a topological sorting of the dependency graph +# (dependent module first). +if(SECP256K1_ENABLE_MODULE_ELLSWIFT) + add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) +endif() + if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS) + message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.") + endif() set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON) add_compile_definitions(ENABLE_MODULE_SCHNORRSIG=1) endif() + if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) add_compile_definitions(ENABLE_MODULE_EXTRAKEYS=1) endif() -option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) -if(SECP256K1_ENABLE_MODULE_ELLSWIFT) - add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) +if(SECP256K1_ENABLE_MODULE_RECOVERY) + add_compile_definitions(ENABLE_MODULE_RECOVERY=1) +endif() + +if(SECP256K1_ENABLE_MODULE_ECDH) + add_compile_definitions(ENABLE_MODULE_ECDH=1) endif() option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) @@ -254,9 +265,14 @@ if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS OR SECP256K1_BUILD_EXHAUST enable_testing() endif() +set(SECP256K1_LATE_CFLAGS "" CACHE STRING "Compiler flags that are added to the command line after all other flags added by the build system.") +include(AllTargetsCompileOptions) + add_subdirectory(src) +all_targets_compile_options(src "${SECP256K1_LATE_CFLAGS}") if(SECP256K1_BUILD_EXAMPLES) add_subdirectory(examples) + all_targets_compile_options(examples "${SECP256K1_LATE_CFLAGS}") endif() message("\n") @@ -330,6 +346,9 @@ else() message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}") message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}") endif() +if(SECP256K1_LATE_CFLAGS) + message("SECP256K1_LATE_CFLAGS ................. ${SECP256K1_LATE_CFLAGS}") +endif() message("\n") if(SECP256K1_EXPERIMENTAL) message( diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5e457913acf8..5fbf7332c97a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ The Contributor Workflow & Peer Review in libsecp256k1 are similar to Bitcoin Co In addition, libsecp256k1 tries to maintain the following coding conventions: -* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Morever, it should be possible to use the library without any heap allocations. +* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Moreover, it should be possible to use the library without any heap allocations. * The tests should cover all lines and branches of the library (see [Test coverage](#coverage)). * Operations involving secret data should be tested for being constant time with respect to the secrets (see [src/ctime_tests.c](src/ctime_tests.c)). * Local variables containing secret data should be cleared explicitly to try to delete secrets from memory. diff --git a/Makefile.am b/Makefile.am index 549861791569d..47f1bba4ecb58 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,6 +181,17 @@ if BUILD_WINDOWS schnorr_example_LDFLAGS += -lbcrypt endif TESTS += schnorr_example +if ENABLE_MODULE_BATCH +noinst_PROGRAMS += batch_example +batch_example_SOURCES = examples/batch.c +batch_example_CPPFLAGS = -I$(top_srcdir)/include +batch_example_LDADD = libsecp256k1.la +batch_example_LDFLAGS = -static +if BUILD_WINDOWS +batch_example_LDFLAGS += -lbcrypt +endif +TESTS += batch_example +endif endif endif @@ -271,3 +282,7 @@ endif if ENABLE_MODULE_ELLSWIFT include src/modules/ellswift/Makefile.am.include endif + +if ENABLE_MODULE_BATCH +include src/modules/batch/Makefile.am.include +endif diff --git a/README.md b/README.md index 4013e6a93b892..6a5332623456b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Features: * Optional module for public key recovery. * Optional module for ECDH key exchange. * Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). +* Optional module for Batch Verification (experimental). Implementation details ---------------------- @@ -79,9 +80,9 @@ To maintain a pristine source tree, CMake encourages to perform an out-of-source $ mkdir build && cd build $ cmake .. - $ make - $ make check # run the test suite - $ sudo make install # optional + $ cmake --build . + $ ctest # run the test suite + $ sudo cmake --build . --target install # optional To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake .. -LH` to see the full list of available flags. diff --git a/ci/cirrus.sh b/ci/cirrus.sh new file mode 100755 index 0000000000000..2b8936a2f122a --- /dev/null +++ b/ci/cirrus.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +set -e +set -x + +export LC_ALL=C + +# Start persistent wineserver if necessary. +# This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously. +case "$WRAPPER_CMD" in + *wine*) + # This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards. + wineserver -p && wine hh.exe + ;; +esac + +env >> test_env.log + +$CC -v || true +valgrind --version || true +$WRAPPER_CMD --version || true + +./autogen.sh + +./configure \ + --enable-experimental="$EXPERIMENTAL" \ + --with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \ + --with-ecmult-window="$ECMULTWINDOW" \ + --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ + --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-batch="$BATCH" \ + --enable-examples="$EXAMPLES" \ + --with-valgrind="$WITH_VALGRIND" \ + --host="$HOST" $EXTRAFLAGS + +# We have set "-j" in MAKEFLAGS. +make + +# Print information about binaries so that we can see that the architecture is correct +file *tests* || true +file bench* || true +file .libs/* || true + +# This tells `make check` to wrap test invocations. +export LOG_COMPILER="$WRAPPER_CMD" + +make "$BUILD" + +if [ "$BENCH" = "yes" ] +then + # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool + EXEC='./libtool --mode=execute' + if [ -n "$WRAPPER_CMD" ] + then + EXEC="$EXEC $WRAPPER_CMD" + fi + { + $EXEC ./bench_ecmult + $EXEC ./bench_internal + $EXEC ./bench + } >> bench.log 2>&1 +fi + +if [ "$CTIMETEST" = "yes" ] +then + ./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1 +fi + +# Rebuild precomputed files (if not cross-compiling). +if [ -z "$HOST" ] +then + make clean-precomp + make precomp +fi + +# Shutdown wineserver again +wineserver -k || true + +# Check that no repo files have been modified by the build. +# (This fails for example if the precomp files need to be updated in the repo.) +git diff --exit-code diff --git a/cmake/AllTargetsCompileOptions.cmake b/cmake/AllTargetsCompileOptions.cmake new file mode 100644 index 0000000000000..6e420e0fdee26 --- /dev/null +++ b/cmake/AllTargetsCompileOptions.cmake @@ -0,0 +1,12 @@ +# Add compile options to all targets added in the subdirectory. +function(all_targets_compile_options dir options) + get_directory_property(targets DIRECTORY ${dir} BUILDSYSTEM_TARGETS) + separate_arguments(options) + set(compiled_target_types STATIC_LIBRARY SHARED_LIBRARY OBJECT_LIBRARY EXECUTABLE) + foreach(target ${targets}) + get_target_property(type ${target} TYPE) + if(type IN_LIST compiled_target_types) + target_compile_options(${target} PRIVATE ${options}) + endif() + endforeach() +endfunction() diff --git a/configure.ac b/configure.ac index 2c1596775ed48..00d9aa058be95 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,10 @@ AC_ARG_ENABLE(module_ellswift, AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) +AC_ARG_ENABLE(module_batch, + AS_HELP_STRING([--enable-module-batch],[enable batch verification module (experimental) [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_batch], [no], [yes])]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -387,29 +391,36 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" ### Handle module options ### -if test x"$enable_module_ecdh" = x"yes"; then - SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1" -fi - -if test x"$enable_module_recovery" = x"yes"; then - SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1" +# Processing must be done in a reverse topological sorting of the dependency graph +# (dependent module first). +if test x"$enable_module_ellswift" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi if test x"$enable_module_schnorrsig" = x"yes"; then - SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1" + if test x"$enable_module_extrakeys" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.]) + fi enable_module_extrakeys=yes + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1" fi -if test x"$enable_module_ellswift" = x"yes"; then - SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" -fi - -# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig -# module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1" fi +if test x"$enable_module_recovery" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1" +fi + +if test x"$enable_module_ecdh" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1" +fi + +if test x"$enable_module_batch" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_BATCH, 1, [Define this symbol to enable the batch verification module]) +fi + if test x"$enable_external_default_callbacks" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1" fi @@ -422,11 +433,15 @@ 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 batch verification module: $enable_module_batch]) AC_MSG_NOTICE([******]) else if test x"$set_asm" = x"arm32"; then AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_batch" = x"yes"; then + AC_MSG_ERROR([batch verification module is experimental. Use --enable-experimental to allow.]) + fi fi ### @@ -447,6 +462,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"ye AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_BATCH], [test x"$enable_module_batch" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) @@ -469,6 +485,7 @@ echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module ellswift = $enable_module_ellswift" +echo " module batch = $enable_module_batch" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/contrib/lax_der_parsing.h b/contrib/lax_der_parsing.h index 034a38e6a0e1d..37c8c691f2f04 100644 --- a/contrib/lax_der_parsing.h +++ b/contrib/lax_der_parsing.h @@ -67,8 +67,8 @@ extern "C" { * * Returns: 1 when the signature could be parsed, 0 otherwise. * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input: a pointer to the signature to be parsed + * Out: sig: pointer to a signature object + * In: input: pointer to the signature to be parsed * inputlen: the length of the array pointed to be input * * This function will accept any valid DER encoded signature, even if the diff --git a/doc/release-process.md b/doc/release-process.md index 51e337a5ab650..cdf62430dfb7d 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -1,4 +1,4 @@ -# Release Process +# Release process This document outlines the process for releasing versions of the form `$MAJOR.$MINOR.$PATCH`. @@ -14,31 +14,30 @@ This process also assumes that there will be no minor releases for old major rel We aim to cut a regular release every 3-4 months, approximately twice as frequent as major Bitcoin Core releases. Every second release should be published one month before the feature freeze of the next major Bitcoin Core release, allowing sufficient time to update the library in Core. -## Sanity Checks -Perform these checks before creating a release: +## Sanity checks +Perform these checks when reviewing the release PR (see below): 1. Ensure `make distcheck` doesn't fail. -```shell -./autogen.sh && ./configure --enable-dev-mode && make distcheck -``` + ```shell + ./autogen.sh && ./configure --enable-dev-mode && make distcheck + ``` 2. Check installation with autotools: -```shell -dir=$(mktemp -d) -./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -RlAh $dir -gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa -``` + ```shell + dir=$(mktemp -d) + ./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -RlAh $dir + gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa + ``` 3. Check installation with CMake: -```shell -dir=$(mktemp -d) -build=$(mktemp -d) -cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build --target install && ls -RlAh $dir -gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa -``` -4. Use the [`check-abi.sh`](/tools/check-abi.sh) tool to ensure there are no unexpected ABI incompatibilities and that the version number and release notes accurately reflect all potential ABI changes. To run this tool, the `abi-dumper` and `abi-compliance-checker` packages are required. - -```shell -tools/check-abi.sh -``` + ```shell + dir=$(mktemp -d) + build=$(mktemp -d) + cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build --target install && ls -RlAh $dir + gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa + ``` +4. Use the [`check-abi.sh`](/tools/check-abi.sh) tool to verify that there are no unexpected ABI incompatibilities and that the version number and the release notes accurately reflect all potential ABI changes. To run this tool, the `abi-dumper` and `abi-compliance-checker` packages are required. + ```shell + tools/check-abi.sh + ``` ## Regular release @@ -47,27 +46,29 @@ tools/check-abi.sh * adding a section for the release (make sure that the version number is a link to a diff between the previous and new version), * removing the `[Unreleased]` section header, and * including an entry for `### ABI Compatibility` if it doesn't exist, - * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and - * if this is not a patch release - * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac` and + * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and, + * if this is not a patch release, + * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac`, and * updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`. -2. After the PR is merged, tag the commit and push it: +2. Perform the [sanity checks](#sanity-checks) on the PR branch. +3. After the PR is merged, tag the commit, and push the tag: ``` RELEASE_COMMIT= git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" $RELEASE_COMMIT git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH ``` -3. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that +4. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that * sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`, * increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and * adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md). If other maintainers are not present to approve the PR, it can be merged without ACKs. -4. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). +5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). +6. Send an announcement email to the bitcoin-dev mailing list. ## Maintenance release -Note that bugfixes only need to be backported to releases for which no compatible release without the bug exists. +Note that bug fixes need to be backported only to releases for which no compatible release without the bug exists. 1. If there's no maintenance branch `$MAJOR.$MINOR`, create one: ``` @@ -75,19 +76,18 @@ Note that bugfixes only need to be backported to releases for which no compatibl git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR ``` 2. Open a pull request to the `$MAJOR.$MINOR` branch that - * includes the bugfixes, + * includes the bug fixes, * finalizes the release notes similar to a regular release, * increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac` and the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt` (with commit message `"release: bump versions for $MAJOR.$MINOR.$PATCH"`, for example). -3. After the PRs are merged, update the release branch and tag the commit: +3. Perform the [sanity checks](#sanity-checks) on the PR branch. +4. After the PRs are merged, update the release branch, tag the commit, and push the tag: ``` git checkout $MAJOR.$MINOR && git pull git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" - ``` -4. Push tag: - ``` git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH ``` -5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). -6. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md). +6. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). +7. Send an announcement email to the bitcoin-dev mailing list. +8. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md). diff --git a/doc/speedup-batch.md b/doc/speedup-batch.md new file mode 100644 index 0000000000000..c695639892554 --- /dev/null +++ b/doc/speedup-batch.md @@ -0,0 +1,15 @@ +# Schnorrsig Batch Verification Speedup + +![Speedup over single verification](speedup-batch/schnorrsig-speedup-batch.png) + +# Tweak Pubkey Check Batch Verification Speedup + +![Speedup over single verification](speedup-batch/tweakcheck-speedup-batch.png) + +Build steps +----------- +To generate the above graphs on your local machine: + + $ cd doc/speedup-batch + $ make + $ make speedup-batch.png diff --git a/doc/speedup-batch/.gitignore b/doc/speedup-batch/.gitignore new file mode 100644 index 0000000000000..773a6df9baff9 --- /dev/null +++ b/doc/speedup-batch/.gitignore @@ -0,0 +1 @@ +*.dat diff --git a/doc/speedup-batch/Makefile b/doc/speedup-batch/Makefile new file mode 100644 index 0000000000000..a6a270d348bff --- /dev/null +++ b/doc/speedup-batch/Makefile @@ -0,0 +1,23 @@ +schnorrsig_data = schnorrsig_batch.dat schnorrsig_single.dat +tweak_data = tweak_batch.dat tweak_single.dat + +bench_output.txt: bench.sh + SECP256K1_BENCH_ITERS=500000 ./bench.sh bench_output.txt + +schnorrsig_batch.dat: bench_output.txt + cat bench_output.txt | grep -v "schnorrsig_batch_verify_1 " | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /schnorrsig_batch_verify_([0-9]+)/, arr) {print arr[1] " " $$3}' > schnorrsig_batch.dat + +schnorrsig_single.dat: bench_output.txt + cat bench_output.txt | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /schnorrsig_verify/) {print $$3}' > schnorrsig_single.dat + +tweak_batch.dat: bench_output.txt + cat bench_output.txt | grep -v "tweak_check_batch_verify_1 " | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /tweak_check_batch_verify_([0-9]+)/, arr) {print arr[1] " " $$3}' > tweak_batch.dat + +tweak_single.dat: bench_output.txt + cat bench_output.txt | awk '{ gsub(/ /,""); print }' | awk -F, 'match($$0, /tweak_add_check/) {print $$3}' > tweak_single.dat + +speedup-batch.png: $(schnorrsig_data) $(tweak_data) plot.gp + gnuplot plot.gp + +clean: + rm *.log *.txt *.dat *.png diff --git a/doc/speedup-batch/bench.sh b/doc/speedup-batch/bench.sh new file mode 100755 index 0000000000000..d38d45dceba20 --- /dev/null +++ b/doc/speedup-batch/bench.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +output_file=$1 +cur_dir=$(pwd) + +cd ../../ +echo "HEAD: $(git rev-parse --short HEAD)" > "$cur_dir/$output_file.log" +make clean +./autogen.sh +./configure --enable-experimental --enable-module-batch --enable-module-schnorrsig >> "$cur_dir/$output_file.log" +make -j +./bench schnorrsig > "$cur_dir/$output_file" +./bench extrakeys >> "$cur_dir/$output_file" \ No newline at end of file diff --git a/doc/speedup-batch/bench_output.txt b/doc/speedup-batch/bench_output.txt new file mode 100644 index 0000000000000..9d257341c1386 --- /dev/null +++ b/doc/speedup-batch/bench_output.txt @@ -0,0 +1,137 @@ +Benchmark , Min(us) , Avg(us) , Max(us) + +schnorrsig_sign , 50.4 , 50.5 , 50.7 +schnorrsig_verify , 89.1 , 89.2 , 89.3 +schnorrsig_batch_verify_1 , 104.0 , 104.0 , 104.0 +schnorrsig_batch_verify_2 , 89.0 , 89.1 , 89.1 +schnorrsig_batch_verify_3 , 84.1 , 84.1 , 84.1 +schnorrsig_batch_verify_4 , 81.5 , 81.5 , 81.5 +schnorrsig_batch_verify_5 , 79.9 , 79.9 , 79.9 +schnorrsig_batch_verify_7 , 78.0 , 78.1 , 78.3 +schnorrsig_batch_verify_9 , 77.0 , 77.0 , 77.1 +schnorrsig_batch_verify_11 , 76.2 , 76.3 , 76.3 +schnorrsig_batch_verify_14 , 75.6 , 75.6 , 75.6 +schnorrsig_batch_verify_17 , 75.2 , 75.2 , 75.2 +schnorrsig_batch_verify_21 , 74.8 , 74.8 , 74.8 +schnorrsig_batch_verify_26 , 74.5 , 74.6 , 74.9 +schnorrsig_batch_verify_32 , 74.3 , 74.5 , 74.7 +schnorrsig_batch_verify_39 , 74.1 , 74.1 , 74.1 +schnorrsig_batch_verify_47 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_57 , 74.5 , 74.5 , 74.5 +schnorrsig_batch_verify_69 , 74.3 , 74.3 , 74.5 +schnorrsig_batch_verify_83 , 74.1 , 74.1 , 74.2 +schnorrsig_batch_verify_100 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_121 , 74.1 , 74.1 , 74.2 +schnorrsig_batch_verify_146 , 73.9 , 73.9 , 74.0 +schnorrsig_batch_verify_176 , 74.0 , 74.2 , 74.5 +schnorrsig_batch_verify_212 , 73.9 , 74.1 , 74.1 +schnorrsig_batch_verify_255 , 74.0 , 74.0 , 74.1 +schnorrsig_batch_verify_307 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_369 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_443 , 73.9 , 74.1 , 74.3 +schnorrsig_batch_verify_532 , 74.0 , 74.0 , 74.1 +schnorrsig_batch_verify_639 , 73.9 , 74.0 , 74.0 +schnorrsig_batch_verify_767 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_921 , 74.0 , 74.0 , 74.1 +schnorrsig_batch_verify_1106 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_1328 , 73.9 , 74.1 , 74.2 +schnorrsig_batch_verify_1594 , 74.0 , 74.1 , 74.1 +schnorrsig_batch_verify_1913 , 74.0 , 74.0 , 74.0 +schnorrsig_batch_verify_2296 , 74.0 , 74.0 , 74.0 +schnorrsig_batch_verify_2756 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_3308 , 74.1 , 74.1 , 74.2 +schnorrsig_batch_verify_3970 , 74.1 , 74.2 , 74.4 +schnorrsig_batch_verify_4765 , 74.0 , 74.1 , 74.2 +schnorrsig_batch_verify_5719 , 74.0 , 74.1 , 74.1 +schnorrsig_batch_verify_6863 , 74.0 , 74.1 , 74.1 +schnorrsig_batch_verify_8236 , 74.0 , 74.1 , 74.1 +schnorrsig_batch_verify_9884 , 74.0 , 74.1 , 74.3 +schnorrsig_batch_verify_11861 , 74.0 , 74.0 , 74.1 +schnorrsig_batch_verify_14234 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_17081 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_20498 , 73.9 , 74.0 , 74.0 +schnorrsig_batch_verify_24598 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_29518 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_35422 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_42507 , 73.9 , 74.0 , 74.0 +schnorrsig_batch_verify_51009 , 73.9 , 74.1 , 74.3 +schnorrsig_batch_verify_61211 , 73.9 , 73.9 , 74.0 +schnorrsig_batch_verify_73454 , 73.9 , 74.0 , 74.3 +schnorrsig_batch_verify_88145 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_105775 , 74.0 , 74.1 , 74.1 +schnorrsig_batch_verify_126931 , 73.9 , 74.0 , 74.1 +schnorrsig_batch_verify_152318 , 73.9 , 73.9 , 74.0 +schnorrsig_batch_verify_182782 , 73.9 , 73.9 , 74.0 +schnorrsig_batch_verify_219339 , 73.9 , 73.9 , 74.0 +schnorrsig_batch_verify_263207 , 74.0 , 74.1 , 74.3 +schnorrsig_batch_verify_315849 , 73.9 , 74.0 , 74.0 +schnorrsig_batch_verify_379019 , 73.9 , 73.9 , 73.9 +schnorrsig_batch_verify_454823 , 74.0 , 74.0 , 74.0 +Benchmark , Min(us) , Avg(us) , Max(us) + +tweak_add_check , 64.7 , 64.7 , 65.0 +tweak_check_batch_verify_1 , 69.7 , 69.8 , 69.8 +tweak_check_batch_verify_2 , 57.2 , 57.2 , 57.3 +tweak_check_batch_verify_3 , 52.0 , 52.1 , 52.2 +tweak_check_batch_verify_4 , 49.4 , 49.5 , 49.5 +tweak_check_batch_verify_5 , 47.9 , 47.9 , 47.9 +tweak_check_batch_verify_7 , 46.1 , 46.1 , 46.2 +tweak_check_batch_verify_9 , 45.2 , 45.2 , 45.4 +tweak_check_batch_verify_11 , 44.5 , 44.6 , 44.6 +tweak_check_batch_verify_14 , 43.9 , 43.9 , 43.9 +tweak_check_batch_verify_17 , 43.5 , 43.5 , 43.5 +tweak_check_batch_verify_21 , 43.1 , 43.1 , 43.1 +tweak_check_batch_verify_26 , 42.8 , 42.8 , 42.8 +tweak_check_batch_verify_32 , 42.5 , 42.6 , 42.6 +tweak_check_batch_verify_39 , 42.3 , 42.4 , 42.4 +tweak_check_batch_verify_47 , 42.2 , 42.2 , 42.2 +tweak_check_batch_verify_57 , 42.1 , 42.2 , 42.3 +tweak_check_batch_verify_69 , 42.0 , 42.1 , 42.1 +tweak_check_batch_verify_83 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_100 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_121 , 42.1 , 42.1 , 42.1 +tweak_check_batch_verify_146 , 42.0 , 42.0 , 42.0 +tweak_check_batch_verify_176 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_212 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_255 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_307 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_369 , 41.9 , 42.0 , 42.1 +tweak_check_batch_verify_443 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_532 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_639 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_767 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_921 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_1106 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_1328 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_1594 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_1913 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_2296 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_2756 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_3308 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_3970 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_4765 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_5719 , 41.9 , 42.0 , 42.1 +tweak_check_batch_verify_6863 , 42.0 , 42.0 , 42.0 +tweak_check_batch_verify_8236 , 42.0 , 42.0 , 42.0 +tweak_check_batch_verify_9884 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_11861 , 41.9 , 42.0 , 42.1 +tweak_check_batch_verify_14234 , 41.9 , 42.0 , 42.0 +tweak_check_batch_verify_17081 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_20498 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_24598 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_29518 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_35422 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_42507 , 41.8 , 41.8 , 41.9 +tweak_check_batch_verify_51009 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_61211 , 41.8 , 41.8 , 41.8 +tweak_check_batch_verify_73454 , 41.8 , 42.0 , 42.2 +tweak_check_batch_verify_88145 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_105775 , 41.8 , 41.8 , 41.8 +tweak_check_batch_verify_126931 , 41.8 , 41.9 , 41.9 +tweak_check_batch_verify_152318 , 41.8 , 41.9 , 42.0 +tweak_check_batch_verify_182782 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_219339 , 41.9 , 42.0 , 42.0 +tweak_check_batch_verify_263207 , 41.9 , 42.0 , 42.1 +tweak_check_batch_verify_315849 , 41.9 , 41.9 , 41.9 +tweak_check_batch_verify_379019 , 41.9 , 41.9 , 42.0 +tweak_check_batch_verify_454823 , 41.9 , 41.9 , 41.9 diff --git a/doc/speedup-batch/bench_output.txt.log b/doc/speedup-batch/bench_output.txt.log new file mode 100644 index 0000000000000..c289c4aab2ecd --- /dev/null +++ b/doc/speedup-batch/bench_output.txt.log @@ -0,0 +1,127 @@ +HEAD: 6ddb0d0c +checking build system type... x86_64-pc-linux-gnu +checking host system type... x86_64-pc-linux-gnu +checking for a BSD-compatible install... /usr/bin/install -c +checking whether build environment is sane... yes +checking for a thread-safe mkdir -p... /usr/bin/mkdir -p +checking for gawk... gawk +checking whether make sets $(MAKE)... yes +checking whether make supports nested variables... yes +checking whether make supports nested variables... (cached) yes +checking for gcc... gcc +checking whether the C compiler works... yes +checking for C compiler default output file name... a.out +checking for suffix of executables... +checking whether we are cross compiling... no +checking for suffix of object files... o +checking whether we are using the GNU C compiler... yes +checking whether gcc accepts -g... yes +checking for gcc option to accept ISO C89... none needed +checking whether gcc understands -c and -o together... yes +checking whether make supports the include directive... yes (GNU style) +checking dependency style of gcc... gcc3 +checking dependency style of gcc... gcc3 +checking for ar... ar +checking the archiver (ar) interface... ar +checking how to print strings... printf +checking for a sed that does not truncate output... /usr/bin/sed +checking for grep that handles long lines and -e... /usr/bin/grep +checking for egrep... /usr/bin/grep -E +checking for fgrep... /usr/bin/grep -F +checking for ld used by gcc... /usr/bin/ld +checking if the linker (/usr/bin/ld) is GNU ld... yes +checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B +checking the name lister (/usr/bin/nm -B) interface... BSD nm +checking whether ln -s works... yes +checking the maximum length of command line arguments... 1572864 +checking how to convert x86_64-pc-linux-gnu file names to x86_64-pc-linux-gnu format... func_convert_file_noop +checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop +checking for /usr/bin/ld option to reload object files... -r +checking for objdump... objdump +checking how to recognize dependent libraries... pass_all +checking for dlltool... no +checking how to associate runtime and link libraries... printf %s\n +checking for archiver @FILE support... @ +checking for strip... strip +checking for ranlib... ranlib +checking command to parse /usr/bin/nm -B output from gcc object... ok +checking for sysroot... no +checking for a working dd... /usr/bin/dd +checking how to truncate binary pipes... /usr/bin/dd bs=4096 count=1 +checking for mt... mt +checking if mt is a manifest tool... no +checking how to run the C preprocessor... gcc -E +checking for ANSI C header files... yes +checking for sys/types.h... yes +checking for sys/stat.h... yes +checking for stdlib.h... yes +checking for string.h... yes +checking for memory.h... yes +checking for strings.h... yes +checking for inttypes.h... yes +checking for stdint.h... yes +checking for unistd.h... yes +checking for dlfcn.h... yes +checking for objdir... .libs +checking if gcc supports -fno-rtti -fno-exceptions... no +checking for gcc option to produce PIC... -fPIC -DPIC +checking if gcc PIC flag -fPIC -DPIC works... yes +checking if gcc static flag -static works... yes +checking if gcc supports -c -o file.o... yes +checking if gcc supports -c -o file.o... (cached) yes +checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes +checking whether -lc should be explicitly linked in... no +checking dynamic linker characteristics... GNU/Linux ld.so +checking how to hardcode library paths into programs... immediate +checking whether stripping libraries is possible... yes +checking if libtool supports shared libraries... yes +checking whether to build shared libraries... yes +checking whether to build static libraries... yes +checking if gcc supports -Werror=unknown-warning-option... no +checking if gcc supports -std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef... yes +checking if gcc supports -Wno-overlength-strings... yes +checking if gcc supports -Wall... yes +checking if gcc supports -Wno-unused-function... yes +checking if gcc supports -Wextra... yes +checking if gcc supports -Wcast-align... yes +checking if gcc supports -Wcast-align=strict... yes +checking if gcc supports -Wconditional-uninitialized... no +checking if gcc supports -fvisibility=hidden... yes +checking for valgrind support... yes +checking for x86_64 assembly availability... yes +configure: ****** +configure: WARNING: experimental build +configure: Experimental features do not have stable APIs or properties, and may not be safe for production use. +configure: Building batch verification module: yes +configure: ****** +checking that generated files are newer than configure... done +configure: creating ./config.status +config.status: creating Makefile +config.status: creating libsecp256k1.pc +config.status: creating src/libsecp256k1-config.h +config.status: src/libsecp256k1-config.h is unchanged +config.status: executing depfiles commands +config.status: executing libtool commands + +Build Options: + with external callbacks = no + with benchmarks = yes + with tests = yes + with coverage = no + with examples = no + module ecdh = no + module recovery = no + module extrakeys = yes + module schnorrsig = yes + module batch = yes + + asm = x86_64 + ecmult window size = 15 + ecmult gen prec. bits = 4 + + valgrind = yes + CC = gcc + CPPFLAGS = + SECP_CFLAGS = -O2 -std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-overlength-strings -Wall -Wno-unused-function -Wextra -Wcast-align -Wcast-align=strict -fvisibility=hidden + CFLAGS = -g -O2 + LDFLAGS = diff --git a/doc/speedup-batch/plot.gp b/doc/speedup-batch/plot.gp new file mode 100644 index 0000000000000..7960ca0fd04b8 --- /dev/null +++ b/doc/speedup-batch/plot.gp @@ -0,0 +1,41 @@ +set style line 80 lt rgb "#808080" +set style line 81 lt 0 +set style line 81 lt rgb "#808080" +set grid back linestyle 81 +set border 3 back linestyle 80 +set xtics nomirror +set ytics nomirror +set style line 1 lt rgb "#A00000" lw 2 pt 1 +set style line 2 lt rgb "#00A000" lw 2 pt 6 +set style line 3 lt rgb "#5060D0" lw 2 pt 2 +set style line 4 lt rgb "#F25900" lw 2 pt 9 +set key bottom right +set autoscale +unset log +unset label +set xtic auto +set ytic auto +set title "Batch signature verification in libsecp256k1" +set xlabel "Number of signatures (logarithmic)" +set ylabel "Verification time per signature in us" +set grid +set logscale x +set mxtics 10 + +# Generate graph of Schnorr signature benchmark +schnorrsig_single_val=system("cat schnorrsig_single.dat") +set xrange [1.1:] +set xtics add ("2" 2) +set yrange [0.9:] +set ytics -1,0.1,3 +set ylabel "Speedup over single verification" +set term png size 800,600 +set output 'schnorrsig-speedup-batch.png' +plot "schnorrsig_batch.dat" using 1:(schnorrsig_single_val/$2) with points title "" ls 1 + +# Generate graph of tweaked x-only pubkey check benchmark +set title "Batch tweaked x-only pubkey check in libsecp256k1" +set xlabel "Number of tweak checks (logarithmic)" +tweak_single_val=system("cat tweak_single.dat") +set output 'tweakcheck-speedup-batch.png' +plot "tweak_batch.dat" using 1:(tweak_single_val/$2) with points title "" ls 1 diff --git a/doc/speedup-batch/schnorrsig-speedup-batch.png b/doc/speedup-batch/schnorrsig-speedup-batch.png new file mode 100644 index 0000000000000000000000000000000000000000..536b831503ce3ef518dd8fb771952831a169d979 GIT binary patch literal 8192 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 Q%Px# literal 0 HcmV?d00001 diff --git a/doc/speedup-batch/tweakcheck-speedup-batch.png b/doc/speedup-batch/tweakcheck-speedup-batch.png new file mode 100644 index 0000000000000000000000000000000000000000..f12e6e273f1915e985c46ff175b0f5c162f0a26d GIT binary patch literal 8639 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 OsHLv=SGn5Fuzv#^D0X%L literal 0 HcmV?d00001 diff --git a/examples/batch.c b/examples/batch.c new file mode 100644 index 0000000000000..d0fc5786d51d0 --- /dev/null +++ b/examples/batch.c @@ -0,0 +1,181 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "random.h" + +/* key pair data */ +unsigned char sk[32]; +secp256k1_keypair keypair; +secp256k1_xonly_pubkey pk; + +/* schnorrsig verification data */ +#define N_SIGS 10 +unsigned char msg[N_SIGS][32]; +unsigned char sig[N_SIGS][64]; + +/* xonly pubkey tweak checks data */ +#define N_CHECKS 10 +unsigned char tweaked_pubkey[N_CHECKS][32]; +int tweaked_pk_parity[N_CHECKS]; +unsigned char tweak[N_CHECKS][32]; + +/* 2*N_SIGS since one schnorrsig creates two scalar-point pairs in batch + * whereas one tweak check creates one scalar-point pair in batch */ +#define N_TERMS (N_CHECKS + 2*N_SIGS) + +/* generate key pair required for sign and verify */ +int create_keypair(secp256k1_context *ctx) { + while(1) { + if (!fill_random(sk, sizeof(sk))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_keypair_create(ctx, &keypair, sk)) { + break; + } + } + if (!secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)) { + return 0; + } + + return 1; +} + +/* create valid schnorrsigs for N_SIGS random messages */ +int generate_schnorrsigs(secp256k1_context *ctx) { + size_t i; + + for (i = 0; i < N_SIGS; i++) { + if(!fill_random(msg[i], sizeof(msg[i]))) { + printf("Failed to generate randomness\n"); + return 1; + } + assert(secp256k1_schnorrsig_sign32(ctx, sig[i], msg[i], &keypair, NULL)); + assert(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], sizeof(msg[i]), &pk)); + } + + return 1; +} + +/* create valid N_CHECKS number of xonly pukey tweak checks */ +int generate_xonlypub_tweak_checks(secp256k1_context *ctx) { + secp256k1_pubkey output_pk; + secp256k1_xonly_pubkey output_xonly_pk; + size_t i; + + for (i = 0; i < N_CHECKS; i++) { + if (!fill_random(tweak[i], sizeof(tweak[i]))) { + printf("Failed to generate randomness\n"); + return 1; + } + assert(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &pk, tweak[i])); + assert(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &tweaked_pk_parity[i], &output_pk)); + assert(secp256k1_xonly_pubkey_serialize(ctx, tweaked_pubkey[i], &output_xonly_pk)); + assert(secp256k1_xonly_pubkey_tweak_add_check(ctx, tweaked_pubkey[i], tweaked_pk_parity[i], &pk, tweak[i])); + } + + return 1; +} + +int main(void) { + int ret; + size_t i; + /* batch object uses secp256k1_context only for the error callback function + * here, we create secp256k1_context that can sign and verify, only to generate + * input data (schnorrsigs, tweak checks) required for the batch */ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_batch *batch; + unsigned char auxiliary_rand[16]; + + /* Generate 16 bytes of randomness to use during batch creation. */ + if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) { + printf("Failed to generate randomness\n"); + return 1; + } + + batch = secp256k1_batch_create(ctx, N_TERMS, auxiliary_rand); + + assert(ctx != NULL); + assert(batch != NULL); + + /* key pair generation */ + printf("Creating a key pair........................."); + if(!create_keypair(ctx)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + + /* create schnorrsigs for N_SIGS random messages */ + printf("Signing messages............................"); + if(!generate_schnorrsigs(ctx)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + + printf("Adding signatures to the batch object......."); + for (i = 0; i < N_SIGS; i++) { + /* It is recommended to check the validity of the batch before adding a + * new input (schnorrsig/tweak check) to it. The `secp256k1_batch_add_` APIs + * won't add any new input to invalid batch since the final `secp256k1_batch_verify` + * API call will fail even if the new input is valid. */ + if(secp256k1_batch_usable(ctx, batch)) { + ret = secp256k1_batch_add_schnorrsig(ctx, batch, sig[i], msg[i], sizeof(msg[i]), &pk); + } else { + printf("INVALID BATCH\n"); + return 1; + } + + if(!ret) { + printf("FAILED\n"); + return 1; + } + } + printf("ok\n"); + + printf("Generating xonlypub tweak checks............"); + if(!generate_xonlypub_tweak_checks(ctx)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + + printf("Adding tweak checks to the batch object....."); + for (i = 0; i < N_CHECKS; i++) { + /* It is recommended to check the validity of the batch before adding a + * new input (schnorrsig/tweak check) to it. The `secp256k1_batch_add_` APIs + * won't add any new input to invalid batch since the final `secp256k1_batch_verify` + * API call will fail even if the new input is valid. */ + if(secp256k1_batch_usable(ctx, batch)) { + ret = secp256k1_batch_add_xonlypub_tweak_check(ctx, batch, tweaked_pubkey[i], tweaked_pk_parity[i], &pk, tweak[i]); + } else { + printf("INVALID BATCH\n"); + return 1; + } + + if(!ret) { + printf("FAILED\n"); + return 1; + } + } + printf("ok\n"); + + printf("Verifying the batch object.................."); + if(!secp256k1_batch_verify(ctx, batch)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + + secp256k1_batch_destroy(ctx, batch); + secp256k1_context_destroy(ctx); + + return 0; +} diff --git a/include/secp256k1.h b/include/secp256k1.h index 936f0b42b7d03..f4053f2a9311d 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -265,7 +265,7 @@ SECP256K1_API void secp256k1_selftest(void); * memory allocation entirely, see secp256k1_context_static and the functions in * secp256k1_preallocated.h. * - * Returns: a newly created context object. + * Returns: pointer to a newly created context object. * In: flags: Always set to SECP256K1_CONTEXT_NONE (see below). * * The only valid non-deprecated flag in recent library versions is @@ -296,8 +296,8 @@ SECP256K1_API secp256k1_context *secp256k1_context_create( * Cloning secp256k1_context_static is not possible, and should not be emulated by * the caller (e.g., using memcpy). Create a new context instead. * - * Returns: a newly created context object. - * Args: ctx: an existing context to copy (not secp256k1_context_static) + * Returns: pointer to a newly created context object. + * Args: ctx: pointer to a context to copy (not secp256k1_context_static). */ SECP256K1_API secp256k1_context *secp256k1_context_clone( const secp256k1_context *ctx @@ -313,7 +313,7 @@ SECP256K1_API secp256k1_context *secp256k1_context_clone( * behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must * be used instead. * - * Args: ctx: an existing context to destroy, constructed using + * Args: ctx: pointer to a context to destroy, constructed using * secp256k1_context_create or secp256k1_context_clone * (i.e., not secp256k1_context_static). */ @@ -350,8 +350,8 @@ SECP256K1_API void secp256k1_context_destroy( * 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. - * In: fun: a pointer to a function to call when an illegal argument is + * Args: ctx: pointer to a context object. + * In: fun: pointer to a function to call when an illegal argument is * 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, must be NULL for the default handler. @@ -377,8 +377,8 @@ SECP256K1_API void secp256k1_context_set_illegal_callback( * for that). After this callback returns, anything may happen, including * crashing. * - * Args: ctx: an existing context object. - * In: fun: a pointer to a function to call when an internal error occurs, + * Args: ctx: pointer to a context object. + * In: fun: pointer to a function to call when an internal error occurs, * taking a message and an opaque pointer (NULL restores the * default handler, see secp256k1_context_set_illegal_callback * for details). @@ -395,7 +395,7 @@ SECP256K1_API void secp256k1_context_set_error_callback( /** Create a secp256k1 scratch space object. * * Returns: a newly created scratch space. - * Args: ctx: an existing context object. + * Args: ctx: pointer to a context object. * In: size: amount of memory to be available as scratch space. Some extra * (<100 bytes) will be allocated for extra accounting. */ @@ -407,7 +407,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space *secp256k1_sc /** Destroy a secp256k1 scratch space. * * The pointer may not be used afterwards. - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object. * scratch: space to destroy */ SECP256K1_API void secp256k1_scratch_space_destroy( @@ -419,7 +419,7 @@ SECP256K1_API void secp256k1_scratch_space_destroy( * * 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. + * Args: ctx: pointer to a 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: input: pointer to a serialized public key @@ -439,14 +439,14 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( /** Serialize a pubkey object into a serialized byte sequence. * * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if + * Args: ctx: pointer to a context object. + * Out: output: pointer to a 65-byte (if compressed==0) or 33-byte (if * compressed==1) byte array to place the serialized key * in. - * In/Out: outputlen: a pointer to an integer which is initially set to the + * In/Out: outputlen: pointer to an integer which is initially set to the * size of output, and is overwritten with the written * size. - * In: pubkey: a pointer to a secp256k1_pubkey containing an + * In: pubkey: pointer to a secp256k1_pubkey containing an * initialized public key. * flags: SECP256K1_EC_COMPRESSED if serialization should be in * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED. @@ -464,7 +464,7 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize( * Returns: <0 if the first public key is less than the second * >0 if the first public key is greater than the second * 0 if the two public keys are equal - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object * In: pubkey1: first public key to compare * pubkey2: second public key to compare */ @@ -477,9 +477,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp( /** Parse an ECDSA signature in compact (64 bytes) format. * * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input64: a pointer to the 64-byte array to parse + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input64: pointer to the 64-byte array to parse * * The signature must consist of a 32-byte big endian R value, followed by a * 32-byte big endian S value. If R or S fall outside of [0..order-1], the @@ -498,9 +498,9 @@ SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( /** Parse a DER ECDSA signature. * * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input: a pointer to the signature to be parsed + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input: pointer to the signature to be parsed * inputlen: the length of the array pointed to be input * * This function will accept any valid DER encoded signature, even if the @@ -520,13 +520,13 @@ SECP256K1_API int secp256k1_ecdsa_signature_parse_der( /** Serialize an ECDSA signature in DER format. * * Returns: 1 if enough space was available to serialize, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: output: a pointer to an array to store the DER serialization - * In/Out: outputlen: a pointer to a length integer. Initially, this integer + * Args: ctx: pointer to a context object + * Out: output: pointer to an array to store the DER serialization + * In/Out: outputlen: pointer to a length integer. Initially, this integer * should be set to the length of output. After the call * it will be set to the length of the serialization (even * if 0 was returned). - * In: sig: a pointer to an initialized signature object + * In: sig: pointer to an initialized signature object */ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( const secp256k1_context *ctx, @@ -538,9 +538,9 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( /** Serialize an ECDSA signature in compact (64 byte) format. * * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: output64: a pointer to a 64-byte array to store the compact serialization - * In: sig: a pointer to an initialized signature object + * Args: ctx: pointer to a context object + * Out: output64: pointer to a 64-byte array to store the compact serialization + * In: sig: pointer to an initialized signature object * * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. */ @@ -554,7 +554,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( * * Returns: 1: correct signature * 0: incorrect or unparseable signature - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object * In: sig: the signature being verified. * msghash32: the 32-byte message hash being verified. * The verifier must make sure to apply a cryptographic @@ -585,12 +585,12 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( /** Convert a signature to a normalized lower-S form. * * Returns: 1 if sigin was not normalized, 0 if it already was. - * Args: ctx: a secp256k1 context object - * Out: sigout: a pointer to a signature to fill with the normalized form, + * Args: ctx: pointer to a context object + * Out: sigout: pointer to a signature to fill with the normalized form, * or copy if the input was already normalized. (can be NULL if * you're only interested in whether the input was already * normalized). - * In: sigin: a pointer to a signature to check/normalize (can be identical to sigout) + * In: sigin: pointer to a signature to check/normalize (can be identical to sigout) * * With ECDSA a third-party can forge a second distinct signature of the same * message, given a single initial signature, but without knowing the key. This diff --git a/include/secp256k1_batch.h b/include/secp256k1_batch.h new file mode 100644 index 0000000000000..6068ec64e4fd6 --- /dev/null +++ b/include/secp256k1_batch.h @@ -0,0 +1,110 @@ +#ifndef SECP256K1_BATCH_H +#define SECP256K1_BATCH_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements a Batch Verification object that supports: + * + * 1. Schnorr signatures compliant with Bitcoin Improvement Proposal 340 + * "Schnorr Signatures for secp256k1" + * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). + * + * 2. Taproot commitments compliant with Bitcoin Improvemtn Proposal 341 + * "Taproot: SegWit version 1 spending rules" + * (https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki). + */ + +/** Opaque data structure that holds information required for the batch verification. + * + * The purpose of this structure is to store elliptic curve points, their scalar + * coefficients, and scalar coefficient of generator point participating in Multi-Scalar + * Point Multiplication computation, which is done by `secp256k1_ecmult_strauss_batch_internal` + */ +typedef struct secp256k1_batch_struct secp256k1_batch; + +/** Create a secp256k1 batch object object (in dynamically allocated memory). + * + * This function uses malloc to allocate memory. It is guaranteed that malloc is + * called at most twice for every call of this function. + * + * Returns: a newly created batch object. + * Args: ctx: an existing `secp256k1_context` object. Not to be confused + * with the batch object object that this function creates. + * In: max_terms: Max number of (scalar, curve point) pairs that the batch + * object can store. + * 1. `batch_add_schnorrsig` - adds two scalar-point pairs to the batch + * 2. `batch_add_xonpub_tweak_check` - adds one scalar-point pair to the batch + * Hence, for adding n schnorrsigs and m tweak checks, `max_terms` + * should be set to 2*n + m. + * aux_rand16: 16 bytes of fresh randomness. While recommended to provide + * this, it is only supplemental to security and can be NULL. A + * NULL argument is treated the same as an all-zero one. + */ +SECP256K1_API secp256k1_batch* secp256k1_batch_create( + const secp256k1_context* ctx, + size_t max_terms, + const unsigned char *aux_rand16 +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 batch object (created in dynamically allocated memory). + * + * The batch object's pointer may not be used afterwards. + * + * Args: ctx: a secp256k1 context object. + * batch: an existing batch object to destroy, constructed + * using `secp256k1_batch_create` + */ +SECP256K1_API void secp256k1_batch_destroy( + const secp256k1_context* ctx, + secp256k1_batch* batch +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Checks if a batch can be used by the `secp256k1_batch_add_*` APIs. + * + * Returns: 1: batch can be used by `secp256k1_batch_add_*` APIs. + * 0: batch cannot be used by `secp256k1_batch_add_*` APIs. + * + * Args: ctx: a secp256k1 context object (can be initialized for none). + * batch: a secp256k1 batch object that contains a set of schnorrsigs/tweaks. + * + * You are advised to check if `secp256k1_batch_usable` returns 1 before calling + * any `secp256k1_batch_add_*` API. We recommend this because `secp256k1_batch_add_*` + * will fail in two cases: + * - case 1: unparsable input (schnorrsig or tweak check) + * - case 2: unusable (or invalid) batch + * Calling `secp256k1_batch_usable` beforehand helps eliminate case 2 if + * `secp256k1_batch_add_*` fails. + * + * If you ignore the above advice, all the secp256k1_batch APIs will still + * work correctly. It simply makes it hard to understand the reason behind + * `secp256k1_batch_add_*` failure (if occurs). + */ +SECP256K1_API int secp256k1_batch_usable( + const secp256k1_context *ctx, + const secp256k1_batch *batch +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Verify the set of schnorr signatures or tweaked pubkeys present in the secp256k1_batch. + * + * Returns: 1: every schnorrsig/tweak (in batch) is valid + * 0: atleaset one of the schnorrsig/tweak (in batch) is invalid + * + * In particular, returns 1 if the batch object is empty (does not contain any schnorrsigs/tweaks). + * + * Args: ctx: a secp256k1 context object (can be initialized for none). + * batch: a secp256k1 batch object that contains a set of schnorrsigs/tweaks. + */ +SECP256K1_API int secp256k1_batch_verify( + const secp256k1_context *ctx, + secp256k1_batch *batch +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_BATCH_H */ diff --git a/include/secp256k1_ecdh.h b/include/secp256k1_ecdh.h index 515e17429986b..4d9da3461d224 100644 --- a/include/secp256k1_ecdh.h +++ b/include/secp256k1_ecdh.h @@ -39,7 +39,7 @@ SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_de * 0: scalar was invalid (zero or overflow) or hashfp returned 0 * Args: ctx: pointer to a context object. * Out: output: pointer to an array to be filled by hashfp. - * In: pubkey: a pointer to a secp256k1_pubkey containing an initialized public key. + * In: pubkey: pointer to a secp256k1_pubkey containing an initialized public key. * seckey: a 32-byte scalar with which to multiply the point. * hashfp: pointer to a hash function. If NULL, * secp256k1_ecdh_hash_function_sha256 is used diff --git a/include/secp256k1_ellswift.h b/include/secp256k1_ellswift.h index f79bd883961eb..ae37287f820ef 100644 --- a/include/secp256k1_ellswift.h +++ b/include/secp256k1_ellswift.h @@ -87,7 +87,7 @@ SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_ * Returns: 1 always. * Args: ctx: pointer to a context object * Out: ell64: pointer to a 64-byte array to be filled - * In: pubkey: a pointer to a secp256k1_pubkey containing an + * In: pubkey: pointer to a secp256k1_pubkey containing an * initialized public key * rnd32: pointer to 32 bytes of randomness * @@ -169,7 +169,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create( * (will not be NULL) * ell_b64: pointer to the 64-byte encoded public key of party B * (will not be NULL) - * seckey32: a pointer to our 32-byte secret key + * seckey32: pointer to our 32-byte secret key * party: boolean indicating which party we are: zero if we are * party A, non-zero if we are party B. seckey32 must be * the private key corresponding to that party's ell_?64. diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index 7fcce68e68181..ad70b92f959cd 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -39,7 +39,7 @@ typedef struct { * 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. + * Args: ctx: pointer to a context object. * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a * parsed version of input. If not, it's set to an invalid value. * In: input32: pointer to a serialized xonly_pubkey. @@ -54,9 +54,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse( * * 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. + * Args: ctx: pointer to a context object. + * Out: output32: pointer to a 32-byte array to place the serialized key in. + * In: pubkey: pointer to a secp256k1_xonly_pubkey containing an initialized public key. */ SECP256K1_API int secp256k1_xonly_pubkey_serialize( const secp256k1_context *ctx, @@ -69,7 +69,7 @@ SECP256K1_API int secp256k1_xonly_pubkey_serialize( * Returns: <0 if the first public key is less than the second * >0 if the first public key is greater than the second * 0 if the two public keys are equal - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object. * In: pubkey1: first public key to compare * pubkey2: second public key to compare */ diff --git a/include/secp256k1_preallocated.h b/include/secp256k1_preallocated.h index f37744777b042..f2d95c245e2c5 100644 --- a/include/secp256k1_preallocated.h +++ b/include/secp256k1_preallocated.h @@ -52,8 +52,8 @@ SECP256K1_API size_t secp256k1_context_preallocated_size( * 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 + * Returns: pointer to newly created context object. + * In: prealloc: pointer to a rewritable contiguous block of memory of * size at least secp256k1_context_preallocated_size(flags) * bytes, as detailed above. * flags: which parts of the context to initialize. @@ -72,7 +72,7 @@ SECP256K1_API secp256k1_context *secp256k1_context_preallocated_create( * caller-provided memory. * * Returns: the required size of the caller-provided memory block. - * In: ctx: an existing context to copy. + * In: ctx: pointer to a context to copy. */ SECP256K1_API size_t secp256k1_context_preallocated_clone_size( const secp256k1_context *ctx @@ -91,9 +91,9 @@ SECP256K1_API size_t secp256k1_context_preallocated_clone_size( * Cloning secp256k1_context_static is not possible, and should not be emulated by * the caller (e.g., using memcpy). Create a new context instead. * - * Returns: a newly created context object. - * Args: ctx: an existing context to copy (not secp256k1_context_static). - * In: prealloc: a pointer to a rewritable contiguous block of memory of + * Returns: pointer to a newly created context object. + * Args: ctx: pointer to a context to copy (not secp256k1_context_static). + * In: prealloc: pointer to a rewritable contiguous block of memory of * size at least secp256k1_context_preallocated_size(flags) * bytes, as detailed above. */ @@ -118,7 +118,7 @@ SECP256K1_API secp256k1_context *secp256k1_context_preallocated_clone( * preallocated pointer given to secp256k1_context_preallocated_create or * secp256k1_context_preallocated_clone. * - * Args: ctx: an existing context to destroy, constructed using + * Args: ctx: pointer to a context to destroy, constructed using * secp256k1_context_preallocated_create or * secp256k1_context_preallocated_clone * (i.e., not secp256k1_context_static). diff --git a/include/secp256k1_recovery.h b/include/secp256k1_recovery.h index b12ca4d972071..341b8bac63b56 100644 --- a/include/secp256k1_recovery.h +++ b/include/secp256k1_recovery.h @@ -28,9 +28,9 @@ typedef struct { /** Parse a compact ECDSA signature (64 bytes + recovery id). * * Returns: 1 when the signature could be parsed, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input64: a pointer to a 64-byte compact signature + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input64: pointer to a 64-byte compact signature * recid: the recovery id (0, 1, 2 or 3) */ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( @@ -43,9 +43,9 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( /** Convert a recoverable signature into a normal signature. * * Returns: 1 - * Args: ctx: a secp256k1 context object. - * Out: sig: a pointer to a normal signature. - * In: sigin: a pointer to a recoverable signature. + * Args: ctx: pointer to a context object. + * Out: sig: pointer to a normal signature. + * In: sigin: pointer to a recoverable signature. */ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( const secp256k1_context *ctx, @@ -56,10 +56,10 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( /** Serialize an ECDSA signature in compact format (64 bytes + recovery id). * * Returns: 1 - * Args: ctx: a secp256k1 context object. - * Out: output64: a pointer to a 64-byte array of the compact signature. - * recid: a pointer to an integer to hold the recovery id. - * In: sig: a pointer to an initialized signature object. + * Args: ctx: pointer to a context object. + * Out: output64: pointer to a 64-byte array of the compact signature. + * recid: pointer to an integer to hold the recovery id. + * In: sig: pointer to an initialized signature object. */ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( const secp256k1_context *ctx, diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 26358533f67fb..23163de2fb002 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -169,11 +169,11 @@ SECP256K1_API int secp256k1_schnorrsig_sign_custom( * * Returns: 1: correct signature * 0: incorrect signature - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object. * In: sig64: pointer to the 64-byte signature to verify. * msg: the message being verified. Can only be NULL if msglen is 0. * msglen: length of the message - * pubkey: pointer to an x-only public key to verify with (cannot be NULL) + * pubkey: pointer to an x-only public key to verify with */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( const secp256k1_context *ctx, diff --git a/include/secp256k1_schnorrsig_batch.h b/include/secp256k1_schnorrsig_batch.h new file mode 100644 index 0000000000000..ffd8399ee7735 --- /dev/null +++ b/include/secp256k1_schnorrsig_batch.h @@ -0,0 +1,42 @@ +#ifndef SECP256K1_SCHNORRSIG_BATCH_H +#define SECP256K1_SCHNORRSIG_BATCH_H + +#include "secp256k1.h" +#include "secp256k1_schnorrsig.h" +#include "secp256k1_batch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This header file implements batch verification functionality for Schnorr + * signature (see include/secp256k1_schnorrsig.h). + */ + +/** Adds a Schnorr signature to the batch object (secp256k1_batch) + * defined in the Batch module (see include/secp256k1_batch.h). + * + * Returns: 1: successfully added the signature to the batch + * 0: unparseable signature or unusable batch (according to + * secp256k1_batch_usable). + * Args: ctx: a secp256k1 context object (can be initialized for none). + * batch: a secp256k1 batch object created using `secp256k1_batch_create`. + * In: sig64: pointer to the 64-byte signature to verify. + * msg: the message being verified. Can only be NULL if msglen is 0. + * msglen: length of the message. + * pubkey: pointer to an x-only public key to verify with (cannot be NULL). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_batch_add_schnorrsig( + const secp256k1_context* ctx, + secp256k1_batch *batch, + const unsigned char *sig64, + const unsigned char *msg, + size_t msglen, + const secp256k1_xonly_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_BATCH_H */ diff --git a/include/secp256k1_tweak_check_batch.h b/include/secp256k1_tweak_check_batch.h new file mode 100644 index 0000000000000..4ae9027fa4a26 --- /dev/null +++ b/include/secp256k1_tweak_check_batch.h @@ -0,0 +1,50 @@ +#ifndef SECP256K1_TWEAK_CHECK_BATCH_H +#define SECP256K1_TWEAK_CHECK_BATCH_H + +#include "secp256k1.h" +#include "secp256k1_extrakeys.h" +#include "secp256k1_batch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This header file implements batch verification functionality for + * x-only tweaked public key check (see include/secp256k1_extrakeys.h). + */ + +/** Adds a x-only tweaked pubkey check to the batch object (secp256k1_batch) + * defined in the Batch module (see include/secp256k1_batch.h). + * + * The tweaked pubkey is represented by its 32-byte x-only serialization and + * its pk_parity, which can both be obtained by converting the result of + * tweak_add to a secp256k1_xonly_pubkey. + * + * Returns: 1: successfully added the tweaked pubkey check to the batch + * 0: unparseable tweaked pubkey check or unusable batch (according to + * secp256k1_batch_usable). + * Args: ctx: pointer to a context object initialized for verification. + * batch: a secp256k1 batch object created using `secp256k1_batch_create`. + * In: tweaked_pubkey32: pointer to a serialized xonly_pubkey. + * tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization + * is passed in as tweaked_pubkey32). This must match the + * pk_parity value that is returned when calling + * secp256k1_xonly_pubkey_from_pubkey with the tweaked pubkey, or + * the final secp256k1_batch_verify on this batch will fail. + * internal_pubkey: pointer to an x-only public key object to apply the tweak to. + * tweak32: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_batch_add_xonlypub_tweak_check( + const secp256k1_context* ctx, + secp256k1_batch *batch, + const unsigned char *tweaked_pubkey32, + int tweaked_pk_parity, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_TWEAK_CHECK_BATCH_H */ diff --git a/src/assumptions.h b/src/assumptions.h index 8ed04209e91dc..7961005350bfe 100644 --- a/src/assumptions.h +++ b/src/assumptions.h @@ -19,65 +19,69 @@ reduce the odds of experiencing an unwelcome surprise. */ -struct secp256k1_assumption_checker { - /* This uses a trick to implement a static assertion in C89: a type with an array of negative size is not - allowed. */ - int dummy_array[( - /* Bytes are 8 bits. */ - (CHAR_BIT == 8) && +#if defined(__has_attribute) +# if __has_attribute(__unavailable__) +__attribute__((__unavailable__("Don't call this function. It only exists because STATIC_ASSERT cannot be used outside a function."))) +# endif +#endif +static void secp256k1_assumption_checker(void) { + /* Bytes are 8 bits. */ + STATIC_ASSERT(CHAR_BIT == 8); - /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32 - without signed overflow, which would be undefined behaviour. */ - (UINT_MAX <= UINT32_MAX) && + /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32 + without signed overflow, which would be undefined behaviour. */ + STATIC_ASSERT(UINT_MAX <= UINT32_MAX); - /* Conversions from unsigned to signed outside of the bounds of the signed type are - implementation-defined. Verify that they function as reinterpreting the lower - bits of the input in two's complement notation. Do this for conversions: - - from uint(N)_t to int(N)_t with negative result - - from uint(2N)_t to int(N)_t with negative result - - from int(2N)_t to int(N)_t with negative result - - from int(2N)_t to int(N)_t with positive result */ + /* Conversions from unsigned to signed outside of the bounds of the signed type are + implementation-defined. Verify that they function as reinterpreting the lower + bits of the input in two's complement notation. Do this for conversions: + - from uint(N)_t to int(N)_t with negative result + - from uint(2N)_t to int(N)_t with negative result + - from int(2N)_t to int(N)_t with negative result + - from int(2N)_t to int(N)_t with positive result */ - /* To int8_t. */ - ((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55) && - ((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33) && - ((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF) && - ((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34) && + /* To int8_t. */ + STATIC_ASSERT(((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55)); + STATIC_ASSERT((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33); + STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF); + STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34); - /* To int16_t. */ - ((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322) && - ((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C) && - ((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4) && - ((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678) && + /* To int16_t. */ + STATIC_ASSERT((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322); + STATIC_ASSERT((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C); + STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4); + STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678); - /* To int32_t. */ - ((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B) && - ((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE) && - ((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8) && - ((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789) && + /* To int32_t. */ + STATIC_ASSERT((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B); + STATIC_ASSERT((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE); + STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8); + STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789); - /* To int64_t. */ - ((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL) && + /* To int64_t. */ + STATIC_ASSERT((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL); #if defined(SECP256K1_INT128_NATIVE) - ((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL) && - (((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL) && - (((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL) && + STATIC_ASSERT((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL); + STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL); + STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL); - /* To int128_t. */ - ((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)) && + /* To int128_t. */ + STATIC_ASSERT((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)); #endif - /* Right shift on negative signed values is implementation defined. Verify that it - acts as a right shift in two's complement with sign extension (i.e duplicating - the top bit into newly added bits). */ - ((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA) && - ((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A) && - ((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48) && - ((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL) && + /* Right shift on negative signed values is implementation defined. Verify that it + acts as a right shift in two's complement with sign extension (i.e duplicating + the top bit into newly added bits). */ + STATIC_ASSERT((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA); + STATIC_ASSERT((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A); + STATIC_ASSERT((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48); + STATIC_ASSERT((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL); #if defined(SECP256K1_INT128_NATIVE) - ((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)) && + STATIC_ASSERT((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)); #endif - 1) * 2 - 1]; -}; + + /* This function is not supposed to be called. */ + VERIFY_CHECK(0); +} #endif /* SECP256K1_ASSUMPTIONS_H */ diff --git a/src/bench.c b/src/bench.c index 1127df67ae0ee..0a9f287e3da3e 100644 --- a/src/bench.c +++ b/src/bench.c @@ -42,17 +42,27 @@ static void help(int default_iters) { printf(" ec_keygen : EC public key generation\n"); #ifdef ENABLE_MODULE_RECOVERY - printf(" ecdsa_recover : ECDSA public key recovery algorithm\n"); + printf(" ecdsa_recover : ECDSA public key recovery algorithm\n"); #endif #ifdef ENABLE_MODULE_ECDH - printf(" ecdh : ECDH key exchange algorithm\n"); + printf(" ecdh : ECDH key exchange algorithm\n"); #endif #ifdef ENABLE_MODULE_SCHNORRSIG - printf(" schnorrsig : all Schnorr signature algorithms (sign, verify)\n"); - printf(" schnorrsig_sign : Schnorr sigining algorithm\n"); - printf(" schnorrsig_verify : Schnorr verification algorithm\n"); + printf(" schnorrsig : all Schnorr signature algorithms (sign, verify)\n"); + printf(" schnorrsig_sign : Schnorr sigining algorithm\n"); + printf(" schnorrsig_verify : Schnorr verification algorithm\n"); +# ifdef ENABLE_MODULE_BATCH + printf(" schnorrsig_batch_verify : Batch verification of Schnorr signatures\n"); +# endif +#endif + +#ifdef ENABLE_MODULE_EXTRAKEYS + printf(" tweak_add_check : Checks if tweaked x-only pubkey is valid\n"); +# ifdef ENABLE_MODULE_BATCH + printf(" tweak_check_batch_verify : Batch verification of tweaked x-only pubkeys check\n"); +# endif #endif #ifdef ENABLE_MODULE_ELLSWIFT @@ -157,6 +167,10 @@ static void bench_keygen_run(void *arg, int iters) { # include "modules/recovery/bench_impl.h" #endif +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/bench_impl.h" +#endif + #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/bench_impl.h" #endif @@ -179,7 +193,8 @@ int main(int argc, char** argv) { char* valid_args[] = {"ecdsa", "verify", "ecdsa_verify", "sign", "ecdsa_sign", "ecdh", "recover", "ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign", "ec", "keygen", "ec_keygen", "ellswift", "encode", "ellswift_encode", "decode", - "ellswift_decode", "ellswift_keygen", "ellswift_ecdh"}; + "ellswift_decode", "ellswift_keygen", "ellswift_ecdh", + "batch_verify", "schnorrsig_batch_verify", "extrakeys", "tweak_add_check", "tweak_check_batch_verify"}; size_t valid_args_size = sizeof(valid_args)/sizeof(valid_args[0]); int invalid_args = have_invalid_args(argc, argv, valid_args, valid_args_size); @@ -221,6 +236,14 @@ int main(int argc, char** argv) { } #endif +#ifndef ENABLE_MODULE_BATCH + if (have_flag(argc, argv, "batch_verify") || have_flag(argc, argv, "schnorrsig_batch_verify") || have_flag(argc, argv, "tweak_check_batch_verify")) { + fprintf(stderr, "./bench: Schnorr signatures module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-schnorrsig.\n\n"); + return 1; + } +#endif + #ifndef ENABLE_MODULE_ELLSWIFT if (have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ellswift_encode") || have_flag(argc, argv, "ellswift_decode") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_keygen") || @@ -265,6 +288,11 @@ int main(int argc, char** argv) { run_recovery_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_EXTRAKEYS + /* Extrakeys benchmarks */ + run_extrakeys_bench(iters, argc, argv); +#endif + #ifdef ENABLE_MODULE_SCHNORRSIG /* Schnorr signature benchmarks */ run_schnorrsig_bench(iters, argc, argv); diff --git a/src/bench.h b/src/bench.h index 1564b1a1760b8..abb5f883443fd 100644 --- a/src/bench.h +++ b/src/bench.h @@ -120,7 +120,7 @@ static void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setu sum += total; } /* ',' is used as a column delimiter */ - printf("%-30s, ", name); + printf("%-35s, ", name); print_number(min * FP_MULT / iter); printf(" , "); print_number(((sum * FP_MULT) / count) / iter); @@ -181,7 +181,7 @@ static void print_output_table_header_row(void) { char* min_str = " Min(us) "; /* center alignment */ char* avg_str = " Avg(us) "; char* max_str = " Max(us) "; - printf("%-30s,%-15s,%-15s,%-15s\n", bench_str, min_str, avg_str, max_str); + printf("%-35s,%-15s,%-15s,%-15s\n", bench_str, min_str, avg_str, max_str); printf("\n"); } diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 6d14c7ac4be7c..2764e4970cf50 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -355,16 +355,27 @@ static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const sec secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng); } -static size_t secp256k1_strauss_scratch_size(size_t n_points) { - static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); - return n_points*point_size; +/** Allocate strauss state on the scratch space */ +static int secp256k1_strauss_scratch_alloc_state(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, struct secp256k1_strauss_state *state, size_t n_points) { + const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); + + /* We allocate three objects on the scratch space. If these allocations + * change, make sure to check if this affects STRAUSS_SCRATCH_OBJECTS + * constant and strauss_scratch_size. */ + state->aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); + state->pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state->ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); + + if (state->aux == NULL || state->pre_a == NULL || state->ps == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + return 1; } -static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, 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; +/** Run ecmult_strauss_wnaf on the given points and scalars */ +static int secp256k1_ecmult_strauss_batch_internal(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, secp256k1_scalar *scalars, secp256k1_gej *points, const secp256k1_scalar *inp_g_sc, size_t n_points) { struct secp256k1_strauss_state state; - size_t i; const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); secp256k1_gej_set_infinity(r); @@ -372,16 +383,30 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba return 1; } - /* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space. If these - * allocations change, make sure to update the STRAUSS_SCRATCH_OBJECTS - * constant and strauss_scratch_size accordingly. */ + if(!secp256k1_strauss_scratch_alloc_state(error_callback, scratch, &state, n_points)) { + return 0; + } + + secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc); + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 1; +} + +/** Run ecmult_strauss_wnaf on the given points and scalars. Returns 0 if the + * scratch space is empty. `n_points` number of scalars and points are + * extracted from `cbdata` using `cb` and stored on the scratch space. + */ +static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, 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; + size_t i; + const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); + /* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space in + * total. If these allocations change, make sure to update the + * STRAUSS_SCRATCH_OBJECTS constant and strauss_scratch_size accordingly. */ 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.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); - state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); - 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.aux == NULL || state.pre_a == NULL || state.ps == NULL) { + if (points == NULL || scalars == NULL) { secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } @@ -394,20 +419,30 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba } secp256k1_gej_set_ge(&points[i], &point); } - secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc); + + secp256k1_ecmult_strauss_batch_internal(error_callback, scratch, r, scalars, points, inp_g_sc, n_points); 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_callback* error_callback, 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, scratch, r, inp_g_sc, cb, cbdata, n, 0); +/** Return the scratch size that is allocated by a call to strauss_batch + * (ignoring padding required for alignment). */ +static size_t secp256k1_strauss_scratch_size(size_t n_points) { + static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); + return n_points*point_size; } +/** Return the maximum number of points that can be provided to strauss_batch + * with a given scratch space. */ 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); } +/* Wrapper for secp256k1_ecmult_multi_func interface */ +static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, 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, scratch, r, inp_g_sc, cb, cbdata, n, 0); +} + /** Convert a number to WNAF notation. * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. * It has the following guarantees: diff --git a/src/modules/batch/Makefile.am.include b/src/modules/batch/Makefile.am.include new file mode 100644 index 0000000000000..f996e0efca748 --- /dev/null +++ b/src/modules/batch/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_batch.h +noinst_HEADERS += src/modules/batch/main_impl.h +noinst_HEADERS += src/modules/batch/tests_impl.h diff --git a/src/modules/batch/main_impl.h b/src/modules/batch/main_impl.h new file mode 100644 index 0000000000000..81badd5b3bcb7 --- /dev/null +++ b/src/modules/batch/main_impl.h @@ -0,0 +1,206 @@ +#ifndef SECP256K1_MODULE_BATCH_MAIN_H +#define SECP256K1_MODULE_BATCH_MAIN_H + +#include "include/secp256k1_batch.h" + +/* Maximum number of scalar-point pairs on the batch + * for which `secp256k1_batch_verify` remains efficient */ +#define STRAUSS_MAX_TERMS_PER_BATCH 106 + +/* Assume two batch objects (batch1 and batch2) and we call + * `batch_add_tweak_check` on batch1 and `batch_add_schnorrsig` on batch2. + * In this case, the same randomizer will be generated if the input bytes to + * batch1 and batch2 are the same (even though we use different `batch_add_` funcs). + * Including this tag during randomizer generation (to differentiate btw + * `batch_add_` funcs) will prevent such mishaps. */ +enum batch_add_type {schnorrsig = 1, tweak_check = 2}; + +/** Opaque data structure that holds information required for the batch verification. + * + * Members: + * data: scratch space object that contains points (_gej) and their + * respective scalars. To be used in Multi-Scalar Multiplication + * algorithms such as Strauss and Pippenger. + * scalars: pointer to scalars allocated on the scratch space. + * points: pointer to points allocated on the scratch space. + * sc_g: scalar corresponding to the generator point (G) in Multi-Scalar + * Multiplication equation. + * sha256: contains hash of all the inputs (schnorrsig/tweaks) present in + * the batch object, expect the first input. Used for generating a random secp256k1_scalar + * for each term added by secp256k1_batch_add_*. + * sha256: contains hash of all inputs (except the first one) present in the batch. + * `secp256k1_batch_add_` APIs use these for randomizing the scalar (i.e., multiplying + * it with a newly generated scalar) before adding it to the batch. + * len: number of scalar-point pairs present in the batch. + * capacity: max number of scalar-point pairs that the batch can hold. + * result: tells whether the given set of inputs (schnorrsigs or tweak checks) is valid + * or invalid. 1 = valid and 0 = invalid. By default, this is set to 1 + * during batch object creation (i.e., `secp256k1_batch_create`). + * + * The following struct name is typdef as secp256k1_batch (in include/secp256k1_batch.h). + */ +struct secp256k1_batch_struct{ + secp256k1_scratch *data; + secp256k1_scalar *scalars; + secp256k1_gej *points; + secp256k1_scalar sc_g; + secp256k1_sha256 sha256; + size_t len; + size_t capacity; + int result; +}; + +static size_t secp256k1_batch_scratch_size(int max_terms) { + size_t ret = secp256k1_strauss_scratch_size(max_terms) + STRAUSS_SCRATCH_OBJECTS*16; + VERIFY_CHECK(ret != 0); + + return ret; +} + +/** Clears the scalar and points allocated on the batch object's scratch space */ +static void secp256k1_batch_scratch_clear(secp256k1_batch* batch) { + secp256k1_scalar_clear(&batch->sc_g); + /* setting the len = 0 will suffice (instead of clearing the memory) + * since, there are no secrets stored on the scratch space */ + batch->len = 0; +} + +/** Allocates space for `batch->capacity` number of scalars and points on batch + * object's scratch space */ +static int secp256k1_batch_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_batch* batch) { + size_t checkpoint = secp256k1_scratch_checkpoint(error_callback, batch->data); + size_t count = batch->capacity; + + VERIFY_CHECK(count > 0); + + batch->scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, batch->data, count*sizeof(secp256k1_scalar)); + batch->points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, batch->data, count*sizeof(secp256k1_gej)); + + /* If scalar or point allocation fails, restore scratch space to previous state */ + if (batch->scalars == NULL || batch->points == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, batch->data, checkpoint); + return 0; + } + + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/batch")||SHA256("BIP0340/batch"). */ +static void secp256k1_batch_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x79e3e0d2ul; + sha->s[1] = 0x12284f32ul; + sha->s[2] = 0xd7d89e1cul; + sha->s[3] = 0x6491ea9aul; + sha->s[4] = 0xad823b2ful; + sha->s[5] = 0xfacfe0b6ul; + sha->s[6] = 0x342b78baul; + sha->s[7] = 0x12ece87cul; + + sha->bytes = 64; +} + +secp256k1_batch* secp256k1_batch_create(const secp256k1_context* ctx, size_t max_terms, const unsigned char *aux_rand16) { + size_t batch_size; + secp256k1_batch* batch; + size_t batch_scratch_size; + unsigned char zeros[16] = {0}; + /* max number of scalar-point pairs on scratch up to which Strauss multi multiplication is efficient */ + if (max_terms > STRAUSS_MAX_TERMS_PER_BATCH) { + max_terms = STRAUSS_MAX_TERMS_PER_BATCH; + } + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(max_terms != 0); + + batch_size = sizeof(secp256k1_batch); + batch = (secp256k1_batch *)checked_malloc(&ctx->error_callback, batch_size); + batch_scratch_size = secp256k1_batch_scratch_size(max_terms); + if (batch != NULL) { + /* create scratch space inside batch object, if that fails return NULL*/ + batch->data = secp256k1_scratch_create(&ctx->error_callback, batch_scratch_size); + if (batch->data == NULL) { + return NULL; + } + /* allocate memeory for `max_terms` number of scalars and points on scratch space */ + batch->capacity = max_terms; + if (!secp256k1_batch_scratch_alloc(&ctx->error_callback, batch)) { + /* if scratch memory allocation fails, free all the previous the allocated memory + and return NULL */ + secp256k1_scratch_destroy(&ctx->error_callback, batch->data); + free(batch); + return NULL; + } + + /* set remaining data members */ + secp256k1_scalar_clear(&batch->sc_g); + secp256k1_batch_sha256_tagged(&batch->sha256); + if (aux_rand16 != NULL) { + secp256k1_sha256_write(&batch->sha256, aux_rand16, 16); + } else { + /* use 16 bytes of 0x0000...000, if no fresh randomness provided */ + secp256k1_sha256_write(&batch->sha256, zeros, 16); + } + batch->len = 0; + batch->result = 1; + } + + return batch; +} + +void secp256k1_batch_destroy(const secp256k1_context *ctx, secp256k1_batch *batch) { + VERIFY_CHECK(ctx != NULL); + + if (batch != NULL) { + if(batch->data != NULL) { + /* can't destroy a scratch space with non-zero size */ + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, batch->data, 0); + secp256k1_scratch_destroy(&ctx->error_callback, batch->data); + } + free(batch); + } +} + +int secp256k1_batch_usable(const secp256k1_context *ctx, const secp256k1_batch *batch) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(batch != NULL); + + return batch->result; +} + +/** verifies the inputs (schnorrsig or tweak_check) by performing multi-scalar point + * multiplication on the scalars (`batch->scalars`) and points (`batch->points`) + * present in the batch. Uses `secp256k1_ecmult_strauss_batch_internal` to perform + * the multi-multiplication. + * + * Fails if: + * 0 != -(s1 + a2*s2 + ... + au*su)G + * + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. + */ +int secp256k1_batch_verify(const secp256k1_context *ctx, secp256k1_batch *batch) { + secp256k1_gej resj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(batch != NULL); + + if(batch->result == 0) { + return 0; + } + + if (batch->len > 0) { + int strauss_ret = secp256k1_ecmult_strauss_batch_internal(&ctx->error_callback, batch->data, &resj, batch->scalars, batch->points, &batch->sc_g, batch->len); + int mid_res = secp256k1_gej_is_infinity(&resj); + + /* `_strauss_batch_internal` should not fail due to insufficient memory. + * `batch_create` will allocate memeory needed by `_strauss_batch_internal`. */ + VERIFY_CHECK(strauss_ret != 0); + + batch->result = batch->result && mid_res; + secp256k1_batch_scratch_clear(batch); + } + + return batch->result; +} + +#endif /* SECP256K1_MODULE_BATCH_MAIN_H */ diff --git a/src/modules/batch/tests_impl.h b/src/modules/batch/tests_impl.h new file mode 100644 index 0000000000000..d84d83e2a32c8 --- /dev/null +++ b/src/modules/batch/tests_impl.h @@ -0,0 +1,210 @@ +#ifndef SECP256K1_MODULE_BATCH_TESTS_H +#define SECP256K1_MODULE_BATCH_TESTS_H + +#include "../../../include/secp256k1_batch.h" +#ifdef ENABLE_MODULE_SCHNORRSIG +#include "../../../include/secp256k1_schnorrsig.h" +#include "../../../include/secp256k1_schnorrsig_batch.h" +#endif +#ifdef ENABLE_MODULE_EXTRAKEYS +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_tweak_check_batch.h" +#endif + +/* 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_batch_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + CHECK(sha1->bytes == sha2->bytes); + CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); +} + +/* Checks that hash initialized by secp256k1_batch_sha256_tagged has the + * expected state. */ +void test_batch_sha256_tagged(void) { + unsigned char tag[13] = "BIP0340/batch"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_batch_sha256_tagged(&sha_optimized); + test_batch_sha256_eq(&sha, &sha_optimized); +} + +#define N_SIGS 10 +#define N_TWK_CHECKS 10 +#define N_TERMS (N_TWK_CHECKS + 2*N_SIGS) +void test_batch_api(void) { + +#ifdef ENABLE_MODULE_EXTRAKEYS + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk; + /* xonly pubkey tweak checks data */ + unsigned char tweaked_pk[N_TWK_CHECKS][32]; + int tweaked_pk_parity[N_TWK_CHECKS]; + unsigned char tweak[N_TWK_CHECKS][32]; + secp256k1_pubkey tmp_pk; + secp256k1_xonly_pubkey tmp_xonly_pk; + size_t i; +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + /* schnorr verification data */ + unsigned char msg[N_SIGS][32]; + unsigned char sig[N_SIGS][64]; +#endif + /* context and batch 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); + secp256k1_context *sttc = malloc(sizeof(*secp256k1_context_no_precomp)); + memcpy(sttc, secp256k1_context_no_precomp, sizeof(secp256k1_context)); + + unsigned char aux_rand16[32]; + int ecount; + + secp256k1_context_set_error_callback(none, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_callback_fn, &ecount); + + /* 16 byte auxiliary randomness */ + secp256k1_testrand256(aux_rand16); + memset(&aux_rand16[16], 0, 16); + +#ifdef ENABLE_MODULE_EXTRAKEYS + /* generate keypair data */ + secp256k1_testrand256(sk); + CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(sign, &pk, NULL, &keypair) == 1); + + /* generate N_TWK_CHECKS tweak check data (tweaked_pk, tweaked_pk_parity, tweak) */ + for (i = 0; i < N_TWK_CHECKS; i++) { + secp256k1_testrand256(tweak[i]); + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &tmp_pk, &pk, tweak[i])); + CHECK(secp256k1_xonly_pubkey_from_pubkey(vrfy, &tmp_xonly_pk, &tweaked_pk_parity[i], &tmp_pk)); + CHECK(secp256k1_xonly_pubkey_serialize(vrfy, tweaked_pk[i], &tmp_xonly_pk)); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(vrfy, tweaked_pk[i], tweaked_pk_parity[i], &pk, tweak[i])); + } +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + /* generate N_SIGS schnorr verify data (msg, sig) */ + for (i = 0; i < N_SIGS; i++) { + secp256k1_testrand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign32(sign, sig[i], msg[i], &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(vrfy, sig[i], msg[i], sizeof(msg[i]), &pk)); + } +#endif + + /** main test body **/ + /* batch_create tests */ + ecount = 0; + secp256k1_batch *batch_none = secp256k1_batch_create(none, 1, NULL); + CHECK(batch_none != NULL); + CHECK(ecount == 0); + /* 2*N_SIGS since one schnorrsig creates two scalar-point pair in batch */ + secp256k1_batch *batch_sign = secp256k1_batch_create(sign, 2*N_SIGS, NULL); + CHECK(batch_sign != NULL); + CHECK(ecount == 0); + secp256k1_batch *batch_vrfy = secp256k1_batch_create(vrfy, N_TWK_CHECKS - 1, aux_rand16); + CHECK(batch_vrfy != NULL); + CHECK(ecount == 0); + secp256k1_batch *batch_both = secp256k1_batch_create(both, N_TERMS/4, aux_rand16); + CHECK(batch_both != NULL); + CHECK(ecount == 0); + /* ARG_CHECK(max_terms != 0) in `batch_create` should fail*/ + secp256k1_batch *batch_sttc = secp256k1_batch_create(sttc, 0, NULL); + CHECK(batch_sttc == NULL); + CHECK(ecount == 1); + +#ifdef ENABLE_MODULE_SCHNORRSIG + ecount = 0; + for (i = 0; i < N_SIGS; i++) { + CHECK(secp256k1_batch_usable(sign, batch_sign) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_schnorrsig(sign, batch_sign, sig[i], msg[i], sizeof(msg[i]), &pk) == 1); + CHECK(ecount == 0); + } +#endif + +#ifdef ENABLE_MODULE_EXTRAKEYS + ecount = 0; + for (i = 0; i < N_TWK_CHECKS; i++) { + CHECK(secp256k1_batch_usable(vrfy, batch_vrfy)); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(vrfy, batch_vrfy, tweaked_pk[i], tweaked_pk_parity[i], &pk, tweak[i])); + CHECK(ecount == 0); + } +#endif + +#if defined(ENABLE_MODULE_SCHNORRSIG) && defined(ENABLE_MODULE_EXTRAKEYS) + /* secp256k1_batch_add_tests for batch_both */ + ecount = 0; + for (i = 0; i < N_SIGS; i++) { + CHECK(secp256k1_batch_usable(both, batch_both) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_schnorrsig(both, batch_both, sig[i], msg[i], sizeof(msg[i]), &pk) == 1); + CHECK(ecount == 0); + } + for (i = 0; i < N_TWK_CHECKS; i++) { + CHECK(secp256k1_batch_usable(both, batch_both)); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(both, batch_both, tweaked_pk[i], tweaked_pk_parity[i], &pk, tweak[i])); + CHECK(ecount == 0); + } +#endif + + /* batch_verify tests */ + ecount = 0; + CHECK(secp256k1_batch_verify(none, batch_none) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(sign, batch_sign) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(vrfy, batch_vrfy) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(both, batch_both) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(sttc, NULL) == 0); + CHECK(ecount == 1); + + ecount = 0; + secp256k1_batch_destroy(none, batch_none); + CHECK(ecount == 0); + secp256k1_batch_destroy(sign, batch_sign); + CHECK(ecount == 0); + secp256k1_batch_destroy(vrfy, batch_vrfy); + CHECK(ecount == 0); + secp256k1_batch_destroy(both, batch_both); + CHECK(ecount == 0); + secp256k1_batch_destroy(sttc, NULL); + CHECK(ecount == 0); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); + secp256k1_context_destroy(sttc); +} +#undef N_SIGS +#undef N_TWK_CHECKS +#undef N_TERMS + + +void run_batch_tests(void) { + test_batch_api(); + test_batch_sha256_tagged(); +} + +#endif /* SECP256K1_MODULE_BATCH_TESTS_H */ diff --git a/src/modules/extrakeys/Makefile.am.include b/src/modules/extrakeys/Makefile.am.include index 0d901ec1f4495..be6efb2d08385 100644 --- a/src/modules/extrakeys/Makefile.am.include +++ b/src/modules/extrakeys/Makefile.am.include @@ -1,4 +1,11 @@ include_HEADERS += include/secp256k1_extrakeys.h +if ENABLE_MODULE_BATCH +include_HEADERS += include/secp256k1_tweak_check_batch.h +endif noinst_HEADERS += src/modules/extrakeys/tests_impl.h noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h noinst_HEADERS += src/modules/extrakeys/main_impl.h +if ENABLE_MODULE_BATCH +noinst_HEADERS += src/modules/extrakeys/batch_add_impl.h +noinst_HEADERS += src/modules/extrakeys/batch_add_tests_impl.h +endif diff --git a/src/modules/extrakeys/batch_add_impl.h b/src/modules/extrakeys/batch_add_impl.h new file mode 100644 index 0000000000000..30742145850f8 --- /dev/null +++ b/src/modules/extrakeys/batch_add_impl.h @@ -0,0 +1,151 @@ +#ifndef SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_IMPL_H +#define SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_IMPL_H + +#include "include/secp256k1_extrakeys.h" +#include "include/secp256k1_tweak_check_batch.h" +#include "src/modules/batch/main_impl.h" + +/* The number of scalar-point pairs allocated on the scratch space + * by `secp256k1_batch_add_xonlypub_tweak_check` */ +#define BATCH_TWEAK_CHECK_SCRATCH_OBJS 1 + +/** Computes a 16-byte deterministic randomizer by + * SHA256(batch_add_tag || tweaked pubkey || parity || tweak || internal pubkey) */ +static void secp256k1_batch_xonlypub_tweak_randomizer_gen(unsigned char *randomizer32, secp256k1_sha256 *sha256, const unsigned char *tweaked_pubkey32, const unsigned char *tweaked_pk_parity, const unsigned char *internal_pk33, const unsigned char *tweak32) { + secp256k1_sha256 sha256_cpy; + unsigned char batch_add_type = (unsigned char) tweak_check; + + secp256k1_sha256_write(sha256, &batch_add_type, sizeof(batch_add_type)); + /* add tweaked pubkey check data to sha object */ + secp256k1_sha256_write(sha256, tweaked_pubkey32, 32); + secp256k1_sha256_write(sha256, tweaked_pk_parity, 1); + secp256k1_sha256_write(sha256, tweak32, 32); + secp256k1_sha256_write(sha256, internal_pk33, 33); + + /* generate randomizer */ + sha256_cpy = *sha256; + secp256k1_sha256_finalize(&sha256_cpy, randomizer32); + /* 16 byte randomizer is sufficient */ + memset(randomizer32, 0, 16); +} + +static int secp256k1_batch_xonlypub_tweak_randomizer_set(const secp256k1_context* ctx, secp256k1_batch *batch, secp256k1_scalar *r, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey,const unsigned char *tweak32) { + unsigned char randomizer[32]; + unsigned char internal_buf[33]; + size_t internal_buflen = sizeof(internal_buf); + unsigned char parity = (unsigned char) tweaked_pk_parity; + int overflow; + /* t = 2^127 */ + secp256k1_scalar t = SECP256K1_SCALAR_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000); + + /* 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, internal_buf, &internal_buflen, (const secp256k1_pubkey *) internal_pubkey, SECP256K1_EC_COMPRESSED)) { + return 0; + } + + secp256k1_batch_xonlypub_tweak_randomizer_gen(randomizer, &batch->sha256, tweaked_pubkey32, &parity, internal_buf, tweak32); + secp256k1_scalar_set_b32(r, randomizer, &overflow); + /* Shift scalar to range [-2^127, 2^127-1] */ + secp256k1_scalar_negate(&t, &t); + secp256k1_scalar_add(r, r, &t); + VERIFY_CHECK(overflow == 0); + + return 1; +} + +/** Adds the given x-only tweaked public key check to the batch. + * + * Updates the batch object by: + * 1. adding the point P-Q to the scratch space + * -> the point is of type `secp256k1_gej` + * 2. adding the scalar ai to the scratch space + * -> ai is the scalar coefficient of P-Q (in multi multiplication) + * 3. incrementing sc_g (scalar of G) by ai.tweak + * + * Conventions used above: + * -> Q (tweaked pubkey) = EC point where parity(y) = tweaked_pk_parity + * and x = tweaked_pubkey32 + * -> P (internal pubkey) = internal pubkey + * -> ai (randomizer) = sha256_tagged(batch_add_tag || tweaked_pubkey32 || + * tweaked_pk_parity || tweak32 || pubkey) + * -> tweak (challenge) = tweak32 + * + * This function is based on `secp256k1_xonly_pubkey_tweak_add_check`. + */ +int secp256k1_batch_add_xonlypub_tweak_check(const secp256k1_context* ctx, secp256k1_batch *batch, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey,const unsigned char *tweak32) { + secp256k1_scalar tweak; + secp256k1_scalar ai; + secp256k1_ge pk; + secp256k1_ge q; + secp256k1_gej tmpj; + secp256k1_fe qx; + int overflow; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(batch != NULL); + ARG_CHECK(internal_pubkey != NULL); + ARG_CHECK(tweaked_pubkey32 != NULL); + ARG_CHECK(tweak32 != NULL); + + if(batch->result == 0) { + return 0; + } + + if (!secp256k1_fe_set_b32_limit(&qx, tweaked_pubkey32)) { + return 0; + } + + secp256k1_scalar_set_b32(&tweak, tweak32, &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)) { + return 0; + } + + /* if insufficient space in batch, verify the inputs (stored in curr batch) and + * save the result. This extends the batch capacity since `secp256k1_batch_verify` + * clears the batch after verification. */ + if (batch->capacity - batch->len < BATCH_TWEAK_CHECK_SCRATCH_OBJS) { + secp256k1_batch_verify(ctx, batch); + } + + i = batch->len; + /* append point P-Q to the scratch space */ + if (!secp256k1_ge_set_xo_var(&q, &qx, tweaked_pk_parity)) { + return 0; + } + if (!secp256k1_ge_is_in_correct_subgroup(&q)) { + return 0; + } + secp256k1_ge_neg(&q, &q); + secp256k1_gej_set_ge(&tmpj, &q); + secp256k1_gej_add_ge_var(&tmpj, &tmpj, &pk, NULL); + batch->points[i] = tmpj; + + /* Compute ai (randomizer) */ + if (batch->len == 0) { + /* set randomizer as 1 for the first term in batch */ + ai = secp256k1_scalar_one; + } else if(!secp256k1_batch_xonlypub_tweak_randomizer_set(ctx, batch, &ai, tweaked_pubkey32, tweaked_pk_parity, internal_pubkey, tweak32)) { + return 0; + } + + /* append scalar ai to scratch space */ + batch->scalars[i] = ai; + + /* increment scalar of G by ai.tweak */ + secp256k1_scalar_mul(&tweak, &tweak, &ai); + secp256k1_scalar_add(&batch->sc_g, &batch->sc_g, &tweak); + + batch->len += 1; + + return 1; +} + +#endif /* SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_IMPL_H */ diff --git a/src/modules/extrakeys/batch_add_tests_impl.h b/src/modules/extrakeys/batch_add_tests_impl.h new file mode 100644 index 0000000000000..030c85788e5f1 --- /dev/null +++ b/src/modules/extrakeys/batch_add_tests_impl.h @@ -0,0 +1,165 @@ +#ifndef SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_TESTS_IMPL_H +#define SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_TESTS_IMPL_H + +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_batch.h" +#include "../../../include/secp256k1_tweak_check_batch.h" + +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function */ +void batch_xonlypub_tweak_randomizer_gen_bitflip(secp256k1_sha256 *sha, unsigned char **args, size_t n_flip, size_t n_bytes) { + unsigned char randomizers[2][32]; + secp256k1_sha256 sha_cpy; + sha_cpy = *sha; + secp256k1_batch_xonlypub_tweak_randomizer_gen(randomizers[0], &sha_cpy, args[0], args[1], args[2], args[3]); + secp256k1_testrand_flip(args[n_flip], n_bytes); + sha_cpy = *sha; + secp256k1_batch_xonlypub_tweak_randomizer_gen(randomizers[1], &sha_cpy, args[0], args[1], args[2], args[3]); + CHECK(secp256k1_memcmp_var(randomizers[0], randomizers[1], 32) != 0); +} + +void run_batch_xonlypub_tweak_randomizer_gen_tests(void) { + secp256k1_sha256 sha; + size_t n_checks = 20; + unsigned char tweaked_pk[32]; + unsigned char tweaked_pk_parity; + unsigned char tweak[32]; + unsigned char internal_pk[33]; + unsigned char *args[4]; + size_t i; /* loops through n_checks */ + int j; /* loops through count */ + + secp256k1_batch_sha256_tagged(&sha); + + for (i = 0; i < n_checks; i++) { + uint8_t temp_rand; + + /* generate i-th tweak check data */ + secp256k1_testrand256(tweaked_pk); + tweaked_pk_parity = (unsigned char) secp256k1_testrand_int(2); + secp256k1_testrand256(tweak); + secp256k1_testrand256(&internal_pk[1]); + temp_rand = secp256k1_testrand_int(2) + 2; /* randomly choose 2 or 3 */ + internal_pk[0] = (unsigned char)temp_rand; + + /* check bitflip in any argument results in generates randomizers */ + args[0] = tweaked_pk; + args[1] = &tweaked_pk_parity; + args[2] = internal_pk; + args[3] = tweak; + + for (j = 0; j < COUNT; j++) { + batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 0, 32); + batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 1, 1); + batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 2, 33); + batch_xonlypub_tweak_randomizer_gen_bitflip(&sha, args, 3, 32); + } + + /* write i-th tweak check data to the sha object + * this is required for generating the next randomizer */ + secp256k1_sha256_write(&sha, tweaked_pk, 32); + secp256k1_sha256_write(&sha, &tweaked_pk_parity, 1); + secp256k1_sha256_write(&sha, tweak, 32); + secp256k1_sha256_write(&sha, internal_pk, 33); + } + +} + +void test_batch_add_xonlypub_tweak_api(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk; + /* xonly pubkey tweak checks data */ + unsigned char tweaked_pk[32]; + int tweaked_pk_parity; + unsigned char tweak[32]; + secp256k1_pubkey tmp_pk; + secp256k1_xonly_pubkey tmp_xonly_pk; + unsigned char overflows[32]; + + /** 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_batch *batch1 = secp256k1_batch_create(none, 1, NULL); + /* batch2 is used when batch_add_xonlypub_tweak is expected to fail */ + secp256k1_batch *batch2 = secp256k1_batch_create(none, 1, NULL); + int ecount; + + secp256k1_context_set_error_callback(none, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_callback_fn, &ecount); + + /** generate keypair data **/ + secp256k1_testrand256(sk); + CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(sign, &pk, NULL, &keypair) == 1); + memset(overflows, 0xFF, sizeof(overflows)); + + /** generate tweak check data (tweaked_pk, tweaked_pk_parity, tweak) **/ + secp256k1_testrand256(tweak); + CHECK(secp256k1_xonly_pubkey_tweak_add(vrfy, &tmp_pk, &pk, tweak)); + CHECK(secp256k1_xonly_pubkey_from_pubkey(vrfy, &tmp_xonly_pk, &tweaked_pk_parity, &tmp_pk)); + CHECK(secp256k1_xonly_pubkey_serialize(vrfy, tweaked_pk, &tmp_xonly_pk)); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(vrfy, tweaked_pk, tweaked_pk_parity, &pk, tweak)); + + CHECK(batch1 != NULL); + CHECK(batch2 != NULL); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch1, tweaked_pk, tweaked_pk_parity, &pk, tweak) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(none, batch1) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, NULL, tweaked_pk_parity, &pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, NULL, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, &pk, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, NULL, tweaked_pk, tweaked_pk_parity, &pk, tweak) == 0); + CHECK(ecount == 4); + /** overflowing tweak not allowed **/ + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, &pk, overflows) == 0); + CHECK(ecount == 4); + /** x-coordinate of tweaked pubkey should be less than prime order **/ + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, overflows, tweaked_pk_parity, &pk, tweak) == 0); + CHECK(ecount == 4); + + /** batch_verify should fail for incorrect tweak **/ + ecount = 0; + CHECK(secp256k1_batch_usable(none, batch2)); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, !tweaked_pk_parity, &pk, tweak) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(none, batch2) == 0); + CHECK(ecount == 0); + + /** batch_add_ should ignore unusable batch object (i.e, batch->result = 0) **/ + ecount = 0; + CHECK(secp256k1_batch_usable(none, batch2) == 0); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(none, batch2, tweaked_pk, tweaked_pk_parity, &pk, tweak) == 0); + CHECK(ecount == 0); + + ecount = 0; + secp256k1_batch_destroy(none, batch1); + CHECK(ecount == 0); + secp256k1_batch_destroy(none, batch2); + CHECK(ecount == 0); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + +void run_batch_add_xonlypub_tweak_tests(void) { + run_batch_xonlypub_tweak_randomizer_gen_tests(); + test_batch_add_xonlypub_tweak_api(); +} + + +#endif /* SECP256K1_MODULE_EXTRAKEYS_BATCH_ADD_TESTS_IMPL_H */ diff --git a/src/modules/extrakeys/bench_impl.h b/src/modules/extrakeys/bench_impl.h new file mode 100644 index 0000000000000..411fd7339c767 --- /dev/null +++ b/src/modules/extrakeys/bench_impl.h @@ -0,0 +1,139 @@ + +#ifndef SECP256K1_MODULE_EXTRAKEYS_BENCH_H +#define SECP256K1_MODULE_EXTRAKEYS_BENCH_H + +#include "../../../include/secp256k1_extrakeys.h" +#ifdef ENABLE_MODULE_BATCH +# include "../../../include/secp256k1_batch.h" +# include "../../../include/secp256k1_tweak_check_batch.h" +#endif + +typedef struct { + secp256k1_context *ctx; +#ifdef ENABLE_MODULE_BATCH + secp256k1_batch *batch; + /* number of tweak checks to batch verify. + * it varies from 1 to iters with 20% increments */ + int n; +#endif + + const secp256k1_keypair **keypairs; + const unsigned char **pks; + const unsigned char **tweaked_pks; + const int **tweaked_pk_parities; + const unsigned char **tweaks; +} bench_tweak_check_data; + +void bench_xonly_pubkey_tweak_add_check(void* arg, int iters) { + bench_tweak_check_data *data = (bench_tweak_check_data *)arg; + int i; + + for (i = 0; i < iters; i++) { + secp256k1_xonly_pubkey pk; + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pks[i]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(data->ctx, data->tweaked_pks[i], *data->tweaked_pk_parities[i], &pk, data->tweaks[i]) == 1); + } +} + +#ifdef ENABLE_MODULE_BATCH +void bench_xonly_pubkey_tweak_add_check_n(void* arg, int iters) { + bench_tweak_check_data *data = (bench_tweak_check_data *)arg; + int i, j; + + for (j = 0; j < iters/data->n; j++) { + for (i = 0; i < data->n; i++) { + secp256k1_xonly_pubkey pk; + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pks[j+i]) == 1); + CHECK(secp256k1_batch_usable(data->ctx, data->batch) == 1); + CHECK(secp256k1_batch_add_xonlypub_tweak_check(data->ctx, data->batch, data->tweaked_pks[j+i], *data->tweaked_pk_parities[j+i], &pk, data->tweaks[j+i]) == 1); + } + CHECK(secp256k1_batch_verify(data->ctx, data->batch) == 1); + } +} +#endif + +void run_extrakeys_bench(int iters, int argc, char** argv) { + int i; + bench_tweak_check_data data; + int d = argc == 1; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *)); + data.pks = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.tweaked_pks = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.tweaked_pk_parities = (const int **)malloc(iters * sizeof(int *)); + data.tweaks = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); +#ifdef ENABLE_MODULE_BATCH + data.batch = secp256k1_batch_create(data.ctx, iters, NULL); + CHECK(data.batch != NULL); +#endif + + for (i = 0; i < iters; i++) { + unsigned char sk[32]; + unsigned char *tweaked_pk_char = (unsigned char *)malloc(32); + int *tweaked_pk_parity = (int *)malloc(sizeof(int)); /*todo: use sizeof(*twk_parity) instead?*/ + unsigned char *tweak = (unsigned char *)malloc(32); + secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair)); + unsigned char *pk_char = (unsigned char *)malloc(32); + secp256k1_xonly_pubkey pk; + secp256k1_pubkey output_pk; + secp256k1_xonly_pubkey output_pk_xonly; + tweak[0] = sk[0] = i; + tweak[1] = sk[1] = i >> 8; + tweak[2] = sk[2] = i >> 16; + tweak[3] = sk[3] = i >> 24; + memset(&tweak[4], 't', 28); + memset(&sk[4], 's', 28); + + data.keypairs[i] = keypair; + data.pks[i] = pk_char; + data.tweaked_pks[i] = tweaked_pk_char; + data.tweaked_pk_parities[i] = tweaked_pk_parity; + data.tweaks[i] = tweak; + + CHECK(secp256k1_keypair_create(data.ctx, keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair)); + CHECK(secp256k1_xonly_pubkey_tweak_add(data.ctx, &output_pk, &pk, tweak)); + CHECK(secp256k1_xonly_pubkey_from_pubkey(data.ctx, &output_pk_xonly, tweaked_pk_parity, &output_pk)); + CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, tweaked_pk_char, &output_pk_xonly) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1); + } + + if (d || have_flag(argc, argv, "extrakeys") || have_flag(argc, argv, "tweak_add_check")) run_benchmark("tweak_add_check", bench_xonly_pubkey_tweak_add_check, NULL, NULL, (void *) &data, 10, iters); +#ifdef ENABLE_MODULE_BATCH + if (d || have_flag(argc, argv, "extrakeys") || have_flag(argc, argv, "batch_verify") || have_flag(argc, argv, "tweak_check_batch_verify")) { + for (i = 1; i <= iters; i = (int)(i*1.2 + 1)) { + char name[64]; + int divisible_iters; + sprintf(name, "tweak_check_batch_verify_%d", (int) i); + + data.n = i; + divisible_iters = iters - (iters % data.n); + run_benchmark(name, bench_xonly_pubkey_tweak_add_check_n, NULL, NULL, (void *) &data, 3, divisible_iters); + fflush(stdout); + } + } +#endif + + for (i = 0; i < iters; i++) { + free((void *)data.keypairs[i]); + free((void *)data.pks[i]); + free((void *)data.tweaked_pks[i]); + free((void *)data.tweaked_pk_parities[i]); + free((void *)data.tweaks[i]); + } + + /* Casting to (void *) avoids a stupid warning in MSVC. */ + free((void *)data.keypairs); + free((void *)data.pks); + free((void *)data.tweaked_pks); + free((void *)data.tweaked_pk_parities); + free((void *)data.tweaks); + +#ifdef ENABLE_MODULE_BATCH + secp256k1_batch_destroy(data.ctx, data.batch); +#endif + secp256k1_context_destroy(data.ctx); +} + +#endif /* SECP256K1_MODULE_EXTRAKEYS_BENCH_H */ diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include index 654fa2e5ae5a4..2c211784fbca7 100644 --- a/src/modules/schnorrsig/Makefile.am.include +++ b/src/modules/schnorrsig/Makefile.am.include @@ -1,5 +1,12 @@ include_HEADERS += include/secp256k1_schnorrsig.h +if ENABLE_MODULE_BATCH +include_HEADERS += include/secp256k1_schnorrsig_batch.h +endif noinst_HEADERS += src/modules/schnorrsig/main_impl.h noinst_HEADERS += src/modules/schnorrsig/tests_impl.h noinst_HEADERS += src/modules/schnorrsig/tests_exhaustive_impl.h noinst_HEADERS += src/modules/schnorrsig/bench_impl.h +if ENABLE_MODULE_BATCH +noinst_HEADERS += src/modules/schnorrsig/batch_add_impl.h +noinst_HEADERS += src/modules/schnorrsig/batch_add_tests_impl.h +endif diff --git a/src/modules/schnorrsig/batch_add_impl.h b/src/modules/schnorrsig/batch_add_impl.h new file mode 100644 index 0000000000000..d43e6a34dba02 --- /dev/null +++ b/src/modules/schnorrsig/batch_add_impl.h @@ -0,0 +1,158 @@ +#ifndef SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_IMPL_H +#define SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_IMPL_H + +#include "include/secp256k1_schnorrsig.h" +#include "include/secp256k1_schnorrsig_batch.h" +#include "src/modules/batch/main_impl.h" + +/* The number of scalar-point pairs allocated on the scratch space + * by `secp256k1_batch_add_schnorrsig` */ +#define BATCH_SCHNORRSIG_SCRATCH_OBJS 2 + +/** Computes a 16-byte deterministic randomizer by + * SHA256(batch_add_tag || sig || msg || compressed pubkey) */ +static void secp256k1_batch_schnorrsig_randomizer_gen(unsigned char *randomizer32, secp256k1_sha256 *sha256, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const unsigned char *compressed_pk33) { + secp256k1_sha256 sha256_cpy; + unsigned char batch_add_type = (unsigned char) schnorrsig; + + secp256k1_sha256_write(sha256, &batch_add_type, sizeof(batch_add_type)); + /* add schnorrsig data to sha256 object */ + secp256k1_sha256_write(sha256, sig64, 64); + secp256k1_sha256_write(sha256, msg, msglen); + secp256k1_sha256_write(sha256, compressed_pk33, 33); + + /* generate randomizer */ + sha256_cpy = *sha256; + secp256k1_sha256_finalize(&sha256_cpy, randomizer32); + /* 16 byte randomizer is sufficient */ + memset(randomizer32, 0, 16); +} + +static int secp256k1_batch_schnorrsig_randomizer_set(const secp256k1_context *ctx, secp256k1_batch *batch, secp256k1_scalar *r, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) { + unsigned char randomizer[32]; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + int overflow; + /* t = 2^127 */ + secp256k1_scalar t = SECP256K1_SCALAR_CONST(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000); + + /* 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 *) pubkey, SECP256K1_EC_COMPRESSED)) { + return 0; + } + + secp256k1_batch_schnorrsig_randomizer_gen(randomizer, &batch->sha256, sig64, msg, msglen, buf); + secp256k1_scalar_set_b32(r, randomizer, &overflow); + /* Shift scalar to range [-2^127, 2^127-1] */ + secp256k1_scalar_negate(&t, &t); + secp256k1_scalar_add(r, r, &t); + VERIFY_CHECK(overflow == 0); + + return 1; +} + +/** Adds the given schnorr signature to the batch. + * + * Updates the batch object by: + * 1. adding the points R and P to the scratch space + * -> both the points are of type `secp256k1_gej` + * 2. adding the scalars ai and ai.e to the scratch space + * -> ai is the scalar coefficient of R (in multi multiplication) + * -> ai.e is the scalar coefficient of P (in multi multiplication) + * 3. incrementing sc_g (scalar of G) by -ai.s + * + * Conventions used above: + * -> R (nonce commitment) = EC point whose y = even and x = sig64[0:32] + * -> P (public key) = pubkey + * -> ai (randomizer) = sha256_tagged(batch_add_tag || sig64 || msg || pubkey) + * -> e (challenge) = sha256_tagged(sig64[0:32] || pk.x || msg) + * -> s = sig64[32:64] + * + * This function is based on `secp256k1_schnorrsig_verify`. + */ +int secp256k1_batch_add_schnorrsig(const secp256k1_context* ctx, secp256k1_batch *batch, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_scalar ai; + secp256k1_ge pk; + secp256k1_fe rx; + secp256k1_ge r; + unsigned char buf[32]; + int overflow; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(batch != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(msg != NULL || msglen == 0); + ARG_CHECK(pubkey != NULL); + + if (batch->result == 0) { + return 0; + } + + if (!secp256k1_fe_set_b32_limit(&rx, &sig64[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig64[32], &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + + /* if insufficient space in batch, verify the inputs (stored in curr batch) and + * save the result. This extends the batch capacity since `secp256k1_batch_verify` + * clears the batch after verification. */ + if (batch->capacity - batch->len < BATCH_SCHNORRSIG_SCRATCH_OBJS) { + secp256k1_batch_verify(ctx, batch); + } + + i = batch->len; + /* append point R to the scratch space */ + if (!secp256k1_ge_set_xo_var(&r, &rx, 0)) { + return 0; + } + if (!secp256k1_ge_is_in_correct_subgroup(&r)) { + return 0; + } + secp256k1_gej_set_ge(&batch->points[i], &r); + + /* append point P to the scratch space */ + secp256k1_gej_set_ge(&batch->points[i+1], &pk); + + /* compute e (challenge) */ + secp256k1_fe_get_b32(buf, &pk.x); + secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, buf); + + /* compute ai (randomizer) */ + if (batch->len == 0) { + /* don't generate a randomizer for the first term in the batch to improve + * the computation speed. hence, set the randomizer to 1. */ + ai = secp256k1_scalar_one; + } else if (!secp256k1_batch_schnorrsig_randomizer_set(ctx, batch, &ai, sig64, msg, msglen, pubkey)) { + return 0; + } + + /* append scalars ai and ai.e to scratch space (order shouldn't change) */ + batch->scalars[i] = ai; + secp256k1_scalar_mul(&e, &e, &ai); + batch->scalars[i+1] = e; + + /* increment scalar of G by -ai.s */ + secp256k1_scalar_mul(&s, &s, &ai); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_add(&batch->sc_g, &batch->sc_g, &s); + + batch->len += 2; + + return 1; +} + +#endif /* SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_IMPL_H */ diff --git a/src/modules/schnorrsig/batch_add_tests_impl.h b/src/modules/schnorrsig/batch_add_tests_impl.h new file mode 100644 index 0000000000000..fe9681b64162f --- /dev/null +++ b/src/modules/schnorrsig/batch_add_tests_impl.h @@ -0,0 +1,313 @@ +#ifndef SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_TESTS_IMPL_H +#define SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_TESTS_IMPL_H + +#include "../../../include/secp256k1_schnorrsig.h" +#include "../../../include/secp256k1_batch.h" +#include "../../../include/secp256k1_schnorrsig_batch.h" + +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function */ +void batch_schnorrsig_randomizer_gen_bitflip(secp256k1_sha256 *sha, unsigned char **args, size_t n_flip, size_t n_bytes, size_t msglen) { + unsigned char randomizers[2][32]; + secp256k1_sha256 sha_cpy; + sha_cpy = *sha; + secp256k1_batch_schnorrsig_randomizer_gen(randomizers[0], &sha_cpy, args[0], args[1], msglen, args[2]); + secp256k1_testrand_flip(args[n_flip], n_bytes); + sha_cpy = *sha; + secp256k1_batch_schnorrsig_randomizer_gen(randomizers[1], &sha_cpy, args[0], args[1], msglen, args[2]); + CHECK(secp256k1_memcmp_var(randomizers[0], randomizers[1], 32) != 0); +} + +void run_batch_schnorrsig_randomizer_gen_tests(void) { + secp256k1_sha256 sha; + size_t n_sigs = 20; + unsigned char msg[32]; + size_t msglen = sizeof(msg); + unsigned char sig[64]; + unsigned char compressed_pk[33]; + unsigned char *args[3]; + size_t i; /* loops through n_sigs */ + int j; /* loops through count */ + + secp256k1_batch_sha256_tagged(&sha); + + for (i = 0; i < n_sigs; i++) { + uint8_t temp_rand; + unsigned char randomizer[32]; + /* batch_schnorrsig_randomizer_gen func modifies the sha object passed + * so, pass the copied obj instead of original */ + secp256k1_sha256 sha_cpy; + + /* generate i-th schnorrsig verify data */ + secp256k1_testrand256(msg); + secp256k1_testrand256(&sig[0]); + secp256k1_testrand256(&sig[32]); + secp256k1_testrand256(&compressed_pk[1]); + temp_rand = secp256k1_testrand_int(2) + 2; /* randomly choose 2 or 3 */ + compressed_pk[0] = (unsigned char)temp_rand; + + /* check that bitflip in an argument results in different nonces */ + args[0] = sig; + args[1] = msg; + args[2] = compressed_pk; + + for (j = 0; j < COUNT; j++) { + batch_schnorrsig_randomizer_gen_bitflip(&sha, args, 0, 64, msglen); + batch_schnorrsig_randomizer_gen_bitflip(&sha, args, 1, 32, msglen); + batch_schnorrsig_randomizer_gen_bitflip(&sha, args, 2, 33, msglen); + } + + /* different msglen should generate different randomizers */ + sha_cpy = sha; + secp256k1_batch_schnorrsig_randomizer_gen(randomizer, &sha_cpy, sig, msg, msglen, compressed_pk); + + for (j = 0; j < COUNT; j++) { + unsigned char randomizer2[32]; + uint32_t offset = secp256k1_testrand_int(msglen - 1); + size_t msglen_tmp = (msglen + offset) % msglen; + + sha_cpy = sha; + secp256k1_batch_schnorrsig_randomizer_gen(randomizer2, &sha_cpy, sig, msg, msglen_tmp, compressed_pk); + CHECK(secp256k1_memcmp_var(randomizer, randomizer2, 32) != 0); + } + + /* write i-th schnorrsig verify data to the sha object + * this is required for generating the next randomizer */ + secp256k1_sha256_write(&sha, sig, 64); + secp256k1_sha256_write(&sha, msg, msglen); + secp256k1_sha256_write(&sha, compressed_pk, 33); + } + +} + +/* Helper for function test_schnorrsig_sign_batch_verify + * Checks that batch_verify fails after flipping random byte. */ +void test_schnorrsig_sign_verify_check_batch(secp256k1_batch *batch, unsigned char *sig64, unsigned char *msg, size_t msglen, secp256k1_xonly_pubkey *pk) { + int ret; + + CHECK(secp256k1_batch_usable(CTX, batch)); + /* filling a random byte (in msg or sig) can cause the following: + * 1. unparsable msg or sig - here, batch_add_schnorrsig fails and batch_verify passes + * 2. invalid schnorr eqn - here, batch_verify fails and batch_add_schnorrsig passes + */ + ret = secp256k1_batch_add_schnorrsig(CTX, batch, sig64, msg, msglen, pk); + if (ret == 0) { + CHECK(secp256k1_batch_verify(CTX, batch) == 1); + } else if (ret == 1) { + CHECK(secp256k1_batch_verify(CTX, batch) == 0); + } +} + +#define N_SIGS 3 +#define ONE_SIG 1 +/* Creates N_SIGS valid signatures and verifies them with batch_verify. + * Then flips some bits and checks that verification now fails. This is a + * variation of `test_schnorrsig_sign_verify` (in schnorrsig/tests_impl.h) */ +void test_schnorrsig_sign_batch_verify(void) { + unsigned char sk[32]; + unsigned char msg[N_SIGS][32]; + unsigned char sig[N_SIGS][64]; + size_t i; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk; + secp256k1_scalar s; + secp256k1_batch *batch[N_SIGS + 1]; + secp256k1_batch *batch_fail1; + secp256k1_batch *batch_fail2; + + /* batch[0] will be used where batch_add and batch_verify + * are expected to succed */ + batch[0] = secp256k1_batch_create(CTX, 2*N_SIGS, NULL); + for (i = 0; i < N_SIGS; i++) { + batch[i+1] = secp256k1_batch_create(CTX, 2*ONE_SIG, NULL); + } + batch_fail1 = secp256k1_batch_create(CTX, 2*ONE_SIG, NULL); + batch_fail2 = secp256k1_batch_create(CTX, 2*ONE_SIG, NULL); + + secp256k1_testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); + + for (i = 0; i < N_SIGS; i++) { + secp256k1_testrand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[i], msg[i], &keypair, NULL)); + CHECK(secp256k1_batch_usable(CTX, batch[0])); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[i], msg[i], sizeof(msg[i]), &pk)); + } + CHECK(secp256k1_batch_verify(CTX, batch[0])); + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch (TODO) fail */ + size_t sig_idx = secp256k1_testrand_int(N_SIGS); + size_t byte_idx = secp256k1_testrand_bits(5); + unsigned char xorbyte = secp256k1_testrand_int(254)+1; + + sig[sig_idx][byte_idx] ^= xorbyte; + test_schnorrsig_sign_verify_check_batch(batch[1], sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk); + sig[sig_idx][byte_idx] ^= xorbyte; + + byte_idx = secp256k1_testrand_bits(5); + sig[sig_idx][32+byte_idx] ^= xorbyte; + test_schnorrsig_sign_verify_check_batch(batch[2], sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk); + sig[sig_idx][32+byte_idx] ^= xorbyte; + + byte_idx = secp256k1_testrand_bits(5); + msg[sig_idx][byte_idx] ^= xorbyte; + test_schnorrsig_sign_verify_check_batch(batch[3], sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk); + 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], sizeof(msg[sig_idx]), &pk)); + } + + /* Test overflowing s */ + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg[0], sizeof(msg[0]), &pk) == 1); + memset(&sig[0][32], 0xFF, 32); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg[0], sizeof(msg[0]), &pk) == 0); + + /* Test negative s */ + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg[0], sizeof(msg[0]), &pk) == 1); + secp256k1_scalar_set_b32(&s, &sig[0][32], NULL); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_get_b32(&sig[0][32], &s); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch_fail1, sig[0], msg[0], sizeof(msg[0]), &pk) == 1); + CHECK(secp256k1_batch_verify(CTX, batch_fail1) == 0); + + /* The empty message can be signed & verified */ + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], NULL, 0, &keypair, NULL) == 1); + CHECK(secp256k1_batch_usable(CTX, batch[0]) == 1); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], NULL, 0, &pk) == 1); + CHECK(secp256k1_batch_verify(CTX, batch[0]) == 1); + + { + /* Test varying message lengths */ + unsigned char msg_large[32 * 8]; + uint32_t msglen = secp256k1_testrand_int(sizeof(msg_large)); + for (i = 0; i < sizeof(msg_large); i += 32) { + secp256k1_testrand256(&msg_large[i]); + } + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], msg_large, msglen, &keypair, NULL) == 1); + CHECK(secp256k1_batch_usable(CTX, batch[0]) == 1); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch[0], sig[0], msg_large, msglen, &pk) == 1); + CHECK(secp256k1_batch_verify(CTX, batch[0]) == 1); + /* batch_add fails for a random wrong message length */ + msglen = (msglen + (sizeof(msg_large) - 1)) % sizeof(msg_large); + CHECK(secp256k1_batch_usable(CTX, batch_fail2) == 1); + CHECK(secp256k1_batch_add_schnorrsig(CTX, batch_fail2, sig[0], msg_large, msglen, &pk) == 1); + CHECK(secp256k1_batch_verify(CTX, batch_fail2) == 0); + } + + /* Destroy the batch objects */ + for (i = 0; i < N_SIGS+1; i++) { + secp256k1_batch_destroy(CTX, batch[i]); + } + secp256k1_batch_destroy(CTX, batch_fail1); + secp256k1_batch_destroy(CTX, batch_fail2); +} +#undef N_SIGS +/* ONE_SIG is undefined after `test_batch_add_schnorrsig_api` */ + +void test_batch_add_schnorrsig_api(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk; + secp256k1_xonly_pubkey zero_pk; + unsigned char msg[32]; + unsigned char sig[64]; + unsigned char nullmsg_sig[64]; + + /** 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_batch *batch1 = secp256k1_batch_create(none, 2*ONE_SIG, NULL); + /* batch2 is used when batch_add_schnorrsig is expected to fail */ + secp256k1_batch *batch2 = secp256k1_batch_create(none, 2*ONE_SIG, NULL); + int ecount; + + secp256k1_context_set_error_callback(none, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_callback_fn, &ecount); + + /** generate keypair data **/ + secp256k1_testrand256(sk); + CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(sign, &pk, NULL, &keypair) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** generate a signature **/ + secp256k1_testrand256(msg); + CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, sizeof(msg), &pk)); + + CHECK(batch1 != NULL); + CHECK(batch2 != NULL); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_batch_add_schnorrsig(none, batch1, sig, msg, sizeof(msg), &pk) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(none, batch1) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_schnorrsig(none, batch2, NULL, msg, sizeof(msg), &pk) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, NULL, sizeof(msg), &pk) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, msg, sizeof(msg), NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, msg, sizeof(msg), &zero_pk) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_batch_add_schnorrsig(none, NULL, sig, msg, sizeof(msg), &pk) == 0); + CHECK(ecount == 5); + + /** NULL msg with valid signature **/ + ecount = 0; + CHECK(secp256k1_schnorrsig_sign_custom(sign, nullmsg_sig, NULL, 0, &keypair, NULL) == 1); + CHECK(secp256k1_batch_usable(none, batch1) == 1); + CHECK(secp256k1_batch_add_schnorrsig(none, batch1, nullmsg_sig, NULL, 0, &pk) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(none, batch1) == 1); + + /** NULL msg with invalid signature **/ + CHECK(secp256k1_batch_usable(none, batch2) == 1); + CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, NULL, 0, &pk) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_batch_verify(none, batch2) == 0); + + /** batch_add_ should ignore unusable batch object (i.e, batch->result = 0) **/ + ecount = 0; + CHECK(secp256k1_batch_usable(none, batch2) == 0); + CHECK(ecount == 0); + CHECK(secp256k1_batch_add_schnorrsig(none, batch2, sig, msg, sizeof(msg), &pk) == 0); + CHECK(ecount == 0); + + ecount = 0; + secp256k1_batch_destroy(CTX, batch1); + CHECK(ecount == 0); + secp256k1_batch_destroy(CTX, batch2); + CHECK(ecount == 0); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} +#undef ONE_SIG + +void run_batch_add_schnorrsig_tests(void) { + int i; + + run_batch_schnorrsig_randomizer_gen_tests(); + test_batch_add_schnorrsig_api(); + for (i = 0; i < COUNT; i++) { + test_schnorrsig_sign_batch_verify(); + } +} + + +#endif /* SECP256K1_MODULE_SCHNORRSIG_BATCH_ADD_TESTS_IMPL_H */ diff --git a/src/modules/schnorrsig/bench_impl.h b/src/modules/schnorrsig/bench_impl.h index 93a878ede3e1b..da2681cca655a 100644 --- a/src/modules/schnorrsig/bench_impl.h +++ b/src/modules/schnorrsig/bench_impl.h @@ -8,12 +8,21 @@ #define SECP256K1_MODULE_SCHNORRSIG_BENCH_H #include "../../../include/secp256k1_schnorrsig.h" +#ifdef ENABLE_MODULE_BATCH +# include "../../../include/secp256k1_batch.h" +# include "../../../include/secp256k1_schnorrsig_batch.h" +#endif #define MSGLEN 32 typedef struct { secp256k1_context *ctx; +#ifdef ENABLE_MODULE_BATCH + secp256k1_batch *batch; + /* number of signatures to batch verify. + * it varies from 1 to iters with 20% increments */ int n; +#endif const secp256k1_keypair **keypairs; const unsigned char **pk; @@ -45,7 +54,24 @@ static void bench_schnorrsig_verify(void* arg, int iters) { } } -static void run_schnorrsig_bench(int iters, int argc, char** argv) { +#ifdef ENABLE_MODULE_BATCH +void bench_schnorrsig_verify_n(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i, j; + + for (j = 0; j < iters/data->n; j++) { + for (i = 0; i < data->n; i++) { + secp256k1_xonly_pubkey pk; + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[j+i]) == 1); + CHECK(secp256k1_batch_usable(data->ctx, data->batch) == 1); + CHECK(secp256k1_batch_add_schnorrsig(data->ctx, data->batch, data->sigs[j+i], data->msgs[j+i], MSGLEN, &pk) == 1); + } + CHECK(secp256k1_batch_verify(data->ctx, data->batch) == 1); + } +} +#endif + +void run_schnorrsig_bench(int iters, int argc, char** argv) { int i; bench_schnorrsig_data data; int d = argc == 1; @@ -55,6 +81,10 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) { data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); +#ifdef ENABLE_MODULE_BATCH + data.batch = secp256k1_batch_create(data.ctx, 2*iters, NULL); + CHECK(data.batch != NULL); +#endif CHECK(MSGLEN >= 4); for (i = 0; i < iters; i++) { @@ -84,6 +114,20 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) { if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "schnorrsig_sign")) run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters); if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "verify") || have_flag(argc, argv, "schnorrsig_verify")) run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters); +#ifdef ENABLE_MODULE_BATCH + if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "batch_verify") || have_flag(argc, argv, "schnorrsig_batch_verify")) { + for (i = 1; i <= iters; i = (int)(i*1.2 + 1)) { + char name[64]; + int divisible_iters; + sprintf(name, "schnorrsig_batch_verify_%d", (int) i); + + data.n = i; + divisible_iters = iters - (iters % data.n); + run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, divisible_iters); + fflush(stdout); + } + } +#endif for (i = 0; i < iters; i++) { free((void *)data.keypairs[i]); @@ -98,6 +142,9 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) { free((void *)data.msgs); free((void *)data.sigs); +#ifdef ENABLE_MODULE_BATCH + secp256k1_batch_destroy(data.ctx, data.batch); +#endif secp256k1_context_destroy(data.ctx); } diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 8ada90a87b0d6..d48ea89098092 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -8,6 +8,10 @@ #define SECP256K1_MODULE_SCHNORRSIG_TESTS_H #include "../../../include/secp256k1_schnorrsig.h" +#ifdef ENABLE_MODULE_BATCH +# include "../../../include/secp256k1_batch.h" +# include "../../../include/secp256k1_schnorrsig_batch.h" +#endif /* Checks that a bit flip in the n_flip-th argument (that has n_bytes many * bytes) changes the hash function @@ -193,7 +197,7 @@ static void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, c } /* Helper function for schnorrsig_bip_vectors - * Checks that both verify and verify_batch (TODO) return the same value as expected. */ + * Checks that schnorrsig_verify returns the same value as expected. */ static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg, size_t msglen, const unsigned char *sig, int expected) { secp256k1_xonly_pubkey pk; @@ -201,6 +205,23 @@ static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_ser CHECK(expected == secp256k1_schnorrsig_verify(CTX, sig, msg, msglen, &pk)); } +#ifdef ENABLE_MODULE_BATCH +/* Helper function for schnorrsig_bip_vectors + * Checks that batch_verify return the same value as expected. */ +void test_schnorrsig_bip_vectors_check_batch_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int add_expected, int verify_expected) { + secp256k1_xonly_pubkey pk; + secp256k1_batch *batch; + + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk, pk_serialized)); + batch = secp256k1_batch_create(CTX, 2, NULL); + CHECK(batch != NULL); + CHECK(secp256k1_batch_usable(CTX, batch) == 1); + CHECK(add_expected == secp256k1_batch_add_schnorrsig(CTX, batch, sig, msg32, 32, &pk)); + CHECK(verify_expected == secp256k1_batch_verify(CTX, batch)); + secp256k1_batch_destroy(CTX, batch); +} +#endif + /* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See * https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */ static void test_schnorrsig_bip_vectors(void) { @@ -242,6 +263,9 @@ static void test_schnorrsig_bip_vectors(void) { }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + #ifdef ENABLE_MODULE_BATCH + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1); + #endif } { /* Test vector 1 */ @@ -281,6 +305,9 @@ static void test_schnorrsig_bip_vectors(void) { }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + #ifdef ENABLE_MODULE_BATCH + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1); + #endif } { /* Test vector 2 */ @@ -320,6 +347,9 @@ static void test_schnorrsig_bip_vectors(void) { }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + #ifdef ENABLE_MODULE_BATCH + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1); + #endif } { /* Test vector 3 */ @@ -359,6 +389,9 @@ static void test_schnorrsig_bip_vectors(void) { }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + #ifdef ENABLE_MODULE_BATCH + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1); + #endif } { /* Test vector 4 */ @@ -385,6 +418,9 @@ static void test_schnorrsig_bip_vectors(void) { 0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4 }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + #ifdef ENABLE_MODULE_BATCH + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 1); + #endif } { /* Test vector 5 */ @@ -423,6 +459,12 @@ static void test_schnorrsig_bip_vectors(void) { 0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2 }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add_schnorrsig adds converts sig[0:32] to point R such + * that R.y is always even. This test vector has R.y = odd, so + * batch_add_schnorrsig returns 1 and batch_verify returns 0. */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0); + #endif } { /* Test vector 7 */ @@ -449,6 +491,12 @@ static void test_schnorrsig_bip_vectors(void) { 0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add_schnorrsig does not verify the schnorr eqn. + * This test vector negated message, so batch_add_schnorrsig + * returns 1 and batch_verify returns 0. */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0); + #endif } { /* Test vector 8 */ @@ -475,6 +523,12 @@ static void test_schnorrsig_bip_vectors(void) { 0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6 }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add_schnorrsig does not verify the schnorr eqn. + * This test vector negated s (sig[32:64]), so batch_add_schnorrsig + * returns 1 and batch_verify returns 0. */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0); + #endif } { /* Test vector 9 */ @@ -501,6 +555,12 @@ static void test_schnorrsig_bip_vectors(void) { 0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51 }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add_schnorrsig fails since R.x = 0. + * batch_verify passes because the batch is empty + * (prev batch_add failed so nothing was added to the batch)*/ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1); + #endif } { /* Test vector 10 */ @@ -527,6 +587,12 @@ static void test_schnorrsig_bip_vectors(void) { 0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97 }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add_schnorrsig passes since R.x = 1. + * batch_verify fails since R (with R.x = 1 & R.y = even) does not + * lie on libsecp256k1 */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 1, 0); + #endif } { /* Test vector 11 */ @@ -553,6 +619,11 @@ static void test_schnorrsig_bip_vectors(void) { 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add fails since R.x is an invalid x-coordinate (not on curve) + * batch_verify passes since the batch is empty */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1); + #endif } { /* Test vector 12 */ @@ -579,6 +650,11 @@ static void test_schnorrsig_bip_vectors(void) { 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add fails since R.x = field modulo `p` + * batch_verify passes since the batch is empty */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1); + #endif } { /* Test vector 13 */ @@ -605,6 +681,11 @@ static void test_schnorrsig_bip_vectors(void) { 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + #ifdef ENABLE_MODULE_BATCH + /* batch_add fails since s (sig[32:64]) = curve order `n` + * batch_verify passes since the batch is empty */ + test_schnorrsig_bip_vectors_check_batch_verify(pk, msg, sig, 0, 1); + #endif } { /* Test vector 14 */ @@ -851,8 +932,10 @@ static void test_schnorrsig_sign(void) { #define N_SIGS 3 /* Creates N_SIGS valid signatures and verifies them with verify and * verify_batch (TODO). Then flips some bits and checks that verification now - * fails. */ -static void test_schnorrsig_sign_verify(void) { + * batch_verify. Then flips some bits and checks that verification now + * fails. The batch_verify variation of this test is implemented as + * test_schnorrsig_sign_batch_verify (in schnorrsig/batch_add_tests_impl.h) */ +void test_schnorrsig_sign_verify(void) { unsigned char sk[32]; unsigned char msg[N_SIGS][32]; unsigned char sig[N_SIGS][64]; diff --git a/src/scalar_impl.h b/src/scalar_impl.h index bbba83e937e06..972d8041b0afb 100644 --- a/src/scalar_impl.h +++ b/src/scalar_impl.h @@ -229,7 +229,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT * <= {triangle inequality} * a1*|k*b2/n - c1| + a2*|k*(-b1)/n - c2| * < {Lemma 1 and Lemma 2} - * a1*(2^-1 + epslion1) + a2*(2^-1 + epsilon2) + * a1*(2^-1 + epsilon1) + a2*(2^-1 + epsilon2) * < {rounding up to an integer} * (a1 + a2 + 1)/2 * < {rounding up to a power of 2} @@ -247,7 +247,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT * <= {triangle inequality} * (-b1)*|k*b2/n - c1| + b2*|k*(-b1)/n - c2| * < {Lemma 1 and Lemma 2} - * (-b1)*(2^-1 + epslion1) + b2*(2^-1 + epsilon2) + * (-b1)*(2^-1 + epsilon1) + b2*(2^-1 + epsilon2) * < {rounding up to an integer} * (-b1 + b2)/2 + 1 * < {rounding up to a power of 2} diff --git a/src/secp256k1.c b/src/secp256k1.c index 4c11e7f0b8b58..cf0348c932eef 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -237,36 +237,25 @@ static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, } static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { - if (sizeof(secp256k1_ge_storage) == 64) { - /* When the secp256k1_ge_storage type is exactly 64 byte, use its - * representation inside secp256k1_pubkey, as conversion is very fast. - * Note that secp256k1_pubkey_save must use the same representation. */ - secp256k1_ge_storage s; - memcpy(&s, &pubkey->data[0], sizeof(s)); - secp256k1_ge_from_storage(ge, &s); - } else { - /* Otherwise, fall back to 32-byte big endian for X and Y. */ - secp256k1_fe x, y; - ARG_CHECK(secp256k1_fe_set_b32_limit(&x, pubkey->data)); - ARG_CHECK(secp256k1_fe_set_b32_limit(&y, pubkey->data + 32)); - secp256k1_ge_set_xy(ge, &x, &y); - } + secp256k1_ge_storage s; + + /* We require that the secp256k1_ge_storage type is exactly 64 bytes. + * This is formally not guaranteed by the C standard, but should hold on any + * sane compiler in the real world. */ + STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); + memcpy(&s, &pubkey->data[0], 64); + secp256k1_ge_from_storage(ge, &s); ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); return 1; } static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { - if (sizeof(secp256k1_ge_storage) == 64) { - secp256k1_ge_storage s; - secp256k1_ge_to_storage(&s, ge); - memcpy(&pubkey->data[0], &s, sizeof(s)); - } else { - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_normalize_var(&ge->y); - secp256k1_fe_get_b32(pubkey->data, &ge->x); - secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); - } + secp256k1_ge_storage s; + + STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_ge_to_storage(&s, ge); + memcpy(&pubkey->data[0], &s, 64); } int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { @@ -815,3 +804,13 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_ELLSWIFT # include "modules/ellswift/main_impl.h" #endif + +#ifdef ENABLE_MODULE_BATCH +# include "modules/batch/main_impl.h" +# ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/batch_add_impl.h" +# endif +# ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/batch_add_impl.h" +# endif +#endif diff --git a/src/tests.c b/src/tests.c index bec1c45585b64..fa87a84f7906f 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2927,20 +2927,18 @@ static void run_scalar_tests(void) { secp256k1_scalar_set_b32(&r2, res[i][1], &overflow); CHECK(!overflow); secp256k1_scalar_mul(&z, &x, &y); - CHECK(!secp256k1_scalar_check_overflow(&z)); CHECK(secp256k1_scalar_eq(&r1, &z)); if (!secp256k1_scalar_is_zero(&y)) { secp256k1_scalar_inverse(&zz, &y); - CHECK(!secp256k1_scalar_check_overflow(&zz)); secp256k1_scalar_inverse_var(&zzv, &y); CHECK(secp256k1_scalar_eq(&zzv, &zz)); secp256k1_scalar_mul(&z, &z, &zz); - CHECK(!secp256k1_scalar_check_overflow(&z)); CHECK(secp256k1_scalar_eq(&x, &z)); secp256k1_scalar_mul(&zz, &zz, &y); - CHECK(!secp256k1_scalar_check_overflow(&zz)); CHECK(secp256k1_scalar_eq(&secp256k1_scalar_one, &zz)); } + secp256k1_scalar_mul(&z, &x, &x); + CHECK(secp256k1_scalar_eq(&r2, &z)); } } } @@ -4861,38 +4859,15 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi } } -static int test_ecmult_multi_random(secp256k1_scratch *scratch) { - /* Large random test for ecmult_multi_* functions which exercises: - * - Few or many inputs (0 up to 128, roughly exponentially distributed). - * - Few or many 0*P or a*INF inputs (roughly uniformly distributed). - * - Including or excluding an nonzero a*G term (or such a term at all). - * - Final expected result equal to infinity or not (roughly 50%). - * - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch - */ - - /* These 4 variables define the eventual input to the ecmult_multi function. - * g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and - * scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points - * which form its normal inputs. */ - int filled = 0; - secp256k1_scalar g_scalar = secp256k1_scalar_zero; - secp256k1_scalar scalars[128]; - secp256k1_gej gejs[128]; - /* The expected result, and the computed result. */ - secp256k1_gej expected, computed; +/** helper function used by `test_ecmult_multi_random` and `test_ecmult_strauss_batch_internal_random` + * to generate inputs (scalars, points, g_scalar) for multi-scalar point multiplication */ +void ecmult_multi_random_generate_inp(secp256k1_gej *expected, secp256k1_scalar *g_scalar, secp256k1_scalar *scalars, secp256k1_gej *gejs, int *inp_len, int *nonzero_inp_len, int *is_g_nonzero, int *mults_performed) { /* Temporaries. */ secp256k1_scalar sc_tmp; secp256k1_ge ge_tmp; - /* Variables needed for the actual input to ecmult_multi. */ - secp256k1_ge ges[128]; - ecmult_multi_data data; int i; - /* Which multiplication function to use */ - int fn = secp256k1_testrand_int(3); - secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var : - fn == 1 ? secp256k1_ecmult_strauss_batch_single : - secp256k1_ecmult_pippenger_batch_single; + int filled = 0; /* Simulate exponentially distributed num. */ int num_bits = 2 + secp256k1_testrand_int(6); /* Number of (scalar, point) inputs (excluding g). */ @@ -4907,25 +4882,25 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { num_nonzero == 1 && !nonzero_result ? 1 : (int)secp256k1_testrand_bits(1); /* Which g_scalar pointer to pass into ecmult_multi(). */ - const secp256k1_scalar* g_scalar_ptr = (g_nonzero || secp256k1_testrand_bits(1)) ? &g_scalar : NULL; + secp256k1_scalar* g_scalar_ptr = (g_nonzero || secp256k1_testrand_bits(1)) ? g_scalar : NULL; /* How many EC multiplications were performed in this function. */ int mults = 0; /* How many randomization steps to apply to the input list. */ int rands = (int)secp256k1_testrand_bits(3); if (rands > num_nonzero) rands = num_nonzero; - secp256k1_gej_set_infinity(&expected); + secp256k1_gej_set_infinity(expected); secp256k1_gej_set_infinity(&gejs[0]); secp256k1_scalar_set_int(&scalars[0], 0); if (g_nonzero) { /* If g_nonzero, set g_scalar to nonzero value r. */ - random_scalar_order_test(&g_scalar); + random_scalar_order_test(g_scalar); if (!nonzero_result) { /* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */ CHECK(num_nonzero > filled); random_scalar_order_test(&sc_tmp); - secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar); + secp256k1_scalar_mul(&scalars[filled], &sc_tmp, g_scalar); secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp); secp256k1_scalar_negate(&sc_tmp, &sc_tmp); secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &gejs[filled], &sc_tmp); @@ -4945,7 +4920,7 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { if (nonzero_result) { /* Compute the expected result using normal ecmult. */ CHECK(filled <= 1); - secp256k1_ecmult(&expected, &gejs[0], &scalars[0], &g_scalar); + secp256k1_ecmult(expected, &gejs[0], &scalars[0], g_scalar); mults += filled + g_nonzero; } @@ -5015,6 +4990,54 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { } } + /* number of (scalars, points) inputs generated */ + *inp_len = filled; + /* number of non-zero (scalars, points) inputs */ + *nonzero_inp_len = num_nonzero; + /* ptr to g_scalar*/ + g_scalar = g_scalar_ptr; + /* is mulciplicand of g nonzero? */ + *is_g_nonzero = g_nonzero; + /* number of mults performed in this function */ + *mults_performed += mults; +} + +int test_ecmult_multi_random(secp256k1_scratch *scratch) { + /* Large random test for ecmult_multi_* functions which exercises: + * - Few or many inputs (0 up to 128, roughly exponentially distributed). + * - Few or many 0*P or a*INF inputs (roughly uniformly distributed). + * - Including or excluding an nonzero a*G term (or such a term at all). + * - Final expected result equal to infinity or not (roughly 50%). + * - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch + */ + + /* These 4 variables define the eventual input to the ecmult_multi function. + * g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and + * scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points + * which form its normal inputs. */ + int filled = 0; + secp256k1_scalar g_scalar = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar *g_scalar_ptr = &g_scalar; + secp256k1_scalar scalars[128]; + secp256k1_gej gejs[128]; + /* The expected result, and the computed result. */ + secp256k1_gej expected, computed; + /* Variables needed for the actual input to ecmult_multi. */ + secp256k1_ge ges[128]; + ecmult_multi_data data; + /* How many EC multiplications were performed in this function. */ + int mults = 0; + int g_nonzero, num_nonzero; + + /* Which multiplication function to use */ + int fn = secp256k1_testrand_int(3); + secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var : + fn == 1 ? secp256k1_ecmult_strauss_batch_single : + secp256k1_ecmult_pippenger_batch_single; + + /* generate inputs and their ecmult_multi output */ + ecmult_multi_random_generate_inp(&expected, g_scalar_ptr, scalars, gejs, &filled, &num_nonzero, &g_nonzero, &mults); + /* Compute affine versions of all inputs. */ secp256k1_ge_set_all_gej_var(ges, gejs, filled); /* Invoke ecmult_multi code. */ @@ -5027,7 +5050,60 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { return mults; } -static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { +int test_ecmult_strauss_batch_internal_random(secp256k1_scratch *scratch) { + /* Large random test for `ecmult_strauss_batch_internal`. This test is + * very similar to `test_ecmult_multi_random`. */ + + /* These 4 variables define the eventual input to the ecmult_multi function. + * g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and + * scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points + * which form its normal inputs. */ + int filled = 0; + secp256k1_scalar g_scalar = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar *g_scalar_ptr = &g_scalar; + secp256k1_scalar scalars[128]; + secp256k1_gej gejs[128]; + /* The expected result, and the computed result. */ + secp256k1_gej expected, computed; + /* How many EC multiplications were performed in this function. */ + int mults = 0; + int g_nonzero, num_nonzero; + secp256k1_scalar *scratch_scalars; + secp256k1_gej *scratch_points; + size_t checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + int i; + + /* generate inputs and their ecmult_multi output */ + ecmult_multi_random_generate_inp(&expected, g_scalar_ptr, scalars, gejs, &filled, &num_nonzero, &g_nonzero, &mults); + + /* allocate inputs on the scratch space */ + scratch_scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, filled*sizeof(secp256k1_scalar)); + scratch_points = (secp256k1_gej*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, filled*sizeof(secp256k1_gej)); + + /* If scalar or point allocation fails, restore scratch space to previous state */ + if (scratch_scalars == NULL || scratch_points == NULL) { + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); + return 0; + } + + /* copy the scalar and points to the scratch space */ + for (i = 0; i < filled; i++) { + scratch_scalars[i] = scalars[i]; + scratch_points[i] = gejs[i]; + } + + CHECK(secp256k1_ecmult_strauss_batch_internal(&CTX->error_callback, scratch, &computed, scratch_scalars, scratch_points, g_scalar_ptr, filled)); + mults += num_nonzero + g_nonzero; + /* Compare with expected result. */ + secp256k1_gej_neg(&computed, &computed); + secp256k1_gej_add_var(&computed, &computed, &expected, NULL); + CHECK(secp256k1_gej_is_infinity(&computed)); + + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); + return mults; +} + +void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { secp256k1_scalar sc; secp256k1_ge pt; secp256k1_gej r; @@ -5213,7 +5289,9 @@ static void test_ecmult_multi_batching(void) { static void run_ecmult_multi_tests(void) { secp256k1_scratch *scratch; - int64_t todo = (int64_t)320 * COUNT; + int64_t todo_multi = (int64_t)320 * COUNT; + /* todo: what should be the intial val of `todo_strauss_internal` */ + int64_t todo_strauss_internal = (int64_t)320 * COUNT; test_secp256k1_pippenger_bucket_window_inv(); test_ecmult_multi_pippenger_max_points(); @@ -5224,8 +5302,11 @@ static void run_ecmult_multi_tests(void) { test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single); test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single); test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single); - while (todo > 0) { - todo -= test_ecmult_multi_random(scratch); + while (todo_multi > 0) { + todo_multi -= test_ecmult_multi_random(scratch); + } + while (todo_strauss_internal > 0) { + todo_strauss_internal -= test_ecmult_strauss_batch_internal_random(scratch); } secp256k1_scratch_destroy(&CTX->error_callback, scratch); @@ -7277,17 +7358,27 @@ static void run_ecdsa_wycheproof(void) { #ifdef ENABLE_MODULE_EXTRAKEYS # include "modules/extrakeys/tests_impl.h" +# ifdef ENABLE_MODULE_BATCH +# include "modules/extrakeys/batch_add_tests_impl.h" +# endif #endif #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/tests_impl.h" +# ifdef ENABLE_MODULE_BATCH +# include "modules/schnorrsig/batch_add_tests_impl.h" +# endif #endif #ifdef ENABLE_MODULE_ELLSWIFT # include "modules/ellswift/tests_impl.h" #endif -static void run_secp256k1_memczero_test(void) { +#ifdef ENABLE_MODULE_BATCH +# include "modules/batch/tests_impl.h" +#endif + +void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7625,16 +7716,26 @@ int main(int argc, char **argv) { #ifdef ENABLE_MODULE_EXTRAKEYS run_extrakeys_tests(); +# ifdef ENABLE_MODULE_BATCH + run_batch_add_xonlypub_tweak_tests(); +# endif #endif #ifdef ENABLE_MODULE_SCHNORRSIG run_schnorrsig_tests(); +# ifdef ENABLE_MODULE_BATCH + run_batch_add_schnorrsig_tests(); +# endif #endif #ifdef ENABLE_MODULE_ELLSWIFT run_ellswift_tests(); #endif +#ifdef ENABLE_MODULE_BATCH + run_batch_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_byteorder_tests(); diff --git a/src/util.h b/src/util.h index 187bf1c5e0a6c..154d9ebcf13a6 100644 --- a/src/util.h +++ b/src/util.h @@ -51,13 +51,27 @@ static void print_buf_plain(const unsigned char *buf, size_t len) { # define SECP256K1_INLINE inline # endif +/** Assert statically that expr is true. + * + * This is a statement-like macro and can only be used inside functions. + */ +#define STATIC_ASSERT(expr) do { \ + switch(0) { \ + case 0: \ + /* If expr evaluates to 0, we have two case labels "0", which is illegal. */ \ + case /* ERROR: static assertion failed */ (expr): \ + ; \ + } \ +} while(0) + /** Assert statically that expr is an integer constant expression, and run stmt. * * Useful for example to enforce that magnitude arguments are constant. */ #define ASSERT_INT_CONST_AND_DO(expr, stmt) do { \ switch(42) { \ - case /* ERROR: integer argument is not constant */ expr: \ + /* C allows only integer constant expressions as case labels. */ \ + case /* ERROR: integer argument is not constant */ (expr): \ break; \ default: ; \ } \ diff --git a/tools/check-abi.sh b/tools/check-abi.sh index 8f6119cd8e82b..55c945ac16122 100755 --- a/tools/check-abi.sh +++ b/tools/check-abi.sh @@ -3,17 +3,19 @@ set -eu default_base_version="$(git describe --match "v*.*.*" --abbrev=0)" -default_new_version="master" +default_new_version="HEAD" display_help_and_exit() { - echo "Usage: $0 " + echo "Usage: $0 [ []]" echo "" echo "Description: This script uses the ABI Compliance Checker tool to determine if the ABI" echo " of a new version of libsecp256k1 has changed in a backward-incompatible way." echo "" echo "Options:" - echo " base_ver Specify the base version (default: $default_base_version)" - echo " new_ver Specify the new version (default: $default_new_version)" + echo " base_ver Specify the base version as a git commit-ish" + echo " (default: most recent reachable tag matching \"v.*.*\", currently \"$default_base_version\")" + echo " new_ver Specify the new version as a git commit-ish" + echo " (default: $default_new_version)" echo " -h, --help Display this help message" exit 0 } @@ -23,9 +25,11 @@ if [ "$#" -eq 0 ]; then new_version="$default_new_version" elif [ "$#" -eq 1 ] && { [ "$1" = "-h" ] || [ "$1" = "--help" ]; }; then display_help_and_exit -elif [ "$#" -eq 2 ]; then +elif [ "$#" -eq 1 ] || [ "$#" -eq 2 ]; then base_version="$1" - new_version="$2" + if [ "$#" -eq 2 ]; then + new_version="$2" + fi else echo "Invalid usage. See help:" echo "" @@ -33,7 +37,8 @@ else fi checkout_and_build() { - git worktree add -d "$1" "$2" + _orig_dir="$(pwd)" + git worktree add --detach "$1" "$2" cd "$1" mkdir build && cd build cmake -S .. --preset dev-mode \ @@ -45,20 +50,18 @@ checkout_and_build() { -DSECP256K1_BUILD_EXAMPLES=OFF cmake --build . -j "$(nproc)" abi-dumper src/libsecp256k1.so -o ABI.dump -lver "$2" + cd "$_orig_dir" } echo "Comparing $base_version (base version) to $new_version (new version)" echo -original_dir="$(pwd)" - -base_source_dir=$(mktemp -d) +base_source_dir="$(mktemp -d)" checkout_and_build "$base_source_dir" "$base_version" -new_source_dir=$(mktemp -d) +new_source_dir="$(mktemp -d)" checkout_and_build "$new_source_dir" "$new_version" -cd "$original_dir" abi-compliance-checker -lib libsecp256k1 -old "${base_source_dir}/build/ABI.dump" -new "${new_source_dir}/build/ABI.dump" git worktree remove "$base_source_dir" git worktree remove "$new_source_dir"