From cd1404164e0a7f56b9ebdfc7ba500b4497c3d8da Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Sun, 23 Mar 2025 16:07:41 +0100 Subject: [PATCH 01/11] Squashed 'src/secp256k1/' changes from 0cdc758a56..74dc25820b 74dc25820b build: Add build option for batch module 01f8b86de7 SQUASH ME: Fix silent merge conflicts 991cf91e65 batch: Generate graphs for batch verification speed up c7b4d2c0b2 batch, extrakeys: Add benchmark for batch verify and `tweak_add_check` f9d09ff8d3 batch: Add tests for `batch_add_*` APIs d94a99bbfd batch,ecmult: Add tests for core batch APIs and `strauss_batch` refactor 220277f062 batch: Add API usage example 6cc9210001 batch: Add `batch_add_*` APIs 9beee683a9 batch, ecmult: Add `batch_verify` API and refactor `strauss_batch` a31e017cbc batch: Add `create` and `destroy` APIs 4feb9f72a1 batch: Initialize an experimental batch module 70f149b9a1 Merge bitcoin-core/secp256k1#1662: bench: add ellswift to bench help output 6b3fe51fb6 bench: add ellswift to bench help output d84bb83e26 Merge bitcoin-core/secp256k1#1661: configure: Show exhaustive tests in summary 3f54ed8c1b Merge bitcoin-core/secp256k1#1659: include: remove WARN_UNUSED_RESULT for functions always returning 1 20b05c9d3f configure: Show exhaustive tests in summary e56716a3bc Merge bitcoin-core/secp256k1#1660: ci: Fix exiting from ci.sh on error d87c3bc58f ci: Fix exiting from ci.sh on error 1b6e081538 include: remove WARN_UNUSED_RESULT for functions always returning 1 2abb35b034 Merge bitcoin-core/secp256k1#1657: tests: remove unused uncounting_illegal_callback_fn 51907fa918 tests: remove unused uncounting_illegal_callback_fn a7a5117144 Merge bitcoin-core/secp256k1#1359: Fix symbol visibility issues, add test for it 13ed6f65dc Merge bitcoin-core/secp256k1#1593: Remove deprecated `_ec_privkey_{negate,tweak_add,tweak_mul}` aliases from API d1478763a5 build: Drop no longer needed `-fvisibility=hidden` compiler option 8ed1d83d92 ci: Run `tools/symbol-check.py` 41d32ab2de test: Add `tools/symbol-check.py` 88548058b3 Introduce `SECP256K1_LOCAL_VAR` macro 03bbe8c615 Merge bitcoin-core/secp256k1#1655: gha: Print all *.log files, in a separate action 59860bcc24 gha: Print all *.log files, in a separate action 4ba1ba2af9 Merge bitcoin-core/secp256k1#1647: cmake: Adjust diagnostic flags for `clang-cl` abd25054a1 Merge bitcoin-core/secp256k1#1656: musig: Fix clearing of pubnonces 961ec25a83 musig: Fix clearing of pubnonces 3186082387 Merge bitcoin-core/secp256k1#1614: Add _ge_set_all_gej and use it in musig for own public nonces 6c2a39dafb Merge bitcoin-core/secp256k1#1639: Make static context const 37d2c60bec Remove deprecated _ec_privkey_{negate,tweak_add,tweak_mul} aliases 432ac57705 Make static context const 1b1fc09341 Merge bitcoin-core/secp256k1#1642: Verify `compressed` argument in `secp256k1_eckey_pubkey_serialize` c0d9480fbb Merge bitcoin-core/secp256k1#1654: use `EXIT_` constants over magic numbers for indicating program execution status 13d389629a CONTRIBUTING: mention that `EXIT_` codes should be used c855581728 test, bench, precompute_ecmult: use `EXIT_...` constants for `main` return values 965393fcea examples: use `EXIT_...` constants for `main` return values 2e3bf13653 Merge bitcoin-core/secp256k1#1646: README: add instructions for verifying GPG signatures b682dbcf84 README: add instructions for verifying GPG signatures 00774d0723 Merge bitcoin-core/secp256k1#1650: schnorrsig: clear out masked secret key in BIP-340 nonce function a82287fb85 schnorrsig: clear out masked secret key in BIP-340 nonce function 4c50d73dd9 ci: Add new "Windows (clang-cl)" job 84c0bd1f72 cmake: Adjust diagnostic flags for clang-cl f79f46c703 Merge bitcoin-core/secp256k1#1641: doc: Improve cmake instructions in README 2ac9f558c4 doc: Improve cmake instructions in README 1823594761 Verify `compressed` argument in `secp256k1_eckey_pubkey_serialize` 8deef00b33 Merge bitcoin-core/secp256k1#1634: Fix some misspellings 39705450eb Fix some misspellings ec329c2501 Merge bitcoin-core/secp256k1#1633: release cleanup: bump version after 0.6.0 c97059f594 release cleanup: bump version after 0.6.0 64228a648f musig: Use _ge_set_all_gej for own public nonces 300aab1c05 tests: Improve _ge_set_all_gej(_var) tests 365f274ce3 group: Simplify secp256k1_ge_set_all_gej d3082ddead group: Add constant-time secp256k1_ge_set_all_gej git-subtree-dir: src/secp256k1 git-subtree-split: 74dc25820b54684769fde919822d1290abbd69e2 --- .cirrus.yml | 3 + .github/actions/print-logs/action.yml | 34 ++ .github/workflows/ci.yml | 295 +++++------------ .gitignore | 1 + CHANGELOG.md | 8 + CMakeLists.txt | 37 ++- CONTRIBUTING.md | 1 + Makefile.am | 16 + README.md | 64 +++- ci/ci.sh | 20 +- ci/cirrus.sh | 82 +++++ ci/linux-debian.Dockerfile | 6 +- configure.ac | 25 +- 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 ++++++++++ examples/ecdh.c | 9 +- examples/ecdsa.c | 13 +- examples/ellswift.c | 11 +- examples/musig.c | 15 +- examples/schnorr.c | 13 +- include/secp256k1.h | 34 +- include/secp256k1_batch.h | 110 ++++++ include/secp256k1_extrakeys.h | 8 +- include/secp256k1_schnorrsig_batch.h | 42 +++ include/secp256k1_tweak_check_batch.h | 50 +++ src/CMakeLists.txt | 5 + src/bench.c | 65 +++- src/bench.h | 6 +- src/bench_ecmult.c | 7 +- src/bench_internal.c | 5 +- src/ctime_tests.c | 5 +- src/eckey_impl.h | 3 + src/ecmult_impl.h | 75 +++-- src/group.h | 6 +- src/group_impl.h | 38 +++ src/modules/batch/Makefile.am.include | 3 + src/modules/batch/main_impl.h | 207 ++++++++++++ src/modules/batch/tests_impl.h | 214 ++++++++++++ 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/musig/session_impl.h | 18 +- 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/main_impl.h | 2 + src/modules/schnorrsig/tests_impl.h | 89 ++++- src/precompute_ecmult.c | 5 +- src/precompute_ecmult_gen.c | 5 +- src/precomputed_ecmult.h | 6 +- src/precomputed_ecmult_gen.h | 4 +- src/scalar_impl.h | 4 +- src/secp256k1.c | 28 +- src/tests.c | 235 +++++++++---- src/tests_exhaustive.c | 4 +- src/util.h | 2 +- src/util_local_visibility.h | 12 + tools/symbol-check.py | 72 ++++ 68 files changed, 3080 insertions(+), 469 deletions(-) create mode 100644 .github/actions/print-logs/action.yml create mode 100755 ci/cirrus.sh 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 create mode 100644 src/util_local_visibility.h create mode 100755 tools/symbol-check.py diff --git a/.cirrus.yml b/.cirrus.yml index 81a4f043285..023cd1916c2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -29,6 +29,8 @@ env: BENCH: yes SECP256K1_BENCH_ITERS: 2 CTIMETESTS: yes + SYMBOL_CHECK: yes + VIRTUAL_ENV: /root/venv # Compile and run the tests EXAMPLES: yes @@ -53,6 +55,7 @@ cat_logs_snippet: &CAT_LOGS linux_arm64_container_snippet: &LINUX_ARM64_CONTAINER env_script: + - export PATH="$VIRTUAL_ENV/bin:$PATH" - env | tee /tmp/env build_script: - DOCKER_BUILDKIT=1 docker build --file "ci/linux-debian.Dockerfile" --tag="ci_secp256k1_arm" diff --git a/.github/actions/print-logs/action.yml b/.github/actions/print-logs/action.yml new file mode 100644 index 00000000000..33de35cb342 --- /dev/null +++ b/.github/actions/print-logs/action.yml @@ -0,0 +1,34 @@ +name: "Print logs" +description: "Print the log files produced by ci/ci.sh" +runs: + using: "composite" + steps: + - shell: bash + run: | + # Print the log files produced by ci/ci.sh + + # Helper functions + group() { + title=$1 + echo "::group::$title" + } + endgroup() { + echo "::endgroup::" + } + cat_file() { + file=$1 + group "$file" + cat "$file" + endgroup + } + + # Print all *.log files + shopt -s nullglob + for file in *.log; do + cat_file "$file" + done + + # Print environment + group "CI env" + env + endgroup diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54b2fab1c46..a3108d6bb1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ env: BENCH: 'yes' SECP256K1_BENCH_ITERS: 2 CTIMETESTS: 'yes' + SYMBOL_CHECK: 'yes' # Compile and run the examples. EXAMPLES: 'yes' @@ -107,23 +108,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} i686_debian: name: "i686: Linux (Debian stable)" @@ -157,23 +144,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} s390x_debian: name: "s390x (big-endian): Linux (Debian stable, QEMU)" @@ -203,23 +176,10 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} + arm32_debian: name: "ARM32: Linux (Debian stable, QEMU)" @@ -257,23 +217,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} arm64_debian: name: "ARM64: Linux (Debian stable, QEMU)" @@ -314,23 +260,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} ppc64le_debian: name: "ppc64le: Linux (Debian stable, QEMU)" @@ -360,23 +292,10 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} + valgrind_debian: name: "Valgrind (memcheck)" @@ -416,23 +335,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} sanitizers_debian: name: "UBSan, ASan, LSan" @@ -461,6 +366,7 @@ jobs: ASAN_OPTIONS: 'strict_string_checks=1:detect_stack_use_after_return=1:detect_leaks=1' LSAN_OPTIONS: 'use_unaligned=1' SECP256K1_TEST_ITERS: 32 + SYMBOL_CHECK: 'no' steps: - name: Checkout @@ -473,23 +379,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} msan_debian: name: "MSan" @@ -525,6 +417,7 @@ jobs: SECP256K1_TEST_ITERS: 32 ASM: 'no' WITH_VALGRIND: 'no' + SYMBOL_CHECK: 'no' steps: - name: Checkout @@ -537,23 +430,10 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} + mingw_debian: name: ${{ matrix.configuration.job_name }} @@ -593,23 +473,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} x86_64-macos-native: name: "x86_64: macOS Ventura, Valgrind" @@ -620,6 +486,7 @@ jobs: CC: 'clang' HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 + SYMBOL_CHECK: 'no' strategy: fail-fast: false @@ -652,23 +519,15 @@ jobs: env: ${{ matrix.env_vars }} run: ./ci/ci.sh - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Symbol check + run: | + python3 --version + python3 -m pip install lief + python3 ./tools/symbol-check.py .libs/libsecp256k1.dylib + + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} arm64-macos-native: name: "ARM64: macOS Sonoma" @@ -681,6 +540,7 @@ jobs: HOMEBREW_NO_INSTALL_CLEANUP: 1 WITH_VALGRIND: 'no' CTIMETESTS: 'no' + SYMBOL_CHECK: 'no' strategy: fail-fast: false @@ -708,23 +568,20 @@ jobs: env: ${{ matrix.env_vars }} run: ./ci/ci.sh - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Symbol check + env: + VIRTUAL_ENV: '${{ github.workspace }}/venv' + run: | + python3 --version + python3 -m venv $VIRTUAL_ENV + export PATH="$VIRTUAL_ENV/bin:$PATH" + python3 -m pip install lief + python3 ./tools/symbol-check.py .libs/libsecp256k1.dylib + + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} + win64-native: name: ${{ matrix.configuration.job_name }} @@ -737,6 +594,7 @@ jobs: configuration: - job_name: 'x64 (MSVC): Windows (VS 2022, shared)' cmake_options: '-A x64 -DBUILD_SHARED_LIBS=ON' + symbol_check: 'true' - job_name: 'x64 (MSVC): Windows (VS 2022, static)' cmake_options: '-A x64 -DBUILD_SHARED_LIBS=OFF' - job_name: 'x64 (MSVC): Windows (VS 2022, int128_struct)' @@ -746,6 +604,8 @@ jobs: cpp_flags: '/DSECP256K1_MSVC_MULH_TEST_OVERRIDE' - job_name: 'x86 (MSVC): Windows (VS 2022)' cmake_options: '-A Win32' + - job_name: 'x64 (MSVC): Windows (clang-cl)' + cmake_options: '-T ClangCL' steps: - name: Checkout @@ -763,6 +623,13 @@ jobs: run: | cd build/bin/RelWithDebInfo && file *tests.exe bench*.exe libsecp256k1-*.dll || true + - name: Symbol check + if: ${{ matrix.configuration.symbol_check }} + run: | + py -3 --version + py -3 -m pip install lief + py -3 .\tools\symbol-check.py build\bin\RelWithDebInfo\libsecp256k1-5.dll + - name: Check run: | ctest -C RelWithDebInfo --test-dir build -j ([int]$env:NUMBER_OF_PROCESSORS + 1) @@ -813,23 +680,9 @@ jobs: dockerfile: ./ci/linux-debian.Dockerfile tag: linux-debian-image - - run: cat tests.log || true - if: ${{ always() }} - - run: cat noverify_tests.log || true - if: ${{ always() }} - - run: cat exhaustive_tests.log || true - if: ${{ always() }} - - run: cat ctime_tests.log || true - if: ${{ always() }} - - run: cat bench.log || true - if: ${{ always() }} - - run: cat config.log || true - if: ${{ always() }} - - run: cat test_env.log || true - if: ${{ always() }} - - name: CI env - run: env - if: ${{ always() }} + - name: Print logs + uses: ./.github/actions/print-logs + if: ${{ !cancelled() }} cxx_headers_debian: name: "C++ (public headers)" diff --git a/.gitignore b/.gitignore index bffba8cb2c9..c26de35157b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ ecdsa_example schnorr_example ellswift_example musig_example +batch_example *.exe *.so *.a diff --git a/CHANGELOG.md b/CHANGELOG.md index ee447c0c1c7..7042171135d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +#### Removed +- Removed previously deprecated function aliases `secp256k1_ec_privkey_negate`, `secp256k1_ec_privkey_tweak_add` and + `secp256k1_ec_privkey_tweak_mul`. Use `secp256k1_ec_seckey_negate`, `secp256k1_ec_seckey_tweak_add` and + `secp256k1_ec_seckey_tweak_mul` instead. + ## [0.6.0] - 2024-11-04 #### Added @@ -162,6 +169,7 @@ This version was in fact never released. The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6). Therefore, this version number does not uniquely identify a set of source files. +[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.6.0...HEAD [0.6.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.1...v0.6.0 [0.5.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.4.1...v0.5.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 041bfa3dca0..47855c29790 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ project(libsecp256k1 # The package (a.k.a. release) version is based on semantic versioning 2.0.0 of # the API. All changes in experimental modules are treated as # backwards-compatible and therefore at most increase the minor version. - VERSION 0.6.0 + VERSION 0.6.1 DESCRIPTION "Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1." HOMEPAGE_URL "https://github.com/bitcoin-core/secp256k1" LANGUAGES C @@ -32,7 +32,7 @@ endif() # All changes in experimental modules are treated as if they don't affect the # interface and therefore only increase the revision. set(${PROJECT_NAME}_LIB_VERSION_CURRENT 5) -set(${PROJECT_NAME}_LIB_VERSION_REVISION 0) +set(${PROJECT_NAME}_LIB_VERSION_REVISION 1) set(${PROJECT_NAME}_LIB_VERSION_AGE 0) #============================= @@ -55,11 +55,12 @@ option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL}) ## Modules # We declare all options before processing them, to make sure we can express -# dependendencies while processing. +# dependencies while processing. option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON) option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF) option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) +option(SECP256K1_ENABLE_MODULE_BATCH "Enable batch module." OFF) option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON) option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) @@ -69,6 +70,18 @@ if(SECP256K1_ENABLE_MODULE_ELLSWIFT) add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) endif() +option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF) +if(SECP256K1_ENABLE_MODULE_BATCH) + if(NOT SECP256K1_EXPERIMENTAL) + message(FATAL_ERROR "Schnorrsig batch validation is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") + endif() + if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG) + message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the Schnorrsig batch validation module.") + endif() + set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON) + add_compile_definitions(ENABLE_MODULE_BATCH=1) +endif() + if(SECP256K1_ENABLE_MODULE_MUSIG) if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG) message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.") @@ -156,7 +169,6 @@ elseif(SECP256K1_ASM) endif() endif() -option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF) if(NOT SECP256K1_EXPERIMENTAL) if(SECP256K1_ASM STREQUAL "arm32") message(FATAL_ERROR "ARM32 assembly is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") @@ -242,17 +254,21 @@ endif() include(TryAppendCFlags) if(MSVC) - # Keep the following commands ordered lexicographically. + # For both cl and clang-cl compilers. try_append_c_flags(/W3) # Production quality warning level. - try_append_c_flags(/wd4146) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned". - try_append_c_flags(/wd4244) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data". - try_append_c_flags(/wd4267) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data". # Eliminate deprecation warnings for the older, less secure functions. add_compile_definitions(_CRT_SECURE_NO_WARNINGS) +else() + try_append_c_flags(-Wall) # GCC >= 2.95 and probably many other compilers. +endif() +if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") + # Keep the following commands ordered lexicographically. + try_append_c_flags(/wd4146) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned". + try_append_c_flags(/wd4244) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data". + try_append_c_flags(/wd4267) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data". else() # Keep the following commands ordered lexicographically. try_append_c_flags(-pedantic) - try_append_c_flags(-Wall) # GCC >= 2.95 and probably many other compilers. try_append_c_flags(-Wcast-align) # GCC >= 2.95. try_append_c_flags(-Wcast-align=strict) # GCC >= 8.0. try_append_c_flags(-Wconditional-uninitialized) # Clang >= 3.0 only. @@ -267,8 +283,6 @@ else() try_append_c_flags(-Wundef) endif() -set(CMAKE_C_VISIBILITY_PRESET hidden) - set(print_msan_notice) if(SECP256K1_BUILD_CTIME_TESTS) include(CheckMemorySanitizer) @@ -325,6 +339,7 @@ message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH} message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}") message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") +message(" batch ............................... ${SECP256K1_ENABLE_MODULE_BATCH}") message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") message("Parameters:") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a366d38b0ec..80890fb7068 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,6 +74,7 @@ In addition, libsecp256k1 tries to maintain the following coding conventions: * User-facing comment lines in headers should be limited to 80 chars if possible. * All identifiers in file scope should start with `secp256k1_`. * Avoid trailing whitespace. +* Use the constants `EXIT_SUCCESS`/`EXIT_FAILURE` (defined in `stdlib.h`) to indicate program execution status for examples and other binaries. ### Tests diff --git a/Makefile.am b/Makefile.am index a95b4809d48..3ec0bb91f93 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ noinst_HEADERS += src/assumptions.h noinst_HEADERS += src/checkmem.h noinst_HEADERS += src/testutil.h noinst_HEADERS += src/util.h +noinst_HEADERS += src/util_local_visibility.h noinst_HEADERS += src/int128.h noinst_HEADERS += src/int128_impl.h noinst_HEADERS += src/int128_native.h @@ -183,6 +184,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 if ENABLE_MODULE_ELLSWIFT noinst_PROGRAMS += ellswift_example @@ -300,3 +312,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 222e5fb7685..a69fbcd610b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Features: * Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). * Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). * Optional module for MuSig2 Schnorr multi-signatures according to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). +* Optional module for Batch Verification (experimental). Implementation details ---------------------- @@ -61,14 +62,50 @@ Implementation details * Optional runtime blinding which attempts to frustrate differential power analysis. * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. +Obtaining and verifying +----------------------- + +The git tag for each release (e.g. `v0.6.0`) is GPG-signed by one of the maintainers. +For a fully verified build of this project, it is recommended to obtain this repository +via git, obtain the GPG keys of the signing maintainer(s), and then verify the release +tag's signature using git. + +This can be done with the following steps: + +1. Obtain the GPG keys listed in [SECURITY.md](./SECURITY.md). +2. If possible, cross-reference these key IDs with another source controlled by its owner (e.g. + social media, personal website). This is to mitigate the unlikely case that incorrect + content is being presented by this repository. +3. Clone the repository: + ``` + git clone https://github.com/bitcoin-core/secp256k1 + ``` +4. Check out the latest release tag, e.g. + ``` + git checkout v0.6.0 + ``` +5. Use git to verify the GPG signature: + ``` + % git tag -v v0.6.0 | grep -C 3 'Good signature' + + gpg: Signature made Mon 04 Nov 2024 12:14:44 PM EST + gpg: using RSA key 4BBB845A6F5A65A69DFAEC234861DBF262123605 + gpg: Good signature from "Jonas Nick " [unknown] + gpg: aka "Jonas Nick " [unknown] + gpg: WARNING: This key is not certified with a trusted signature! + gpg: There is no indication that the signature belongs to the owner. + Primary key fingerprint: 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 + Subkey fingerprint: 4BBB 845A 6F5A 65A6 9DFA EC23 4861 DBF2 6212 3605 + ``` + Building with Autotools ----------------------- - $ ./autogen.sh - $ ./configure - $ make - $ make check # run the test suite - $ sudo make install # optional + $ ./autogen.sh # Generate a ./configure script + $ ./configure # Generate a build system + $ make # Run the actual build process + $ make check # Run the test suite + $ sudo make install # Install the library into the system (optional) To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. @@ -79,24 +116,23 @@ To maintain a pristine source tree, CMake encourages to perform an out-of-source ### Building on POSIX systems - $ mkdir build && cd build - $ cmake .. - $ cmake --build . - $ ctest # run the test suite - $ sudo cmake --install . # optional + $ cmake -B build # Generate a build system in subdirectory "build" + $ cmake --build build # Run the actual build process + $ ctest --test-dir build # Run the test suite + $ sudo cmake --install build # Install the library into the system (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. +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 -B build -LH` or `ccmake -B build` to see the full list of available flags. ### Cross compiling To alleviate issues with cross compiling, preconfigured toolchain files are available in the `cmake` directory. For example, to cross compile for Windows: - $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/x86_64-w64-mingw32.toolchain.cmake + $ cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/x86_64-w64-mingw32.toolchain.cmake To cross compile for Android with [NDK](https://developer.android.com/ndk/guides/cmake) (using NDK's toolchain file, and assuming the `ANDROID_NDK_ROOT` environment variable has been set): - $ cmake .. -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28 + $ cmake -B build -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28 ### Building on Windows @@ -106,7 +142,7 @@ The following example assumes using of Visual Studio 2022 and CMake v3.21+. In "Developer Command Prompt for VS 2022": - >cmake -G "Visual Studio 17 2022" -A x64 -S . -B build + >cmake -G "Visual Studio 17 2022" -A x64 -B build >cmake --build build --config RelWithDebInfo Usage examples diff --git a/ci/ci.sh b/ci/ci.sh index 3636deafa11..3285ecc951d 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -14,7 +14,7 @@ print_environment() { for var in WERROR_CFLAGS MAKEFLAGS BUILD \ ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ EXPERIMENTAL ECDH RECOVERY EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT \ - SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ + SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ CC CFLAGS CPPFLAGS AR NM \ @@ -94,10 +94,10 @@ if [ $build_exit_code -ne 0 ]; then *snapshot*) # Ignore internal compiler errors in gcc-snapshot and clang-snapshot grep -e "internal compiler error:" -e "PLEASE submit a bug report" make.log - return $?; + exit $? ;; *) - return 1; + exit 1 ;; esac fi @@ -107,6 +107,20 @@ file *tests* || true file bench* || true file .libs/* || true +if [ "$SYMBOL_CHECK" = "yes" ] +then + python3 --version + case "$HOST" in + *mingw*) + ls -l .libs + python3 ./tools/symbol-check.py .libs/libsecp256k1-5.dll + ;; + *) + python3 ./tools/symbol-check.py .libs/libsecp256k1.so + ;; + esac +fi + # This tells `make check` to wrap test invocations. export LOG_COMPILER="$WRAPPER_CMD" diff --git a/ci/cirrus.sh b/ci/cirrus.sh new file mode 100755 index 00000000000..2b8936a2f12 --- /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/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index 241bfa97194..547b402232a 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -32,7 +32,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \ gcc-mingw-w64-x86-64-win32 wine64 wine \ gcc-mingw-w64-i686-win32 wine32 \ - python3 && \ + python3-full && \ if ! ( dpkg --print-architecture | grep --quiet "arm64" ) ; then \ apt-get install --no-install-recommends -y \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 ;\ @@ -77,3 +77,7 @@ RUN \ apt-get autoremove -y wget && \ apt-get clean && rm -rf /var/lib/apt/lists/* +ENV VIRTUAL_ENV=/root/venv +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" +RUN pip install lief diff --git a/configure.ac b/configure.ac index f880a3578dd..1857d1cf9e7 100644 --- a/configure.ac +++ b/configure.ac @@ -5,8 +5,8 @@ AC_PREREQ([2.60]) # backwards-compatible and therefore at most increase the minor version. define(_PKG_VERSION_MAJOR, 0) define(_PKG_VERSION_MINOR, 6) -define(_PKG_VERSION_PATCH, 0) -define(_PKG_VERSION_IS_RELEASE, true) +define(_PKG_VERSION_PATCH, 1) +define(_PKG_VERSION_IS_RELEASE, false) # The library version is based on libtool versioning of the ABI. The set of # rules for updating the version can be found here: @@ -14,7 +14,7 @@ define(_PKG_VERSION_IS_RELEASE, true) # All changes in experimental modules are treated as if they don't affect the # interface and therefore only increase the revision. define(_LIB_VERSION_CURRENT, 5) -define(_LIB_VERSION_REVISION, 0) +define(_LIB_VERSION_REVISION, 1) define(_LIB_VERSION_AGE, 0) AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_PATCH)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-dev]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1]) @@ -111,7 +111,6 @@ AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only SECP_TRY_APPEND_CFLAGS([-Wreserved-identifier], $1) # Clang >= 13.0 only - SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" fi @@ -192,6 +191,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])]) @@ -254,8 +257,8 @@ fi print_msan_notice=no if test x"$enable_ctime_tests" = x"yes"; then SECP_MSAN_CHECK - # MSan on Clang >=16 reports unitialized memory in function parameters and return values, even if - # the uninitalized variable is never actually "used". This is called "eager" checking, and it's + # MSan on Clang >=16 reports uninitialized memory in function parameters and return values, even if + # the uninitialized variable is never actually "used". This is called "eager" checking, and it's # sounds like good idea for normal use of MSan. However, it yields many false positives in the # ctime_tests because many return values depend on secret (i.e., "uninitialized") values, and # we're only interested in detecting branches (which count as "uses") on secret data. @@ -430,6 +433,10 @@ 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 @@ -442,6 +449,9 @@ if test x"$enable_experimental" = x"no"; then 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 ### @@ -463,6 +473,7 @@ AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x" AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = 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"]) @@ -477,6 +488,7 @@ echo "Build Options:" echo " with external callbacks = $enable_external_default_callbacks" echo " with benchmarks = $enable_benchmark" echo " with tests = $enable_tests" +echo " with exhaustive tests = $enable_exhaustive_tests" echo " with ctime tests = $enable_ctime_tests" echo " with coverage = $enable_coverage" echo " with examples = $enable_examples" @@ -486,6 +498,7 @@ echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module musig = $enable_module_musig" 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/doc/speedup-batch.md b/doc/speedup-batch.md new file mode 100644 index 00000000000..c6956398925 --- /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 00000000000..773a6df9baf --- /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 00000000000..a6a270d348b --- /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 00000000000..d38d45dceba --- /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 00000000000..9d257341c13 --- /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 00000000000..c289c4aab2e --- /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 00000000000..7960ca0fd04 --- /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 00000000000..d0fc5786d51 --- /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/examples/ecdh.c b/examples/ecdh.c index 13aa760b2dc..67b8c2047ab 100644 --- a/examples/ecdh.c +++ b/examples/ecdh.c @@ -8,6 +8,7 @@ *************************************************************************/ #include +#include #include #include @@ -33,7 +34,7 @@ int main(void) { secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); if (!fill_random(randomize, sizeof(randomize))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* Randomizing the context is recommended to protect against side-channel * leakage See `secp256k1_context_randomize` in secp256k1.h for more @@ -44,14 +45,14 @@ int main(void) { /*** Key Generation ***/ if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* If the secret key is zero or out of range (greater than secp256k1's * order), we fail. Note that the probability of this occurring is negligible * with a properly functioning random number generator. */ if (!secp256k1_ec_seckey_verify(ctx, seckey1) || !secp256k1_ec_seckey_verify(ctx, seckey2)) { printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n"); - return 1; + return EXIT_FAILURE; } /* Public key creation using a valid context with a verified secret key should never fail */ @@ -116,5 +117,5 @@ int main(void) { secure_erase(shared_secret1, sizeof(shared_secret1)); secure_erase(shared_secret2, sizeof(shared_secret2)); - return 0; + return EXIT_SUCCESS; } diff --git a/examples/ecdsa.c b/examples/ecdsa.c index 80ae9d46c52..ae16c180dc9 100644 --- a/examples/ecdsa.c +++ b/examples/ecdsa.c @@ -8,6 +8,7 @@ *************************************************************************/ #include +#include #include #include @@ -40,7 +41,7 @@ int main(void) { secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); if (!fill_random(randomize, sizeof(randomize))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* Randomizing the context is recommended to protect against side-channel * leakage See `secp256k1_context_randomize` in secp256k1.h for more @@ -51,14 +52,14 @@ int main(void) { /*** Key Generation ***/ if (!fill_random(seckey, sizeof(seckey))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* If the secret key is zero or out of range (greater than secp256k1's * order), we fail. Note that the probability of this occurring is negligible * with a properly functioning random number generator. */ if (!secp256k1_ec_seckey_verify(ctx, seckey)) { printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n"); - return 1; + return EXIT_FAILURE; } /* Public key creation using a valid context with a verified secret key should never fail */ @@ -92,13 +93,13 @@ int main(void) { /* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */ if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) { printf("Failed parsing the signature\n"); - return 1; + return EXIT_FAILURE; } /* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */ if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) { printf("Failed parsing the public key\n"); - return 1; + return EXIT_FAILURE; } /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */ @@ -133,5 +134,5 @@ int main(void) { * will remove any writes that aren't used. */ secure_erase(seckey, sizeof(seckey)); - return 0; + return EXIT_SUCCESS; } diff --git a/examples/ellswift.c b/examples/ellswift.c index afb2fee40be..d58e96b053d 100644 --- a/examples/ellswift.c +++ b/examples/ellswift.c @@ -13,6 +13,7 @@ */ #include +#include #include #include @@ -38,7 +39,7 @@ int main(void) { ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); if (!fill_random(randomize, sizeof(randomize))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* Randomizing the context is recommended to protect against side-channel * leakage. See `secp256k1_context_randomize` in secp256k1.h for more @@ -49,14 +50,14 @@ int main(void) { /*** Generate secret keys ***/ if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* If the secret key is zero or out of range (greater than secp256k1's * order), we fail. Note that the probability of this occurring is negligible * with a properly functioning random number generator. */ if (!secp256k1_ec_seckey_verify(ctx, seckey1) || !secp256k1_ec_seckey_verify(ctx, seckey2)) { printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n"); - return 1; + return EXIT_FAILURE; } /* Generate ElligatorSwift public keys. This should never fail with valid context and @@ -64,7 +65,7 @@ int main(void) { optional, but recommended. */ if (!fill_random(auxrand1, sizeof(auxrand1)) || !fill_random(auxrand2, sizeof(auxrand2))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } return_val = secp256k1_ellswift_create(ctx, ellswift_pubkey1, seckey1, auxrand1); assert(return_val); @@ -117,5 +118,5 @@ int main(void) { secure_erase(shared_secret1, sizeof(shared_secret1)); secure_erase(shared_secret2, sizeof(shared_secret2)); - return 0; + return EXIT_SUCCESS; } diff --git a/examples/musig.c b/examples/musig.c index 0352dc40f37..bdb8fced0cf 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -12,6 +12,7 @@ */ #include +#include #include #include @@ -193,7 +194,7 @@ int main(void) { for (i = 0; i < N_SIGNERS; i++) { if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) { printf("FAILED\n"); - return 1; + return EXIT_FAILURE; } pubkeys_ptr[i] = &signers[i].pubkey; } @@ -208,7 +209,7 @@ int main(void) { fflush(stdout); if (!secp256k1_ec_pubkey_sort(ctx, pubkeys_ptr, N_SIGNERS)) { printf("FAILED\n"); - return 1; + return EXIT_FAILURE; } printf("ok\n"); @@ -219,7 +220,7 @@ int main(void) { * while providing a non-NULL agg_pk argument. */ if (!secp256k1_musig_pubkey_agg(ctx, NULL, &cache, pubkeys_ptr, N_SIGNERS)) { printf("FAILED\n"); - return 1; + return EXIT_FAILURE; } printf("ok\n"); printf("Tweaking................"); @@ -227,21 +228,21 @@ int main(void) { /* Optionally tweak the aggregate key */ if (!tweak(ctx, &agg_pk, &cache)) { printf("FAILED\n"); - return 1; + return EXIT_FAILURE; } printf("ok\n"); printf("Signing message........."); fflush(stdout); if (!sign(ctx, signer_secrets, signers, &cache, msg, sig)) { printf("FAILED\n"); - return 1; + return EXIT_FAILURE; } printf("ok\n"); printf("Verifying signature....."); fflush(stdout); if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) { printf("FAILED\n"); - return 1; + return EXIT_FAILURE; } printf("ok\n"); @@ -256,5 +257,5 @@ int main(void) { secure_erase(&signer_secrets[i], sizeof(signer_secrets[i])); } secp256k1_context_destroy(ctx); - return 0; + return EXIT_SUCCESS; } diff --git a/examples/schnorr.c b/examples/schnorr.c index 909fcaa1f3f..49baed24beb 100644 --- a/examples/schnorr.c +++ b/examples/schnorr.c @@ -8,6 +8,7 @@ *************************************************************************/ #include +#include #include #include @@ -34,7 +35,7 @@ int main(void) { secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); if (!fill_random(randomize, sizeof(randomize))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* Randomizing the context is recommended to protect against side-channel * leakage See `secp256k1_context_randomize` in secp256k1.h for more @@ -45,7 +46,7 @@ int main(void) { /*** Key Generation ***/ if (!fill_random(seckey, sizeof(seckey))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* Try to create a keypair with a valid context. This only fails if the * secret key is zero or out of range (greater than secp256k1's order). Note @@ -53,7 +54,7 @@ int main(void) { * functioning random number generator. */ if (!secp256k1_keypair_create(ctx, &keypair, seckey)) { printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n"); - return 1; + return EXIT_FAILURE; } /* Extract the X-only public key from the keypair. We pass NULL for @@ -90,7 +91,7 @@ int main(void) { /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */ if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) { printf("Failed to generate randomness\n"); - return 1; + return EXIT_FAILURE; } /* Generate a Schnorr signature. @@ -110,7 +111,7 @@ int main(void) { * be parsed correctly */ if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) { printf("Failed parsing the public key\n"); - return 1; + return EXIT_FAILURE; } /* Compute the tagged hash on the received messages using the same tag as the signer. */ @@ -149,5 +150,5 @@ int main(void) { * Here we are preventing these writes from being optimized out, as any good compiler * will remove any writes that aren't used. */ secure_erase(seckey, sizeof(seckey)); - return 0; + return EXIT_SUCCESS; } diff --git a/include/secp256k1.h b/include/secp256k1.h index c6e9417f055..1d25a02ab29 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -230,10 +230,10 @@ typedef int (*secp256k1_nonce_function)( * * It is highly recommended to call secp256k1_selftest before using this context. */ -SECP256K1_API const secp256k1_context *secp256k1_context_static; +SECP256K1_API const secp256k1_context * const secp256k1_context_static; /** Deprecated alias for secp256k1_context_static. */ -SECP256K1_API const secp256k1_context *secp256k1_context_no_precomp +SECP256K1_API const secp256k1_context * const secp256k1_context_no_precomp SECP256K1_DEPRECATED("Use secp256k1_context_static instead"); /** Perform basic self tests (to be used in conjunction with secp256k1_context_static) @@ -701,21 +701,13 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate( unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); -/** Same as secp256k1_ec_seckey_negate, but DEPRECATED. Will be removed in - * future versions. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate( - const secp256k1_context *ctx, - unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) - SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_negate instead"); - /** Negates a public key in place. * * Returns: 1 always * Args: ctx: pointer to a context object * In/Out: pubkey: pointer to the public key to be negated. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate( +SECP256K1_API int secp256k1_ec_pubkey_negate( const secp256k1_context *ctx, secp256k1_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); @@ -741,15 +733,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add( const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in - * future versions. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( - const secp256k1_context *ctx, - unsigned char *seckey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) - SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_add instead"); - /** Tweak a public key by adding tweak times the generator to it. * * Returns: 0 if the arguments are invalid or the resulting public key would be @@ -788,15 +771,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul( const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in - * future versions. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( - const secp256k1_context *ctx, - unsigned char *seckey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) - SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_mul instead"); - /** Tweak a public key by multiplying it by a tweak value. * * Returns: 0 if the arguments are invalid. 1 otherwise. @@ -883,7 +857,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( * msg: pointer to an array containing the message * msglen: length of the message array */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_tagged_sha256( +SECP256K1_API int secp256k1_tagged_sha256( const secp256k1_context *ctx, unsigned char *hash32, const unsigned char *tag, diff --git a/include/secp256k1_batch.h b/include/secp256k1_batch.h new file mode 100644 index 00000000000..6068ec64e4f --- /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_extrakeys.h b/include/secp256k1_extrakeys.h index 48c98693cfa..1a517ded966 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -90,7 +90,7 @@ SECP256K1_API int secp256k1_xonly_pubkey_cmp( * the negation of the pubkey and set to 0 otherwise. * In: pubkey: pointer to a public key that is converted. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey( +SECP256K1_API int secp256k1_xonly_pubkey_from_pubkey( const secp256k1_context *ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, @@ -179,7 +179,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create( * Out: seckey: pointer to a 32-byte buffer for the secret key. * In: keypair: pointer to a keypair. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec( +SECP256K1_API int secp256k1_keypair_sec( const secp256k1_context *ctx, unsigned char *seckey, const secp256k1_keypair *keypair @@ -192,7 +192,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec( * Out: pubkey: pointer to a pubkey object, set to the keypair public key. * In: keypair: pointer to a keypair. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( +SECP256K1_API int secp256k1_keypair_pub( const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair @@ -211,7 +211,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( * pk_parity argument of secp256k1_xonly_pubkey_from_pubkey. * In: keypair: pointer to a keypair. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub( +SECP256K1_API int secp256k1_keypair_xonly_pub( const secp256k1_context *ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, diff --git a/include/secp256k1_schnorrsig_batch.h b/include/secp256k1_schnorrsig_batch.h new file mode 100644 index 00000000000..ffd8399ee77 --- /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 00000000000..4ae9027fa4a --- /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/CMakeLists.txt b/src/CMakeLists.txt index f31b8c8f551..701f8471cd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,6 +132,11 @@ if(SECP256K1_INSTALL) if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h") endif() + if(SECP256K1_ENABLE_MODULE_BATCH) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_batch.h") + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig_batch.h") + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_tweak_check_batch.h") + endif() if(SECP256K1_ENABLE_MODULE_MUSIG) list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h") endif() diff --git a/src/bench.c b/src/bench.c index 1127df67ae0..46b96d49f6f 100644 --- a/src/bench.c +++ b/src/bench.c @@ -5,6 +5,7 @@ ***********************************************************************/ #include +#include #include #include "../include/secp256k1.h" @@ -15,18 +16,22 @@ static void help(int default_iters) { printf("Benchmarks the following algorithms:\n"); printf(" - ECDSA signing/verification\n"); -#ifdef ENABLE_MODULE_ECDH - printf(" - ECDH key exchange (optional module)\n"); -#endif - #ifdef ENABLE_MODULE_RECOVERY printf(" - Public key recovery (optional module)\n"); #endif +#ifdef ENABLE_MODULE_ECDH + printf(" - ECDH key exchange (optional module)\n"); +#endif + #ifdef ENABLE_MODULE_SCHNORRSIG printf(" - Schnorr signatures (optional module)\n"); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + printf(" - ElligatorSwift (optional module)\n"); +#endif + printf("\n"); printf("The default number of iterations for each benchmark is %d. This can be\n", default_iters); printf("customized using the SECP256K1_BENCH_ITERS environment variable.\n"); @@ -42,17 +47,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 +172,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 +198,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); @@ -188,11 +208,11 @@ int main(int argc, char** argv) { || have_flag(argc, argv, "--help") || have_flag(argc, argv, "help")) { help(default_iters); - return 0; + return EXIT_SUCCESS; } else if (invalid_args) { fprintf(stderr, "./bench: unrecognized argument.\n\n"); help(default_iters); - return 1; + return EXIT_FAILURE; } } @@ -201,7 +221,7 @@ int main(int argc, char** argv) { if (have_flag(argc, argv, "ecdh")) { fprintf(stderr, "./bench: ECDH module not enabled.\n"); fprintf(stderr, "Use ./configure --enable-module-ecdh.\n\n"); - return 1; + return EXIT_FAILURE; } #endif @@ -209,12 +229,20 @@ int main(int argc, char** argv) { if (have_flag(argc, argv, "recover") || have_flag(argc, argv, "ecdsa_recover")) { fprintf(stderr, "./bench: Public key recovery module not enabled.\n"); fprintf(stderr, "Use ./configure --enable-module-recovery.\n\n"); - return 1; + return EXIT_FAILURE; } #endif #ifndef ENABLE_MODULE_SCHNORRSIG if (have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "schnorrsig_sign") || have_flag(argc, argv, "schnorrsig_verify")) { + fprintf(stderr, "./bench: Schnorr signatures module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-schnorrsig.\n\n"); + return EXIT_FAILURE; + } +#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; @@ -227,7 +255,7 @@ int main(int argc, char** argv) { have_flag(argc, argv, "ellswift_ecdh")) { fprintf(stderr, "./bench: ElligatorSwift module not enabled.\n"); fprintf(stderr, "Use ./configure --enable-module-ellswift.\n\n"); - return 1; + return EXIT_FAILURE; } #endif @@ -265,6 +293,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); @@ -275,5 +308,5 @@ int main(int argc, char** argv) { run_ellswift_bench(iters, argc, argv); #endif - return 0; + return EXIT_SUCCESS; } diff --git a/src/bench.h b/src/bench.h index 1564b1a1760..d187383fa99 100644 --- a/src/bench.h +++ b/src/bench.h @@ -24,7 +24,7 @@ static int64_t gettime_i64(void) { struct timespec tv; if (!timespec_get(&tv, TIME_UTC)) { fputs("timespec_get failed!", stderr); - exit(1); + exit(EXIT_FAILURE); } return (int64_t)tv.tv_nsec / 1000 + (int64_t)tv.tv_sec * 1000000LL; #else @@ -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/bench_ecmult.c b/src/bench_ecmult.c index 3974af75f44..172292d5706 100644 --- a/src/bench_ecmult.c +++ b/src/bench_ecmult.c @@ -4,6 +4,7 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ #include +#include #include "secp256k1.c" #include "../include/secp256k1.h" @@ -287,7 +288,7 @@ int main(int argc, char **argv) { || have_flag(argc, argv, "--help") || have_flag(argc, argv, "help")) { help(argv); - return 0; + return EXIT_SUCCESS; } else if(have_flag(argc, argv, "pippenger_wnaf")) { printf("Using pippenger_wnaf:\n"); data.ecmult_multi = secp256k1_ecmult_pippenger_batch_single; @@ -299,7 +300,7 @@ int main(int argc, char **argv) { } else { fprintf(stderr, "%s: unrecognized argument '%s'.\n\n", argv[0], argv[1]); help(argv); - return 1; + return EXIT_FAILURE; } } @@ -363,5 +364,5 @@ int main(int argc, char **argv) { free(data.output); free(data.expected_output); - return(0); + return EXIT_SUCCESS; } diff --git a/src/bench_internal.c b/src/bench_internal.c index a700684922a..8688a4dc77c 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -4,6 +4,7 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ #include +#include #include "secp256k1.c" #include "../include/secp256k1.h" @@ -393,7 +394,7 @@ int main(int argc, char **argv) { || have_flag(argc, argv, "--help") || have_flag(argc, argv, "help")) { help(default_iters); - return 0; + return EXIT_SUCCESS; } } @@ -432,5 +433,5 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "context")) run_benchmark("context_create", bench_context, bench_setup, NULL, &data, 10, iters); - return 0; + return EXIT_SUCCESS; } diff --git a/src/ctime_tests.c b/src/ctime_tests.c index bbde863d969..f81bdb9228d 100644 --- a/src/ctime_tests.c +++ b/src/ctime_tests.c @@ -5,6 +5,7 @@ ***********************************************************************/ #include +#include #include #include "../include/secp256k1.h" @@ -49,7 +50,7 @@ int main(void) { if (!SECP256K1_CHECKMEM_RUNNING()) { fprintf(stderr, "This test can only usefully be run inside valgrind because it was not compiled under msan.\n"); fprintf(stderr, "Usage: libtool --mode=execute valgrind ./ctime_tests\n"); - return 1; + return EXIT_FAILURE; } ctx = secp256k1_context_create(SECP256K1_CONTEXT_DECLASSIFY); /** In theory, testing with a single secret input should be sufficient: @@ -69,7 +70,7 @@ int main(void) { CHECK(ret); secp256k1_context_destroy(ctx); - return 0; + return EXIT_SUCCESS; } static void run_tests(secp256k1_context *ctx, unsigned char *key) { diff --git a/src/eckey_impl.h b/src/eckey_impl.h index 121966f8b5f..a88a5964d81 100644 --- a/src/eckey_impl.h +++ b/src/eckey_impl.h @@ -9,6 +9,7 @@ #include "eckey.h" +#include "util.h" #include "scalar.h" #include "field.h" #include "group.h" @@ -35,6 +36,8 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char } static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { + VERIFY_CHECK(compressed == 0 || compressed == 1); + if (secp256k1_ge_is_infinity(elem)) { return 0; } diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 0b53b3fcb98..248a4cd8b9f 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -358,16 +358,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); @@ -375,16 +386,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; } @@ -397,20 +422,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; } +/** 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); } -static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) { - return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); -} - /** Convert a number to WNAF notation. * 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/group.h b/src/group.h index 992ff5c98cf..05ae0d203cf 100644 --- a/src/group.h +++ b/src/group.h @@ -80,7 +80,11 @@ static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); /** Set a group element equal to another which is given in jacobian coordinates. */ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a); -/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ +/** Set group elements r[0:len] (affine) equal to group elements a[0:len] (jacobian). + * None of the group elements in a[0:len] may be infinity. Constant time. */ +static void secp256k1_ge_set_all_gej(secp256k1_ge *r, const secp256k1_gej *a, size_t len); + +/** Set group elements r[0:len] (affine) equal to group elements a[0:len] (jacobian). */ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len); /** Bring a batch of inputs to the same global z "denominator", based on ratios between diff --git a/src/group_impl.h b/src/group_impl.h index c668fb24010..b8f2395d93a 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -195,6 +195,44 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { SECP256K1_GE_VERIFY(r); } +static void secp256k1_ge_set_all_gej(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { + secp256k1_fe u; + size_t i; +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GEJ_VERIFY(&a[i]); + VERIFY_CHECK(!secp256k1_gej_is_infinity(&a[i])); + } +#endif + + if (len == 0) { + return; + } + + /* Use destination's x coordinates as scratch space */ + r[0].x = a[0].z; + for (i = 1; i < len; i++) { + secp256k1_fe_mul(&r[i].x, &r[i - 1].x, &a[i].z); + } + secp256k1_fe_inv(&u, &r[len - 1].x); + + for (i = len - 1; i > 0; i--) { + secp256k1_fe_mul(&r[i].x, &r[i - 1].x, &u); + secp256k1_fe_mul(&u, &u, &a[i].z); + } + r[0].x = u; + + for (i = 0; i < len; i++) { + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x); + } + +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GE_VERIFY(&r[i]); + } +#endif +} + static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { secp256k1_fe u; size_t i; diff --git a/src/modules/batch/Makefile.am.include b/src/modules/batch/Makefile.am.include new file mode 100644 index 00000000000..f996e0efca7 --- /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 00000000000..a649805ae78 --- /dev/null +++ b/src/modules/batch/main_impl.h @@ -0,0 +1,207 @@ +#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); + (void)strauss_ret; + 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 00000000000..f9a81ec942e --- /dev/null +++ b/src/modules/batch/tests_impl.h @@ -0,0 +1,214 @@ +#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) { + secp256k1_batch *batch_none; + secp256k1_batch *batch_sign; + secp256k1_batch *batch_vrfy; + secp256k1_batch *batch_both; + secp256k1_batch *batch_sttc; + unsigned char aux_rand16[32]; + int ecount; + +#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)); + + 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 */ + testrand256(aux_rand16); + memset(&aux_rand16[16], 0, 16); + +#ifdef ENABLE_MODULE_EXTRAKEYS + /* generate keypair data */ + 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++) { + 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++) { + 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; + 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 */ + batch_sign = secp256k1_batch_create(sign, 2*N_SIGS, NULL); + CHECK(batch_sign != NULL); + CHECK(ecount == 0); + batch_vrfy = secp256k1_batch_create(vrfy, N_TWK_CHECKS - 1, aux_rand16); + CHECK(batch_vrfy != NULL); + CHECK(ecount == 0); + 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*/ + 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 0d901ec1f44..be6efb2d083 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 00000000000..46798fbe46c --- /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 "..//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 00000000000..d18aafcc1bd --- /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]); + 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 */ + testrand256(tweaked_pk); + tweaked_pk_parity = (unsigned char) testrand_int(2); + testrand256(tweak); + testrand256(&internal_pk[1]); + temp_rand = 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 **/ + 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) **/ + 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 00000000000..411fd7339c7 --- /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/musig/session_impl.h b/src/modules/musig/session_impl.h index dde38085827..d8dcd00c95a 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -395,6 +395,7 @@ static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c static int secp256k1_musig_nonce_gen_internal(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *input_nonce, const unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) { secp256k1_scalar k[2]; secp256k1_ge nonce_pts[2]; + secp256k1_gej nonce_ptj[2]; int i; unsigned char pk_ser[33]; size_t pk_ser_len = sizeof(pk_ser); @@ -444,13 +445,20 @@ static int secp256k1_musig_nonce_gen_internal(const secp256k1_context* ctx, secp secp256k1_musig_secnonce_save(secnonce, k, &pk); secp256k1_musig_secnonce_invalidate(ctx, secnonce, !ret); + /* Compute pubnonce as two gejs */ for (i = 0; i < 2; i++) { - secp256k1_gej nonce_ptj; - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj, &k[i]); - secp256k1_ge_set_gej(&nonce_pts[i], &nonce_ptj); - secp256k1_declassify(ctx, &nonce_pts[i], sizeof(nonce_pts[i])); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj[i], &k[i]); secp256k1_scalar_clear(&k[i]); - secp256k1_gej_clear(&nonce_ptj); + } + + /* Batch convert to two public ges */ + secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2); + for (i = 0; i < 2; i++) { + secp256k1_gej_clear(&nonce_ptj[i]); + } + + for (i = 0; i < 2; i++) { + secp256k1_declassify(ctx, &nonce_pts[i], sizeof(nonce_pts[i])); } /* None of the nonce_pts will be infinity because k != 0 with overwhelming * probability */ diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include index 654fa2e5ae5..2c211784fbc 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 00000000000..7c3da4a29d1 --- /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 "../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 00000000000..89c31222256 --- /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]); + 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 */ + testrand256(msg); + testrand256(&sig[0]); + testrand256(&sig[32]); + testrand256(&compressed_pk[1]); + temp_rand = 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 = 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); + + 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++) { + 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 = testrand_int(N_SIGS); + size_t byte_idx = testrand_bits(5); + unsigned char xorbyte = 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 = 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 = 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 = testrand_int(sizeof(msg_large)); + for (i = 0; i < sizeof(msg_large); i += 32) { + 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 **/ + 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 **/ + 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 93a878ede3e..da2681cca65 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/main_impl.h b/src/modules/schnorrsig/main_impl.h index 82bba2f5975..2ed7be677fd 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -94,6 +94,8 @@ static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *ms secp256k1_sha256_write(&sha, msg, msglen); secp256k1_sha256_finalize(&sha, nonce32); secp256k1_sha256_clear(&sha); + secp256k1_memclear(masked_key, sizeof(masked_key)); + return 1; } diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 2d716a01f89..0ffcc67bed4 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/precompute_ecmult.c b/src/precompute_ecmult.c index 5ef198a7700..021fe3940c5 100644 --- a/src/precompute_ecmult.c +++ b/src/precompute_ecmult.c @@ -6,6 +6,7 @@ #include #include +#include #include "../include/secp256k1.h" @@ -62,7 +63,7 @@ int main(void) { fp = fopen(outfile, "w"); if (fp == NULL) { fprintf(stderr, "Could not open %s for writing!\n", outfile); - return -1; + return EXIT_FAILURE; } fprintf(fp, "/* This file was automatically generated by precompute_ecmult. */\n"); @@ -86,5 +87,5 @@ int main(void) { fprintf(fp, "#undef S\n"); fclose(fp); - return 0; + return EXIT_SUCCESS; } diff --git a/src/precompute_ecmult_gen.c b/src/precompute_ecmult_gen.c index 4d153a65744..cd0fe70fc25 100644 --- a/src/precompute_ecmult_gen.c +++ b/src/precompute_ecmult_gen.c @@ -6,6 +6,7 @@ #include #include +#include #include "../include/secp256k1.h" @@ -64,7 +65,7 @@ int main(int argc, char **argv) { fp = fopen(outfile, "w"); if (fp == NULL) { fprintf(stderr, "Could not open %s for writing!\n", outfile); - return -1; + return EXIT_FAILURE; } fprintf(fp, "/* This file was automatically generated by precompute_ecmult_gen. */\n"); @@ -96,5 +97,5 @@ int main(int argc, char **argv) { fprintf(fp, "#undef S\n"); fclose(fp); - return 0; + return EXIT_SUCCESS; } diff --git a/src/precomputed_ecmult.h b/src/precomputed_ecmult.h index 17df1029672..e5a85f684f0 100644 --- a/src/precomputed_ecmult.h +++ b/src/precomputed_ecmult.h @@ -13,6 +13,8 @@ extern "C" { #include "ecmult.h" #include "group.h" +#include "util_local_visibility.h" + #if defined(EXHAUSTIVE_TEST_ORDER) # if EXHAUSTIVE_TEST_ORDER == 7 # define WINDOW_G 3 @@ -27,8 +29,8 @@ static secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)]; static secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)]; #else /* !defined(EXHAUSTIVE_TEST_ORDER) */ # define WINDOW_G ECMULT_WINDOW_SIZE -extern const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)]; -extern const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)]; +SECP256K1_LOCAL_VAR const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)]; +SECP256K1_LOCAL_VAR const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)]; #endif /* defined(EXHAUSTIVE_TEST_ORDER) */ #ifdef __cplusplus diff --git a/src/precomputed_ecmult_gen.h b/src/precomputed_ecmult_gen.h index 283738a5ce1..00ddce108b6 100644 --- a/src/precomputed_ecmult_gen.h +++ b/src/precomputed_ecmult_gen.h @@ -13,10 +13,12 @@ extern "C" { #include "group.h" #include "ecmult_gen.h" +#include "util_local_visibility.h" + #ifdef EXHAUSTIVE_TEST_ORDER static secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS]; #else -extern const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS]; +SECP256K1_LOCAL_VAR const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS]; #endif /* defined(EXHAUSTIVE_TEST_ORDER) */ #ifdef __cplusplus diff --git a/src/scalar_impl.h b/src/scalar_impl.h index dbb5b0a01ca..0232a8c223e 100644 --- a/src/scalar_impl.h +++ b/src/scalar_impl.h @@ -90,11 +90,11 @@ static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, con #endif /* - * Both lambda and beta are primitive cube roots of unity. That is lamba^3 == 1 mod n and + * Both lambda and beta are primitive cube roots of unity. That is lambda^3 == 1 mod n and * beta^3 == 1 mod p, where n is the curve order and p is the field order. * * Furthermore, because (X^3 - 1) = (X - 1)(X^2 + X + 1), the primitive cube roots of unity are - * roots of X^2 + X + 1. Therefore lambda^2 + lamba == -1 mod n and beta^2 + beta == -1 mod p. + * roots of X^2 + X + 1. Therefore lambda^2 + lambda == -1 mod n and beta^2 + beta == -1 mod p. * (The other primitive cube roots of unity are lambda^2 and beta^2 respectively.) * * Let l = -1/2 + i*sqrt(3)/2, the complex root of X^2 + X + 1. We can define a ring diff --git a/src/secp256k1.c b/src/secp256k1.c index a248519dfd8..9bc26d050f4 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -71,8 +71,8 @@ static const secp256k1_context secp256k1_context_static_ = { { secp256k1_default_error_callback_fn, 0 }, 0 }; -const secp256k1_context *secp256k1_context_static = &secp256k1_context_static_; -const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_static_; +const secp256k1_context * const secp256k1_context_static = &secp256k1_context_static_; +const secp256k1_context * const secp256k1_context_no_precomp = &secp256k1_context_static_; /* Helper function that determines if a context is proper, i.e., is not the static context or a copy thereof. * @@ -280,7 +280,7 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o ARG_CHECK(pubkey != NULL); ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { - ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); + ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, !!(flags & SECP256K1_FLAGS_BIT_COMPRESSION)); if (ret) { *outputlen = len; } @@ -634,10 +634,6 @@ int secp256k1_ec_seckey_negate(const secp256k1_context* ctx, unsigned char *seck return ret; } -int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) { - return secp256k1_ec_seckey_negate(ctx, seckey); -} - int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) { int ret = 0; secp256k1_ge p; @@ -681,10 +677,6 @@ int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *s return ret; } -int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { - return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak32); -} - static int secp256k1_ec_pubkey_tweak_add_helper(secp256k1_ge *p, const unsigned char *tweak32) { secp256k1_scalar term; int overflow = 0; @@ -729,10 +721,6 @@ int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *s return ret; } -int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { - return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak32); -} - int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) { secp256k1_ge p; secp256k1_scalar factor; @@ -829,3 +817,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 78533b11c28..2bd1efa02b3 100644 --- a/src/tests.c +++ b/src/tests.c @@ -87,15 +87,6 @@ static void counting_callback_fn(const char* str, void* data) { (*p)++; } -static void uncounting_illegal_callback_fn(const char* str, void* data) { - /* Dummy callback function that just counts (backwards). */ - int32_t *p; - (void)str; - p = data; - CHECK(*p != INT32_MIN); - (*p)--; -} - static void run_xoshiro256pp_tests(void) { { size_t i; @@ -3821,14 +3812,38 @@ static void test_ge(void) { /* Test batch gej -> ge conversion without known z ratios. */ { + secp256k1_ge *ge_set_all_var = (secp256k1_ge *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); - secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1); + secp256k1_ge_set_all_gej_var(&ge_set_all_var[0], &gej[0], 4 * runs + 1); for (i = 0; i < 4 * runs + 1; i++) { secp256k1_fe s; testutil_random_fe_non_zero(&s); secp256k1_gej_rescale(&gej[i], &s); - CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all[i])); + CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all_var[i])); } + + /* Skip infinity at &gej[0]. */ + secp256k1_ge_set_all_gej(&ge_set_all[1], &gej[1], 4 * runs); + for (i = 1; i < 4 * runs + 1; i++) { + secp256k1_fe s; + testutil_random_fe_non_zero(&s); + secp256k1_gej_rescale(&gej[i], &s); + CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all[i])); + CHECK(secp256k1_ge_eq_var(&ge_set_all_var[i], &ge_set_all[i])); + } + + /* Test with an array of length 1. */ + secp256k1_ge_set_all_gej_var(ge_set_all_var, &gej[1], 1); + secp256k1_ge_set_all_gej(ge_set_all, &gej[1], 1); + CHECK(secp256k1_gej_eq_ge_var(&gej[1], &ge_set_all_var[1])); + CHECK(secp256k1_gej_eq_ge_var(&gej[1], &ge_set_all[1])); + CHECK(secp256k1_ge_eq_var(&ge_set_all_var[1], &ge_set_all[1])); + + /* Test with an array of length 0. */ + secp256k1_ge_set_all_gej_var(NULL, NULL, 0); + secp256k1_ge_set_all_gej(NULL, NULL, 0); + + free(ge_set_all_var); free(ge_set_all); } @@ -4857,38 +4872,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 = 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 + testrand_int(6); /* Number of (scalar, point) inputs (excluding g). */ @@ -4903,25 +4895,25 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { num_nonzero == 1 && !nonzero_result ? 1 : (int)testrand_bits(1); /* Which g_scalar pointer to pass into ecmult_multi(). */ - const secp256k1_scalar* g_scalar_ptr = (g_nonzero || testrand_bits(1)) ? &g_scalar : NULL; + secp256k1_scalar* g_scalar_ptr = (g_nonzero || 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)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. */ - testutil_random_scalar_order_test(&g_scalar); + testutil_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); testutil_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); @@ -4941,7 +4933,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; } @@ -5011,6 +5003,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_zero; + 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 = 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. */ @@ -5023,7 +5063,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; @@ -5209,7 +5302,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(); @@ -5220,8 +5315,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); @@ -6248,11 +6346,6 @@ static void run_eckey_negate_test(void) { CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); - /* Check that privkey alias gives same result */ - CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); - CHECK(secp256k1_ec_privkey_negate(CTX, seckey_tmp) == 1); - CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); - /* Negating all 0s fails */ memset(seckey, 0, 32); memset(seckey_tmp, 0, 32); @@ -6413,22 +6506,15 @@ static void test_ecdsa_end_to_end(void) { if (testrand_int(3) == 0) { int ret1; int ret2; - int ret3; unsigned char rnd[32]; - unsigned char privkey_tmp[32]; secp256k1_pubkey pubkey2; testrand256_test(rnd); - memcpy(privkey_tmp, privkey, 32); ret1 = secp256k1_ec_seckey_tweak_add(CTX, privkey, rnd); ret2 = secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, rnd); - /* Check that privkey alias gives same result */ - ret3 = secp256k1_ec_privkey_tweak_add(CTX, privkey_tmp, rnd); CHECK(ret1 == ret2); - CHECK(ret2 == ret3); if (ret1 == 0) { return; } - CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0); CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, privkey) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } @@ -6437,22 +6523,15 @@ static void test_ecdsa_end_to_end(void) { if (testrand_int(3) == 0) { int ret1; int ret2; - int ret3; unsigned char rnd[32]; - unsigned char privkey_tmp[32]; secp256k1_pubkey pubkey2; testrand256_test(rnd); - memcpy(privkey_tmp, privkey, 32); ret1 = secp256k1_ec_seckey_tweak_mul(CTX, privkey, rnd); ret2 = secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, rnd); - /* Check that privkey alias gives same result */ - ret3 = secp256k1_ec_privkey_tweak_mul(CTX, privkey_tmp, rnd); CHECK(ret1 == ret2); - CHECK(ret2 == ret3); if (ret1 == 0) { return; } - CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0); CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, privkey) == 1); CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } @@ -7441,10 +7520,16 @@ 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_MUSIG @@ -7455,7 +7540,11 @@ static void run_ecdsa_wycheproof(void) { # 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)]; @@ -7809,10 +7898,16 @@ 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_MUSIG @@ -7823,6 +7918,10 @@ int main(int argc, char **argv) { run_ellswift_tests(); #endif +#ifdef ENABLE_MODULE_BATCH + run_batch_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_is_zero_array_test(); @@ -7837,5 +7936,5 @@ int main(int argc, char **argv) { testrand_finish(); printf("no problems found\n"); - return 0; + return EXIT_SUCCESS; } diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c index 6efa88982ef..f8bbcaaf5c4 100644 --- a/src/tests_exhaustive.c +++ b/src/tests_exhaustive.c @@ -383,7 +383,7 @@ int main(int argc, char** argv) { this_core = strtol(argv[4], NULL, 0); if (num_cores < 1 || this_core >= num_cores) { fprintf(stderr, "Usage: %s [count] [seed] [numcores] [thiscore]\n", argv[0]); - return 1; + return EXIT_FAILURE; } printf("running tests for core %lu (out of [0..%lu])\n", (unsigned long)this_core, (unsigned long)num_cores - 1); } @@ -462,5 +462,5 @@ int main(int argc, char** argv) { testrand_finish(); printf("no problems found\n"); - return 0; + return EXIT_SUCCESS; } diff --git a/src/util.h b/src/util.h index 88a4149421a..5f29f4076ce 100644 --- a/src/util.h +++ b/src/util.h @@ -232,7 +232,7 @@ static SECP256K1_INLINE void secp256k1_memclear(void *ptr, size_t len) { * As best as we can tell, this is sufficient to break any optimisations that * might try to eliminate "superfluous" memsets. * This method is used in memzero_explicit() the Linux kernel, too. Its advantage is that it - * is pretty efficient, because the compiler can still implement the memset() efficently, + * is pretty efficient, because the compiler can still implement the memset() efficiently, * just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by * Yang et al. (USENIX Security 2017) for more background. */ diff --git a/src/util_local_visibility.h b/src/util_local_visibility.h new file mode 100644 index 00000000000..8912a64d1da --- /dev/null +++ b/src/util_local_visibility.h @@ -0,0 +1,12 @@ +#ifndef SECP256K1_LOCAL_VISIBILITY_H +#define SECP256K1_LOCAL_VISIBILITY_H + +/* Global variable visibility */ +/* See: https://github.com/bitcoin-core/secp256k1/issues/1181 */ +#if !defined(_WIN32) && defined(__GNUC__) && (__GNUC__ >= 4) +# define SECP256K1_LOCAL_VAR extern __attribute__ ((visibility ("hidden"))) +#else +# define SECP256K1_LOCAL_VAR extern +#endif + +#endif /* SECP256K1_LOCAL_VISIBILITY_H */ diff --git a/tools/symbol-check.py b/tools/symbol-check.py new file mode 100755 index 00000000000..e7f478082f2 --- /dev/null +++ b/tools/symbol-check.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Check that a libsecp256k1 shared library exports only expected symbols. + +Usage examples: + - When building with Autotools: + ./tools/symbol-check.py .libs/libsecp256k1.so + ./tools/symbol-check.py .libs/libsecp256k1-.dll + ./tools/symbol-check.py .libs/libsecp256k1.dylib + + - When building with CMake: + ./tools/symbol-check.py build/lib/libsecp256k1.so + ./tools/symbol-check.py build/bin/libsecp256k1-.dll + ./tools/symbol-check.py build/lib/libsecp256k1.dylib""" + +import re +import sys +import subprocess + +import lief + + +class UnexpectedExport(RuntimeError): + pass + + +def get_exported_exports(library) -> list[str]: + """Adapter function to get exported symbols based on the library format.""" + if library.format == lief.Binary.FORMATS.ELF: + return [symbol.name for symbol in library.exported_symbols] + elif library.format == lief.Binary.FORMATS.PE: + return [entry.name for entry in library.get_export().entries] + elif library.format == lief.Binary.FORMATS.MACHO: + return [symbol.name[1:] for symbol in library.exported_symbols] + raise NotImplementedError(f"Unsupported format: {library.format}") + + +def grep_expected_symbols() -> list[str]: + """Guess the list of expected exported symbols from the source code.""" + grep_output = subprocess.check_output( + ["git", "grep", r"^\s*SECP256K1_API", "--", "include"], + universal_newlines=True, + encoding="utf-8" + ) + lines = grep_output.split("\n") + pattern = re.compile(r'\bsecp256k1_\w+') + exported: list[str] = [pattern.findall(line)[-1] for line in lines if line.strip()] + return exported + + +def check_symbols(library, expected_exports) -> None: + """Check that the library exports only the expected symbols.""" + actual_exports = get_exported_exports(library) + unexpected_exports = set(actual_exports) - set(expected_exports) + if unexpected_exports != set(): + raise UnexpectedExport(f"Unexpected exported symbols: {unexpected_exports}") + +def main(): + if len(sys.argv) != 2: + print(__doc__) + return 1 + library = lief.parse(sys.argv[1]) + expected_exports = grep_expected_symbols() + try: + check_symbols(library, expected_exports) + except UnexpectedExport as e: + print(f"{sys.argv[0]}: In {sys.argv[1]}: {e}") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 1c472ca34c136e2791574cb5181c892324b091fc Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Tue, 11 Mar 2025 20:14:30 +0100 Subject: [PATCH 02/11] build: Enable secp256k1 experimental batch module --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41577b2ad6d..96ea91b2add 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,8 @@ set(SECP256K1_DISABLE_SHARED ON CACHE BOOL "" FORCE) set(SECP256K1_ENABLE_MODULE_ECDH OFF CACHE BOOL "" FORCE) set(SECP256K1_ENABLE_MODULE_RECOVERY ON CACHE BOOL "" FORCE) set(SECP256K1_ENABLE_MODULE_MUSIG OFF CACHE BOOL "" FORCE) +set(SECP256K1_EXPERIMENTAL ON CACHE BOOL "" FORCE) +set(SECP256K1_ENABLE_MODULE_BATCH ON CACHE BOOL "" FORCE) set(SECP256K1_BUILD_BENCHMARK OFF CACHE BOOL "" FORCE) set(SECP256K1_BUILD_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE) set(SECP256K1_BUILD_EXHAUSTIVE_TESTS ${BUILD_TESTS} CACHE BOOL "" FORCE) From bcd51a49f8b8892254288d7522435287f055f92f Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Tue, 11 Mar 2025 20:27:29 +0100 Subject: [PATCH 03/11] validation: Add BatchVerify (unused) --- src/CMakeLists.txt | 1 + src/batchverify.cpp | 56 +++++++++++++++++++++++++++++++++++++++ src/batchverify.h | 34 ++++++++++++++++++++++++ src/kernel/CMakeLists.txt | 1 + 4 files changed, 92 insertions(+) create mode 100644 src/batchverify.cpp create mode 100644 src/batchverify.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 96ea91b2add..ba9b6fdb57e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -131,6 +131,7 @@ endif() add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL addresstype.cpp base58.cpp + batchverify.cpp bech32.cpp chain.cpp chainparams.cpp diff --git a/src/batchverify.cpp b/src/batchverify.cpp new file mode 100644 index 00000000000..4c4ae27ccc1 --- /dev/null +++ b/src/batchverify.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include + +#include +#include +#include + +// This is the maximum number of scalar-point pairs on the batch for which +// Strauss' algorithm, which is used in the secp256k1 implementation, is +// still efficient. TODO: This will be changed when Pippenger algorithm is +// being used in the secp implementation too. +const size_t MAX_BATCH_SIZE{106}; + +BatchSchnorrVerifier::BatchSchnorrVerifier() { + unsigned char rnd[16]; + GetRandBytes(rnd); + secp256k1_batch* batch{secp256k1_batch_create(secp256k1_context_static, MAX_BATCH_SIZE, rnd)}; + m_batch = batch; +} + +BatchSchnorrVerifier::~BatchSchnorrVerifier() { + (void)secp256k1_batch_destroy(secp256k1_context_static, m_batch); +} + +bool BatchSchnorrVerifier::Add(const std::span sig, const XOnlyPubKey& pubkey, const uint256& sighash) { + LOCK(m_batch_mutex); + if (secp256k1_batch_usable(secp256k1_context_static, m_batch) == 0) { + return false; + } + + secp256k1_xonly_pubkey pubkey_parsed; + (void)secp256k1_xonly_pubkey_parse(secp256k1_context_static, &pubkey_parsed, pubkey.data()); + + return secp256k1_batch_add_schnorrsig(secp256k1_context_static, m_batch, sig.data(), sighash.begin(), 32, &pubkey_parsed); +} + +bool BatchSchnorrVerifier::Verify() { + LOCK(m_batch_mutex); + return secp256k1_batch_verify(secp256k1_context_static, m_batch); +} + +void BatchSchnorrVerifier::Reset() { + LOCK(m_batch_mutex); + (void)secp256k1_batch_destroy(secp256k1_context_static, m_batch); + unsigned char rnd[16]; + GetRandBytes(rnd); + secp256k1_batch* batch{secp256k1_batch_create(secp256k1_context_static, MAX_BATCH_SIZE, rnd)}; + m_batch = batch; +} diff --git a/src/batchverify.h b/src/batchverify.h new file mode 100644 index 00000000000..63cd301f470 --- /dev/null +++ b/src/batchverify.h @@ -0,0 +1,34 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_BATCHVERIFY_H +#define BITCOIN_BATCHVERIFY_H + +#include +#include + +#include +#include + +struct SchnorrSignatureToVerify { + std::vector sig; + XOnlyPubKey pubkey; + uint256 sighash; +}; + +class BatchSchnorrVerifier { +private: + secp256k1_batch* m_batch GUARDED_BY(m_batch_mutex); + mutable Mutex m_batch_mutex; + +public: + BatchSchnorrVerifier(); + ~BatchSchnorrVerifier(); + + bool Add(const std::span sig, const XOnlyPubKey& pubkey, const uint256& sighash) EXCLUSIVE_LOCKS_REQUIRED(!m_batch_mutex); + bool Verify() EXCLUSIVE_LOCKS_REQUIRED(!m_batch_mutex); + void Reset() EXCLUSIVE_LOCKS_REQUIRED(!m_batch_mutex); +}; + +#endif // BITCOIN_BATCHVERIFY_H diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index b9f37969d37..c7371268855 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(bitcoinkernel disconnected_transactions.cpp mempool_removal_reason.cpp ../arith_uint256.cpp + ../batchverify.cpp ../chain.cpp ../coins.cpp ../compressor.cpp From 2a2a2030ad007c9e151203d551c5cb0d25a988a1 Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Tue, 11 Mar 2025 21:06:09 +0100 Subject: [PATCH 04/11] script: Add batch validation error --- src/script/script_error.cpp | 2 ++ src/script/script_error.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index fadc04262c3..d8b664b8423 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -115,6 +115,8 @@ std::string ScriptErrorString(const ScriptError serror) return "Using OP_CODESEPARATOR in non-witness script"; case SCRIPT_ERR_SIG_FINDANDDELETE: return "Signature is found in scriptCode"; + case SCRIPT_ERR_BATCH_VALIDATION_FAILED: + return "Schnorr batch validation failed"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 44e68fe0fae..569566e8409 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -78,6 +78,9 @@ typedef enum ScriptError_t SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG, SCRIPT_ERR_TAPSCRIPT_MINIMALIF, + /* Batch validation */ + SCRIPT_ERR_BATCH_VALIDATION_FAILED, + /* Constant scriptCode */ SCRIPT_ERR_OP_CODESEPARATOR, SCRIPT_ERR_SIG_FINDANDDELETE, From 696bd7547e0e4ab44388d3c55ed636c46f9bdad5 Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Tue, 11 Mar 2025 21:25:41 +0100 Subject: [PATCH 05/11] sigcache: Add CollectingSignatureChecker --- src/script/sigcache.cpp | 16 ++++++++++++++++ src/script/sigcache.h | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 6b308258bcf..51ba1af333a 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -5,6 +5,7 @@ #include