diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 152f9a1f..040d3f19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,14 @@ env: SCHNORRSIG: 'no' MUSIG: 'no' ELLSWIFT: 'no' + ECDSA_S2C: 'no' + GENERATOR: 'no' + RANGEPROOF: 'no' + SURJECTIONPROOF: 'no' + WHITELIST: 'no' + ECDSAADAPTOR: 'no' + BPPP: 'no' + SCHNORRSIG_HALFAGG: 'no' ### test options SECP256K1_TEST_ITERS: 64 BENCH: 'yes' @@ -96,14 +104,14 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} - env_vars: { WIDEMUL: 'int128' } - - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' } - - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } - - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' } - - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} + - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } + - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } @@ -160,6 +168,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + GENERATOR: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CC: ${{ matrix.cc }} steps: @@ -188,6 +205,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + GENERATOR: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' steps: @@ -218,6 +244,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' steps: @@ -239,6 +274,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' CC: ${{ matrix.cc }} @@ -279,6 +323,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' steps: @@ -324,6 +377,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' SECP256K1_TEST_ITERS: 2 @@ -353,6 +415,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' CFLAGS: '-fsanitize=undefined,address -g' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' @@ -399,6 +470,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CC: ${{ matrix.cc }} SECP256K1_TEST_ITERS: 32 ASM: 'no' @@ -424,6 +504,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' strategy: @@ -456,15 +545,15 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: @@ -513,13 +602,13 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } - BUILD: 'distcheck' steps: @@ -632,6 +721,15 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + EXPERIMENTAL: 'yes' + ECDSA_S2C: 'yes' + GENERATOR: 'yes' + RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' + WHITELIST: 'yes' + ECDSAADAPTOR: 'yes' + BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' steps: - *CHECKOUT diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 00000000..cd534821 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,91 @@ +name: Upstream Sync + +on: + schedule: + - cron: '0 0 1 * *' + workflow_dispatch: + +jobs: + sync-upstream: + runs-on: ubuntu-latest + env: + UPSTREAM: "https://github.com/bitcoin-core/secp256k1.git" + UPSTREAM_BRANCH: "master" + ORIGIN_BRANCH: "master" + MIN_UPSTREAM_MERGES: 1 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + token: ${{ secrets.SYNC_PAT }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch upstream & generate branch name & check for new commits + id: check_commits + env: + GH_TOKEN: ${{ secrets.SYNC_PAT }} + run: | + gh repo set-default ${{ github.repository }} # Set the default repo to the origin repository + git remote add upstream ${{ env.UPSTREAM }} + git fetch upstream + + MERGES=$(git rev-list --count --merges HEAD..upstream/${{ env.UPSTREAM_BRANCH }}) + echo "Found $MERGES new merge commits in upstream." + + if [ "$MERGES" -lt ${{ env.MIN_UPSTREAM_MERGES }} ]; then + echo "Exiting." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + # Generate a sync branch name "sync-UPSTREAM_HEAD", where UPSTREAM_HEAD is a commit ID. + UPSTREAM_HEAD=$(git rev-parse --short upstream/${{ env.UPSTREAM_BRANCH }}) + SYNC_BRANCH="sync-$UPSTREAM_HEAD" + echo "Sync branch name: $SYNC_BRANCH" + echo "SYNC_BRANCH=$SYNC_BRANCH" >> "$GITHUB_ENV" + + # Check if the sync branch already exists in the origin repository + if git ls-remote --heads origin "$SYNC_BRANCH" | grep -q "$SYNC_BRANCH"; then + echo "Branch $SYNC_BRANCH already exists. Skipping the sync." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Generate PR metadata + id: branch_pr_metadata + if: steps.check_commits.outputs.skip == 'false' + run: | + # Call the sync script to generate PR title and body + ./contrib/sync-upstream.sh -b ${{ env.ORIGIN_BRANCH }} "$SYNC_BRANCH" + + - name: Push a sync branch + id: push_branch + if: steps.check_commits.outputs.skip == 'false' + run: | + echo "Creating sync branch: $SYNC_BRANCH" + git checkout upstream/${{ env.UPSTREAM_BRANCH }} + git checkout -b "$SYNC_BRANCH" + git push -u origin "$SYNC_BRANCH" + + - name: Create pull request + id: create_pr + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.SYNC_PAT }} + run: | + # Execute the generated PR creation script + ./contrib/gh-pr-create.sh + + - name: Cleanup sync branch on failure + if: steps.push_branch.outcome == 'success' && steps.create_pr.outcome == 'failure' + run: | + echo "PR creation failed but branch was pushed. Deleting: $SYNC_BRANCH" + git push origin --delete "$SYNC_BRANCH" \ No newline at end of file diff --git a/.gitignore b/.gitignore index fbc311d7..8658272e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ bench +bench_bppp bench_ecmult +bench_generator +bench_rangeproof bench_internal +bench_whitelist noverify_tests tests exhaustive_tests @@ -49,6 +53,7 @@ coverage.*.html !/autotools-aux/m4/bitcoin_secp.m4 libsecp256k1.pc +contrib/gh-pr-create.sh ### CMake /CMakeUserPresets.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d4beb9c8..48cbdaf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +**This changelog is not the libsecp256k1-zkp's changelog.** +Instead, it is the changelog of the upstream library [libsecp256k1](https://github.com/bitcoin-core/secp256k1). + # Changelog All notable changes to this project will be documented in this file. diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ef69c08..c55cf10f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,15 @@ option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON) option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) +option(SECP256K1_ENABLE_MODULE_GENERATOR "Enable NUMS generator module." ON) +option(SECP256K1_ENABLE_MODULE_RANGEPROOF "Enable Range proof module." ON) +option(SECP256K1_ENABLE_MODULE_SURJECTIONPROOF "Enable Surjection proof module." ON) +option(SECP256K1_ENABLE_MODULE_WHITELIST "Enable key whitelist module." ON) +option(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR "Enable ecdsa adaptor signatures module." ON) +option(SECP256K1_ENABLE_MODULE_ECDSA_S2C "Enable ECDSA sign-to-contract module." ON) +option(SECP256K1_ENABLE_MODULE_BPPP "Enable Bulletproofs++ module." ON) +option(SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG "Enable schnorrsig half-aggregation module." ON) + option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) add_compile_definitions(USE_EXTERNAL_DEFAULT_CALLBACKS=1) @@ -287,6 +296,14 @@ message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRA message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") +message(" generator ........................... ${SECP256K1_ENABLE_MODULE_GENERATOR}") +message(" rangeproof .......................... ${SECP256K1_ENABLE_MODULE_RANGEPROOF}") +message(" surjectionproof ..................... ${SECP256K1_ENABLE_MODULE_SURJECTIONPROOF}") +message(" whitelist ........................... ${SECP256K1_ENABLE_MODULE_WHITELIST}") +message(" ecdsa-s2c ........................... ${SECP256K1_ENABLE_MODULE_ECDSA_S2C}") +message(" ecdsa-adaptor ....................... ${SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR}") +message(" bppp ................................ ${SECP256K1_ENABLE_MODULE_BPPP}") +message(" schnorrsig-halfagg .................. ${SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG}") message("Parameters:") message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") message(" ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB") diff --git a/Makefile.am b/Makefile.am index 07d7a2ba..51c8f6bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,8 @@ noinst_HEADERS += src/scalar_8x32_impl.h noinst_HEADERS += src/scalar_low_impl.h noinst_HEADERS += src/group.h noinst_HEADERS += src/group_impl.h +noinst_HEADERS += src/eccommit.h +noinst_HEADERS += src/eccommit_impl.h noinst_HEADERS += src/ecdsa.h noinst_HEADERS += src/ecdsa_impl.h noinst_HEADERS += src/eckey.h @@ -291,6 +293,14 @@ EXTRA_DIST += src/wycheproof/ecdh_secp256k1_test.json EXTRA_DIST += tools/tests_wycheproof_generate_ecdsa.py EXTRA_DIST += tools/tests_wycheproof_generate_ecdh.py +if ENABLE_MODULE_SCHNORRSIG_HALFAGG +include src/modules/schnorrsig_halfagg/Makefile.am.include +endif + +if ENABLE_MODULE_BPPP +include src/modules/bppp/Makefile.am.include +endif + if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif @@ -299,6 +309,22 @@ if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif +if ENABLE_MODULE_GENERATOR +include src/modules/generator/Makefile.am.include +endif + +if ENABLE_MODULE_RANGEPROOF +include src/modules/rangeproof/Makefile.am.include +endif + +if ENABLE_MODULE_WHITELIST +include src/modules/whitelist/Makefile.am.include +endif + +if ENABLE_MODULE_SURJECTIONPROOF +include src/modules/surjection/Makefile.am.include +endif + if ENABLE_MODULE_EXTRAKEYS include src/modules/extrakeys/Makefile.am.include endif @@ -314,3 +340,11 @@ endif if ENABLE_MODULE_ELLSWIFT include src/modules/ellswift/Makefile.am.include endif + +if ENABLE_MODULE_ECDSA_S2C +include src/modules/ecdsa_s2c/Makefile.am.include +endif + +if ENABLE_MODULE_ECDSA_ADAPTOR +include src/modules/ecdsa_adaptor/Makefile.am.include +endif diff --git a/README.md b/README.md index 90edae1a..af62b832 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,21 @@ -libsecp256k1 -============ +libsecp256k1-zkp +================ ![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) -[![irc.libera.chat #secp256k1](https://img.shields.io/badge/irc.libera.chat-%23secp256k1-success)](https://web.libera.chat/#secp256k1) -High-performance high-assurance C library for digital signatures and other cryptographic primitives on the secp256k1 elliptic curve. +A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features -This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose. +Added features: +* Experimental module for ECDSA adaptor signatures. +* Experimental module for ECDSA sign-to-contract. +* Experimental modules for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)). +* Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md). +* Experimental module for Schnorr signature half-aggregation. -Features: -* secp256k1 ECDSA signing/verification and key generation. -* Additive and multiplicative tweaking of secret/public keys. -* Serialization/parsing of secret keys, public keys, signatures. -* Constant time, constant memory access signing and public key generation. -* Derandomized ECDSA (via RFC6979 or with a caller provided function.) -* Very efficient implementation. -* Suitable for embedded systems. -* No runtime dependencies. -* Optional module for public key recovery. -* Optional module for ECDH key exchange. -* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). -* Optional module for 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). +Experimental features are made available for testing and review by the community. The APIs of these features should not be considered stable. -Implementation details ----------------------- - -* General - * No runtime heap allocation. - * Extensive testing infrastructure. - * Structured to facilitate review and analysis. - * Intended to be portable to any system with a C89 compiler and uint64_t support. - * No use of floating types. - * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") -* Field operations - * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). - * Using 5 52-bit limbs - * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan). - * This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community. -* Scalar operations - * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. - * Using 4 64-bit limbs (relying on __int128 support in the compiler). - * Using 8 32-bit limbs. -* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman). -* Group operations - * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). - * Use addition between points in Jacobian and affine coordinates where possible. - * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. - * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. -* Point multiplication for verification (a*P + b*G). - * Use wNAF notation for point multiplicands. - * Use a much larger window for multiples of G, using precomputed multiples. - * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. - * Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. -* Point multiplication for signing - * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. - * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains) - * Access the table with branch-free conditional moves so memory access is uniform. - * No data-dependent branches - * Optional runtime blinding which attempts to frustrate differential power analysis. - * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. +Build steps +----------- Obtaining and verifying ----------------------- @@ -106,7 +62,7 @@ Building with Autotools $ 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. +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. For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-rangeproof`. Building with CMake ------------------- @@ -144,6 +100,7 @@ In "Developer Command Prompt for VS 2022": Usage examples ----------- + Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. * [ECDSA example](examples/ecdsa.c) * [Schnorr signatures example](examples/schnorr.c) @@ -155,7 +112,7 @@ To compile the examples, make sure the corresponding modules are enabled. Benchmark ------------ -If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1 functions will be present in the root directory after the build. +If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1-zkp functions will be present in the root directory after the build. To print the benchmark result to the command line: diff --git a/ci/ci.sh b/ci/ci.sh index 515c14cd..2185e789 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -13,7 +13,8 @@ print_environment() { # does not rely on bash. for var in WERROR_CFLAGS MAKEFLAGS BUILD \ ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ - EXPERIMENTAL ECDH RECOVERY EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT \ + EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG MUSIG SCHNORRSIG_HALFAGG ELLSWIFT \ + ECDSA_S2C GENERATOR RANGEPROOF SURJECTIONPROOF WHITELIST ECDSAADAPTOR BPPP \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ @@ -62,8 +63,12 @@ fi --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-ellswift="$ELLSWIFT" \ --enable-module-extrakeys="$EXTRAKEYS" \ - --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-ecdsa-s2c="$ECDSA_S2C" \ + --enable-module-bppp="$BPPP" \ + --enable-module-rangeproof="$RANGEPROOF" --enable-module-surjectionproof="$SURJECTIONPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-musig="$MUSIG" \ + --enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ @@ -123,6 +128,10 @@ then $EXEC ./bench_ecmult $EXEC ./bench_internal $EXEC ./bench + if [ "$BPPP" = "yes" ] + then + $EXEC ./bench_bppp + fi } >> bench.log 2>&1 fi diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index a575d9b1..a862f1b1 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -67,6 +67,9 @@ RUN \ wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ # Add repository for this Debian release . /etc/os-release && echo "deb http://apt.llvm.org/${VERSION_CODENAME} llvm-toolchain-${VERSION_CODENAME} main" >> /etc/apt/sources.list && \ + # Temporarily work around Sequoia PGP policy deadline for legacy repositories. + # See https://github.com/llvm/llvm-project/issues/153385. + sed -i 's/\(sha1\.second_preimage_resistance =\).*/\1 9999-01-01/' /usr/share/apt/default-sequoia.config && \ apt-get update && \ # Determine the version number of the LLVM development branch LLVM_VERSION=$(apt-cache search --names-only '^clang-[0-9]+$' | sort -V | tail -1 | cut -f1 -d" " | cut -f2 -d"-" ) && \ diff --git a/configure.ac b/configure.ac index a21447ce..05a8c586 100644 --- a/configure.ac +++ b/configure.ac @@ -4,8 +4,8 @@ AC_PREREQ([2.60]) # the API. All changes in experimental modules are treated as # backwards-compatible and therefore at most increase the minor version. define(_PKG_VERSION_MAJOR, 0) -define(_PKG_VERSION_MINOR, 7) -define(_PKG_VERSION_PATCH, 2) +define(_PKG_VERSION_MINOR, 1) +define(_PKG_VERSION_PATCH, 0) define(_PKG_VERSION_IS_RELEASE, false) # The library version is based on libtool versioning of the ABI. The set of @@ -13,8 +13,8 @@ define(_PKG_VERSION_IS_RELEASE, false) # https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # 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, 6) -define(_LIB_VERSION_REVISION, 2) +define(_LIB_VERSION_CURRENT, 0) +define(_LIB_VERSION_REVISION, 0) 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]) @@ -189,10 +189,53 @@ AC_ARG_ENABLE(module_musig, AS_HELP_STRING([--enable-module-musig],[enable MuSig2 module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_musig], [yes], [yes])]) +AC_ARG_ENABLE(module_schnorrsig_halfagg, + AS_HELP_STRING([--enable-module-schnorrsig-halfagg],[enable schnorrsig half-aggregation module (experimental) [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_schnorrsig_halfagg], [no], [yes])]) + 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_generator, + AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_generator], [no], [yes])]) + +AC_ARG_ENABLE(module_rangeproof, + AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_rangeproof], [no], [yes])]) + +AC_ARG_ENABLE(module_surjectionproof, + AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_surjectionproof], [no], [yes])]) +AC_ARG_ENABLE(reduced_surjection_proof_size, + AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), + [], + [SECP_SET_DEFAULT([enable_reduced_surjection_proof_size], [no], [no])]) + +AC_ARG_ENABLE(module_whitelist, + AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])]) + +AC_ARG_ENABLE(module_ecdsa_s2c, + AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_ecdsa_s2c], [no], [yes])]) + +AC_ARG_ENABLE(module_bppp, + AS_HELP_STRING([--enable-module-bppp],[enable Bulletproofs++ module (experimental)]), + [], + [SECP_SET_DEFAULT([enable_module_bppp], [no], [yes])]) + +AC_ARG_ENABLE(module_ecdsa-adaptor, + AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [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])]) @@ -283,6 +326,18 @@ else SECP_CFLAGS="-O2 $SECP_CFLAGS" fi +AC_MSG_CHECKING([for __builtin_popcount]) +AC_LINK_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])], + [ AC_MSG_RESULT([yes]); SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DHAVE_BUILTIN_POPCOUNT=1"], + [ AC_MSG_RESULT([no]) + ]) + +AC_MSG_CHECKING([for __builtin_clzll]) +AC_LINK_IFELSE([AC_LANG_SOURCE([[void myfunc() { __builtin_clzll(1);}]])], + [ AC_MSG_RESULT([yes]); SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DHAVE_BUILTIN_CLZLL=1"], + [ AC_MSG_RESULT([no]) + ]) + if test x"$req_asm" = x"auto"; then SECP_X86_64_ASM_CHECK if test x"$has_x86_64_asm" = x"yes"; then @@ -399,6 +454,58 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" # Processing must be done in a reverse topological sorting of the dependency graph # (dependent module first). +if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then + if test x"$enable_module_schnorrsig" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the schnorrsig_halfagg module.]) + fi + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG_HALFAGG=1" + enable_module_schnorrsig=yes +fi + +if test x"$enable_module_bppp" = x"yes"; then + if test x"$enable_module_generator" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the generator module explicitly, but it is required by the bppp module.]) + fi + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_BPPP=1" + enable_module_generator=yes +fi + +if test x"$enable_module_ecdsa_s2c" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_S2C=1" +fi + +if test x"$enable_module_ecdsa_adaptor" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1" +fi + +if test x"$enable_module_whitelist" = x"yes"; then + if test x"$enable_module_rangeproof" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the whitelist module.]) + fi + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_WHITELIST=1" + enable_module_rangeproof=yes +fi + +if test x"$enable_module_surjectionproof" = x"yes"; then + if test x"$enable_module_rangeproof" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the surjectionproof module.]) + fi + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SURJECTIONPROOF=1" + enable_module_rangeproof=yes +fi + +if test x"$enable_module_rangeproof" = x"yes"; then + if test x"$enable_module_generator" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the generator module explicitly, but it is required by the rangeproof module.]) + fi + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RANGEPROOF=1" + enable_module_generator=yes +fi + +if test x"$enable_module_generator" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_GENERATOR=1" +fi + if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi @@ -435,11 +542,43 @@ if test x"$enable_external_default_callbacks" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1" fi +if test x"$enable_reduced_surjection_proof_size" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_REDUCED_SURJECTION_PROOF_SIZE=1" +fi + ### ### Check for --enable-experimental if necessary ### if test x"$enable_experimental" = x"no"; then + # The order of the following tests matters. If the user enables a dependent + # module (which automatically enables the module dependencies) we want to + # print an error for the dependent module, not the module dependency. Hence, + # we first test dependent modules. + if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then + AC_MSG_ERROR([Schnorrsig Half-Aggregation module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_bppp" = x"yes"; then + AC_MSG_ERROR([Bulletproofs++ module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_ecdsa_adaptor" = x"yes"; then + AC_MSG_ERROR([ecdsa adaptor signatures module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_ecdsa_s2c" = x"yes"; then + AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_whitelist" = x"yes"; then + AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_surjectionproof" = x"yes"; then + AC_MSG_ERROR([Surjection proof module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_rangeproof" = x"yes"; then + AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_generator" = x"yes"; then + AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm32"; then AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.]) fi @@ -472,6 +611,15 @@ 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_GENERATOR], [test x"$enable_module_generator" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_BPPP], [test x"$enable_module_bppp" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG_HALFAGG], [test x"$enable_module_schnorrsig_halfagg" = x"yes"]) +AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = 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"]) @@ -481,6 +629,10 @@ AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE) AC_OUTPUT +# The order in which all modules are listed here should be considered the +# canonical order. This order (or, when appropriate, its reserve) should be used +# everywhere we list or process modules, i.e., here and in other build system +# files and docs. echo echo "Build Options:" echo " with external callbacks = $enable_external_default_callbacks" @@ -496,13 +648,25 @@ echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module musig = $enable_module_musig" echo " module ellswift = $enable_module_ellswift" +# libsecp256k1-zkp modules, in the order they were added to the libsecp256k1-zkp +echo " module generator = $enable_module_generator" +echo " module rangeproof = $enable_module_rangeproof" +echo " module surjectionproof = $enable_module_surjectionproof" +if test x"$enable_module_surjectionproof" = x"yes" && test x"$enable_reduced_surjection_proof_size" = x"yes"; then +echo " reduced proof size = $enable_reduced_surjection_proof_size" +fi +echo " module whitelist = $enable_module_whitelist" +echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" +echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" +echo " module bppp = $enable_module_bppp" +echo " module schnorrsig-halfagg = $enable_module_schnorrsig_halfagg" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" echo " ecmult gen table size = $set_ecmult_gen_kb KiB" # Hide test-only options unless they're used. if test x"$set_widemul" != xauto; then -echo " wide multiplication = $set_widemul" +echo " wide multiplication = $set_widemul" fi echo echo " valgrind = $enable_valgrind" diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh new file mode 100755 index 00000000..aaf8cb10 --- /dev/null +++ b/contrib/sync-upstream.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -eou pipefail + +help() { + echo "Sync merge commits from bitcoin-core/secp256k1 into secp256k1-zkp." + echo + echo "Usage:" + echo " $0 [-b ] " + echo " Find every merge commit present in upstream/master and missing in (default: master)." + echo + echo "This tool prepares the title and body for a sync PR" + echo "and generates a helper script contrib/gh-pr-create.sh." + echo + echo "Setup:" + echo " Requires a remote named 'upstream' pointing to bitcoin-core/secp256k1." + echo + echo "Listing upstream merge commits:" + echo " To list merge commits in upstream/master that are missing from (oldest first):" + echo " git log --oneline --topo-order --reverse --merges \$(git merge-base upstream/master )..upstream/master" + exit 1 +} + +REMOTE=upstream +REMOTE_BRANCH="$REMOTE/master" +LOCAL_BRANCH="master" + +if ! git remote get-url "$REMOTE" &> /dev/null; then +echo "Error: Remote '$REMOTE' not found." +echo "Add it with: git remote add upstream git@github.com:bitcoin-core/secp256k1.git" +echo "Then run: git fetch upstream" +exit 1 +fi + +range() { + RANGESTART_COMMIT=$(git merge-base "$REMOTE_BRANCH" "$LOCAL_BRANCH") + RANGEEND_COMMIT=$(git rev-parse "$REMOTE_BRANCH") + COMMITS=$(git --no-pager log --pretty=format:%H --topo-order --reverse --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") +} + +# Process -b and -h arguments +while getopts "b:h" opt; do + case $opt in + b) + LOCAL_BRANCH=$OPTARG + ;; + h) + help + ;; + *) + echo + help + ;; + esac +done + +# Shift off the processed options +shift $((OPTIND -1)) +if [ "$#" -lt 1 ]; then + echo "Error: argument is required." >&2 + echo + help + exit 1 +fi + +# Extract the PR branch argument +PR_BRANCH=$1 + +range + +TITLE="Upstream PRs" +BODY="${GITHUB_ACTIONS+This PR has been created by a GitHub Actions workflow without human involvement.}"$'\n' +for COMMIT in $COMMITS +do + PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\).*'/'\2'/) + TITLE="$TITLE $PRNUM," + BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\)'/'[bitcoin-core\/secp256k1#\2]'/)") + LAST_COMMIT="$COMMIT" +done +# Remove trailing "," +TITLE=${TITLE%?} +BODY+=$(cat <\` to show the conflict resolution in the merge commit. + * Use \`git read-tree --reset -u \` to replay these resolutions during the conflict resolution stage when recreating the PR branch locally. + Be aware that this may discard your index as well as the uncommitted changes and untracked files in your worktree. +EOF +) + +echo "-----------------------------------" +echo "$TITLE" +echo "-----------------------------------" +echo "$BODY" +echo "-----------------------------------" + +# Escape single quote +# ' -> '\'' +quote() { + local quoted=${1//\'/\'\\\'\'} + printf "%s" "$quoted" +} +TITLE=$(quote "$TITLE") +BODY=$(quote "$BODY") + +BASEDIR=$(dirname "$0") +FNAME="$BASEDIR/gh-pr-create.sh" +cat < "$FNAME" +#!/bin/sh +gh pr create -t '$TITLE' -b '$BODY' --base '$LOCAL_BRANCH' --head '$PR_BRANCH' +EOT +chmod +x "$FNAME" +echo "Generated $FNAME for creating a pull request with the above title and body." \ No newline at end of file diff --git a/doc/musig.md b/doc/musig.md index ae21f9b1..3732782f 100644 --- a/doc/musig.md +++ b/doc/musig.md @@ -18,6 +18,7 @@ Therefore, users of the musig module must take great care to make sure of the fo See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`. 3. Opaque data structures are never written to or read from directly. Instead, only the provided accessor functions are used. +4. If adaptor signatures are used, all partial signatures are verified. ## Key Aggregation and (Taproot) Tweaking @@ -52,3 +53,14 @@ Similarly, the API supports an alternative protocol flow where generating the ag ## Verification A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7. + +## Atomic Swaps + +The signing API supports the production of "adaptor signatures", modified partial signatures +which are offset by an auxiliary secret known to one party. That is, +1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`. +2. When calling `secp256k1_musig_nonce_process`, the public adaptor `T` is provided as the `adaptor` argument. +3. The party who is going to extract the secret adaptor `t` later must verify all partial signatures. +4. Due to step 2, the signature output of `secp256k1_musig_partial_sig_agg` is a pre-signature and not a valid Schnorr signature. All parties involved extract this session's `nonce_parity` with `secp256k1_musig_nonce_parity`. +5. The party who knows `t` must "adapt" the pre-signature with `t` (and the `nonce_parity` using `secp256k1_musig_adapt` to complete the signature. +6. Any party who sees both the final signature and the pre-signature (and has the `nonce_parity`) can extract `t` with `secp256k1_musig_extract_adaptor`. diff --git a/examples/musig.c b/examples/musig.c index bdb8fced..1aff0b56 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -143,7 +143,7 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr /* Every signer creates a partial signature */ for (i = 0; i < N_SIGNERS; i++) { /* Initialize the signing session by processing the aggregate nonce */ - if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache)) { + if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache, NULL)) { return 0; } /* partial_sign will clear the secnonce by setting it to 0. That's because diff --git a/include/secp256k1_bppp.h b/include/secp256k1_bppp.h new file mode 100644 index 00000000..d34c2e4d --- /dev/null +++ b/include/secp256k1_bppp.h @@ -0,0 +1,73 @@ +#ifndef SECP256K1_BPPP_H +# define SECP256K1_BPPP_H + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** Opaque structure representing a large number of NUMS generators */ +typedef struct secp256k1_bppp_generators secp256k1_bppp_generators; + +/** Allocates and initializes a list of NUMS generators. + * Returns a list of generators, or calls the error callback if the allocation fails. + * Args: ctx: pointer to a context object + * n: number of NUMS generators to produce. + * + * TODO: In a followup range-proof PR, this is would still require 16 + 8 = 24 NUMS + * points. We will later use G = H0(required for compatibility with pedersen_commitment DS) + * in a separate commit to make review easier. + */ +SECP256K1_API secp256k1_bppp_generators *secp256k1_bppp_generators_create( + const secp256k1_context *ctx, + size_t n +) SECP256K1_ARG_NONNULL(1); + +/** Allocates a list of generators from a static array + * Returns a list of generators or NULL in case of failure. + * Args: ctx: pointer to a context object + * In: data: data that came from `secp256k1_bppp_generators_serialize` + * data_len: the length of the `data` buffer + */ +SECP256K1_API secp256k1_bppp_generators *secp256k1_bppp_generators_parse( + const secp256k1_context *ctx, + const unsigned char *data, + size_t data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Serializes a list of generators to an array + * Returns 1 on success, 0 if the provided array was not large enough + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be serialized + * Out: data: pointer to buffer into which the generators will be serialized + * In/Out: data_len: the length of the `data` buffer. Should be at least + * k = 33 * num_gens. Will be set to k on successful return + * + * TODO: For ease of review, this setting G = H0 is not included in this commit. We will + * add it in the follow-up rangeproof PR. + */ +SECP256K1_API int secp256k1_bppp_generators_serialize( + const secp256k1_context *ctx, + const secp256k1_bppp_generators *gen, + unsigned char *data, + size_t *data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Destroys a list of NUMS generators, freeing allocated memory + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be destroyed + * (can be NULL, in which case this function is a no-op) + */ +SECP256K1_API void secp256k1_bppp_generators_destroy( + const secp256k1_context *ctx, + secp256k1_bppp_generators *gen +) SECP256K1_ARG_NONNULL(1); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h new file mode 100644 index 00000000..e50f94e9 --- /dev/null +++ b/include/secp256k1_ecdsa_adaptor.h @@ -0,0 +1,164 @@ +#ifndef SECP256K1_ECDSA_ADAPTOR_H +#define SECP256K1_ECDSA_ADAPTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "secp256k1.h" + +/** This module implements single signer ECDSA adaptor signatures following + * "One-Time Verifiably Encrypted Signatures A.K.A. Adaptor Signatures" by + * Lloyd Fournier + * (https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-November/002316.html + * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). + * + * WARNING! DANGER AHEAD! + * As mentioned in Lloyd Fournier's paper, the adaptor signature leaks the + * Elliptic-curve Diffie–Hellman (ECDH) key between the signing key and the + * encryption key. This is not a problem for ECDSA adaptor signatures + * themselves, but may result in a complete loss of security when they are + * composed with other schemes. More specifically, let us refer to the + * signer's public key as X = x*G, and to the encryption key as Y = y*G. + * Given X, Y and the adaptor signature, it is trivial to compute Y^x = X^y. + * + * A defense is to not reuse the signing key of ECDSA adaptor signatures in + * protocols that rely on the hardness of the CDH problem, e.g., Diffie-Hellman + * key exchange and ElGamal encryption. In general, it is a well-established + * cryptographic practice to seperate keys for different purposes whenever + * possible. + */ + +/** A pointer to a function to deterministically generate a nonce. + * + * Same as secp256k1_nonce_function_hardened with the exception of using the + * compressed 33-byte encoding for the pubkey argument. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to + * return an error. + * Out: nonce32: pointer to a 32-byte array to be filled by the function + * In: msg32: the 32-byte message hash being verified + * key32: pointer to a 32-byte secret key + * pk33: the 33-byte serialized pubkey corresponding to key32 + * algo: pointer to an array describing the signature algorithm + * algolen: the length of the algo array + * data: arbitrary data pointer that is passed through + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the key, the pubkey, the algorithm description, and data. + */ +typedef int (*secp256k1_nonce_function_hardened_ecdsa_adaptor)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *pk33, + const unsigned char *algo, + size_t algolen, + void *data +); + +/** A modified BIP-340 nonce generation function. If a data pointer is passed, it is + * assumed to be a pointer to 32 bytes of auxiliary random data as defined in BIP-340. + * The hash will be tagged with algo after removing all terminating null bytes. + */ +SECP256K1_API const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor; + +/** Encrypted Signing + * + * Creates an adaptor signature, which includes a proof to verify the adaptor + * signature. + * WARNING: Make sure you have read and understood the WARNING at the top of + * this file and applied the suggested countermeasures. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: adaptor_sig162: pointer to 162 byte to store the returned signature + * In: seckey32: pointer to 32 byte secret key that will be used for + * signing + * enckey: pointer to the encryption public key + * msg32: pointer to the 32-byte message hash to sign + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_ecdsa_adaptor is used + * ndata: pointer to arbitrary data used by the nonce generation + * function (can be NULL). If it is non-NULL and + * secp256k1_nonce_function_ecdsa_adaptor is used, then + * ndata must be a pointer to 32-byte auxiliary randomness + * as per BIP-340. + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_encrypt( + const secp256k1_context *ctx, + unsigned char *adaptor_sig162, + unsigned char *seckey32, + const secp256k1_pubkey *enckey, + const unsigned char *msg32, + secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Encryption Verification + * + * Verifies that the adaptor decryption key can be extracted from the adaptor signature + * and the completed ECDSA signature. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: pointer to a context object + * In: adaptor_sig162: pointer to 162-byte signature to verify + * pubkey: pointer to the public key corresponding to the secret key + * used for signing + * msg32: pointer to the 32-byte message hash being verified + * enckey: pointer to the adaptor encryption public key + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_verify( + const secp256k1_context *ctx, + const unsigned char *adaptor_sig162, + const secp256k1_pubkey *pubkey, + const unsigned char *msg32, + const secp256k1_pubkey *enckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Signature Decryption + * + * Derives an ECDSA signature from an adaptor signature and an adaptor decryption key. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: pointer to a context object + * Out: sig: pointer to the ECDSA signature to create + * In: deckey32: pointer to 32-byte decryption secret key for the adaptor + * encryption public key + * adaptor_sig162: pointer to 162-byte adaptor sig + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_decrypt( + const secp256k1_context *ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *deckey32, + const unsigned char *adaptor_sig162 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decryption Key Recovery + * + * Extracts the adaptor decryption key from the complete signature and the adaptor + * signature. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: deckey32: pointer to 32-byte adaptor decryption key for the adaptor + * encryption public key + * In: sig: pointer to ECDSA signature to recover the adaptor decryption + * key from + * adaptor_sig162: pointer to adaptor signature to recover the adaptor + * decryption key from + * enckey: pointer to the adaptor encryption public key + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_recover( + const secp256k1_context *ctx, + unsigned char *deckey32, + const secp256k1_ecdsa_signature *sig, + const unsigned char *adaptor_sig162, + const secp256k1_pubkey *enckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDSA_ADAPTOR_H */ diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h new file mode 100644 index 00000000..c931457d --- /dev/null +++ b/include/secp256k1_ecdsa_s2c.h @@ -0,0 +1,234 @@ +#ifndef SECP256K1_ECDSA_S2C_H +#define SECP256K1_ECDSA_S2C_H + +#include "secp256k1.h" + +/** This module implements the sign-to-contract scheme for ECDSA signatures, as + * well as the "ECDSA Anti-Exfil Protocol" that is based on sign-to-contract + * and is specified further down. The sign-to-contract scheme allows creating a + * signature that also commits to some data. This works by offsetting the public + * nonce point of the signature R by hash(R, data)*G where G is the secp256k1 + * group generator. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Data structure that holds a sign-to-contract ("s2c") opening information. + * Sign-to-contract allows a signer to commit to some data as part of a signature. It + * can be used as an Out-argument in certain signing functions. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_ecdsa_s2c_opening_serialize and secp256k1_ecdsa_s2c_opening_parse. + */ +typedef struct secp256k1_ecdsa_s2c_opening { + unsigned char data[64]; +} secp256k1_ecdsa_s2c_opening; + +/** Parse a sign-to-contract opening. + * + * Returns: 1 if the opening could be parsed + * 0 if the opening could not be parsed + * Args: ctx: pointer to a context object + * Out: opening: pointer to an opening object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is unspecified. + * In: input33: pointer to 33-byte array with a serialized opening + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_parse( + const secp256k1_context *ctx, + secp256k1_ecdsa_s2c_opening *opening, + const unsigned char *input33 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a sign-to-contract opening into a byte sequence. + * + * Returns: 1 if the opening was successfully serialized. + * 0 if the opening could not be serialized + * Args: ctx: pointer to a context object + * Out: output33: pointer to a 33-byte array to place the serialized opening in + * In: opening: pointer to an initialized `secp256k1_ecdsa_s2c_opening` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_serialize( + const secp256k1_context *ctx, + unsigned char *output33, + const secp256k1_ecdsa_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Same as secp256k1_ecdsa_sign, but s2c_data32 is committed to inside the nonce + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * s2c_opening: if non-NULL, pointer to an secp256k1_ecdsa_s2c_opening structure to populate + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * s2c_data32: pointer to a 32-byte data to commit to in the nonce (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_s2c_sign( + const secp256k1_context *ctx, + secp256k1_ecdsa_signature *sig, + secp256k1_ecdsa_s2c_opening *s2c_opening, + const unsigned char *msg32, + const unsigned char *seckey, + const unsigned char *s2c_data32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +/** Verify a sign-to-contract commitment. + * + * Returns: 1: the signature contains a commitment to data32 (though it does + * not necessarily need to be a valid siganture!) + * 0: incorrect opening + * Args: ctx: pointer to a context object + * In: sig: the signature containing the sign-to-contract commitment (cannot be NULL) + * data32: the 32-byte data that was committed to (cannot be NULL) + * opening: pointer to the opening created during signing (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit( + const secp256k1_context *ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *data32, + const secp256k1_ecdsa_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + + +/** ECDSA Anti-Exfil Protocol + * + * The ecdsa_anti_exfil_* functions can be used to prevent a signing device from + * exfiltrating the secret signing keys through biased signature nonces. The general + * idea is that a host provides additional randomness to the signing device client + * and the client commits to the randomness in the nonce using sign-to-contract. + * + * The following scheme is described by Stepan Snigirev here: + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017655.html + * and by Pieter Wuille (as "Scheme 6") here: + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017667.html + * + * In order to ensure the host cannot trick the signing device into revealing its + * keys, or the signing device to bias the nonce despite the host's contributions, + * the host and client must engage in a commit-reveal protocol as follows: + * 1. The host draws randomness `rho` and computes a sha256 commitment to it using + * `secp256k1_ecdsa_anti_exfil_host_commit`. It sends this to the signing device. + * 2. The signing device computes a public nonce `R` using the host's commitment + * as auxiliary randomness, using `secp256k1_ecdsa_anti_exfil_signer_commit`. + * The signing device sends the resulting `R` to the host as a s2c_opening. + * + * If, at any point from this step onward, the hardware device fails, it is + * okay to restart the protocol using **exactly the same `rho`** and checking + * that the hardware device proposes **exactly the same** `R`. Otherwise, the + * hardware device may be selectively aborting and thereby biasing the set of + * nonces that are used in actual signatures. + * + * It takes many (>100) such aborts before there is a plausible attack, given + * current knowledge in 2020. However such aborts accumulate even across a total + * replacement of all relevant devices (but not across replacement of the actual + * signing keys with new independently random ones). + * + * In case the hardware device cannot be made to sign with the given `rho`, `R` + * pair, wallet authors should alert the user and present a very scary message + * implying that if this happens more than even a few times, say 20 or more times + * EVER, they should change hardware vendors and perhaps sweep their coins. + * + * 3. The host replies with `rho` generated in step 1. + * 4. The device signs with `secp256k1_anti_exfil_sign`, using `rho` as `host_data32`, + * and sends the signature to the host. + * 5. The host verifies that the signature's public nonce matches the opening from + * step 2 and its original randomness `rho`, using `secp256k1_anti_exfil_host_verify`. + * + * Rationale: + * - The reason for having a host commitment is to allow the signing device to + * deterministically derive a unique nonce even if the host restarts the protocol + * using the same message and keys. Otherwise the signer might reuse the original + * nonce in two iterations of the protocol with different `rho`, which leaks the + * the secret key. + * - The signer does not need to check that the host commitment matches the host's + * claimed `rho`. Instead it re-derives the commitment (and its original `R`) from + * the provided `rho`. If this differs from the original commitment, the result + * will be an invalid `s2c_opening`, but since `R` was unique there is no risk to + * the signer's secret keys. Because of this, the signing device does not need to + * maintain any state about the progress of the protocol. + */ + +/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Exfil Protocol. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL) + * In: rand32: the 32-byte randomness to commit to (cannot be NULL). It must come from + * a cryptographically secure RNG. As per the protocol, this value must not + * be revealed to the client until after the host has received the client + * commitment. + */ +SECP256K1_API int secp256k1_ecdsa_anti_exfil_host_commit( + const secp256k1_context *ctx, + unsigned char *rand_commitment32, + const unsigned char *rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute signer's original nonce. Part of the ECDSA Anti-Exfil Protocol. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: s2c_opening: pointer to an s2c_opening where the signer's public nonce will be + * placed. (cannot be NULL) + * In: msg32: the 32-byte message hash to be signed (cannot be NULL) + * seckey32: the 32-byte secret key used for signing (cannot be NULL) + * rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_anti_exfil_signer_commit( + const secp256k1_context *ctx, + secp256k1_ecdsa_s2c_opening *s2c_opening, + const unsigned char *msg32, + const unsigned char *seckey32, + const unsigned char *rand_commitment32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Same as secp256k1_ecdsa_sign, but commits to host randomness in the nonce. Part of the + * ECDSA Anti-Exfil Protocol. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * host_data32: pointer to 32-byte host-provided randomness (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_exfil_sign( + const secp256k1_context *ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + const unsigned char *host_data32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a signature was correctly constructed using the ECDSA Anti-Exfil Protocol. + * + * Returns: 1: the signature is valid and contains a commitment to host_data32 + * 0: incorrect opening + * Args: ctx: pointer to a context object + * In: sig: the signature produced by the signer (cannot be NULL) + * msghash32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to the signer's public key (cannot be NULL) + * host_data32: the 32-byte data provided by the host (cannot be NULL) + * opening: the s2c opening provided by the signer (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_exfil_host_verify( + const secp256k1_context *ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey, + const unsigned char *host_data32, + const secp256k1_ecdsa_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDSA_S2C_H */ diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h new file mode 100644 index 00000000..7bf32314 --- /dev/null +++ b/include/secp256k1_generator.h @@ -0,0 +1,241 @@ +#ifndef SECP256K1_GENERATOR_H +# define SECP256K1_GENERATOR_H + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** Opaque data structure that stores a base point + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_generator_serialize and secp256k1_generator_parse. + */ +typedef struct secp256k1_generator { + unsigned char data[64]; +} secp256k1_generator; + +/** + * Static constant generator 'h' maintained for historical reasons. + */ +SECP256K1_API const secp256k1_generator *secp256k1_generator_h; + +/** Parse a 33-byte generator byte sequence into a generator object. + * + * Returns: 1 if input contains a valid generator. + * Args: ctx: pointer to a context object + * Out: gen: pointer to the output generator object + * In: input: pointer to a 33-byte serialized generator + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse( + const secp256k1_context *ctx, + secp256k1_generator *gen, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a 33-byte generator into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object + * Out: output: pointer to a 33-byte byte array + * In: gen: pointer to a generator object + */ +SECP256K1_API int secp256k1_generator_serialize( + const secp256k1_context *ctx, + unsigned char *output, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a generator for the curve. + * + * Returns: 0 in the highly unlikely case the seed is not acceptable, + * 1 otherwise. + * Args: ctx: pointer to a context object + * Out: gen: pointer to a the new generator object + * In: seed32: 32-byte seed + * + * If successful a valid generator will be placed in gen. The produced + * generators are distributed uniformly over the curve, and will not have a + * known discrete logarithm with respect to any other generator produced, + * or to the base generator G. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( + const secp256k1_context *ctx, + secp256k1_generator *gen, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a blinded generator for the curve. + * + * Returns: 0 in the highly unlikely case the seed is not acceptable or when + * blind is out of range. 1 otherwise. + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: gen: pointer to a generator object + * In: seed32: 32-byte seed + * blind32: 32-byte secret value to blind the generator with. + * + * The result is equivalent to first calling secp256k1_generator_generate, + * converting the result to a public key, calling secp256k1_ec_pubkey_tweak_add, + * and then converting back to generator form. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blinded( + const secp256k1_context *ctx, + secp256k1_generator *gen, + const unsigned char *seed32, + const unsigned char *blind32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Opaque data structure that stores a Pedersen commitment + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_pedersen_commitment_serialize and + * secp256k1_pedersen_commitment_parse. + */ +typedef struct secp256k1_pedersen_commitment { + unsigned char data[64]; +} secp256k1_pedersen_commitment; + +/** Parse a 33-byte commitment into a commitment object. + * + * Returns: 1 if input contains a valid commitment. + * Args: ctx: pointer to a context object + * Out: commit: pointer to the output commitment object + * In: input: pointer to a 33-byte serialized commitment key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( + const secp256k1_context *ctx, + secp256k1_pedersen_commitment *commit, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a commitment object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object + * Out: output: pointer to a 33-byte byte array + * In: commit: pointer to a secp256k1_pedersen_commitment containing an + * initialized commitment + */ +SECP256K1_API int secp256k1_pedersen_commitment_serialize( + const secp256k1_context *ctx, + unsigned char *output, + const secp256k1_pedersen_commitment *commit +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a pedersen commitment. + * Returns 1: Commitment successfully created. + * 0: Error. The blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127) or results in the + * point at infinity. Retry with a different factor. + * In: ctx: pointer to a context object (not secp256k1_context_static) + * blind: pointer to a 32-byte blinding factor (cannot be NULL) + * value: unsigned 64-bit integer value to commit to. + * gen: additional generator 'h' + * Out: commit: pointer to the commitment (cannot be NULL) + * + * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( + const secp256k1_context *ctx, + secp256k1_pedersen_commitment *commit, + const unsigned char *blind, + uint64_t value, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Computes the sum of multiple positive and negative blinding factors. + * Returns 1: Sum successfully computed. + * 0: Error. A blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127). Retry with + * different factors. + * In: ctx: pointer to a context object (cannot be NULL) + * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) + * n: number of factors pointed to by blinds. + * npositive: how many of the initial factors should be treated with a positive sign. + * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( + const secp256k1_context *ctx, + unsigned char *blind_out, + const unsigned char * const *blinds, + size_t n, + size_t npositive +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a tally of pedersen commitments + * Returns 1: commitments successfully sum to zero. + * 0: Commitments do not sum to zero or other error. + * In: ctx: pointer to a context object (cannot be NULL) + * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) + * pcnt: number of commitments pointed to by commits. + * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) + * ncnt: number of commitments pointed to by ncommits. + * + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. + * + * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator + * A all blinding factors and all values must sum to zero. + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( + const secp256k1_context *ctx, + const secp256k1_pedersen_commitment * const *commits, + size_t pcnt, + const secp256k1_pedersen_commitment * const *ncommits, + size_t ncnt +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Sets the final Pedersen blinding factor correctly when the generators themselves + * have blinding factors. + * + * Consider a generator of the form A' = A + rG, where A is the "real" generator + * but A' is the generator provided to verifiers. Then a Pedersen commitment + * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') + * to sum to zero for multiple commitments, we take three arrays consisting of + * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s + * and `blinding_factor`s, and sum them. + * + * The function then subtracts the sum of all (vr + r') from the last element + * of the `blinding_factor` array, setting the total sum to zero. + * + * Returns 1: Blinding factor successfully computed. + * 0: Error. A blinding_factor or generator_blind are larger than the group + * order (probability for random 32 byte number < 2^-127). Retry with + * different values. + * + * In: ctx: pointer to a context object + * value: array of asset values, `v` in the above paragraph. + * May not be NULL unless `n_total` is 0. + * generator_blind: array of asset blinding factors, `r` in the above paragraph + * May not be NULL unless `n_total` is 0. + * n_total: Total size of the above arrays + * n_inputs: How many of the initial array elements represent commitments that + * will be negated in the final sum + * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph + * May not be NULL unless `n_total` is 0. + * the last value will be modified to get the total sum to zero. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( + const secp256k1_context *ctx, + const uint64_t *value, + const unsigned char * const *generator_blind, + unsigned char * const *blinding_factor, + size_t n_total, + size_t n_inputs +); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 11b8f08c..43836a64 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -16,7 +16,7 @@ extern "C" { * v1.0.0. You can find an example demonstrating the musig module in * examples/musig.c. * - * The module also supports BIP 341 ("Taproot") public key tweaking. + * The module also supports BIP 341 ("Taproot") public key tweaking and adaptor signatures. * * It is recommended to read the documentation in this include file carefully. * Further notes on API usage can be found in doc/musig.md @@ -462,6 +462,11 @@ SECP256K1_API int secp256k1_musig_nonce_agg( /** Takes the aggregate nonce and creates a session that is required for signing * and verification of partial signatures. * + * If the adaptor argument is non-NULL, then the output of + * musig_partial_sig_agg will be a pre-signature which is not a valid Schnorr + * signature. In order to create a valid signature, the pre-signature and the + * secret adaptor must be provided to `musig_adapt`. + * * Returns: 0 if the arguments are invalid, 1 otherwise * Args: ctx: pointer to a context object * Out: session: pointer to a struct to store the session @@ -470,13 +475,17 @@ SECP256K1_API int secp256k1_musig_nonce_agg( * msg32: the 32-byte message to sign * keyagg_cache: pointer to the keyagg_cache that was used to create the * aggregate (and potentially tweaked) pubkey + * adaptor: optional pointer to an adaptor point encoded as a public + * key if this signing session is part of an adaptor + * signature protocol (can be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process( const secp256k1_context *ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, - const secp256k1_musig_keyagg_cache *keyagg_cache + const secp256k1_musig_keyagg_cache *keyagg_cache, + const secp256k1_pubkey *adaptor ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Produces a partial signature @@ -534,7 +543,8 @@ SECP256K1_API int secp256k1_musig_partial_sign( * before aggregating it with `musig_nonce_agg` and using the result to * create the `session` with `musig_nonce_process`. * - * It is not required to call this function in regular MuSig sessions, because + * This function is essential when using protocols with adaptor signatures. + * Without adaptor signatures, it is not required to call this function in regular MuSig sessions, because * if any partial signature does not verify, the final signature will not * verify either, so the problem will be caught. However, this function * provides the ability to identify which specific partial signature fails @@ -581,6 +591,77 @@ SECP256K1_API int secp256k1_musig_partial_sig_agg( size_t n_sigs ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Extracts the nonce_parity bit from a session + * + * This is used for adaptor signatures. + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: nonce_parity: pointer to an integer that indicates the parity + * of the aggregate public nonce. Used for adaptor + * signatures. + * In: session: pointer to the session that was created with + * musig_nonce_process + */ +SECP256K1_API int secp256k1_musig_nonce_parity( + const secp256k1_context *ctx, + int *nonce_parity, + const secp256k1_musig_session *session +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Creates a signature from a pre-signature and an adaptor. + * + * If the sec_adaptor32 argument is incorrect, the output signature will be + * invalid. This function does not verify the signature. + * + * Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain + * invalid (overflowing) values. 1 otherwise (which does NOT mean the + * signature or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out: sig64: 64-byte signature. This pointer may point to the same + * memory area as `pre_sig`. + * In: pre_sig64: 64-byte pre-signature + * sec_adaptor32: 32-byte secret adaptor to add to the pre-signature + * nonce_parity: the output of `musig_nonce_parity` called with the + * session used for producing the pre-signature + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_adapt( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *pre_sig64, + const unsigned char *sec_adaptor32, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Extracts a secret adaptor from a MuSig pre-signature and corresponding + * signature + * + * This function will not fail unless given grossly invalid data; if it is + * merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier + * steps of any protocol that uses this function. In particular, this includes + * verifying all partial signatures that were aggregated into pre_sig64. + * + * Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain + * grossly invalid (overflowing) values. 1 otherwise (which does NOT + * mean the signatures or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out:sec_adaptor32: 32-byte secret adaptor + * In: sig64: complete, valid 64-byte signature + * pre_sig64: the pre-signature corresponding to sig64, i.e., the + * aggregate of partial signatures without the secret + * adaptor + * nonce_parity: the output of `musig_nonce_parity` called with the + * session used for producing sig64 + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_adaptor( + const secp256k1_context *ctx, + unsigned char *sec_adaptor32, + const unsigned char *sig64, + const unsigned char *pre_sig64, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h new file mode 100644 index 00000000..0224972f --- /dev/null +++ b/include/secp256k1_rangeproof.h @@ -0,0 +1,217 @@ +#ifndef SECP256K1_RANGEPROOF_H +# define SECP256K1_RANGEPROOF_H + +# include "secp256k1.h" +# include "secp256k1_generator.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** This module implements a variant of Back-Maxwell range proofs as described + * in the Confidential Assets paper (https://blockstream.com/bitcoin17-final41.pdf). + * The construction is based on Borromean ring signatures. + * (https://nt4tn.net/papers/borromean_draft_0.01_34241bb.pdf) + * + * This implementation differs from the variant in the paper mainly in that it + * omits an optimization that saves one scalar per ring. This optimization complicates + * the protocol and security analysis, as it requires differentiating cases where + * the i-th bit v_i = 0 versus otherwise, and makes calculating response points R_i less + * straightforward. The implemented version uses Borromean ring signatures in + * an unmodified way. + * + * Another difference is that the implementation omits the last ring's commitment + * from the proof, which is recovered by the verifier by subtracting all other digit + * commitments from the total, reducing proof size by one group element. + * + * Furthermore, in the implementation every hash calculation includes a message + * m=SHA256(C||H||header||C_0||...||C_(n-2)||extra_commit), binding the commitment C, + * generator H, proof header, the n-1 explicit digit commitments, and any extra data. + * This prevents an attack that would compromise non-malleability. In the paper's + * version of the protocol, a prover could pick distinct indices i, j and a scalar y, + * and modify digit commitments in the original proof by setting C'_i = C_i + yG and + * C'_j = C_j - yG, obtaining a different valid proof for the same commitment and + * witness. + * + * In the current implementation, up to 3968 bytes of message data can be + * embedded and recovered within maximally-sized proofs. The implemented embedding + * method using the forged parts of ring signatures could also be applied to the + * construction in the paper, but is not mentioned there. Message embedding is used + * in Confidential Assets to transmit values and blinding factors of the corresponding + * commitments. This is possible because randomness is generated by seeding HMAC-DRBG + * with the shared ECDH key, allowing the receiver to rewind the proof using the same + * random values the sender used. + */ + +/** Length of a message that can be embedded into a maximally-sized rangeproof + * + * It is not be possible to fit a message of this size into a non-maximally-sized + * rangeproof, but it is guaranteed that any embeddable message can fit into an + * array of this size. This constant is intended to be used for memory allocations + * and sanity checks. + */ +#define SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN 3968 + +/** Verify a proof that a committed value is within a range. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. + * 0: Proof failed or other error. + * In: ctx: pointer to a context object (not secp256k1_context_static) + * commit: the commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * extra_commit: additional data covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) + * gen: additional generator 'h' + * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( + const secp256k1_context *ctx, + uint64_t *min_value, + uint64_t *max_value, + const secp256k1_pedersen_commitment *commit, + const unsigned char *proof, + size_t plen, + const unsigned char *extra_commit, + size_t extra_commit_len, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(9); + +/** Verify a range proof proof and rewind the proof to recover information sent by its author. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. + * 0: Proof failed, rewind failed, or other error. + * In: ctx: pointer to a context object (not secp256k1_context_static) + * commit: the commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * nonce: 32-byte secret nonce used by the prover (cannot be NULL) + * extra_commit: additional data covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) + * gen: additional generator 'h' + * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment + * value_out: pointer to an unsigned int64 which has the exact value of the commitment. + * message_out: pointer to a 4096 byte character array to receive message data from the proof author. + * outlen: length of message data written to message_out. This is generally not equal to the + * msg_len used by the signer. However, for all i with msg_len <= i < outlen, it is + * guaranteed that message_out[i] == 0. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( + const secp256k1_context *ctx, + unsigned char *blind_out, + uint64_t *value_out, + unsigned char *message_out, + size_t *outlen, + const unsigned char *nonce, + uint64_t *min_value, + uint64_t *max_value, + const secp256k1_pedersen_commitment *commit, + const unsigned char *proof, + size_t plen, + const unsigned char *extra_commit, + size_t extra_commit_len, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(14); + +/** Author a proof that a committed value is within a range. + * Returns 1: Proof successfully created. + * 0: Error + * In: ctx: pointer to a context object (not secp256k1_context_static) + * proof: pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL) + * min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount. + * commit: the commitment being proved. + * blind: 32-byte blinding factor used by commit. The blinding factor may be all-zeros as long as min_bits is set to 3 or greater. + * This is a side-effect of the underlying crypto, not a deliberate API choice, but it may be useful when balancing CT transactions. + * nonce: 32-byte secret nonce used to initialize the proof (value can be reverse-engineered out of the proof if this secret is known.) + * exp: Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18. + * (-1 is a special case that makes the value public. 0 is the most private.) + * min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64). + * value: Actual value of the commitment. + * message: pointer to a byte array of data to be embedded in the rangeproof that can be recovered by rewinding the proof + * msg_len: size of the message to be embedded in the rangeproof + * extra_commit: additional data to be covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) + * gen: additional generator 'h' + * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. + * + * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. + * + * If exp is -1 the value is revealed by the proof (e.g. it proves that the proof is a blinding of a specific value, without revealing the blinding key.) + * + * This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding. + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( + const secp256k1_context *ctx, + unsigned char *proof, + size_t *plen, + uint64_t min_value, + const secp256k1_pedersen_commitment *commit, + const unsigned char *blind, + const unsigned char *nonce, + int exp, + int min_bits, + uint64_t value, + const unsigned char *message, + size_t msg_len, + const unsigned char *extra_commit, + size_t extra_commit_len, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(15); + +/** Extract some basic information from a range-proof. + * Returns 1: Information successfully extracted. + * 0: Decode failed. + * In: ctx: pointer to a context object + * proof: pointer to character array with the proof. + * plen: length of proof in bytes. + * Out: exp: Exponent used in the proof (-1 means the value isn't private). + * mantissa: Number of bits covered by the proof. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( + const secp256k1_context *ctx, + int *exp, + int *mantissa, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *proof, + size_t plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Returns an upper bound on the size of a rangeproof with the given parameters + * + * An actual rangeproof may be smaller, for example if the actual value + * is less than both the provided `max_value` and 2^`min_bits`, or if + * the `exp` parameter to `secp256k1_rangeproof_sign` is set such that + * the proven range is compressed. In particular this function will always + * overestimate the size of single-value proofs. Also, if `min_value` + * is set to 0 in the proof, the result will usually, but not always, + * be 8 bytes smaller than if a nonzero value had been passed. + * + * The goal of this function is to provide a useful upper bound for + * memory allocation or fee estimation purposes, without requiring + * too many parameters be fixed in advance. + * + * To obtain the size of largest possible proof, set `max_value` to + * `UINT64_MAX` (and `min_bits` to any valid value such as 0). + * + * In: ctx: pointer to a context object + * max_value: the maximum value that might be passed for `value` for the proof. + * min_bits: the value that will be passed as `min_bits` for the proof. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT size_t secp256k1_rangeproof_max_size( + const secp256k1_context *ctx, + uint64_t max_value, + int min_bits +) SECP256K1_ARG_NONNULL(1); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/include/secp256k1_schnorrsig_halfagg.h b/include/secp256k1_schnorrsig_halfagg.h new file mode 100644 index 00000000..39eb5080 --- /dev/null +++ b/include/secp256k1_schnorrsig_halfagg.h @@ -0,0 +1,107 @@ +#ifndef SECP256K1_SCHNORRSIG_HALFAGG_H +#define SECP256K1_SCHNORRSIG_HALFAGG_H + +#include "secp256k1.h" +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** Incrementally (Half-)Aggregate a sequence of Schnorr + * signatures to an existing half-aggregate signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: a secp256k1 context object. + * In/Out: aggsig: pointer to the serialized aggregate signature + * that is input. The first 32*(n_before+1) of this + * array should hold the input aggsig. It will be + * overwritten by the new serialized aggregate signature. + * It should be large enough for that, see aggsig_len. + * aggsig_len: size of aggsig array in bytes. + * Should be large enough to hold the new + * serialized aggregate signature, i.e., + * should satisfy aggsig_size >= 32*(n_before+n_new+1). + * It will be overwritten to be the exact size of the + * resulting aggsig. + * In: all_pubkeys: Array of (n_before + n_new) many x-only public keys, + * including both the ones for the already aggregated signature + * and the ones for the signatures that should be added. + * Can only be NULL if n_before + n_new is 0. + * all_msgs32: Array of (n_before + n_new) many 32-byte messages, + * including both the ones for the already aggregated signature + * and the ones for the signatures that should be added. + * Can only be NULL if n_before + n_new is 0. + * new_sigs64: Array of n_new many 64-byte signatures, containing the new + * signatures that should be added. Can only be NULL if n_new is 0. + * n_before: Number of signatures that have already been aggregated + * in the input aggregate signature. + * n_new: Number of signatures that should now be added + * to the aggregate signature. + */ +SECP256K1_API int secp256k1_schnorrsig_inc_aggregate( + const secp256k1_context *ctx, + unsigned char *aggsig, + size_t *aggsig_len, + const secp256k1_xonly_pubkey* all_pubkeys, + const unsigned char *all_msgs32, + const unsigned char *new_sigs64, + size_t n_before, + size_t n_new +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** (Half-)Aggregate a sequence of Schnorr signatures. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: a secp256k1 context object. + * Out: aggsig: pointer to an array of aggsig_len many bytes to + * store the serialized aggregate signature. + * In/Out: aggsig_len: size of the aggsig array that is passed in bytes; + * will be overwritten to be the exact size of aggsig. + * In: pubkeys: Array of n many x-only public keys. + * Can only be NULL if n is 0. + * msgs32: Array of n many 32-byte messages. + * Can only be NULL if n is 0. + * sigs64: Array of n many 64-byte signatures. + * Can only be NULL if n is 0. + * n: number of signatures to be aggregated. + */ +SECP256K1_API int secp256k1_schnorrsig_aggregate( + const secp256k1_context *ctx, + unsigned char *aggsig, + size_t *aggsig_len, + const secp256k1_xonly_pubkey *pubkeys, + const unsigned char *msgs32, + const unsigned char *sigs64, + size_t n +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a (Half-)aggregate Schnorr signature. + * + * Returns: 1: correct signature. + * 0: incorrect signature. + * Args: ctx: a secp256k1 context object. + * In: pubkeys: Array of n many x-only public keys. Can only be NULL if n is 0. + * msgs32: Array of n many 32-byte messages. Can only be NULL if n is 0. + * n: number of signatures to that have been aggregated. + * aggsig: Pointer to an array of aggsig_size many bytes + * containing the serialized aggregate + * signature to be verified. + * aggsig_len: Size of the aggregate signature in bytes. + * Should be aggsig_len = 32*(n+1) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_aggverify( + const secp256k1_context *ctx, + const secp256k1_xonly_pubkey *pubkeys, + const unsigned char *msgs32, + size_t n, + const unsigned char *aggsig, + size_t aggsig_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_HALFAGG_H */ diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h new file mode 100644 index 00000000..95c58384 --- /dev/null +++ b/include/secp256k1_surjectionproof.h @@ -0,0 +1,269 @@ +#ifndef SECP256K1_SURJECTIONPROOF_H +#define SECP256K1_SURJECTIONPROOF_H + +#include "secp256k1.h" +#include "secp256k1_rangeproof.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Maximum number of inputs that may be given in a surjection proof */ +#define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256 + +/** Maximum number of inputs that may be used in a surjection proof */ +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 256 + +/** Number of bytes a serialized surjection proof requires given the + * number of inputs and the number of used inputs. + */ +#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used_inputs) \ + (2 + (n_inputs + 7)/8 + 32 * (1 + (n_used_inputs))) + +/** Maximum number of bytes a serialized surjection proof requires. */ +#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX \ + SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) + +/** Opaque data structure that holds a parsed surjection proof + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. Nor is + * it guaranteed to have any particular size, nor that identical proofs + * will have identical representation. (That is, memcmp may return nonzero + * even for identical proofs.) + * + * To obtain these properties, instead use secp256k1_surjectionproof_parse + * and secp256k1_surjectionproof_serialize to encode/decode proofs into a + * well-defined format. + * + * The representation is exposed to allow creation of these objects on the + * stack; please *do not* use these internals directly. + */ +typedef struct secp256k1_surjectionproof { +#ifdef VERIFY + /** Mark whether this proof has gone through `secp256k1_surjectionproof_initialize` */ + int initialized; +#endif + /** Total number of input asset tags */ + size_t n_inputs; + /** Bitmap of which input tags are used in the surjection proof */ + unsigned char used_inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8]; + /** Borromean signature: e0, scalars */ + unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)]; +} secp256k1_surjectionproof; + +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE +/** Parse a surjection proof + * + * Returns: 1 when the proof could be parsed, 0 otherwise. + * Args: ctx: pointer to a context object + * Out: proof: pointer to a proof object + * In: input: pointer to the array to parse + * inputlen: length of the array pointed to by input + * + * The proof must consist of: + * - A 2-byte little-endian total input count `n` + * - A ceil(n/8)-byte bitmap indicating which inputs are used. + * - A big-endian 32-byte borromean signature e0 value + * - `m` big-endian 32-byte borromean signature s values, where `m` + * is the number of set bits in the bitmap + */ +SECP256K1_API int secp256k1_surjectionproof_parse( + const secp256k1_context *ctx, + secp256k1_surjectionproof *proof, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +#endif + +/** Serialize a surjection proof + * + * Returns: 1 if enough space was available to serialize, 0 otherwise + * Args: ctx: pointer to a context object + * Out: output: pointer to an array to store the serialization + * In/Out: outputlen: pointer to an integer which is initially set to the size + * of output, and is overwritten with the written size. + * In: proof: pointer to an initialized proof object + * + * See secp256k1_surjectionproof_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_surjectionproof_serialize( + const secp256k1_context *ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_surjectionproof *proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Data structure that holds a fixed asset tag. + * + * This data type is *not* opaque. It will always be 32 bytes of whatever + * data the API user wants to use as an asset tag. Its contents have no + * semantic meaning to libsecp whatsoever. + */ +typedef struct secp256k1_fixed_asset_tag { + unsigned char data[32]; +} secp256k1_fixed_asset_tag; + +/** Returns the total number of inputs a proof expects to be over. + * + * Returns: the number of inputs for the given proof + * In: ctx: pointer to a context object + * proof: pointer to a proof object + */ +SECP256K1_API size_t secp256k1_surjectionproof_n_total_inputs( + const secp256k1_context *ctx, + const secp256k1_surjectionproof *proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Returns the actual number of inputs that a proof uses + * + * Returns: the number of inputs for the given proof + * In: ctx: pointer to a context object + * proof: pointer to a proof object + */ +SECP256K1_API size_t secp256k1_surjectionproof_n_used_inputs( + const secp256k1_context *ctx, + const secp256k1_surjectionproof *proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Returns the total size this proof would take, in bytes, when serialized + * + * Returns: the total size + * In: ctx: pointer to a context object + * proof: pointer to a proof object + */ +SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( + const secp256k1_context *ctx, + const secp256k1_surjectionproof *proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Surjection proof initialization function; decides on inputs to use + * To be used to initialize stack-allocated secp256k1_surjectionproof struct + * Returns 0: inputs could not be selected + * n: inputs were selected after n iterations of random selection + * + * In: ctx: pointer to a context object + * fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known, + * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; + * this won't match the output tag but might be used in the anonymity set.) + * n_input_tags: the number of entries in the fixed_input_tags array + * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * Must be <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS + * fixed_output_tag: fixed output tag + * max_n_iterations: the maximum number of iterations to do before giving up. Because the + * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is + * limited to 256 the probability of giving up is smaller than + * (255/256)^(n_input_tags_to_use*max_n_iterations). + * + * random_seed32: random seed to be used for input selection + * Out: proof: The proof whose bitvector will be initialized. In case of failure, + * the state of the proof is undefined. + * input_index: The index of the actual input that is secretly mapped to the output + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initialize( + const secp256k1_context *ctx, + secp256k1_surjectionproof *proof, + size_t *input_index, + const secp256k1_fixed_asset_tag *fixed_input_tags, + const size_t n_input_tags, + const size_t n_input_tags_to_use, + const secp256k1_fixed_asset_tag *fixed_output_tag, + const size_t n_max_iterations, + const unsigned char *random_seed32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + + +/** Surjection proof allocation and initialization function; decides on inputs to use + * Returns 0: inputs could not be selected, or malloc failure + * n: inputs were selected after n iterations of random selection + * + * In: ctx: pointer to a context object + * proof_out_p: pointer to a pointer to `secp256k1_surjectionproof*`. + * The newly-allocated struct pointer will be saved here. + * fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known, + * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; + * this won't match the output tag but might be used in the anonymity set.) + * n_input_tags: the number of entries in the fixed_input_tags array + * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * fixed_output_tag: fixed output tag + * max_n_iterations: the maximum number of iterations to do before giving up. Because the + * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is + * limited to 256 the probability of giving up is smaller than + * (255/256)^(n_input_tags_to_use*max_n_iterations). + * + * random_seed32: random seed to be used for input selection + * Out: proof_out_p: pointer to newly-allocated proof whose bitvector will be initialized. + * In case of failure, the pointer will be NULL. + * input_index: The index of the actual input that is secretly mapped to the output + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_allocate_initialized( + const secp256k1_context *ctx, + secp256k1_surjectionproof **proof_out_p, + size_t *input_index, + const secp256k1_fixed_asset_tag *fixed_input_tags, + const size_t n_input_tags, + const size_t n_input_tags_to_use, + const secp256k1_fixed_asset_tag *fixed_output_tag, + const size_t n_max_iterations, + const unsigned char *random_seed32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + +/** Surjection proof destroy function + * deallocates the struct that was allocated with secp256k1_surjectionproof_allocate_initialized + * + * In: proof: pointer to secp256k1_surjectionproof struct + */ +SECP256K1_API void secp256k1_surjectionproof_destroy( + secp256k1_surjectionproof *proof +) SECP256K1_ARG_NONNULL(1); + +/** Surjection proof generation function + * Returns 0: proof could not be created + * 1: proof was successfully created + * + * In: ctx: pointer to a context object (not secp256k1_context_static) + * ephemeral_input_tags: the ephemeral asset tag of all inputs + * n_ephemeral_input_tags: the number of entries in the ephemeral_input_tags array + * ephemeral_output_tag: the ephemeral asset tag of the output + * input_index: the index of the input that actually maps to the output + * input_blinding_key: the blinding key of the input + * output_blinding_key: the blinding key of the output + * In/Out: proof: The produced surjection proof. Must have already gone through `secp256k1_surjectionproof_initialize` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_generate( + const secp256k1_context *ctx, + secp256k1_surjectionproof *proof, + const secp256k1_generator *ephemeral_input_tags, + size_t n_ephemeral_input_tags, + const secp256k1_generator *ephemeral_output_tag, + size_t input_index, + const unsigned char *input_blinding_key, + const unsigned char *output_blinding_key +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); + + +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE +/** Surjection proof verification function + * Returns 0: proof was invalid + * 1: proof was valid + * + * In: ctx: pointer to a context object (not secp256k1_context_static) + * proof: proof to be verified + * ephemeral_input_tags: the ephemeral asset tag of all inputs + * n_ephemeral_input_tags: the number of entries in the ephemeral_input_tags array + * ephemeral_output_tag: the ephemeral asset tag of the output + */ +SECP256K1_API int secp256k1_surjectionproof_verify( + const secp256k1_context *ctx, + const secp256k1_surjectionproof *proof, + const secp256k1_generator *ephemeral_input_tags, + size_t n_ephemeral_input_tags, + const secp256k1_generator *ephemeral_output_tag +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h new file mode 100644 index 00000000..20d495d3 --- /dev/null +++ b/include/secp256k1_whitelist.h @@ -0,0 +1,148 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_WHITELIST_H +#define SECP256K1_WHITELIST_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECP256K1_WHITELIST_MAX_N_KEYS 255 + +/** Opaque data structure that holds a parsed whitelist proof + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. Nor is + * it guaranteed to have any particular size, nor that identical signatures + * will have identical representation. (That is, memcmp may return nonzero + * even for identical signatures.) + * + * To obtain these properties, instead use secp256k1_whitelist_signature_parse + * and secp256k1_whitelist_signature_serialize to encode/decode signatures + * into a well-defined format. + * + * The representation is exposed to allow creation of these objects on the + * stack; please *do not* use these internals directly. To learn the number + * of keys for a signature, use `secp256k1_whitelist_signature_n_keys`. + */ +typedef struct secp256k1_whitelist_signature { + size_t n_keys; + /* e0, scalars */ + unsigned char data[32 * (1 + SECP256K1_WHITELIST_MAX_N_KEYS)]; +} secp256k1_whitelist_signature; + +/** Parse a whitelist signature + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input: pointer to the array to parse + * input_len: the length of the above array + * + * The signature must consist of a 1-byte n_keys value, followed by a 32-byte + * big endian e0 value, followed by n_keys many 32-byte big endian s values. + * If n_keys falls outside of [0..SECP256K1_WHITELIST_MAX_N_KEYS] the encoding + * is invalid. + * + * The total length of the input array must therefore be 33 + 32 * n_keys. + * If the length `input_len` does not match this value, parsing will fail. + * + * After the call, sig will always be initialized. If parsing failed or any + * scalar values overflow or are zero, the resulting sig value is guaranteed + * to fail validation for any set of keys. + */ +SECP256K1_API int secp256k1_whitelist_signature_parse( + const secp256k1_context *ctx, + secp256k1_whitelist_signature *sig, + const unsigned char *input, + size_t input_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Returns the number of keys a signature expects to have. + * + * Returns: the number of keys for the given signature + * In: sig: pointer to a signature object + */ +SECP256K1_API size_t secp256k1_whitelist_signature_n_keys( + const secp256k1_whitelist_signature *sig +) SECP256K1_ARG_NONNULL(1); + +/** Serialize a whitelist signature + * + * Returns: 1 + * Args: ctx: pointer to a context object + * Out: output64: pointer to an array to store the serialization + * In/Out: output_len: length of the above array, updated with the actual serialized length + * In: sig: pointer to an initialized signature object + * + * See secp256k1_whitelist_signature_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_whitelist_signature_serialize( + const secp256k1_context *ctx, + unsigned char *output, + size_t *output_len, + const secp256k1_whitelist_signature *sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Compute a whitelist signature + * Returns 1: signature was successfully created + * 0: signature was not successfully created + * In: ctx: pointer to a context object (not secp256k1_context_static) + * online_pubkeys: list of all online pubkeys + * offline_pubkeys: list of all offline pubkeys + * n_keys: the number of entries in each of the above two arrays + * sub_pubkey: the key to be whitelisted + * online_seckey: the secret key to the signer's online pubkey + * summed_seckey: the secret key to the sum of (whitelisted key, signer's offline pubkey) + * index: the signer's index in the lists of keys + * Out: sig: The produced signature. + * + * The signatures are of the list of all passed pubkeys in the order + * ( whitelist, online_1, offline_1, online_2, offline_2, ... ) + * The verification key list consists of + * online_i + H(offline_i + whitelist)(offline_i + whitelist) + * for each public key pair (offline_i, offline_i). Here H means sha256 of the + * compressed serialization of the key. + */ +SECP256K1_API int secp256k1_whitelist_sign( + const secp256k1_context *ctx, + secp256k1_whitelist_signature *sig, + const secp256k1_pubkey *online_pubkeys, + const secp256k1_pubkey *offline_pubkeys, + const size_t n_keys, + const secp256k1_pubkey *sub_pubkey, + const unsigned char *online_seckey, + const unsigned char *summed_seckeyx, + const size_t index +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); + +/** Verify a whitelist signature + * Returns 1: signature is valid + * 0: signature is not valid + * In: ctx: pointer to a context object (not secp256k1_context_static) + * sig: the signature to be verified + * online_pubkeys: list of all online pubkeys + * offline_pubkeys: list of all offline pubkeys + * n_keys: the number of entries in each of the above two arrays + * sub_pubkey: the key to be whitelisted + */ +SECP256K1_API int secp256k1_whitelist_verify( + const secp256k1_context *ctx, + const secp256k1_whitelist_signature *sig, + const secp256k1_pubkey *online_pubkeys, + const secp256k1_pubkey *offline_pubkeys, + const size_t n_keys, + const secp256k1_pubkey *sub_pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sage/shallue_van_de_woestijne.sage b/sage/shallue_van_de_woestijne.sage new file mode 100644 index 00000000..1cc97b65 --- /dev/null +++ b/sage/shallue_van_de_woestijne.sage @@ -0,0 +1,51 @@ + +### http://www.di.ens.fr/~fouque/pub/latincrypt12.pdf + +# Parameters for secp256k1 +p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +a = 0 +b = 7 +F = FiniteField (p) +C = EllipticCurve ([F(a), F(b)]) + +def svdw(t): + sqrt_neg_3 = F(-3).nth_root(2) + + ## Compute candidate x values + w = sqrt_neg_3 * t / (1 + b + t^2) + x = [ F(0), F(0), F(0) ] + x[0] = (-1 + sqrt_neg_3) / 2 - t * w + x[1] = -1 - x[0] + x[2] = 1 + 1 / w^2 + + print + print "On %2d" % t + print " x1 %064x" % x[0] + print " x2 %064x" % x[1] + print " x3 %064x" % x[2] + + ## Select which to use + alph = jacobi_symbol(x[0]^3 + b, p) + beta = jacobi_symbol(x[1]^3 + b, p) + if alph == 1 and beta == 1: + i = 0 + elif alph == 1 and beta == -1: + i = 0 + elif alph == -1 and beta == 1: + i = 1 + elif alph == -1 and beta == -1: + i = 2 + else: + print "Help! I don't understand Python!" + + ## Expand to full point + sign = 1 - 2 * (int(F(t)) % 2) + ret_x = x[i] + ret_y = sign * F(x[i]^3 + b).nth_root(2) + return C.point((ret_x, ret_y)) + + +## main +for i in range(1, 11): + res = svdw(i) + print "Result: %064x %064x" % res.xy() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 322f1987..ddd5d314 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,67 @@ set_property(TARGET secp256k1 PROPERTY PUBLIC_HEADER # Processing must be done in a topological sorting of the dependency graph # (dependent module first). +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG) + 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_halfagg module.") + endif() + set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON) + add_compile_definitions(ENABLE_MODULE_SCHNORRSIG_HALFAGG=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig_halfagg.h) +endif() + +if(SECP256K1_ENABLE_MODULE_BPPP) + if(DEFINED SECP256K1_ENABLE_MODULE_GENERATOR AND NOT SECP256K1_ENABLE_MODULE_GENERATOR) + message(FATAL_ERROR "Module dependency error: You have disabled the generator module explicitly, but it is required by the bppp module.") + endif() + set(SECP256K1_ENABLE_MODULE_GENERATOR ON) + add_compile_definitions(ENABLE_MODULE_BPPP=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_bppp.h) +endif() + +if(SECP256K1_ENABLE_MODULE_ECDSA_S2C) + add_compile_definitions(ENABLE_MODULE_ECDSA_S2C=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ecdsa_s2c.h) +endif() + +if(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR) + add_compile_definitions(ENABLE_MODULE_ECDSA_ADAPTOR=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ecdsa_adaptor.h) +endif() + +if(SECP256K1_ENABLE_MODULE_WHITELIST) + if(DEFINED SECP256K1_ENABLE_MODULE_RANGEPROOF AND NOT SECP256K1_ENABLE_MODULE_RANGEPROOF) + message(FATAL_ERROR "Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the whitelist module.") + endif() + set(SECP256K1_ENABLE_MODULE_RANGEPROOF ON) + add_compile_definitions(ENABLE_MODULE_WHITELIST=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_whitelist.h) +endif() + +if(SECP256K1_ENABLE_MODULE_SURJECTIONPROOF) + if(DEFINED SECP256K1_ENABLE_MODULE_RANGEPROOF AND NOT SECP256K1_ENABLE_MODULE_RANGEPROOF) + message(FATAL_ERROR "Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the surjectionproof module.") + endif() + set(SECP256K1_ENABLE_MODULE_RANGEPROOF ON) + add_compile_definitions(ENABLE_MODULE_SURJECTIONPROOF=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_surjectionproof.h) +endif() + +if(SECP256K1_ENABLE_MODULE_RANGEPROOF) + if(DEFINED SECP256K1_ENABLE_MODULE_GENERATOR AND NOT SECP256K1_ENABLE_MODULE_GENERATOR) + message(FATAL_ERROR "Module dependency error: You have disabled the generator module explicitly, but it is required by the rangeproof module.") + endif() + set(SECP256K1_ENABLE_MODULE_GENERATOR ON) + add_compile_definitions(ENABLE_MODULE_RANGEPROOF=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_rangeproof.h) +endif() + +if(SECP256K1_ENABLE_MODULE_GENERATOR) + add_compile_definitions(ENABLE_MODULE_GENERATOR=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_generator.h) +endif() + + if(SECP256K1_ENABLE_MODULE_ELLSWIFT) add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h) diff --git a/src/bench_bppp.c b/src/bench_bppp.c new file mode 100644 index 00000000..63ad8cea --- /dev/null +++ b/src/bench_bppp.c @@ -0,0 +1,42 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "../include/secp256k1_bppp.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; +} bench_bppp_data; + +static void bench_bppp_setup(void* arg) { + (void) arg; +} + +static void bench_bppp(void* arg, int iters) { + bench_bppp_data *data = (bench_bppp_data*)arg; + + (void) data; + (void) iters; +} + +int main(void) { + bench_bppp_data data; + int iters = get_iters(32); + if (iters == 0) { + return EXIT_FAILURE; + } + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + run_benchmark("bppp_verify_bit", bench_bppp, bench_bppp_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return EXIT_SUCCESS; +} diff --git a/src/bench_generator.c b/src/bench_generator.c new file mode 100644 index 00000000..93af6129 --- /dev/null +++ b/src/bench_generator.c @@ -0,0 +1,64 @@ +/********************************************************************** + * Copyright (c) 2016 Pieter Wuille * + * 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/secp256k1_generator.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + unsigned char key[32]; + unsigned char blind[32]; +} bench_generator_t; + +static void bench_generator_setup(void* arg) { + bench_generator_t *data = (bench_generator_t*)arg; + memset(data->key, 0x31, 32); + memset(data->blind, 0x13, 32); +} + +static void bench_generator_generate(void* arg, int iters) { + int i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_generator gen; + CHECK(secp256k1_generator_generate(data->ctx, &gen, data->key)); + data->key[i & 31]++; + } +} + +static void bench_generator_generate_blinded(void* arg, int iters) { + int i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_generator gen; + CHECK(secp256k1_generator_generate_blinded(data->ctx, &gen, data->key, data->blind)); + data->key[1 + (i & 30)]++; + data->blind[1 + (i & 30)]++; + } +} + +int main(void) { + bench_generator_t data; + int iters = get_iters(20000); + if (iters == 0) { + return EXIT_FAILURE; + } + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, iters); + run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return EXIT_SUCCESS; +} diff --git a/src/bench_internal.c b/src/bench_internal.c index f3c1be98..bdfc3380 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -121,6 +121,15 @@ static void bench_scalar_negate(void* arg, int iters) { } } +static void bench_scalar_sqr(void* arg, int iters) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]); + } +} + static void bench_scalar_half(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -303,6 +312,26 @@ static void bench_group_add_affine_var(void* arg, int iters) { } } +static void bench_group_jacobi_var(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + j += secp256k1_gej_has_quad_y_var(&data->gej[0]); + /* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to + secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will + generally not correspond to a point on the curve, but this is not a problem + for the code being benchmarked here. Adding and normalizing have less + overhead than EC operations (which could guarantee the point remains on the + curve). */ + secp256k1_fe_add(&data->gej[0].y, &data->fe[1]); + secp256k1_fe_add(&data->gej[0].z, &data->fe[2]); + secp256k1_fe_normalize_var(&data->gej[0].y); + secp256k1_fe_normalize_var(&data->gej[0].z); + } + CHECK(j <= iters); +} + static void bench_group_add_zinv_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -319,10 +348,8 @@ static void bench_group_to_affine_var(void* arg, int iters) { for (i = 0; i < iters; ++i) { secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]); /* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates. - Note that the resulting coordinates will generally not correspond to a point - on the curve, but this is not a problem for the code being benchmarked here. - Adding and normalizing have less overhead than EC operations (which could - guarantee the point remains on the curve). */ + Similar to bench_group_jacobi_var, this approach does not result in + coordinates of points on the curve. */ secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y); secp256k1_fe_add(&data->gej[0].y, &data->fe[2]); secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x); @@ -414,6 +441,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "half")) run_benchmark("scalar_half", bench_scalar_half, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters); @@ -433,6 +461,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_zinv_var", bench_group_add_zinv_var, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters); diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c new file mode 100644 index 00000000..add1e1e1 --- /dev/null +++ b/src/bench_rangeproof.c @@ -0,0 +1,69 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "../include/secp256k1_rangeproof.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + secp256k1_pedersen_commitment commit; + unsigned char proof[5134]; + unsigned char blind[32]; + size_t len; + int min_bits; + uint64_t v; +} bench_rangeproof_t; + +static void bench_rangeproof_setup(void* arg) { + int i; + uint64_t minv; + uint64_t maxv; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + data->v = 0; + for (i = 0; i < 32; i++) data->blind[i] = i + 1; + CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v, secp256k1_generator_h)); + data->len = 5134; + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h)); +} + +static void bench_rangeproof(void* arg, int iters) { + int i; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + for (i = 0; i < iters/data->min_bits; i++) { + int j; + uint64_t minv; + uint64_t maxv; + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h); + for (j = 0; j < 4; j++) { + data->proof[j + 2 + 32 *((data->min_bits + 1) >> 1) - 4] = (i >> 8)&255; + } + } +} + +int main(void) { + bench_rangeproof_t data; + int iters; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + data.min_bits = 32; + iters = data.min_bits*get_iters(32); + if (iters == 0) { + return EXIT_FAILURE; + } + + run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return EXIT_SUCCESS; +} diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c new file mode 100644 index 00000000..46de7d5a --- /dev/null +++ b/src/bench_whitelist.c @@ -0,0 +1,110 @@ +/********************************************************************** + * Copyright (c) 2017 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include +#include + +#include "../include/secp256k1.h" + +#include "../include/secp256k1_whitelist.h" +#include "util.h" +#include "bench.h" +#include "hash_impl.h" +#include "int128_impl.h" +#include "scalar_impl.h" + +#define MAX_N_KEYS 30 + +typedef struct { + secp256k1_context* ctx; + unsigned char online_seckey[MAX_N_KEYS][32]; + unsigned char summed_seckey[MAX_N_KEYS][32]; + secp256k1_pubkey online_pubkeys[MAX_N_KEYS]; + secp256k1_pubkey offline_pubkeys[MAX_N_KEYS]; + unsigned char csub[32]; + secp256k1_pubkey sub_pubkey; + secp256k1_whitelist_signature sig; + size_t n_keys; +} bench_data; + +static void bench_whitelist(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + for (i = 0; i < iters; i++) { + CHECK(secp256k1_whitelist_verify(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey) == 1); + } +} + +static void bench_whitelist_setup(void* arg) { + bench_data* data = (bench_data*)arg; + int i = 0; + CHECK(secp256k1_whitelist_sign(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey, data->online_seckey[i], data->summed_seckey[i], i)); +} + +static void run_test(bench_data* data, int iters) { + char str[32]; + sprintf(str, "whitelist_%i", (int)data->n_keys); + run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, iters); +} + +static void generate_scalar(secp256k1_scalar *scalar, unsigned char *seckey, uint32_t num) { + secp256k1_sha256 sha256; + unsigned char c[13] = {'w','h','i','t','e','l','i','s','t', 0, 0, 0, 0}; + int is_valid; + c[9] = num; + c[10] = num >> 8; + c[11] = num >> 16; + c[12] = num >> 24; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, c, sizeof(c)); + secp256k1_sha256_finalize(&sha256, seckey); + is_valid = secp256k1_scalar_set_b32_seckey(scalar, seckey); + CHECK(is_valid); +} + +int main(void) { + bench_data data; + size_t i; + size_t n_keys = 30; + secp256k1_scalar ssub; + int iters = get_iters(5); + if (iters == 0) { + return EXIT_FAILURE; + } + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + /* Start with subkey */ + generate_scalar(&ssub, data.csub, 0); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.csub) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.sub_pubkey, data.csub) == 1); + /* Then offline and online whitelist keys */ + for (i = 0; i < n_keys; i++) { + secp256k1_scalar son, soff; + + /* Create two keys using different counter values to ensure different keys */ + generate_scalar(&son, data.online_seckey[i], i + 1); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.online_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.online_pubkeys[i], data.online_seckey[i]) == 1); + + generate_scalar(&soff, data.summed_seckey[i], i + 1 + n_keys); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.offline_pubkeys[i], data.summed_seckey[i]) == 1); + + /* Make summed_seckey correspond to the sum of offline_pubkey and sub_pubkey */ + secp256k1_scalar_add(&soff, &soff, &ssub); + secp256k1_scalar_get_b32(data.summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); + } + + /* Run test */ + for (i = 1; i <= n_keys; ++i) { + data.n_keys = i; + run_test(&data, iters); + } + + secp256k1_context_destroy(data.ctx); + return EXIT_SUCCESS; +} diff --git a/src/ctime_tests.c b/src/ctime_tests.c index f80042a8..3a12d0bc 100644 --- a/src/ctime_tests.c +++ b/src/ctime_tests.c @@ -40,6 +40,14 @@ #include "../include/secp256k1_ellswift.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +#include "../include/secp256k1_ecdsa_s2c.h" +#endif + +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +#include "../include/secp256k1_ecdsa_adaptor.h" +#endif + static void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -200,7 +208,12 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { secp256k1_musig_keyagg_cache cache; secp256k1_musig_session session; secp256k1_musig_partial_sig partial_sig; + const secp256k1_musig_partial_sig *partial_sig_ptr[1]; unsigned char extra_input[32]; + unsigned char sec_adaptor[32]; + secp256k1_pubkey adaptor; + unsigned char pre_sig[64]; + int nonce_parity; pk_ptr[0] = &pk; pubnonce_ptr[0] = &pubnonce; @@ -209,14 +222,19 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { session_secrand[0] = session_secrand[0] + 1; memcpy(extra_input, key, sizeof(extra_input)); extra_input[0] = extra_input[0] + 2; + memcpy(sec_adaptor, key, sizeof(sec_adaptor)); + sec_adaptor[0] = extra_input[0] + 3; + partial_sig_ptr[0] = &partial_sig; CHECK(secp256k1_keypair_create(ctx, &keypair, key)); CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair)); CHECK(secp256k1_musig_pubkey_agg(ctx, &agg_pk, &cache, pk_ptr, 1)); + CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor)); SECP256K1_CHECKMEM_UNDEFINE(key, 32); SECP256K1_CHECKMEM_UNDEFINE(session_secrand, sizeof(session_secrand)); SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input)); + SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor)); ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_secrand, key, &pk, msg, &cache, extra_input); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); @@ -227,7 +245,7 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1)); /* Make sure that previous tests don't undefine msg. It's not used as a secret here. */ SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg)); - CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache, &adaptor) == 1); ret = secp256k1_keypair_create(ctx, &keypair, key); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); @@ -235,6 +253,18 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { ret = secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &cache, &session); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); + + SECP256K1_CHECKMEM_DEFINE(&partial_sig, sizeof(partial_sig)); + CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1)); + SECP256K1_CHECKMEM_DEFINE(pre_sig, sizeof(pre_sig)); + + CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity, &session)); + ret = secp256k1_musig_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_musig_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); } #endif @@ -262,6 +292,68 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); } +#endif +#ifdef ENABLE_MODULE_ECDSA_S2C + { + unsigned char s2c_data[32] = {0}; + unsigned char s2c_data_comm[32] = {0}; + secp256k1_ecdsa_s2c_opening s2c_opening; + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(s2c_data, 32); + ret = secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, msg, key, s2c_data); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(s2c_data, 32); + ret = secp256k1_ecdsa_anti_exfil_host_commit(ctx, s2c_data_comm, s2c_data); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(s2c_data, 32); + ret = secp256k1_ecdsa_anti_exfil_signer_commit(ctx, &s2c_opening, msg, key, s2c_data); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + } +#endif + +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + { + unsigned char adaptor_sig[162]; + unsigned char deckey[32]; + unsigned char expected_deckey[32]; + secp256k1_pubkey enckey; + + for (i = 0; i < 32; i++) { + deckey[i] = i + 2; + } + + ret = secp256k1_ec_pubkey_create(ctx, &enckey, deckey); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, key, &enckey, msg, NULL, NULL); + SECP256K1_CHECKMEM_DEFINE(adaptor_sig, sizeof(adaptor_sig)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(deckey, 32); + ret = secp256k1_ecdsa_adaptor_decrypt(ctx, &signature, deckey, adaptor_sig); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(&signature, 32); + ret = secp256k1_ecdsa_adaptor_recover(ctx, expected_deckey, &signature, adaptor_sig, &enckey); + SECP256K1_CHECKMEM_DEFINE(expected_deckey, sizeof(expected_deckey)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_DEFINE(deckey, sizeof(deckey)); + ret = secp256k1_memcmp_var(deckey, expected_deckey, sizeof(expected_deckey)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 0); + } #endif } diff --git a/src/eccommit.h b/src/eccommit.h new file mode 100644 index 00000000..6bb11039 --- /dev/null +++ b/src/eccommit.h @@ -0,0 +1,28 @@ +/********************************************************************** + * Copyright (c) 2020 The libsecp256k1-zkp Developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECCOMMIT_H +#define SECP256K1_ECCOMMIT_H + +/** Helper function to add a 32-byte value to a scalar */ +static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak); +/** Helper function to add a 32-byte value, times G, to an EC point */ +static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak); + +/** Serializes elem as a 33 byte array. This is non-constant time with respect to + * whether pubp is the point at infinity. Thus, you may need to declassify + * pubp->infinity before calling this function. */ +static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33); +/** Compute an ec commitment tweak as hash(pubkey, data). */ +static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); +/** Compute an ec commitment as pubkey + hash(pubkey, data)*G. */ +static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); +/** Compute a secret key commitment as seckey + hash(pubkey, data). */ +static int secp256k1_ec_commit_seckey(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); +/** Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */ +static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); + +#endif /* SECP256K1_ECCOMMIT_H */ diff --git a/src/eccommit_impl.h b/src/eccommit_impl.h new file mode 100644 index 00000000..2dc24257 --- /dev/null +++ b/src/eccommit_impl.h @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (c) 2020 The libsecp256k1 Developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "eckey.h" +#include "hash.h" + +/* from secp256k1.c */ +static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak); +static int secp256k1_ec_pubkey_tweak_add_helper(secp256k1_ge *pubp, const unsigned char *tweak); + +static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33) { + if (secp256k1_ge_is_infinity(pubp)) { + return 0; + } + secp256k1_fe_normalize(&pubp->x); + secp256k1_fe_normalize(&pubp->y); + secp256k1_fe_get_b32(&buf33[1], &pubp->x); + buf33[0] = secp256k1_fe_is_odd(&pubp->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; + return 1; +} + +/* Compute an ec commitment tweak as hash(pubp, data). */ +static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) +{ + unsigned char rbuf[33]; + + if (!secp256k1_ec_commit_pubkey_serialize_const(pubp, rbuf)) { + return 0; + } + secp256k1_sha256_write(sha, rbuf, sizeof(rbuf)); + secp256k1_sha256_write(sha, data, data_size); + secp256k1_sha256_finalize(sha, tweak32); + return 1; +} + +/* Compute an ec commitment as pubp + hash(pubp, data)*G. */ +static int secp256k1_ec_commit(secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { + unsigned char tweak[32]; + + *commitp = *pubp; + return secp256k1_ec_commit_tweak(tweak, commitp, sha, data, data_size) + && secp256k1_ec_pubkey_tweak_add_helper(commitp, tweak); +} + +/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey + + * hash(pubp, data). */ +static int secp256k1_ec_commit_seckey(secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { + unsigned char tweak[32]; + return secp256k1_ec_commit_tweak(tweak, pubp, sha, data, data_size) + && secp256k1_ec_seckey_tweak_add_helper(seckey, tweak); +} + +/* Verify an ec commitment as pubp + hash(pubp, data)*G ?= commitment. */ +static int secp256k1_ec_commit_verify(const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { + secp256k1_gej pj; + secp256k1_ge p; + + if (!secp256k1_ec_commit(&p, pubp, sha, data, data_size)) { + return 0; + } + + /* Return p == commitp */ + secp256k1_ge_neg(&p, &p); + secp256k1_gej_set_ge(&pj, &p); + secp256k1_gej_add_ge_var(&pj, &pj, commitp, NULL); + return secp256k1_gej_is_infinity(&pj); +} + diff --git a/src/group.h b/src/group.h index ee3ebbbe..81e17c1e 100644 --- a/src/group.h +++ b/src/group.h @@ -55,6 +55,12 @@ typedef struct { /** Set a group element equal to the point with given X and Y coordinates */ static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); +/** Set a group element (affine) equal to the point with the given X coordinate + * and a Y coordinate that is a quadratic residue modulo p. The return value + * is true iff a coordinate with the given X coordinate exists. + */ +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); + /** Set a group element (affine) equal to the point with the given X coordinate, and given oddness * for Y. Return value indicates whether the result is valid. */ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); @@ -134,6 +140,9 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); /** Check whether a group element is the point at infinity. */ static int secp256k1_gej_is_infinity(const secp256k1_gej *a); +/** Check whether a group element's y coordinate is a quadratic residue. */ +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); + /** Set r equal to the double of a. Constant time. */ static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a); diff --git a/src/group_impl.h b/src/group_impl.h index f5169650..8be9a985 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -344,7 +344,7 @@ static void secp256k1_ge_clear(secp256k1_ge *r) { secp256k1_memclear_explicit(r, sizeof(secp256k1_ge)); } -static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { secp256k1_fe x2, x3; int ret; SECP256K1_FE_VERIFY(x); @@ -355,6 +355,14 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int o r->infinity = 0; secp256k1_fe_add_int(&x3, SECP256K1_B); ret = secp256k1_fe_sqrt(&r->y, &x3); + + SECP256K1_GE_VERIFY(r); + return ret; +} + +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { + int ret; + ret = secp256k1_ge_set_xquad(r, x); secp256k1_fe_normalize_var(&r->y); if (secp256k1_fe_is_odd(&r->y) != odd) { secp256k1_fe_negate(&r->y, &r->y, 1); @@ -923,6 +931,20 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { SECP256K1_GE_VERIFY(r); } +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as + * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z + is */ + secp256k1_fe_mul(&yz, &a->y, &a->z); + return secp256k1_fe_is_square_var(&yz); +} + static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { #ifdef EXHAUSTIVE_TEST_ORDER secp256k1_gej out; diff --git a/src/modules/bppp/Makefile.am.include b/src/modules/bppp/Makefile.am.include new file mode 100644 index 00000000..de90e06e --- /dev/null +++ b/src/modules/bppp/Makefile.am.include @@ -0,0 +1,13 @@ +include_HEADERS += include/secp256k1_bppp.h +noinst_HEADERS += src/modules/bppp/bppp_util.h +noinst_HEADERS += src/modules/bppp/main_impl.h +noinst_HEADERS += src/modules/bppp/bppp_transcript_impl.h +noinst_HEADERS += src/modules/bppp/bppp_norm_product_impl.h +noinst_HEADERS += src/modules/bppp/tests_impl.h + +if USE_BENCHMARK +noinst_PROGRAMS += bench_bppp +bench_bppp_SOURCES = src/bench_bppp.c +bench_bppp_LDADD = libsecp256k1.la +bench_bppp_CPPFLAGS = $(SECP_CONFIG_DEFINES) +endif diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h new file mode 100644 index 00000000..daf7b0da --- /dev/null +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -0,0 +1,551 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_H +#define SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_H + +#include "../../group.h" +#include "../../scalar.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" +#include "../../hash.h" + +#include "../bppp/main.h" +#include "../bppp/bppp_util.h" +#include "../bppp/bppp_transcript_impl.h" + +/* Computes the inner product of two vectors of scalars + * with elements starting from offset a and offset b + * skipping elements according to specified step. + * Returns: Sum_{i=0..len-1}(a[offset_a + i*step] * b[offset_b + i*step]) */ +static int secp256k1_scalar_inner_product( + secp256k1_scalar* res, + const secp256k1_scalar* a_vec, + const size_t a_offset, + const secp256k1_scalar* b_vec, + const size_t b_offset, + const size_t step, + const size_t len +) { + size_t i; + secp256k1_scalar_set_int(res, 0); + for (i = 0; i < len; i++) { + secp256k1_scalar term; + secp256k1_scalar_mul(&term, &a_vec[a_offset + step*i], &b_vec[b_offset + step*i]); + secp256k1_scalar_add(res, res, &term); + } + return 1; +} + +/* Computes the q-weighted inner product of two vectors of scalars + * for elements starting from offset a and offset b respectively with the + * given step. + * Returns: Sum_{i=0..len-1}(a[offset_a + step*i] * b[offset_b2 + step*i]*mu^(i+1)) */ +static int secp256k1_weighted_scalar_inner_product( + secp256k1_scalar* res, + const secp256k1_scalar* a_vec, + const size_t a_offset, + const secp256k1_scalar* b_vec, + const size_t b_offset, + const size_t step, + const size_t len, + const secp256k1_scalar* mu +) { + secp256k1_scalar mu_pow; + size_t i; + secp256k1_scalar_set_int(res, 0); + mu_pow = *mu; + for (i = 0; i < len; i++) { + secp256k1_scalar term; + secp256k1_scalar_mul(&term, &a_vec[a_offset + step*i], &b_vec[b_offset + step*i]); + secp256k1_scalar_mul(&term, &term, &mu_pow); + secp256k1_scalar_mul(&mu_pow, &mu_pow, mu); + secp256k1_scalar_add(res, res, &term); + } + return 1; +} + +/* Compute the powers of rho as rho, rho^2, rho^4 ... rho^(2^(n-1)) */ +static void secp256k1_bppp_powers_of_rho(secp256k1_scalar *powers, const secp256k1_scalar *rho, size_t n) { + size_t i; + if (n == 0) { + return; + } + powers[0] = *rho; + for (i = 1; i < n; i++) { + secp256k1_scalar_sqr(&powers[i], &powers[i - 1]); + } +} + +typedef struct ecmult_bp_commit_cb_data { + const secp256k1_scalar *n; + const secp256k1_ge *g; + const secp256k1_scalar *l; + size_t g_len; +} ecmult_bp_commit_cb_data; + +static int ecmult_bp_commit_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_bp_commit_cb_data *data = (ecmult_bp_commit_cb_data*) cbdata; + *pt = data->g[idx]; + if (idx < data->g_len) { + *sc = data->n[idx]; + } else { + *sc = data->l[idx - data->g_len]; + } + return 1; +} + +/* Create a commitment `commit` = vG + n_vec*G_vec + l_vec*H_vec where + v = |n_vec*n_vec|_mu + . |w|_mu denotes mu-weighted norm of w and + denotes inner product of l and r. +*/ +static int secp256k1_bppp_commit( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + secp256k1_ge* commit, + const secp256k1_bppp_generators* g_vec, + const secp256k1_scalar* n_vec, + size_t n_vec_len, + const secp256k1_scalar* l_vec, + size_t l_vec_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_scalar* mu +) { + secp256k1_scalar v, l_c; + /* First n_vec_len generators are Gs, rest are Hs*/ + VERIFY_CHECK(g_vec->n == (n_vec_len + l_vec_len)); +#ifdef VERIFY + VERIFY_CHECK(l_vec_len == c_vec_len); +#else + (void)c_vec_len; +#endif + + /* It is possible to extend to support n_vec and c_vec to not be power of + two. For the initial iterations of the code, we stick to powers of two for simplicity.*/ + VERIFY_CHECK(secp256k1_is_power_of_two(n_vec_len)); + VERIFY_CHECK(secp256k1_is_power_of_two(c_vec_len)); + + /* Compute v = n_vec*n_vec*mu + l_vec*c_vec */ + secp256k1_weighted_scalar_inner_product(&v, n_vec, 0 /*a offset */, n_vec, 0 /*b offset*/, 1 /*step*/, n_vec_len, mu); + secp256k1_scalar_inner_product(&l_c, l_vec, 0 /*a offset */, c_vec, 0 /*b offset*/, 1 /*step*/, l_vec_len); + secp256k1_scalar_add(&v, &v, &l_c); + + { + ecmult_bp_commit_cb_data data; + secp256k1_gej commitj; + data.g = g_vec->gens; + data.n = n_vec; + data.l = l_vec; + data.g_len = n_vec_len; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &commitj, &v, ecmult_bp_commit_cb, (void*) &data, n_vec_len + l_vec_len)) { + return 0; + } + secp256k1_ge_set_gej_var(commit, &commitj); + } + return 1; +} + +typedef struct ecmult_x_cb_data { + const secp256k1_scalar *n; + const secp256k1_ge *g; + const secp256k1_scalar *l; + const secp256k1_scalar *rho; + const secp256k1_scalar *rho_inv; + size_t G_GENS_LEN; /* Figure out initialization syntax so that this can also be const */ + size_t n_len; +} ecmult_x_cb_data; + +static int ecmult_x_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_x_cb_data *data = (ecmult_x_cb_data*) cbdata; + if (idx < data->n_len) { + if (idx % 2 == 0) { + secp256k1_scalar_mul(sc, &data->n[idx + 1], data->rho); + *pt = data->g[idx]; + } else { + secp256k1_scalar_mul(sc, &data->n[idx - 1], data->rho_inv); + *pt = data->g[idx]; + } + } else { + idx -= data->n_len; + if (idx % 2 == 0) { + *sc = data->l[idx + 1]; + *pt = data->g[data->G_GENS_LEN + idx]; + } else { + *sc = data->l[idx - 1]; + *pt = data->g[data->G_GENS_LEN + idx]; + } + } + return 1; +} + +typedef struct ecmult_r_cb_data { + const secp256k1_scalar *n1; + const secp256k1_ge *g1; + const secp256k1_scalar *l1; + size_t G_GENS_LEN; + size_t n_len; +} ecmult_r_cb_data; + +static int ecmult_r_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_r_cb_data *data = (ecmult_r_cb_data*) cbdata; + if (idx < data->n_len) { + *sc = data->n1[2*idx + 1]; + *pt = data->g1[2*idx + 1]; + } else { + idx -= data->n_len; + *sc = data->l1[2*idx + 1]; + *pt = data->g1[data->G_GENS_LEN + 2*idx + 1]; + } + return 1; +} + +/* Recursively compute the norm argument proof satisfying the relation + * _mu + = v for some commitment + * C = v*G + + . _mu is the weighted inner + * product of x with itself, where the weights are the first n powers of mu. + * _mu = mu*x_1^2 + mu^2*x_2^2 + mu^3*x_3^2 + ... + mu^n*x_n^2. + * The API computes mu as square of the r challenge (`r^2`). + * + * The norm argument is not zero knowledge and does not operate on any secret data. + * Thus the following code uses variable time operations while computing the proof. + * This function also modifies the values of n_vec, l_vec, c_vec and g_vec. The caller + * is expected to copy these values if they need to be preserved. + * + * Assumptions: This function is intended to be used in conjunction with the + * some parent protocol. To use this norm protocol in a standalone manner, the user + * should add the commitment, generators and initial public data to the transcript hash. +*/ +static int secp256k1_bppp_rangeproof_norm_product_prove( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + unsigned char* proof, + size_t *proof_len, + secp256k1_sha256* transcript, /* Transcript hash of the parent protocol */ + const secp256k1_scalar* rho, + secp256k1_ge* g_vec, + size_t g_vec_len, + secp256k1_scalar* n_vec, + size_t n_vec_len, + secp256k1_scalar* l_vec, + size_t l_vec_len, + secp256k1_scalar* c_vec, + size_t c_vec_len +) { + secp256k1_scalar mu_f, rho_f = *rho; + size_t proof_idx = 0; + ecmult_x_cb_data x_cb_data; + ecmult_r_cb_data r_cb_data; + size_t g_len = n_vec_len, h_len = l_vec_len; + const size_t G_GENS_LEN = g_len; + +#ifdef VERIFY + { + size_t log_g_len_ver, log_h_len_ver, num_rounds_ver; + VERIFY_CHECK(g_len > 0 && h_len > 0); /* Precondition for secp256k1_bppp_log2() */ + log_g_len_ver = secp256k1_bppp_log2(g_len); + log_h_len_ver = secp256k1_bppp_log2(h_len); + num_rounds_ver = log_g_len_ver > log_h_len_ver ? log_g_len_ver : log_h_len_ver; + /* Check proof sizes.*/ + VERIFY_CHECK(*proof_len >= 65 * num_rounds_ver + 64); + VERIFY_CHECK(g_vec_len == (n_vec_len + l_vec_len) && l_vec_len == c_vec_len); + VERIFY_CHECK(secp256k1_is_power_of_two(n_vec_len) && secp256k1_is_power_of_two(c_vec_len)); + } +#else + (void)g_vec_len; + (void)c_vec_len; +#endif + + x_cb_data.n = n_vec; + x_cb_data.g = g_vec; + x_cb_data.l = l_vec; + x_cb_data.G_GENS_LEN = G_GENS_LEN; + + r_cb_data.n1 = n_vec; + r_cb_data.g1 = g_vec; + r_cb_data.l1 = l_vec; + r_cb_data.G_GENS_LEN = G_GENS_LEN; + secp256k1_scalar_sqr(&mu_f, &rho_f); + + + while (g_len > 1 || h_len > 1) { + size_t i, num_points; + secp256k1_scalar mu_sq, rho_inv, c0_l1, c1_l0, x_v, c1_l1, r_v; + secp256k1_gej rj, xj; + secp256k1_ge r_ge, x_ge; + secp256k1_scalar gamma; + + secp256k1_scalar_inverse_var(&rho_inv, &rho_f); + secp256k1_scalar_sqr(&mu_sq, &mu_f); + + /* Compute the X commitment X = WIP(rho_inv*n0,n1)_mu2 * g + r + */ + secp256k1_scalar_inner_product(&c0_l1, c_vec, 0, l_vec, 1, 2, h_len/2); + secp256k1_scalar_inner_product(&c1_l0, c_vec, 1, l_vec, 0, 2, h_len/2); + secp256k1_weighted_scalar_inner_product(&x_v, n_vec, 0, n_vec, 1, 2, g_len/2, &mu_sq); + secp256k1_scalar_mul(&x_v, &x_v, &rho_inv); + secp256k1_scalar_add(&x_v, &x_v, &x_v); + secp256k1_scalar_add(&x_v, &x_v, &c0_l1); + secp256k1_scalar_add(&x_v, &x_v, &c1_l0); + + x_cb_data.rho = &rho_f; + x_cb_data.rho_inv = &rho_inv; + x_cb_data.n_len = g_len >= 2 ? g_len : 0; + num_points = x_cb_data.n_len + (h_len >= 2 ? h_len : 0); + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &xj, &x_v, ecmult_x_cb, (void*)&x_cb_data, num_points)) { + return 0; + } + + secp256k1_weighted_scalar_inner_product(&r_v, n_vec, 1, n_vec, 1, 2, g_len/2, &mu_sq); + secp256k1_scalar_inner_product(&c1_l1, c_vec, 1, l_vec, 1, 2, h_len/2); + secp256k1_scalar_add(&r_v, &r_v, &c1_l1); + + r_cb_data.n_len = g_len/2; + num_points = r_cb_data.n_len + h_len/2; + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &rj, &r_v, ecmult_r_cb, (void*)&r_cb_data, num_points)) { + return 0; + } + + secp256k1_ge_set_gej_var(&x_ge, &xj); + secp256k1_ge_set_gej_var(&r_ge, &rj); + secp256k1_bppp_serialize_points(&proof[proof_idx], &x_ge, &r_ge); + proof_idx += 65; + + /* Obtain challenge gamma for the the next round */ + secp256k1_sha256_write(transcript, &proof[proof_idx - 65], 65); + secp256k1_bppp_challenge_scalar(&gamma, transcript, 0); + + if (g_len > 1) { + for (i = 0; i < g_len; i = i + 2) { + secp256k1_scalar nl, nr; + secp256k1_gej gl, gr; + secp256k1_scalar_mul(&nl, &n_vec[i], &rho_inv); + secp256k1_scalar_mul(&nr, &n_vec[i + 1], &gamma); + secp256k1_scalar_add(&n_vec[i/2], &nl, &nr); + + secp256k1_gej_set_ge(&gl, &g_vec[i]); + secp256k1_ecmult(&gl, &gl, &rho_f, NULL); + secp256k1_gej_set_ge(&gr, &g_vec[i + 1]); + secp256k1_ecmult(&gr, &gr, &gamma, NULL); + secp256k1_gej_add_var(&gl, &gl, &gr, NULL); + secp256k1_ge_set_gej_var(&g_vec[i/2], &gl); + } + } + + if (h_len > 1) { + for (i = 0; i < h_len; i = i + 2) { + secp256k1_scalar temp1; + secp256k1_gej grj; + secp256k1_scalar_mul(&temp1, &c_vec[i + 1], &gamma); + secp256k1_scalar_add(&c_vec[i/2], &c_vec[i], &temp1); + + secp256k1_scalar_mul(&temp1, &l_vec[i + 1], &gamma); + secp256k1_scalar_add(&l_vec[i/2], &l_vec[i], &temp1); + + secp256k1_gej_set_ge(&grj, &g_vec[G_GENS_LEN + i + 1]); + secp256k1_ecmult(&grj, &grj, &gamma, NULL); + secp256k1_gej_add_ge_var(&grj, &grj, &g_vec[G_GENS_LEN + i], NULL); + secp256k1_ge_set_gej_var(&g_vec[G_GENS_LEN + i/2], &grj); + } + } + g_len = g_len / 2; + h_len = h_len / 2; + rho_f = mu_f; + mu_f = mu_sq; + } + + secp256k1_scalar_get_b32(&proof[proof_idx], &n_vec[0]); + secp256k1_scalar_get_b32(&proof[proof_idx + 32], &l_vec[0]); + proof_idx += 64; + *proof_len = proof_idx; + return 1; +} + +typedef struct ec_mult_verify_cb_data1 { + const unsigned char *proof; + const secp256k1_ge *commit; + const secp256k1_scalar *gammas; +} ec_mult_verify_cb_data1; + +static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ec_mult_verify_cb_data1 *data = (ec_mult_verify_cb_data1*) cbdata; + if (idx == 0) { + *pt = *data->commit; + secp256k1_scalar_set_int(sc, 1); + return 1; + } + idx -= 1; + if (idx % 2 == 0) { + idx /= 2; + *sc = data->gammas[idx]; + if (!secp256k1_bppp_parse_one_of_points(pt, &data->proof[65*idx], 0)) { + return 0; + } + } else { + secp256k1_scalar neg_one; + idx /= 2; + secp256k1_scalar_set_int(&neg_one, 1); + secp256k1_scalar_negate(&neg_one, &neg_one); + *sc = data->gammas[idx]; + secp256k1_scalar_sqr(sc, sc); + secp256k1_scalar_add(sc, sc, &neg_one); + if (!secp256k1_bppp_parse_one_of_points(pt, &data->proof[65*idx], 1)) { + return 0; + } + } + return 1; +} + +typedef struct ec_mult_verify_cb_data2 { + const secp256k1_scalar *s_g; + const secp256k1_scalar *s_h; + const secp256k1_ge *g_vec; + size_t g_vec_len; +} ec_mult_verify_cb_data2; + +static int ec_mult_verify_cb2(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ec_mult_verify_cb_data2 *data = (ec_mult_verify_cb_data2*) cbdata; + if (idx < data->g_vec_len) { + *sc = data->s_g[idx]; + } else { + *sc = data->s_h[idx - data->g_vec_len]; + } + *pt = data->g_vec[idx]; + return 1; +} + +/* Verify the proof. This function modifies the generators, c_vec and the challenge r. The + caller should make sure to back them up if they need to be reused. +*/ +static int secp256k1_bppp_rangeproof_norm_product_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const unsigned char* proof, + size_t proof_len, + secp256k1_sha256* transcript, + const secp256k1_scalar* rho, + const secp256k1_bppp_generators* g_vec, + size_t g_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + secp256k1_scalar rho_f, mu_f, v, n, l, rho_inv, h_c; + secp256k1_scalar *gammas, *s_g, *s_h, *rho_inv_pows; + secp256k1_gej res1, res2; + size_t i = 0, scratch_checkpoint; + int overflow; + size_t log_g_len, log_h_len; + size_t n_rounds; + size_t h_len = c_vec_len; + + if (g_len == 0 || c_vec_len == 0) { + return 0; + } + log_g_len = secp256k1_bppp_log2(g_len); + log_h_len = secp256k1_bppp_log2(c_vec_len); + n_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; + + if (g_vec->n != (h_len + g_len) || (proof_len != 65 * n_rounds + 64)) { + return 0; + } + + if (!secp256k1_is_power_of_two(g_len) || !secp256k1_is_power_of_two(h_len)) { + return 0; + } + + secp256k1_scalar_set_b32(&n, &proof[n_rounds*65], &overflow); /* n */ + if (overflow) return 0; + secp256k1_scalar_set_b32(&l, &proof[n_rounds*65 + 32], &overflow); /* l */ + if (overflow) return 0; + if (secp256k1_scalar_is_zero(rho)) return 0; + + /* Collect the gammas in a new vector */ + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + gammas = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_rounds * sizeof(secp256k1_scalar)); + s_g = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); + s_h = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + rho_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, log_g_len * sizeof(secp256k1_scalar)); + if (gammas == NULL || s_g == NULL || s_h == NULL || rho_inv_pows == NULL) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + /* Compute powers of rho_inv. Later used in g_factor computations*/ + secp256k1_scalar_inverse_var(&rho_inv, rho); + secp256k1_bppp_powers_of_rho(rho_inv_pows, &rho_inv, log_g_len); + + /* Compute rho_f = rho^(2^log_g_len) */ + rho_f = *rho; + for (i = 0; i < log_g_len; i++) { + secp256k1_scalar_sqr(&rho_f, &rho_f); + } + + for (i = 0; i < n_rounds; i++) { + secp256k1_scalar gamma; + secp256k1_sha256_write(transcript, &proof[i * 65], 65); + secp256k1_bppp_challenge_scalar(&gamma, transcript, 0); + gammas[i] = gamma; + } + /* s_g[0] = n * \prod_{j=0}^{log_g_len - 1} rho^(2^j) + * = n * rho^(2^log_g_len - 1) + * = n * rho_f * rho_inv */ + secp256k1_scalar_mul(&s_g[0], &n, &rho_f); + secp256k1_scalar_mul(&s_g[0], &s_g[0], &rho_inv); + for (i = 1; i < g_len; i++) { + size_t log_i = secp256k1_bppp_log2(i); + size_t nearest_pow_of_two = (size_t)1 << log_i; + /* This combines the two multiplications of gammas and rho_invs in a + * single loop. + * s_g[i] = s_g[i - nearest_pow_of_two] + * * e[log_i] * rho_inv^(2^log_i) */ + secp256k1_scalar_mul(&s_g[i], &s_g[i - nearest_pow_of_two], &gammas[log_i]); + secp256k1_scalar_mul(&s_g[i], &s_g[i], &rho_inv_pows[log_i]); + } + s_h[0] = l; + secp256k1_scalar_set_int(&h_c, 0); + for (i = 1; i < h_len; i++) { + size_t log_i = secp256k1_bppp_log2(i); + size_t nearest_pow_of_two = (size_t)1 << log_i; + secp256k1_scalar_mul(&s_h[i], &s_h[i - nearest_pow_of_two], &gammas[log_i]); + } + secp256k1_scalar_inner_product(&h_c, c_vec, 0 /* a_offset */ , s_h, 0 /* b_offset */, 1 /* step */, h_len); + /* Compute v = n*n*mu_f + l*h_c where mu_f = rho_f^2 */ + secp256k1_scalar_sqr(&mu_f, &rho_f); + secp256k1_scalar_mul(&v, &n, &n); + secp256k1_scalar_mul(&v, &v, &mu_f); + secp256k1_scalar_add(&v, &v, &h_c); + + { + ec_mult_verify_cb_data1 data; + data.proof = proof; + data.commit = commit; + data.gammas = gammas; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res1, NULL, ec_mult_verify_cb1, &data, 2*n_rounds + 1)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + } + { + ec_mult_verify_cb_data2 data; + data.g_vec = g_vec->gens; + data.g_vec_len = g_len; + data.s_g = s_g; + data.s_h = s_h; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res2, &v, ec_mult_verify_cb2, &data, g_len + h_len)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + } + + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + + return secp256k1_gej_eq_var(&res1, &res2); +} +#endif diff --git a/src/modules/bppp/bppp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h new file mode 100644 index 00000000..5e212231 --- /dev/null +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -0,0 +1,35 @@ +/********************************************************************** + * Copyright (c) 2022 Sanket Kanjalkar * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#ifndef SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_H +#define SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_H + +#include "../../group.h" +#include "../../scalar.h" +#include "bppp_util.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("Bulletproofs_pp/v0/commitment")||SHA256("Bulletproofs_pp/v0/commitment"). + */ +static void secp256k1_bppp_sha256_tagged_commitment_init(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0x52fc8185ul, 0x0e7debf0ul, 0xb0967270ul, 0x6f5abfe1ul, + 0x822bdec0ul, 0x36db8beful, 0x03d9e1f1ul, 0x8a5cef6ful + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +/* Obtain a challenge scalar from the current transcript.*/ +static void secp256k1_bppp_challenge_scalar(secp256k1_scalar* ch, const secp256k1_sha256 *transcript, uint64_t idx) { + unsigned char buf[32]; + secp256k1_sha256 sha = *transcript; + secp256k1_bppp_le64(buf, idx); + secp256k1_sha256_write(&sha, buf, 8); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_clear(&sha); + secp256k1_scalar_set_b32(ch, buf, NULL); +} + +#endif diff --git a/src/modules/bppp/bppp_util.h b/src/modules/bppp/bppp_util.h new file mode 100644 index 00000000..ec979c69 --- /dev/null +++ b/src/modules/bppp/bppp_util.h @@ -0,0 +1,83 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_BPPP_UTIL_H +#define SECP256K1_MODULE_BPPP_UTIL_H + +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../eckey.h" + +/* Outputs a pair of points, amortizing the parity byte between them + * Assumes both points' coordinates have been normalized. + */ +static void secp256k1_bppp_serialize_points(unsigned char *output, secp256k1_ge *lpt, secp256k1_ge *rpt) { + unsigned char tmp[33]; + secp256k1_ge_serialize_ext(tmp, lpt); + output[0] = (tmp[0] & 1) << 1; + memcpy(&output[1], &tmp[1], 32); + secp256k1_ge_serialize_ext(tmp, rpt); + output[0] |= (tmp[0] & 1); + memcpy(&output[33], &tmp[1], 32); +} + +static int secp256k1_bppp_parse_one_of_points(secp256k1_ge *pt, const unsigned char *in65, int idx) { + unsigned char tmp[33] = { 0 }; + if (in65[0] > 3) { + return 0; + } + /* Check if the input array encodes the point at infinity */ + if ((secp256k1_memcmp_var(tmp, &in65[1 + 32*idx], 32)) != 0) { + tmp[0] = 2 | ((in65[0] & (2 - idx)) >> (1 - idx)); + memcpy(&tmp[1], &in65[1 + 32*idx], 32); + } else { + /* If we're parsing the point at infinity, enforce that the sign bit is + * 0. */ + if ((in65[0] & (2 - idx)) != 0) { + return 0; + } + } + return secp256k1_ge_parse_ext(pt, tmp); +} + +/* Outputs a serialized point in compressed form. Returns 0 at point at infinity. +*/ +static int secp256k1_bppp_serialize_pt(unsigned char *output, secp256k1_ge *lpt) { + if (secp256k1_ge_is_infinity(lpt)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(lpt, output); + return 1; +} + +/* little-endian encodes a uint64 */ +static void secp256k1_bppp_le64(unsigned char *output, const uint64_t n) { + output[0] = n; + output[1] = n >> 8; + output[2] = n >> 16; + output[3] = n >> 24; + output[4] = n >> 32; + output[5] = n >> 40; + output[6] = n >> 48; + output[7] = n >> 56; +} + +/* Check if n is power of two*/ +static int secp256k1_is_power_of_two(size_t n) { + return n > 0 && (n & (n - 1)) == 0; +} + +/* Compute the log2 of n. n must NOT be 0. If n is not a power of two, it + * returns the largest `k` such that 2^k <= n. Assumes 0 < n < 2^64. In + * Bulletproofs, this is bounded by len of input vectors which can be safely + * assumed to be less than 2^64. +*/ +static size_t secp256k1_bppp_log2(size_t n) { + return 64 - 1 - secp256k1_clz64_var((uint64_t)n); +} + +#endif diff --git a/src/modules/bppp/main.h b/src/modules/bppp/main.h new file mode 100644 index 00000000..47405f45 --- /dev/null +++ b/src/modules/bppp/main.h @@ -0,0 +1,13 @@ +#ifndef SECP256K1_MODULE_BPPP_MAIN_H +#define SECP256K1_MODULE_BPPP_MAIN_H + +/* this type must be completed before any of the modules/bppp includes */ +struct secp256k1_bppp_generators { + size_t n; + /* n total generators; includes both G_i and H_i */ + /* For BP++, the generators are G_i from [0..(n - 8)] and the last 8 values + are generators are for H_i */ + secp256k1_ge* gens; +}; + +#endif diff --git a/src/modules/bppp/main_impl.h b/src/modules/bppp/main_impl.h new file mode 100644 index 00000000..e8e9e30a --- /dev/null +++ b/src/modules/bppp/main_impl.h @@ -0,0 +1,115 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_BPPP_MAIN_IMPL_H +#define SECP256K1_MODULE_BPPP_MAIN_IMPL_H + +#include "../../../include/secp256k1_bppp.h" +#include "../../../include/secp256k1_generator.h" +#include "../generator/main_impl.h" /* for generator_{load, save} */ +#include "../../hash.h" +#include "../../util.h" +#include "../bppp/main.h" +#include "../bppp/bppp_norm_product_impl.h" + +secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_context *ctx, size_t n) { + secp256k1_bppp_generators *ret; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned char seed[64]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + + ret = checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->gens = checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + ret->n = n; + + secp256k1_fe_get_b32(&seed[0], &secp256k1_ge_const_g.x); + secp256k1_fe_get_b32(&seed[32], &secp256k1_ge_const_g.y); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed, 64); + for (i = 0; i < n; i++) { + secp256k1_generator gen; + unsigned char tmp[32] = { 0 }; + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, tmp)); + secp256k1_generator_load(&ret->gens[i], &gen); + } + + return ret; +} + +secp256k1_bppp_generators* secp256k1_bppp_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { + size_t n = data_len / 33; + secp256k1_bppp_generators* ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(data != NULL); + + if (data_len % 33 != 0) { + return NULL; + } + + ret = checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->n = n; + ret->gens = checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + + while (n--) { + secp256k1_generator gen; + if (!secp256k1_generator_parse(ctx, &gen, &data[33 * n])) { + free(ret->gens); + free(ret); + return NULL; + } + secp256k1_generator_load(&ret->gens[n], &gen); + } + return ret; +} + +int secp256k1_bppp_generators_serialize(const secp256k1_context* ctx, const secp256k1_bppp_generators* gens, unsigned char* data, size_t *data_len) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(data != NULL); + ARG_CHECK(data_len != NULL); + ARG_CHECK(*data_len >= 33 * gens->n); + + memset(data, 0, *data_len); + for (i = 0; i < gens->n; i++) { + secp256k1_generator gen; + secp256k1_generator_save(&gen, &gens->gens[i]); + secp256k1_generator_serialize(ctx, &data[33 * i], &gen); + } + + *data_len = 33 * gens->n; + return 1; +} + +void secp256k1_bppp_generators_destroy(const secp256k1_context* ctx, secp256k1_bppp_generators *gens) { + VERIFY_CHECK(ctx != NULL); + (void) ctx; + if (gens != NULL) { + free(gens->gens); + free(gens); + } +} + +#endif diff --git a/src/modules/bppp/test_vectors/prove.h b/src/modules/bppp/test_vectors/prove.h new file mode 100644 index 00000000..06559b38 --- /dev/null +++ b/src/modules/bppp/test_vectors/prove.h @@ -0,0 +1,47 @@ +static const unsigned char prove_vector_gens[264] = { 0x03, 0xAF, 0x2C, 0x40, 0xAD, 0x03, 0xCD, 0xC5, 0x76, 0x8C, 0x07, 0x1E, 0x58, 0xD6, 0x8C, 0x73, 0x45, 0xBA, 0xEB, 0xB5, 0x3F, 0x40, 0xFA, 0x8B, 0xBF, 0x73, 0x6E, 0x7B, 0x4A, 0x54, 0x06, 0xED, 0x32, 0x03, 0xCC, 0x11, 0x19, 0x22, 0x2C, 0xA1, 0x0A, 0x45, 0x23, 0xAF, 0x9B, 0x40, 0x0D, 0xA4, 0x5E, 0x06, 0x24, 0xF4, 0x5F, 0x07, 0x89, 0x88, 0xCD, 0x71, 0xAE, 0x77, 0xC1, 0xF5, 0x87, 0x4E, 0xFC, 0xA5, 0x03, 0xDE, 0x61, 0xB1, 0x8F, 0x2C, 0xAC, 0x18, 0xF5, 0xE4, 0x06, 0x8F, 0x65, 0x55, 0xA1, 0x30, 0x5E, 0xF5, 0xF4, 0x84, 0xED, 0x6B, 0xDD, 0xC2, 0xCC, 0xE8, 0x51, 0x38, 0xB8, 0xA5, 0x4C, 0x43, 0xBD, 0x02, 0xA5, 0xF9, 0x8C, 0x1F, 0x82, 0x2D, 0xC6, 0xF3, 0x0F, 0x53, 0xDB, 0x74, 0x77, 0xC7, 0x91, 0x04, 0xB0, 0xB1, 0xA6, 0x17, 0xB2, 0x91, 0xF4, 0x8B, 0x93, 0x3E, 0xBB, 0x73, 0x15, 0x3E, 0x5A, 0xD1, 0x02, 0x44, 0xF5, 0xC6, 0x4E, 0x77, 0x60, 0x81, 0x83, 0xFF, 0xC2, 0x8E, 0x06, 0xFE, 0x67, 0x0C, 0x9A, 0x4B, 0xF2, 0x34, 0xB9, 0xEA, 0xE9, 0x37, 0xDA, 0x30, 0xE2, 0x32, 0x27, 0xF3, 0x88, 0x5F, 0x2A, 0x02, 0x1D, 0x49, 0x5D, 0x04, 0xED, 0x61, 0x95, 0x37, 0xDD, 0x95, 0xB1, 0x4F, 0x64, 0x0E, 0x1E, 0xFB, 0x47, 0x9F, 0xA7, 0xD7, 0xE0, 0x7A, 0xB1, 0x02, 0x81, 0x95, 0xD1, 0xA5, 0x7E, 0xB2, 0x74, 0x8F, 0x03, 0x26, 0xA5, 0xEC, 0xE9, 0x71, 0x46, 0x37, 0xAC, 0x3D, 0x74, 0x84, 0x26, 0xCB, 0x7C, 0xE8, 0xFE, 0x4E, 0xB0, 0x6D, 0x70, 0x3D, 0x00, 0x10, 0x1A, 0x3A, 0x5B, 0xB8, 0xAA, 0x29, 0x59, 0x93, 0x15, 0x03, 0xE1, 0xA5, 0x39, 0x44, 0x75, 0x16, 0x28, 0x5F, 0xBA, 0x69, 0xA2, 0x4A, 0x2A, 0xC3, 0x5B, 0x63, 0x1F, 0x40, 0x10, 0x36, 0xF9, 0x4C, 0xD2, 0x76, 0x0F, 0xCF, 0x7F, 0x50, 0x30, 0x6E, 0x2B, 0x1D }; +static const unsigned char prove_vector_0_n_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F } }; +static secp256k1_scalar prove_vector_0_n_vec[1]; +static const unsigned char prove_vector_0_l_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E } }; +static secp256k1_scalar prove_vector_0_l_vec[1]; +static const unsigned char prove_vector_0_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_0_c_vec[1]; +static const unsigned char prove_vector_0_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char prove_vector_0_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int prove_vector_0_result = 1; +static const unsigned char prove_vector_1_n_vec32[2][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }; +static secp256k1_scalar prove_vector_1_n_vec[2]; +static const unsigned char prove_vector_1_l_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B } }; +static secp256k1_scalar prove_vector_1_l_vec[4]; +static const unsigned char prove_vector_1_c_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x30 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D } }; +static secp256k1_scalar prove_vector_1_c_vec[4]; +static const unsigned char prove_vector_1_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char prove_vector_1_proof[] = { 0x00, 0xD2, 0xEC, 0xE2, 0x53, 0x97, 0x28, 0x68, 0x22, 0x59, 0x34, 0xEF, 0xE4, 0x7B, 0x87, 0x4D, 0xE9, 0x57, 0xD5, 0xB7, 0xC7, 0x72, 0xF4, 0xC9, 0xEA, 0x66, 0x14, 0x59, 0xE1, 0xA9, 0xD5, 0xB2, 0x10, 0xDF, 0xE2, 0xFF, 0xF5, 0xA4, 0x38, 0x6B, 0xFE, 0x36, 0x89, 0xE4, 0x9D, 0x90, 0x9F, 0x71, 0x19, 0xE6, 0xA3, 0x1E, 0xAA, 0xAA, 0x4E, 0xFE, 0xC2, 0xD3, 0x37, 0xBB, 0xDE, 0xDB, 0x46, 0x43, 0xC2, 0x01, 0x42, 0x5F, 0xFC, 0xC6, 0x25, 0xA0, 0xB4, 0xF0, 0x76, 0x99, 0xF4, 0x7C, 0xE9, 0x83, 0x82, 0xED, 0x7C, 0x95, 0xBA, 0xD0, 0xE6, 0x5B, 0x88, 0xFD, 0x38, 0xEA, 0x23, 0x54, 0xD4, 0xBD, 0xD4, 0x37, 0xB8, 0x2B, 0x49, 0xAF, 0x81, 0xFD, 0xBE, 0x88, 0xB2, 0xE5, 0x3F, 0xF4, 0x30, 0x52, 0x00, 0x63, 0x9D, 0xAE, 0x82, 0x44, 0xE9, 0x62, 0x87, 0x2A, 0x23, 0x89, 0x10, 0xE4, 0x9A, 0x64, 0x9F, 0x71, 0xD9, 0x32, 0x57, 0x3B, 0xCB, 0xAC, 0x30, 0xAE, 0x71, 0x61, 0xE9, 0x50, 0x1F, 0xCB, 0x49, 0x9C, 0x52, 0xBA, 0x0C, 0xC4, 0x00, 0x58, 0x73, 0x63, 0xD3, 0x42, 0xDE, 0x42, 0x5E, 0xC5, 0x97, 0xE5, 0xDA, 0x88, 0x76, 0x49, 0x6C, 0x8B, 0x92, 0x99, 0xEE, 0xD0, 0xA9, 0xEB, 0x6E, 0xCA, 0xE1, 0x93, 0x81, 0x56, 0x2E, 0xCA, 0xF3, 0x8E, 0xF0, 0x04, 0xD2, 0x96, 0xD8, 0xDB, 0xEE, 0xEE, 0x1C, 0x44 }; +static const int prove_vector_1_result = 1; +static const unsigned char prove_vector_2_n_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B } }; +static secp256k1_scalar prove_vector_2_n_vec[4]; +static const unsigned char prove_vector_2_l_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E } }; +static secp256k1_scalar prove_vector_2_l_vec[1]; +static const unsigned char prove_vector_2_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_2_c_vec[1]; +static const unsigned char prove_vector_2_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char prove_vector_2_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int prove_vector_2_result = 1; +static const unsigned char prove_vector_3_n_vec32[1][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static secp256k1_scalar prove_vector_3_n_vec[1]; +static const unsigned char prove_vector_3_l_vec32[1][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static secp256k1_scalar prove_vector_3_l_vec[1]; +static const unsigned char prove_vector_3_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_3_c_vec[1]; +static const unsigned char prove_vector_3_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char prove_vector_3_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const int prove_vector_3_result = 1; +static const unsigned char prove_vector_4_n_vec32[2][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static secp256k1_scalar prove_vector_4_n_vec[2]; +static const unsigned char prove_vector_4_l_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A } }; +static secp256k1_scalar prove_vector_4_l_vec[1]; +static const unsigned char prove_vector_4_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_4_c_vec[1]; +static const unsigned char prove_vector_4_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char prove_vector_4_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int prove_vector_4_result = 1; + diff --git a/src/modules/bppp/test_vectors/verify.h b/src/modules/bppp/test_vectors/verify.h new file mode 100644 index 00000000..9ab43fd0 --- /dev/null +++ b/src/modules/bppp/test_vectors/verify.h @@ -0,0 +1,93 @@ +static const unsigned char verify_vector_gens[264] = { 0x03, 0xAF, 0x2C, 0x40, 0xAD, 0x03, 0xCD, 0xC5, 0x76, 0x8C, 0x07, 0x1E, 0x58, 0xD6, 0x8C, 0x73, 0x45, 0xBA, 0xEB, 0xB5, 0x3F, 0x40, 0xFA, 0x8B, 0xBF, 0x73, 0x6E, 0x7B, 0x4A, 0x54, 0x06, 0xED, 0x32, 0x03, 0xCC, 0x11, 0x19, 0x22, 0x2C, 0xA1, 0x0A, 0x45, 0x23, 0xAF, 0x9B, 0x40, 0x0D, 0xA4, 0x5E, 0x06, 0x24, 0xF4, 0x5F, 0x07, 0x89, 0x88, 0xCD, 0x71, 0xAE, 0x77, 0xC1, 0xF5, 0x87, 0x4E, 0xFC, 0xA5, 0x03, 0xDE, 0x61, 0xB1, 0x8F, 0x2C, 0xAC, 0x18, 0xF5, 0xE4, 0x06, 0x8F, 0x65, 0x55, 0xA1, 0x30, 0x5E, 0xF5, 0xF4, 0x84, 0xED, 0x6B, 0xDD, 0xC2, 0xCC, 0xE8, 0x51, 0x38, 0xB8, 0xA5, 0x4C, 0x43, 0xBD, 0x02, 0xA5, 0xF9, 0x8C, 0x1F, 0x82, 0x2D, 0xC6, 0xF3, 0x0F, 0x53, 0xDB, 0x74, 0x77, 0xC7, 0x91, 0x04, 0xB0, 0xB1, 0xA6, 0x17, 0xB2, 0x91, 0xF4, 0x8B, 0x93, 0x3E, 0xBB, 0x73, 0x15, 0x3E, 0x5A, 0xD1, 0x02, 0x44, 0xF5, 0xC6, 0x4E, 0x77, 0x60, 0x81, 0x83, 0xFF, 0xC2, 0x8E, 0x06, 0xFE, 0x67, 0x0C, 0x9A, 0x4B, 0xF2, 0x34, 0xB9, 0xEA, 0xE9, 0x37, 0xDA, 0x30, 0xE2, 0x32, 0x27, 0xF3, 0x88, 0x5F, 0x2A, 0x02, 0x1D, 0x49, 0x5D, 0x04, 0xED, 0x61, 0x95, 0x37, 0xDD, 0x95, 0xB1, 0x4F, 0x64, 0x0E, 0x1E, 0xFB, 0x47, 0x9F, 0xA7, 0xD7, 0xE0, 0x7A, 0xB1, 0x02, 0x81, 0x95, 0xD1, 0xA5, 0x7E, 0xB2, 0x74, 0x8F, 0x03, 0x26, 0xA5, 0xEC, 0xE9, 0x71, 0x46, 0x37, 0xAC, 0x3D, 0x74, 0x84, 0x26, 0xCB, 0x7C, 0xE8, 0xFE, 0x4E, 0xB0, 0x6D, 0x70, 0x3D, 0x00, 0x10, 0x1A, 0x3A, 0x5B, 0xB8, 0xAA, 0x29, 0x59, 0x93, 0x15, 0x03, 0xE1, 0xA5, 0x39, 0x44, 0x75, 0x16, 0x28, 0x5F, 0xBA, 0x69, 0xA2, 0x4A, 0x2A, 0xC3, 0x5B, 0x63, 0x1F, 0x40, 0x10, 0x36, 0xF9, 0x4C, 0xD2, 0x76, 0x0F, 0xCF, 0x7F, 0x50, 0x30, 0x6E, 0x2B, 0x1D }; +static const unsigned char verify_vector_0_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_0_n_vec_len = 1; +static const unsigned char verify_vector_0_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_0_c_vec[1]; +static const unsigned char verify_vector_0_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_0_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_0_result = 1; +static const unsigned char verify_vector_1_commit33[33] = { 0x02, 0x6C, 0x09, 0xD7, 0x06, 0x2D, 0x1C, 0x07, 0x0A, 0x64, 0x34, 0x82, 0xF6, 0x46, 0x03, 0xEB, 0x24, 0x3E, 0x54, 0x0F, 0xDA, 0xAF, 0x3A, 0x69, 0x5F, 0x86, 0xB6, 0xD2, 0xC2, 0x06, 0xE9, 0x49, 0xC7 }; +static const size_t verify_vector_1_n_vec_len = 1; +static const unsigned char verify_vector_1_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_1_c_vec[1]; +static const unsigned char verify_vector_1_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_1_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_1_result = 0; +static const unsigned char verify_vector_2_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_2_n_vec_len = 1; +static const unsigned char verify_vector_2_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_2_c_vec[1]; +static const unsigned char verify_vector_2_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_2_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; +static const int verify_vector_2_result = 0; +static const unsigned char verify_vector_3_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_3_n_vec_len = 1; +static const unsigned char verify_vector_3_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_3_c_vec[1]; +static const unsigned char verify_vector_3_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_3_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; +static const int verify_vector_3_result = 0; +static const unsigned char verify_vector_4_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_4_n_vec_len = 1; +static const unsigned char verify_vector_4_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_4_c_vec[1]; +static const unsigned char verify_vector_4_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_4_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41 }; +static const int verify_vector_4_result = 0; +static const unsigned char verify_vector_5_commit33[33] = { 0x03, 0x83, 0x6A, 0xD4, 0x2D, 0xD2, 0x02, 0x49, 0xC8, 0x6E, 0x53, 0x22, 0x53, 0x24, 0xDA, 0x52, 0x08, 0xC0, 0x62, 0x4C, 0xCB, 0xB3, 0x13, 0xD7, 0x14, 0x59, 0x68, 0x47, 0x56, 0x00, 0xC0, 0x8D, 0xBA }; +static const size_t verify_vector_5_n_vec_len = 2; +static const unsigned char verify_vector_5_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_5_c_vec[1]; +static const unsigned char verify_vector_5_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x36 }; +static const unsigned char verify_vector_5_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x4C, 0xB9, 0xD4, 0x34, 0xA2, 0xD6, 0xD5, 0x4C, 0x0F, 0x2E, 0x2C, 0xE3, 0x82, 0x17, 0x48, 0x63, 0xE0, 0xAE, 0x6B, 0xD7, 0x64, 0x9D, 0x43, 0x2B, 0x6E, 0x6E, 0x1C, 0x62, 0x55, 0x4B, 0xC5, 0x73, 0x3D, 0x74, 0x7B, 0x78, 0x43, 0xF4, 0x8B, 0x7C, 0x84, 0x10, 0x00, 0x8B, 0x12, 0xAF, 0xA4, 0xF1, 0xF4, 0x01, 0x96, 0x21, 0x8B, 0xE9, 0x05, 0x01, 0xF8, 0x23, 0x7A, 0x8F, 0x66, 0xC9, 0xDE, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_5_result = 0; +static const unsigned char verify_vector_6_commit33[33] = { 0x03, 0xCF, 0x7F, 0x08, 0xF5, 0x8A, 0x06, 0x74, 0x5C, 0xDB, 0xCE, 0xC6, 0x51, 0xF3, 0xE5, 0xE4, 0xDC, 0xAD, 0xF4, 0x40, 0x3C, 0xFA, 0xE6, 0x78, 0xBE, 0x49, 0x2D, 0x90, 0xC8, 0xD0, 0x16, 0x3D, 0x78 }; +static const size_t verify_vector_6_n_vec_len = 2; +static const unsigned char verify_vector_6_c_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x30 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D } }; +static secp256k1_scalar verify_vector_6_c_vec[4]; +static const unsigned char verify_vector_6_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_6_proof[] = { 0x00, 0xD2, 0xEC, 0xE2, 0x53, 0x97, 0x28, 0x68, 0x22, 0x59, 0x34, 0xEF, 0xE4, 0x7B, 0x87, 0x4D, 0xE9, 0x57, 0xD5, 0xB7, 0xC7, 0x72, 0xF4, 0xC9, 0xEA, 0x66, 0x14, 0x59, 0xE1, 0xA9, 0xD5, 0xB2, 0x10, 0xDF, 0xE2, 0xFF, 0xF5, 0xA4, 0x38, 0x6B, 0xFE, 0x36, 0x89, 0xE4, 0x9D, 0x90, 0x9F, 0x71, 0x19, 0xE6, 0xA3, 0x1E, 0xAA, 0xAA, 0x4E, 0xFE, 0xC2, 0xD3, 0x37, 0xBB, 0xDE, 0xDB, 0x46, 0x43, 0xC2, 0x01, 0x42, 0x5F, 0xFC, 0xC6, 0x25, 0xA0, 0xB4, 0xF0, 0x76, 0x99, 0xF4, 0x7C, 0xE9, 0x83, 0x82, 0xED, 0x7C, 0x95, 0xBA, 0xD0, 0xE6, 0x5B, 0x88, 0xFD, 0x38, 0xEA, 0x23, 0x54, 0xD4, 0xBD, 0xD4, 0x37, 0xB8, 0x2B, 0x49, 0xAF, 0x81, 0xFD, 0xBE, 0x88, 0xB2, 0xE5, 0x3F, 0xF4, 0x30, 0x52, 0x00, 0x63, 0x9D, 0xAE, 0x82, 0x44, 0xE9, 0x62, 0x87, 0x2A, 0x23, 0x89, 0x10, 0xE4, 0x9A, 0x64, 0x9F, 0x71, 0xD9, 0x32, 0x57, 0x3B, 0xCB, 0xAC, 0x30, 0xAE, 0x71, 0x61, 0xE9, 0x50, 0x1F, 0xCB, 0x49, 0x9C, 0x52, 0xBA, 0x0C, 0xC4, 0x00, 0x58, 0x73, 0x63, 0xD3, 0x42, 0xDE, 0x42, 0x5E, 0xC5, 0x97, 0xE5, 0xDA, 0x88, 0x76, 0x49, 0x6C, 0x8B, 0x92, 0x99, 0xEE, 0xD0, 0xA9, 0xEB, 0x6E, 0xCA, 0xE1, 0x93, 0x81, 0x56, 0x2E, 0xCA, 0xF3, 0x8E, 0xF0, 0x04, 0xD2, 0x96, 0xD8, 0xDB, 0xEE, 0xEE, 0x1C, 0x44 }; +static const int verify_vector_6_result = 1; +static const unsigned char verify_vector_7_commit33[33] = { 0x02, 0x7A, 0xAA, 0xB2, 0x7E, 0xA5, 0x5B, 0x77, 0x08, 0xE5, 0x43, 0xB6, 0x22, 0x7F, 0xC9, 0xAC, 0x53, 0x10, 0x32, 0x61, 0x7B, 0x7D, 0xAC, 0xB1, 0xB6, 0xF6, 0xAC, 0xDE, 0x63, 0x79, 0x82, 0x9C, 0x24 }; +static const size_t verify_vector_7_n_vec_len = 4; +static const unsigned char verify_vector_7_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_7_c_vec[1]; +static const unsigned char verify_vector_7_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_7_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_7_result = 1; +static const unsigned char verify_vector_8_commit33[33] = { 0x02, 0x2D, 0x4F, 0xF9, 0xB7, 0x15, 0x22, 0xBC, 0xB0, 0x8B, 0xF8, 0xBA, 0x31, 0x0A, 0x80, 0x76, 0x7A, 0xE9, 0xA9, 0x83, 0x00, 0xBC, 0x5A, 0x01, 0xCC, 0xE9, 0x00, 0x83, 0x56, 0xEA, 0x77, 0xEB, 0x75 }; +static const size_t verify_vector_8_n_vec_len = 4; +static const unsigned char verify_vector_8_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_8_c_vec[1]; +static const unsigned char verify_vector_8_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_8_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_8_result = 0; +static const unsigned char verify_vector_9_commit33[33] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const size_t verify_vector_9_n_vec_len = 1; +static const unsigned char verify_vector_9_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_9_c_vec[1]; +static const unsigned char verify_vector_9_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_9_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const int verify_vector_9_result = 1; +static const unsigned char verify_vector_10_commit33[33] = { 0x03, 0x62, 0x8A, 0xC2, 0xF1, 0xF2, 0x00, 0xE0, 0x81, 0xBD, 0xA0, 0xA9, 0x6D, 0x25, 0x53, 0xB4, 0x17, 0xC1, 0x02, 0x93, 0x50, 0x3E, 0x91, 0xD4, 0xD1, 0x3A, 0x82, 0x89, 0x02, 0x24, 0x78, 0x49, 0xA5 }; +static const size_t verify_vector_10_n_vec_len = 2; +static const unsigned char verify_vector_10_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_10_c_vec[1]; +static const unsigned char verify_vector_10_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_10_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int verify_vector_10_result = 1; +static const unsigned char verify_vector_11_commit33[33] = { 0x03, 0x62, 0x8A, 0xC2, 0xF1, 0xF2, 0x00, 0xE0, 0x81, 0xBD, 0xA0, 0xA9, 0x6D, 0x25, 0x53, 0xB4, 0x17, 0xC1, 0x02, 0x93, 0x50, 0x3E, 0x91, 0xD4, 0xD1, 0x3A, 0x82, 0x89, 0x02, 0x24, 0x78, 0x49, 0xA5 }; +static const size_t verify_vector_11_n_vec_len = 2; +static const unsigned char verify_vector_11_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_11_c_vec[1]; +static const unsigned char verify_vector_11_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_11_proof[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int verify_vector_11_result = 0; +static const unsigned char verify_vector_12_commit33[33] = { 0x02, 0x7D, 0x5F, 0x4B, 0x11, 0xC0, 0xE4, 0x2E, 0x4C, 0x1B, 0x56, 0xAE, 0xF0, 0x5F, 0xAA, 0xD8, 0x77, 0x0C, 0x93, 0x71, 0xA2, 0x92, 0xF9, 0x89, 0xA2, 0xB4, 0x69, 0x9B, 0x46, 0x8A, 0x03, 0xF1, 0x50 }; +static const size_t verify_vector_12_n_vec_len = 0; +static const unsigned char verify_vector_12_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_12_c_vec[1]; +static const unsigned char verify_vector_12_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_12_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int verify_vector_12_result = 0; + diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h new file mode 100644 index 00000000..dda9a8c0 --- /dev/null +++ b/src/modules/bppp/tests_impl.h @@ -0,0 +1,677 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_BPPP_TEST_H +#define SECP256K1_MODULE_BPPP_TEST_H + +#include + +#include "../../../include/secp256k1_bppp.h" +#include "bppp_norm_product_impl.h" +#include "bppp_util.h" +#include "bppp_transcript_impl.h" +#include "test_vectors/verify.h" +#include "test_vectors/prove.h" +#include "../../unit_test.h" + +static void test_bppp_generators_api(void) { + secp256k1_bppp_generators *gens; + secp256k1_bppp_generators *gens_orig; + unsigned char gens_ser[330]; + size_t len = sizeof(gens_ser); + + /* Create */ + gens = secp256k1_bppp_generators_create(CTX, 10); + CHECK(gens != NULL); + gens_orig = gens; /* Preserve for round-trip test */ + + /* Serialize */ + CHECK_ILLEGAL(CTX, secp256k1_bppp_generators_serialize(CTX, NULL, gens_ser, &len)); + CHECK_ILLEGAL(CTX, secp256k1_bppp_generators_serialize(CTX, gens, NULL, &len)); + CHECK_ILLEGAL(CTX, secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, NULL)); + len = 0; + CHECK_ILLEGAL(CTX, secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); + len = sizeof(gens_ser) - 1; + CHECK_ILLEGAL(CTX, secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); + len = sizeof(gens_ser); + { + /* Output buffer can be greater than minimum needed */ + unsigned char gens_ser_tmp[331]; + size_t len_tmp = sizeof(gens_ser_tmp); + CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser_tmp, &len_tmp)); + CHECK(len_tmp == sizeof(gens_ser_tmp) - 1); + } + + /* Parse */ + CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); + CHECK_ILLEGAL_VOID(CTX, gens = secp256k1_bppp_generators_parse(CTX, NULL, sizeof(gens_ser)); + CHECK(gens == NULL)); + /* Not a multiple of 33 */ + gens = secp256k1_bppp_generators_parse(CTX, gens_ser, sizeof(gens_ser) - 1); + CHECK(gens == NULL); + gens = secp256k1_bppp_generators_parse(CTX, gens_ser, sizeof(gens_ser)); + CHECK(gens != NULL); + /* Not valid generators */ + memset(gens_ser, 1, sizeof(gens_ser)); + CHECK(secp256k1_bppp_generators_parse(CTX, gens_ser, sizeof(gens_ser)) == NULL); + + /* Check that round-trip succeeded */ + CHECK(gens->n == gens_orig->n); + for (len = 0; len < gens->n; len++) { + secp256k1_ge_eq_var(&gens->gens[len], &gens_orig->gens[len]); + } + + /* Destroy (we allow destroying a NULL context, it's just a noop. like free().) */ + secp256k1_bppp_generators_destroy(CTX, NULL); + secp256k1_bppp_generators_destroy(CTX, gens); + secp256k1_bppp_generators_destroy(CTX, gens_orig); +} + +static void test_bppp_generators_fixed(void) { + secp256k1_bppp_generators *gens = secp256k1_bppp_generators_create(CTX, 3); + unsigned char gens_ser[330]; + const unsigned char fixed_first_3[99] = { + 0x0b, + 0xb3, 0x4d, 0x5f, 0xa6, 0xb8, 0xf3, 0xd1, 0x38, + 0x49, 0xce, 0x51, 0x91, 0xb7, 0xf6, 0x76, 0x18, + 0xfe, 0x5b, 0xd1, 0x2a, 0x88, 0xb2, 0x0e, 0xac, + 0x33, 0x89, 0x45, 0x66, 0x7f, 0xb3, 0x30, 0x56, + 0x0a, + 0x62, 0x86, 0x15, 0x16, 0x92, 0x42, 0x10, 0x9e, + 0x9e, 0x64, 0xd4, 0xcb, 0x28, 0x81, 0x60, 0x9c, + 0x24, 0xb9, 0x89, 0x51, 0x2a, 0xd9, 0x01, 0xae, + 0xff, 0x75, 0x64, 0x9c, 0x37, 0x5d, 0xbd, 0x79, + 0x0a, + 0xed, 0xe0, 0x6e, 0x07, 0x5e, 0x79, 0xd0, 0xf7, + 0x7b, 0x03, 0x3e, 0xb9, 0xa9, 0x21, 0xa4, 0x5b, + 0x99, 0xf3, 0x9b, 0xee, 0xfe, 0xa0, 0x37, 0xa2, + 0x1f, 0xe9, 0xd7, 0x4f, 0x95, 0x8b, 0x10, 0xe2, + }; + size_t len; + + len = 99; + CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); + CHECK(secp256k1_memcmp_var(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + len = sizeof(gens_ser); + CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); + CHECK(secp256k1_memcmp_var(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + secp256k1_bppp_generators_destroy(CTX, gens); +} + +static void test_bppp_tagged_hash(void) { + unsigned char tag_data[] = {'B', 'u', 'l', 'l', 'e', 't', 'p', 'r', 'o', 'o', 'f', 's', '_', 'p', 'p', '/', 'v', '0', '/', 'c', 'o', 'm', 'm', 'i', 't', 'm', 'e', 'n', 't'}; + secp256k1_sha256 sha; + secp256k1_sha256 sha_cached; + unsigned char output[32]; + unsigned char output_cached[32]; + secp256k1_scalar s; + + secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data)); + secp256k1_bppp_sha256_tagged_commitment_init(&sha_cached); + secp256k1_sha256_finalize(&sha, output); + secp256k1_sha256_finalize(&sha_cached, output_cached); + CHECK(secp256k1_memcmp_var(output, output_cached, 32) == 0); + + { + unsigned char expected[32] = { 0x21, 0x2F, 0xB6, 0x4F, 0x9D, 0x8C, 0x3B, 0xC5, + 0xF6, 0x91, 0x15, 0xEE, 0x74, 0xF5, 0x12, 0x67, + 0x8A, 0x41, 0xC6, 0x85, 0x1A, 0x79, 0x14, 0xFC, + 0x48, 0x15, 0xC7, 0x2D, 0xF8, 0x63, 0x8F, 0x1B }; + secp256k1_bppp_sha256_tagged_commitment_init(&sha); + secp256k1_bppp_challenge_scalar(&s, &sha, 0); + secp256k1_scalar_get_b32(output, &s); + CHECK(secp256k1_memcmp_var(output, expected, sizeof(output)) == 0); + } + + { + unsigned char tmp[3] = {0, 1, 2}; + unsigned char expected[32] = { 0x8D, 0xAA, 0xB7, 0x7E, 0x3C, 0x6A, 0x9E, 0xEC, + 0x72, 0x7E, 0x3E, 0xB7, 0x10, 0x03, 0xF0, 0xE9, + 0x69, 0x4D, 0xAA, 0x96, 0xCE, 0x98, 0xBB, 0x39, + 0x1C, 0x2F, 0x7C, 0x2E, 0x1C, 0x17, 0x78, 0x6D }; + secp256k1_sha256_write(&sha, tmp, sizeof(tmp)); + secp256k1_bppp_challenge_scalar(&s, &sha, 0); + secp256k1_scalar_get_b32(output, &s); + CHECK(secp256k1_memcmp_var(output, expected, sizeof(output)) == 0); + } +} + +static void test_log_exp(void) { + CHECK(secp256k1_is_power_of_two(0) == 0); + CHECK(secp256k1_is_power_of_two(1) == 1); + CHECK(secp256k1_is_power_of_two(2) == 1); + CHECK(secp256k1_is_power_of_two(64) == 1); + CHECK(secp256k1_is_power_of_two(63) == 0); + CHECK(secp256k1_is_power_of_two(256) == 1); + + CHECK(secp256k1_bppp_log2(1) == 0); + CHECK(secp256k1_bppp_log2(2) == 1); + CHECK(secp256k1_bppp_log2(255) == 7); + CHECK(secp256k1_bppp_log2(256) == 8); + CHECK(secp256k1_bppp_log2(257) == 8); +} + +static void test_norm_util_helpers(void) { + secp256k1_scalar a_vec[4], b_vec[4], rho_pows[4], res, res2, mu, rho; + int i; + /* a = {1, 2, 3, 4} b = {5, 6, 7, 8}, mu = 4, rho = 2 */ + for (i = 0; i < 4; i++) { + secp256k1_scalar_set_int(&a_vec[i], i + 1); + secp256k1_scalar_set_int(&b_vec[i], i + 5); + } + secp256k1_scalar_set_int(&mu, 4); + secp256k1_scalar_set_int(&rho, 2); + secp256k1_scalar_inner_product(&res, a_vec, 0, b_vec, 0, 1, 4); + secp256k1_scalar_set_int(&res2, 70); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_scalar_inner_product(&res, a_vec, 0, b_vec, 1, 2, 2); + secp256k1_scalar_set_int(&res2, 30); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_scalar_inner_product(&res, a_vec, 1, b_vec, 0, 2, 2); + secp256k1_scalar_set_int(&res2, 38); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_scalar_inner_product(&res, a_vec, 1, b_vec, 1, 2, 2); + secp256k1_scalar_set_int(&res2, 44); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_weighted_scalar_inner_product(&res, a_vec, 0, a_vec, 0, 1, 4, &mu); + secp256k1_scalar_set_int(&res2, 4740); /*i*i*4^(i+1) */ + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_bppp_powers_of_rho(rho_pows, &rho, 4); + secp256k1_scalar_set_int(&res, 2); CHECK(secp256k1_scalar_eq(&res, &rho_pows[0])); + secp256k1_scalar_set_int(&res, 4); CHECK(secp256k1_scalar_eq(&res, &rho_pows[1])); + secp256k1_scalar_set_int(&res, 16); CHECK(secp256k1_scalar_eq(&res, &rho_pows[2])); + secp256k1_scalar_set_int(&res, 256); CHECK(secp256k1_scalar_eq(&res, &rho_pows[3])); +} + + +static void test_serialize_two_points_roundtrip(secp256k1_ge *X, secp256k1_ge *R) { + secp256k1_ge X_tmp, R_tmp; + unsigned char buf[65]; + secp256k1_bppp_serialize_points(buf, X, R); + CHECK(secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); + CHECK(secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1)); + secp256k1_ge_eq_var(X, &X_tmp); + secp256k1_ge_eq_var(R, &R_tmp); +} + +static void test_serialize_two_points(void) { + secp256k1_ge X, R; + int i; + + for (i = 0; i < COUNT; i++) { + testutil_random_ge_test(&X); + testutil_random_ge_test(&R); + test_serialize_two_points_roundtrip(&X, &R); + } + + for (i = 0; i < COUNT; i++) { + testutil_random_ge_test(&X); + secp256k1_ge_set_infinity(&R); + test_serialize_two_points_roundtrip(&X, &R); + } + + for (i = 0; i < COUNT; i++) { + secp256k1_ge_set_infinity(&X); + testutil_random_ge_test(&R); + test_serialize_two_points_roundtrip(&X, &R); + } + + secp256k1_ge_set_infinity(&X); + secp256k1_ge_set_infinity(&R); + test_serialize_two_points_roundtrip(&X, &R); + + /* Test invalid sign byte */ + { + secp256k1_ge X_tmp, R_tmp; + unsigned char buf[65]; + testutil_random_ge_test(&X); + testutil_random_ge_test(&R); + secp256k1_bppp_serialize_points(buf, &X, &R); + + /* buf is valid if 0 <= buf[0] < 4. */ + buf[0] = (unsigned char)testrandi64(4, 255); + CHECK(!secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); + CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 0)); + } + /* Check that sign bit is 0 for point at infinity */ + for (i = 0; i < COUNT; i++) { + secp256k1_ge X_tmp, R_tmp; + unsigned char buf[65]; + int expect; + testutil_random_ge_test(&X); + testutil_random_ge_test(&R); + secp256k1_bppp_serialize_points(buf, &X, &R); + memset(&buf[1], 0, 32); + if ((buf[0] & 2) == 0) { + expect = 1; + } else { + expect = 0; + } + CHECK(secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0) == expect); + CHECK(secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1)); + memset(&buf[33], 0, 32); + if ((buf[0] & 1) == 0) { + expect = 1; + } else { + expect = 0; + } + CHECK(secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1) == expect); + } +} + +static void secp256k1_norm_arg_commit_initial_data( + secp256k1_sha256* transcript, + const secp256k1_scalar* rho, + const secp256k1_bppp_generators* gens_vec, + size_t g_len, /* Same as n_vec_len, g_len + c_vec_len = gens->n */ + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + /* Commit to the initial public values */ + unsigned char ser_commit[33], ser_scalar[32], ser_le64[8]; + size_t i; + secp256k1_ge comm = *commit; + secp256k1_bppp_sha256_tagged_commitment_init(transcript); + secp256k1_fe_normalize(&comm.x); + secp256k1_fe_normalize(&comm.y); + CHECK(secp256k1_ge_is_infinity(&comm) == 0); + CHECK(secp256k1_bppp_serialize_pt(&ser_commit[0], &comm)); + secp256k1_sha256_write(transcript, ser_commit, 33); + secp256k1_scalar_get_b32(ser_scalar, rho); + secp256k1_sha256_write(transcript, ser_scalar, 32); + secp256k1_bppp_le64(ser_le64, g_len); + secp256k1_sha256_write(transcript, ser_le64, 8); + secp256k1_bppp_le64(ser_le64, gens_vec->n); + secp256k1_sha256_write(transcript, ser_le64, 8); + for (i = 0; i < gens_vec->n; i++) { + secp256k1_fe_normalize(&gens_vec->gens[i].x); + secp256k1_fe_normalize(&gens_vec->gens[i].y); + CHECK(secp256k1_bppp_serialize_pt(&ser_commit[0], &gens_vec->gens[i])); + secp256k1_sha256_write(transcript, ser_commit, 33); + } + secp256k1_bppp_le64(ser_le64, c_vec_len); + secp256k1_sha256_write(transcript, ser_le64, 8); + for (i = 0; i < c_vec_len; i++) { + secp256k1_scalar_get_b32(ser_scalar, &c_vec[i]); + secp256k1_sha256_write(transcript, ser_scalar, 32); + } +} + +static void copy_vectors_into_scratch(secp256k1_scratch_space* scratch, + secp256k1_scalar **ns, + secp256k1_scalar **ls, + secp256k1_scalar **cs, + secp256k1_ge **gs, + const secp256k1_scalar *n_vec, + const secp256k1_scalar *l_vec, + const secp256k1_scalar *c_vec, + const secp256k1_ge *gens_vec, + size_t g_len, + size_t h_len) { + *ns = (secp256k1_scalar*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); + *ls = (secp256k1_scalar*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + *cs = (secp256k1_scalar*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + *gs = (secp256k1_ge*)secp256k1_scratch_alloc(&CTX->error_callback, scratch, (g_len + h_len) * sizeof(secp256k1_ge)); + CHECK(ns != NULL && ls != NULL && cs != NULL && gs != NULL); + memcpy(*ns, n_vec, g_len * sizeof(secp256k1_scalar)); + memcpy(*ls, l_vec, h_len * sizeof(secp256k1_scalar)); + memcpy(*cs, c_vec, h_len * sizeof(secp256k1_scalar)); + memcpy(*gs, gens_vec, (g_len + h_len) * sizeof(secp256k1_ge)); +} + +/* Same as secp256k1_bppp_rangeproof_norm_product_prove but does not modify the inputs */ +static int secp256k1_bppp_rangeproof_norm_product_prove_const( + secp256k1_scratch_space* scratch, + unsigned char* proof, + size_t *proof_len, + secp256k1_sha256 *transcript, + const secp256k1_scalar* rho, + const secp256k1_ge* g_vec, + size_t g_vec_len, + const secp256k1_scalar* n_vec, + size_t n_vec_len, + const secp256k1_scalar* l_vec, + size_t l_vec_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len +) { + secp256k1_scalar *ns, *ls, *cs; + secp256k1_ge *gs; + size_t scratch_checkpoint; + size_t g_len = n_vec_len, h_len = l_vec_len; + int res; + + scratch_checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, g_vec, g_len, h_len); + res = secp256k1_bppp_rangeproof_norm_product_prove( + CTX, + scratch, + proof, + proof_len, + transcript, /* Transcript hash of the parent protocol */ + rho, + gs, + g_vec_len, + ns, + n_vec_len, + ls, + l_vec_len, + cs, + c_vec_len + ); + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, scratch_checkpoint); + return res; +} + +/* A complete norm argument. In contrast to secp256k1_bppp_rangeproof_norm_product_prove, this is meant + to be used as a standalone norm argument. + This is a simple wrapper around secp256k1_bppp_rangeproof_norm_product_prove + that also commits to the initial public values used in the protocol. In this case, these public + values are commitment. +*/ +static int secp256k1_norm_arg_prove( + secp256k1_scratch_space* scratch, + unsigned char* proof, + size_t *proof_len, + const secp256k1_scalar* rho, + const secp256k1_bppp_generators* gens_vec, + const secp256k1_scalar* n_vec, + size_t n_vec_len, + const secp256k1_scalar* l_vec, + size_t l_vec_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + secp256k1_sha256 transcript; + secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, n_vec_len, c_vec, c_vec_len, commit); + + return secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, proof, proof_len, &transcript, rho, gens_vec->gens, gens_vec->n, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len); +} + +/* Verify the proof */ +static int secp256k1_norm_arg_verify( + secp256k1_scratch_space* scratch, + const unsigned char* proof, + size_t proof_len, + const secp256k1_scalar* rho, + const secp256k1_bppp_generators* gens_vec, + size_t g_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + secp256k1_ge comm = *commit; + int res; + secp256k1_sha256 transcript; + + /* Commit to the initial public values */ + secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, g_len, c_vec, c_vec_len, &comm); + + res = secp256k1_bppp_rangeproof_norm_product_verify( + CTX, + scratch, + proof, + proof_len, + &transcript, + rho, + gens_vec, + g_len, + c_vec, + c_vec_len, + commit + ); + return res; +} + +/* Verify |c| = 0 */ +static void norm_arg_verify_zero_len(void) { + secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; + secp256k1_scalar rho, mu; + secp256k1_ge commit; + secp256k1_scratch *scratch = secp256k1_scratch_space_create(CTX, 1000*10); /* shouldn't need much */ + unsigned char proof[1000]; + unsigned int n_vec_len = 1; + unsigned int c_vec_len = 1; + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(CTX, n_vec_len + c_vec_len); + size_t plen = sizeof(proof); + + testutil_random_scalar_order(&rho); + secp256k1_scalar_sqr(&mu, &rho); + + testutil_random_scalar_order(&n_vec[0]); + testutil_random_scalar_order(&c_vec[0]); + testutil_random_scalar_order(&l_vec[0]); + CHECK(secp256k1_bppp_commit(CTX, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, 0, &commit)); + + secp256k1_bppp_generators_destroy(CTX, gs); + + secp256k1_scratch_space_destroy(CTX, scratch); +} + +static void norm_arg_test(unsigned int n, unsigned int m) { + secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; + secp256k1_scalar rho, mu; + secp256k1_ge commit; + size_t i, plen; + int res; + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(CTX, n + m); + secp256k1_scratch *scratch = secp256k1_scratch_space_create(CTX, 1000*1000); /* shouldn't need much */ + unsigned char proof[1000]; + plen = 1000; + testutil_random_scalar_order(&rho); + secp256k1_scalar_sqr(&mu, &rho); + + for (i = 0; i < n; i++) { + testutil_random_scalar_order(&n_vec[i]); + } + + for (i = 0; i < m; i++) { + testutil_random_scalar_order(&l_vec[i]); + testutil_random_scalar_order(&c_vec[i]); + } + + res = secp256k1_bppp_commit(CTX, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &mu); + CHECK(res == 1); + res = secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n, l_vec, m, c_vec, m, &commit); + CHECK(res == 1); + + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); + CHECK(res == 1); + + /* Changing any of last two scalars should break the proof */ + proof[plen - 1] ^= 1; + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); + CHECK(res == 0); + proof[plen - 1 - 32] ^= 1; + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); + CHECK(res == 0); + + secp256k1_scratch_space_destroy(CTX, scratch); + secp256k1_bppp_generators_destroy(CTX, gs); +} + +/* Parses generators from points compressed as pubkeys */ +secp256k1_bppp_generators* bppp_generators_parse_regular(const unsigned char* data, size_t data_len) { + size_t n = data_len / 33; + secp256k1_bppp_generators* ret; + + if (data_len % 33 != 0) { + return NULL; + } + + ret = checked_malloc(&CTX->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->n = n; + ret->gens = checked_malloc(&CTX->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + + while (n--) { + if (!secp256k1_eckey_pubkey_parse(&ret->gens[n], &data[33 * n], 33)) { + free(ret->gens); + free(ret); + return NULL; + } + } + return ret; +} + +int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, size_t n_vec_len, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, const unsigned char *commit33) { + secp256k1_sha256 transcript; + secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); + secp256k1_scalar rho; + secp256k1_ge commit; + int overflow; + int i; + int ret; + + CHECK(gs != NULL); + secp256k1_sha256_initialize(&transcript); + + secp256k1_scalar_set_b32(&rho, r32, &overflow); + CHECK(!overflow); + + for (i = 0; i < (int)c_vec_len; i++) { + secp256k1_scalar_set_b32(&c_vec[i], c_vec32[i], &overflow); + CHECK(!overflow); + } + CHECK(secp256k1_ge_parse_ext(&commit, commit33)); + ret = secp256k1_bppp_rangeproof_norm_product_verify(CTX, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit); + + secp256k1_bppp_generators_destroy(CTX, gs); + return ret; +} + +#define IDX_TO_TEST(i) (norm_arg_verify_vectors_helper(scratch, verify_vector_gens, verify_vector_##i##_proof, sizeof(verify_vector_##i##_proof), verify_vector_##i##_r32, verify_vector_##i##_n_vec_len, verify_vector_##i##_c_vec32, verify_vector_##i##_c_vec, sizeof(verify_vector_##i##_c_vec)/sizeof(secp256k1_scalar), verify_vector_##i##_commit33) == verify_vector_##i##_result) + +static void norm_arg_verify_vectors(void) { + secp256k1_scratch *scratch = secp256k1_scratch_space_create(CTX, 1000*1000); /* shouldn't need much */ + size_t alloc = scratch->alloc_size; + + CHECK(IDX_TO_TEST(0)); + CHECK(IDX_TO_TEST(1)); + CHECK(IDX_TO_TEST(2)); + CHECK(IDX_TO_TEST(3)); + CHECK(IDX_TO_TEST(4)); + CHECK(IDX_TO_TEST(5)); + CHECK(IDX_TO_TEST(6)); + CHECK(IDX_TO_TEST(7)); + CHECK(IDX_TO_TEST(8)); + CHECK(IDX_TO_TEST(9)); + CHECK(IDX_TO_TEST(10)); + CHECK(IDX_TO_TEST(11)); + CHECK(IDX_TO_TEST(12)); + + CHECK(alloc == scratch->alloc_size); + secp256k1_scratch_space_destroy(CTX, scratch); +} +#undef IDX_TO_TEST + +static void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, const unsigned char n_vec32[][32], secp256k1_scalar *n_vec, size_t n_vec_len, const unsigned char l_vec32[][32], secp256k1_scalar *l_vec, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, int result) { + secp256k1_sha256 transcript; + secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); + secp256k1_scalar rho, mu; + secp256k1_ge commit; + unsigned char myproof[1024]; + size_t myplen = sizeof(myproof); + int overflow; + int i; + + CHECK(gs != NULL); + secp256k1_sha256_initialize(&transcript); + secp256k1_scalar_set_b32(&rho, r32, &overflow); + CHECK(!overflow); + secp256k1_scalar_sqr(&mu, &rho); + + for (i = 0; i < (int)n_vec_len; i++) { + secp256k1_scalar_set_b32(&n_vec[i], n_vec32[i], &overflow); + CHECK(!overflow); + } + for (i = 0; i < (int)c_vec_len; i++) { + secp256k1_scalar_set_b32(&l_vec[i], l_vec32[i], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&c_vec[i], c_vec32[i], &overflow); + CHECK(!overflow); + } + + CHECK(secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, myproof, &myplen, &transcript, &rho, gs->gens, gs->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len) == result); + if (!result) { + secp256k1_bppp_generators_destroy(CTX, gs); + return; + } + CHECK(plen == myplen); + CHECK(secp256k1_memcmp_var(proof, myproof, plen) == 0); + + CHECK(secp256k1_bppp_commit(CTX, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + secp256k1_sha256_initialize(&transcript); + CHECK(secp256k1_bppp_rangeproof_norm_product_verify(CTX, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); + secp256k1_bppp_generators_destroy(CTX, gs); +} + + +#define IDX_TO_TEST(i) (norm_arg_prove_vectors_helper(scratch, prove_vector_gens, prove_vector_##i##_proof, sizeof(prove_vector_##i##_proof), prove_vector_##i##_r32,\ + prove_vector_##i##_n_vec32, prove_vector_##i##_n_vec, sizeof(prove_vector_##i##_n_vec)/sizeof(secp256k1_scalar),\ + prove_vector_##i##_l_vec32, prove_vector_##i##_l_vec,\ + prove_vector_##i##_c_vec32, prove_vector_##i##_c_vec, sizeof(prove_vector_##i##_c_vec)/sizeof(secp256k1_scalar), \ + prove_vector_##i##_result)) + +static void norm_arg_prove_vectors(void) { + secp256k1_scratch *scratch = secp256k1_scratch_space_create(CTX, 1000*1000); /* shouldn't need much */ + size_t alloc = scratch->alloc_size; + + IDX_TO_TEST(0); + IDX_TO_TEST(1); + IDX_TO_TEST(2); + IDX_TO_TEST(3); + IDX_TO_TEST(4); + + CHECK(alloc == scratch->alloc_size); + secp256k1_scratch_space_destroy(CTX, scratch); +} + +#undef IDX_TO_TEST + +static void norm_arg_test_all(void) { + norm_arg_test(1, 1); + norm_arg_test(1, 64); + norm_arg_test(64, 1); + norm_arg_test(32, 32); + norm_arg_test(32, 64); + norm_arg_test(64, 32); + norm_arg_test(64, 64); +} + +/* --- Test registry --- */ +static const struct tf_test_entry tests_bppp[] = { + CASE1(test_log_exp), + CASE1(test_norm_util_helpers), + CASE1(test_serialize_two_points), + CASE1(test_bppp_generators_api), + CASE1(test_bppp_generators_fixed), + CASE1(test_bppp_tagged_hash), + CASE1(norm_arg_verify_zero_len), + CASE1(norm_arg_test_all), + CASE1(norm_arg_verify_vectors), + CASE1(norm_arg_prove_vectors), +}; + +#endif diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include new file mode 100644 index 00000000..e855a17a --- /dev/null +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_ecdsa_adaptor.h +noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h +noinst_HEADERS += src/modules/ecdsa_adaptor/dleq_impl.h +noinst_HEADERS += src/modules/ecdsa_adaptor/tests_impl.h diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h new file mode 100644 index 00000000..a117d69d --- /dev/null +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -0,0 +1,163 @@ +#ifndef SECP256K1_DLEQ_IMPL_H +#define SECP256K1_DLEQ_IMPL_H + +#include + +#include "../../../include/secp256k1_ecdsa_adaptor.h" + +#include "../../../src/eckey.h" +#include "../../../src/ecmult_const.h" +#include "../../../src/group.h" +#include "../../../src/hash.h" +#include "../../../src/scalar.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("DLEQ")||SHA256("DLEQ"). */ +static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0x8cc4beacul, 0x2e011f3ful, 0x355c75fbul, 0x3ba6a2c5ul, + 0xe96f3aeful, 0x180530fdul, 0x94582499ul, 0x577fd564ul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ +static const unsigned char dleq_algo[] = {'D','L','E','Q'}; + +static void secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { + unsigned char buf[33]; + + secp256k1_eckey_pubkey_serialize33(p, buf); + secp256k1_sha256_write(sha, buf, 33); +} + +static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, const unsigned char *gen2_33, const unsigned char *p1_33, const unsigned char *p2_33, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_sha256 sha; + unsigned char buf[32]; + unsigned char nonce[32]; + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_ecdsa_adaptor; + } + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, p1_33, 33); + secp256k1_sha256_write(&sha, p2_33, 33); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_clear(&sha); + + if (!noncefp(nonce, buf, sk32, gen2_33, dleq_algo, sizeof(dleq_algo), ndata)) { + return 0; + } + secp256k1_scalar_set_b32(k, nonce, NULL); + if (secp256k1_scalar_is_zero(k)) { + return 0; + } + + return 1; +} + +/* Generates a challenge as defined in the DLC Specification at + * https://github.com/discreetlogcontracts/dlcspecs */ +static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *gen2, secp256k1_ge *r1, secp256k1_ge *r2, secp256k1_ge *p1, secp256k1_ge *p2) { + unsigned char buf[32]; + secp256k1_sha256 sha; + + secp256k1_nonce_function_dleq_sha256_tagged(&sha); + secp256k1_dleq_hash_point(&sha, p1); + secp256k1_dleq_hash_point(&sha, gen2); + secp256k1_dleq_hash_point(&sha, p2); + secp256k1_dleq_hash_point(&sha, r1); + secp256k1_dleq_hash_point(&sha, r2); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_clear(&sha); + + secp256k1_scalar_set_b32(e, buf, NULL); +} + +/* p[0] = x*G, p[1] = x*Y */ +static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *p, const secp256k1_scalar *sk, const secp256k1_ge *gen2) { + secp256k1_gej pj[2]; + + secp256k1_ecmult_gen(ecmult_gen_ctx, &pj[0], sk); + secp256k1_ecmult_const(&pj[1], gen2, sk); + secp256k1_ge_set_all_gej(p, pj, 2); +} + +/* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the + * same as the discrete logarithm of P2 to the base Y */ +static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + /* Note: r[2] and k are local to the DLEQ proof, and they differ from the + * values with the same identifiers in main_impl.h. */ + secp256k1_ge r[2]; + secp256k1_scalar k = { 0 }; + unsigned char sk32[32]; + unsigned char gen2_33[33]; + unsigned char p1_33[33]; + unsigned char p2_33[33]; + int ret; + + secp256k1_eckey_pubkey_serialize33(gen2, gen2_33); + secp256k1_eckey_pubkey_serialize33(p1, p1_33); + secp256k1_eckey_pubkey_serialize33(p2, p2_33); + + secp256k1_scalar_get_b32(sk32, sk); + + ret = secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, noncefp, ndata); + secp256k1_declassify(ctx, &ret, sizeof(ret)); + if (!ret) { + secp256k1_memclear_explicit(sk32, sizeof(sk32)); + return 0; + } + /* R1 = k*G, R2 = k*Y */ + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, r, &k, gen2); + /* We declassify the non-secret values r[0] and r[1] to allow using them as + * branch points. */ + secp256k1_declassify(ctx, &r[0], sizeof(r[0])); + secp256k1_declassify(ctx, &r[1], sizeof(r[1])); + + /* e = tagged hash(p1, gen2, p2, r[0], r[1]) */ + /* s = k + e * sk */ + secp256k1_dleq_challenge(e, gen2, &r[0], &r[1], p1, p2); + secp256k1_scalar_mul(s, e, sk); + secp256k1_scalar_add(s, s, &k); + + secp256k1_scalar_clear(&k); + secp256k1_memclear_explicit(sk32, sizeof(sk32)); + return 1; +} + +static int secp256k1_dleq_verify(const secp256k1_scalar *s, const secp256k1_scalar *e, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2) { + secp256k1_scalar e_neg; + secp256k1_scalar e_expected; + secp256k1_gej gen2j; + secp256k1_gej p1j, p2j; + secp256k1_gej rj[2]; + secp256k1_ge r[2]; + secp256k1_gej tmpj; + + secp256k1_gej_set_ge(&p1j, p1); + secp256k1_gej_set_ge(&p2j, p2); + + secp256k1_scalar_negate(&e_neg, e); + /* R1 = s*G - e*P1 */ + secp256k1_ecmult(&rj[0], &p1j, &e_neg, s); + /* R2 = s*gen2 - e*P2 */ + secp256k1_ecmult(&tmpj, &p2j, &e_neg, &secp256k1_scalar_zero); + secp256k1_gej_set_ge(&gen2j, gen2); + secp256k1_ecmult(&rj[1], &gen2j, s, &secp256k1_scalar_zero); + secp256k1_gej_add_var(&rj[1], &rj[1], &tmpj, NULL); + + if (secp256k1_gej_is_infinity(&rj[0]) || secp256k1_gej_is_infinity(&rj[1])) { + return 0; + } + + secp256k1_ge_set_all_gej_var(r, rj, 2); + + secp256k1_dleq_challenge(&e_expected, gen2, &r[0], &r[1], p1, p2); + + secp256k1_scalar_add(&e_expected, &e_expected, &e_neg); + return secp256k1_scalar_is_zero(&e_expected); +} + +#endif diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h new file mode 100644 index 00000000..0d590907 --- /dev/null +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -0,0 +1,371 @@ +/********************************************************************** + * Copyright (c) 2020-2021 Jonas Nick, Jesse Posner * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H +#define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H + +#include + +#include "../../../include/secp256k1_ecdsa_adaptor.h" +#include "dleq_impl.h" + +#include "../../../src/eckey.h" +#include "../../../src/ecmult.h" +#include "../../../src/ecmult_const.h" +#include "../../../src/group.h" +#include "../../../src/hash.h" +#include "../../../src/scalar.h" + +/* (R, R', s', dleq_proof) */ +static void secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { + secp256k1_eckey_pubkey_serialize33(r, adaptor_sig162); + secp256k1_eckey_pubkey_serialize33(rp, &adaptor_sig162[33]); + secp256k1_scalar_get_b32(&adaptor_sig162[66], sp); + secp256k1_scalar_get_b32(&adaptor_sig162[98], dleq_proof_e); + secp256k1_scalar_get_b32(&adaptor_sig162[130], dleq_proof_s); +} + +static int secp256k1_ecdsa_adaptor_sig_deserialize(secp256k1_ge *r, secp256k1_scalar *sigr, secp256k1_ge *rp, secp256k1_scalar *sp, secp256k1_scalar *dleq_proof_e, secp256k1_scalar *dleq_proof_s, const unsigned char *adaptor_sig162) { + /* If r is deserialized, require that a sigr is provided to receive + * the X-coordinate */ + VERIFY_CHECK((r == NULL) || (r != NULL && sigr != NULL)); + if (r != NULL) { + if (!secp256k1_eckey_pubkey_parse(r, &adaptor_sig162[0], 33)) { + return 0; + } + } + if (sigr != NULL) { + secp256k1_scalar_set_b32(sigr, &adaptor_sig162[1], NULL); + if (secp256k1_scalar_is_zero(sigr)) { + return 0; + } + } + if (rp != NULL) { + if (!secp256k1_eckey_pubkey_parse(rp, &adaptor_sig162[33], 33)) { + return 0; + } + } + if (sp != NULL) { + if (!secp256k1_scalar_set_b32_seckey(sp, &adaptor_sig162[66])) { + return 0; + } + } + if (dleq_proof_e != NULL) { + secp256k1_scalar_set_b32(dleq_proof_e, &adaptor_sig162[98], NULL); + } + if (dleq_proof_s != NULL) { + int overflow; + secp256k1_scalar_set_b32(dleq_proof_s, &adaptor_sig162[130], &overflow); + if (overflow) { + return 0; + } + } + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ +static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0x791dae43ul, 0xe52d3b44ul, 0x37f9edeaul, 0x9bfd2ab1ul, + 0xcfb0f44dul, 0xccf1d880ul, 0xd18f2c13ul, 0xa37b9024ul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("ECDSAadaptor/aux")||SHA256("ECDSAadaptor/aux"). */ +static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0xd14c7bd9ul, 0x095d35e6ul, 0xb8490a88ul, 0xfb00ef74ul, + 0x0baa488ful, 0x69366693ul, 0x1c81c5baul, 0xc33b296aul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ +static const unsigned char ecdsa_adaptor_algo[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; + +/* Modified BIP-340 nonce function */ +static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *pk33, const unsigned char *algo, size_t algolen, void *data) { + secp256k1_sha256 sha; + unsigned char masked_key[32]; + int i; + + if (algo == NULL) { + return 0; + } + + if (data != NULL) { + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha); + secp256k1_sha256_write(&sha, data, 32); + secp256k1_sha256_finalize(&sha, masked_key); + secp256k1_sha256_clear(&sha); + for (i = 0; i < 32; i++) { + masked_key[i] ^= key32[i]; + } + } + + /* Tag the hash with algo which is important to avoid nonce reuse across + * algorithims. An optimized tagging implementation is used if the default + * tag is provided. */ + if (algolen == sizeof(ecdsa_adaptor_algo) + && secp256k1_memcmp_var(algo, ecdsa_adaptor_algo, algolen) == 0) { + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha); + } else if (algolen == sizeof(dleq_algo) + && secp256k1_memcmp_var(algo, dleq_algo, algolen) == 0) { + secp256k1_nonce_function_dleq_sha256_tagged(&sha); + } else { + secp256k1_sha256_initialize_tagged(&sha, algo, algolen); + } + + /* Hash (masked-)key||pk||msg using the tagged hash as per BIP-340 */ + if (data != NULL) { + secp256k1_sha256_write(&sha, masked_key, 32); + } else { + secp256k1_sha256_write(&sha, key32, 32); + } + secp256k1_sha256_write(&sha, pk33, 33); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, nonce32); + secp256k1_sha256_clear(&sha); + return 1; +} + +const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor = nonce_function_ecdsa_adaptor; + +int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char *adaptor_sig162, unsigned char *seckey32, const secp256k1_pubkey *enckey, const unsigned char *msg32, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_scalar k; + secp256k1_ge r[2]; /* R, R' */ + secp256k1_gej rj[2]; /* R, R' */ + secp256k1_ge enckey_ge; /* Y */ + secp256k1_scalar dleq_proof_s; + secp256k1_scalar dleq_proof_e; + secp256k1_scalar sk; + secp256k1_scalar msg; + secp256k1_scalar sp; + secp256k1_scalar sigr; + secp256k1_scalar n; + unsigned char nonce32[32] = { 0 }; + unsigned char buf33[33]; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(enckey != NULL); + ARG_CHECK(msg32 != NULL); + + secp256k1_scalar_clear(&dleq_proof_e); + secp256k1_scalar_clear(&dleq_proof_s); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_ecdsa_adaptor; + } + + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { + return 0; + } + + secp256k1_eckey_pubkey_serialize33(&enckey_ge, buf33); + ret &= !!noncefp(nonce32, msg32, seckey32, buf33, ecdsa_adaptor_algo, sizeof(ecdsa_adaptor_algo), ndata); + secp256k1_scalar_set_b32(&k, nonce32, NULL); + ret &= !secp256k1_scalar_is_zero(&k); + secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); + + /* R := k*Y */ + secp256k1_ecmult_const(&rj[0], &enckey_ge, &k); + /* R' := k*G */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj[1], &k); + + secp256k1_ge_set_all_gej(r, rj, 2); + + /* We declassify the non-secret nonce values to allow using them as branch points. */ + secp256k1_declassify(ctx, &r[0], sizeof(r[0])); + secp256k1_declassify(ctx, &r[1], sizeof(r[1])); + + /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ + if (!secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &r[1], &enckey_ge, &r[0], noncefp, ndata)) { + memset(adaptor_sig162, 0, 162); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); + secp256k1_scalar_clear(&k); + return 0; + } + ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey32); + secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret); + secp256k1_scalar_set_b32(&msg, msg32, NULL); + secp256k1_fe_normalize(&r[0].x); + secp256k1_fe_get_b32(buf33, &r[0].x); + secp256k1_scalar_set_b32(&sigr, buf33, NULL); + ret &= !secp256k1_scalar_is_zero(&sigr); + /* s' = k⁻¹(m + R.x * x) */ + secp256k1_scalar_mul(&n, &sigr, &sk); + secp256k1_scalar_add(&n, &n, &msg); + secp256k1_scalar_inverse(&sp, &k); + secp256k1_scalar_mul(&sp, &sp, &n); + ret &= !secp256k1_scalar_is_zero(&sp); + + /* return (R, R', s', dleq_proof) */ + secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &r[0], &r[1], &sp, &dleq_proof_e, &dleq_proof_s); + + secp256k1_memczero(adaptor_sig162, 162, !ret); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); + secp256k1_scalar_clear(&n); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&sk); + + return ret; +} + +int secp256k1_ecdsa_adaptor_verify(const secp256k1_context* ctx, const unsigned char *adaptor_sig162, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_pubkey *enckey) { + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_scalar msg; + secp256k1_ge pubkey_ge; + secp256k1_ge r, rp; + secp256k1_scalar sp; + secp256k1_scalar sigr; + secp256k1_ge enckey_ge; + secp256k1_gej derived_rp; + secp256k1_scalar sn, u1, u2; + secp256k1_gej pubkeyj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(enckey != NULL); + + if (!secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)) { + return 0; + } + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { + return 0; + } + /* DLEQ_verify((R', Y, R), dleq_proof) */ + if(!secp256k1_dleq_verify(&dleq_proof_s, &dleq_proof_e, &rp, &enckey_ge, &r)) { + return 0; + } + secp256k1_scalar_set_b32(&msg, msg32, NULL); + if (!secp256k1_pubkey_load(ctx, &pubkey_ge, pubkey)) { + return 0; + } + + /* return R' == s'⁻¹(m * G + R.x * X) */ + secp256k1_scalar_inverse_var(&sn, &sp); + secp256k1_scalar_mul(&u1, &sn, &msg); + secp256k1_scalar_mul(&u2, &sn, &sigr); + secp256k1_gej_set_ge(&pubkeyj, &pubkey_ge); + secp256k1_ecmult(&derived_rp, &pubkeyj, &u2, &u1); + if (secp256k1_gej_is_infinity(&derived_rp)) { + return 0; + } + secp256k1_gej_neg(&derived_rp, &derived_rp); + secp256k1_gej_add_ge_var(&derived_rp, &derived_rp, &rp, NULL); + return secp256k1_gej_is_infinity(&derived_rp); +} + +int secp256k1_ecdsa_adaptor_decrypt(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sig, const unsigned char *deckey32, const unsigned char *adaptor_sig162) { + secp256k1_scalar deckey; + secp256k1_scalar sp; + secp256k1_scalar s; + secp256k1_scalar sigr; + int overflow; + int high; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(deckey32 != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + + secp256k1_scalar_clear(&sp); + secp256k1_scalar_set_b32(&deckey, deckey32, &overflow); + ret &= !overflow; + ret &= secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, &sp, NULL, NULL, adaptor_sig162); + ret &= !secp256k1_scalar_is_zero(&deckey); + secp256k1_scalar_inverse(&s, &deckey); + /* s = s' * y⁻¹ */ + secp256k1_scalar_mul(&s, &s, &sp); + high = secp256k1_scalar_is_high(&s); + secp256k1_scalar_cond_negate(&s, high); + secp256k1_ecdsa_signature_save(sig, &sigr, &s); + + secp256k1_memczero(&sig->data[0], 64, !ret); + secp256k1_scalar_clear(&deckey); + secp256k1_scalar_clear(&sp); + secp256k1_scalar_clear(&s); + + return ret; +} + +int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char *deckey32, const secp256k1_ecdsa_signature *sig, const unsigned char *adaptor_sig162, const secp256k1_pubkey *enckey) { + secp256k1_scalar sp, adaptor_sigr; + secp256k1_scalar s, r; + secp256k1_scalar deckey; + secp256k1_ge enckey_expected_ge; + secp256k1_ge enckey_ge; + secp256k1_gej enckey_expected_gej; + unsigned char enckey33[33]; + unsigned char enckey_expected33[33]; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(deckey32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(enckey != NULL); + + if (!secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &adaptor_sigr, NULL, &sp, NULL, NULL, adaptor_sig162)) { + return 0; + } + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + /* Check that we're not looking at some unrelated signature */ + ret &= secp256k1_scalar_eq(&adaptor_sigr, &r); + /* y = s⁻¹ * s' */ + ret &= !secp256k1_scalar_is_zero(&s); + secp256k1_scalar_inverse(&deckey, &s); + secp256k1_scalar_mul(&deckey, &deckey, &sp); + + /* Deal with ECDSA malleability */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &enckey_expected_gej, &deckey); + secp256k1_ge_set_gej(&enckey_expected_ge, &enckey_expected_gej); + /* We declassify non-secret enckey_expected_ge to allow using it as a + * branch point. */ + secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge)); + /* enckey_expected_ge cannot be infinity: + * + * Proof: + * enckey_expected_ge is infinity <=> deckey = 0 + * deckey = 0 <=> s^-1 = 0 or sp = 0 + * case 1: s^-1 = 0 impossible by the definition of multiplicative + * inverse and because the scalar_inverse implementation + * VERIFY_CHECKs that the inputs are valid scalars. + * case 2: sp = 0 impossible because ecdsa_adaptor_sig_deserialize would have already failed + */ + secp256k1_eckey_pubkey_serialize33(&enckey_expected_ge, enckey_expected33); + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(&enckey_ge, enckey33); + if (secp256k1_memcmp_var(&enckey_expected33[1], &enckey33[1], 32) != 0) { + return 0; + } + if (enckey_expected33[0] != enckey33[0]) { + /* try Y_implied == -Y */ + secp256k1_scalar_negate(&deckey, &deckey); + } + secp256k1_scalar_get_b32(deckey32, &deckey); + + secp256k1_scalar_clear(&deckey); + secp256k1_scalar_clear(&sp); + secp256k1_scalar_clear(&s); + + return ret; +} + +#endif diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h new file mode 100644 index 00000000..09058997 --- /dev/null +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -0,0 +1,1199 @@ +#ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H +#define SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H + +#include "../../../include/secp256k1_ecdsa_adaptor.h" +#include "../../unit_test.h" + +static void rand_scalar(secp256k1_scalar *scalar) { + unsigned char buf32[32]; + testrand256(buf32); + secp256k1_scalar_set_b32(scalar, buf32, NULL); +} + +static void rand_point(secp256k1_ge *point) { + secp256k1_scalar x; + secp256k1_gej pointj; + rand_scalar(&x); + + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pointj, &x); + secp256k1_ge_set_gej(point, &pointj); +} + +static void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { + secp256k1_scalar k1, k2; + + CHECK(secp256k1_dleq_nonce(&k1, args[0], args[1], args[2], args[3], NULL, args[4]) == 1); + testrand_flip(args[n_flip], n_bytes); + CHECK(secp256k1_dleq_nonce(&k2, args[0], args[1], args[2], args[3], NULL, args[4]) == 1); + CHECK(secp256k1_scalar_eq(&k1, &k2) == 0); +} + +static void dleq_tests_internal(void) { + secp256k1_scalar s, e, sk, k; + secp256k1_ge gen2, p1, p2; + secp256k1_ge p[2]; + unsigned char *args[5]; + unsigned char sk32[32]; + unsigned char gen2_33[33]; + unsigned char p1_33[33]; + unsigned char p2_33[33]; + unsigned char aux_rand[32]; + int i; + + rand_point(&gen2); + rand_scalar(&sk); + secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, p, &sk, &gen2); + p1 = p[0]; + p2 = p[1]; + CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &p1, &gen2, &p2, NULL, NULL) == 1); + CHECK(secp256k1_dleq_verify(&s, &e, &p1, &gen2, &p2) == 1); + + { + secp256k1_scalar tmp; + secp256k1_scalar_set_int(&tmp, 1); + CHECK(secp256k1_dleq_verify(&tmp, &e, &p1, &gen2, &p2) == 0); + CHECK(secp256k1_dleq_verify(&s, &tmp, &p1, &gen2, &p2) == 0); + } + { + secp256k1_ge p_tmp; + rand_point(&p_tmp); + CHECK(secp256k1_dleq_verify(&s, &e, &p_tmp, &gen2, &p2) == 0); + CHECK(secp256k1_dleq_verify(&s, &e, &p1, &p_tmp, &p2) == 0); + CHECK(secp256k1_dleq_verify(&s, &e, &p1, &gen2, &p_tmp) == 0); + } + + /* Nonce tests */ + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_eckey_pubkey_serialize33(&gen2, gen2_33); + secp256k1_eckey_pubkey_serialize33(&p1, p1_33); + secp256k1_eckey_pubkey_serialize33(&p2, p2_33); + CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); + + testrand_bytes_test(sk32, sizeof(sk32)); + testrand_bytes_test(gen2_33, sizeof(gen2_33)); + testrand_bytes_test(p1_33, sizeof(p1_33)); + testrand_bytes_test(p2_33, sizeof(p2_33)); + testrand_bytes_test(aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = sk32; + args[1] = gen2_33; + args[2] = p1_33; + args[3] = p2_33; + args[4] = aux_rand; + for (i = 0; i < COUNT; i++) { + dleq_nonce_bitflip(args, 0, sizeof(sk32)); + dleq_nonce_bitflip(args, 1, sizeof(gen2_33)); + dleq_nonce_bitflip(args, 2, sizeof(p1_33)); + /* Flip p2 */ + dleq_nonce_bitflip(args, 3, sizeof(p2_33)); + /* Flip p2 again */ + dleq_nonce_bitflip(args, 3, sizeof(p2_33)); + dleq_nonce_bitflip(args, 4, sizeof(aux_rand)); + } + + /* NULL aux_rand argument is allowed. */ + CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); +} + +static void rand_flip_bit(unsigned char *array, size_t n) { + array[testrand_int(n)] ^= 1 << testrand_int(8); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the adaptor signature is valid for the public and encryption keys. */ +static void test_ecdsa_adaptor_spec_vectors_check_verify(const unsigned char *adaptor_sig162, const unsigned char *msg32, const unsigned char *pubkey33, const unsigned char *encryption_key33, int expected) { + secp256k1_pubkey pubkey; + secp256k1_ge pubkey_ge; + secp256k1_pubkey encryption_key; + secp256k1_ge encryption_key_ge; + + CHECK(secp256k1_eckey_pubkey_parse(&encryption_key_ge, encryption_key33, 33) == 1); + secp256k1_pubkey_save(&encryption_key, &encryption_key_ge); + CHECK(secp256k1_eckey_pubkey_parse(&pubkey_ge, pubkey33, 33) == 1); + secp256k1_pubkey_save(&pubkey, &pubkey_ge); + + CHECK(expected == secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig162, &pubkey, msg32, &encryption_key)); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the signature can be decrypted from the adaptor signature and the decryption key. */ +static void test_ecdsa_adaptor_spec_vectors_check_decrypt(const unsigned char *adaptor_sig162, const unsigned char *decryption_key32, const unsigned char *signature64, int expected) { + unsigned char signature[64]; + secp256k1_ecdsa_signature s; + + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &s, decryption_key32, adaptor_sig162) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, signature, &s) == 1); + + CHECK(expected == !(secp256k1_memcmp_var(signature, signature64, 64))); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the decryption key can be recovered from the adaptor signature, encryption key, and the signature. */ +static void test_ecdsa_adaptor_spec_vectors_check_recover(const unsigned char *adaptor_sig162, const unsigned char *encryption_key33, const unsigned char *decryption_key32, const unsigned char *signature64, int expected) { + unsigned char deckey32[32] = { 0 }; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey encryption_key; + secp256k1_ge encryption_key_ge; + + CHECK(secp256k1_eckey_pubkey_parse(&encryption_key_ge, encryption_key33, 33) == 1); + secp256k1_pubkey_save(&encryption_key, &encryption_key_ge); + + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &sig, signature64) == 1); + CHECK(expected == secp256k1_ecdsa_adaptor_recover(CTX, deckey32, &sig, adaptor_sig162, &encryption_key)); + if (decryption_key32 != NULL) { + CHECK(expected == !(secp256k1_memcmp_var(deckey32, decryption_key32, 32))); + } +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks deserialization and serialization. */ +static void test_ecdsa_adaptor_spec_vectors_check_serialization(const unsigned char *adaptor_sig162, int expected) { + unsigned char buf[162]; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_ge r, rp; + secp256k1_scalar sp; + secp256k1_scalar sigr; + + CHECK(expected == secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)); + if (expected == 1) { + secp256k1_ecdsa_adaptor_sig_serialize(buf, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); + CHECK(secp256k1_memcmp_var(buf, adaptor_sig162, 162) == 0); + } +} + +/* Test vectors according to ECDSA adaptor signature spec. See + * https://github.com/discreetlogcontracts/dlcspecs/blob/596a177375932a47306f07e7385f398f52519a83/test/ecdsa_adaptor.json. */ +static void test_ecdsa_adaptor_spec_vectors(void) { + { + /* Test vector 0 */ + /* kind: verification test */ + /* plain valid adaptor signature */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, + 0x8a, 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, + 0x12, 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, + 0x7a, 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, + 0x73, 0x02, 0x23, 0xf3, 0x25, 0x04, 0x2f, 0xce, + 0x53, 0x5d, 0x04, 0x0f, 0xee, 0x52, 0xec, 0x13, + 0x23, 0x1b, 0xf7, 0x09, 0xcc, 0xd8, 0x42, 0x33, + 0xc6, 0x94, 0x4b, 0x90, 0x31, 0x7e, 0x62, 0x52, + 0x8b, 0x25, 0x27, 0xdf, 0xf9, 0xd6, 0x59, 0xa9, + 0x6d, 0xb4, 0xc9, 0x9f, 0x97, 0x50, 0x16, 0x83, + 0x08, 0x63, 0x3c, 0x18, 0x67, 0xb7, 0x0f, 0x3a, + 0x18, 0xfb, 0x0f, 0x45, 0x39, 0xa1, 0xae, 0xce, + 0xdc, 0xd1, 0xfc, 0x01, 0x48, 0xfc, 0x22, 0xf3, + 0x6b, 0x63, 0x03, 0x08, 0x3e, 0xce, 0x3f, 0x87, + 0x2b, 0x18, 0xe3, 0x5d, 0x36, 0x8b, 0x39, 0x58, + 0xef, 0xe5, 0xfb, 0x08, 0x1f, 0x77, 0x16, 0x73, + 0x6c, 0xcb, 0x59, 0x8d, 0x26, 0x9a, 0xa3, 0x08, + 0x4d, 0x57, 0xe1, 0x85, 0x5e, 0x1e, 0xa9, 0xa4, + 0x5e, 0xfc, 0x10, 0x46, 0x3b, 0xbf, 0x32, 0xae, + 0x37, 0x80, 0x29, 0xf5, 0x76, 0x3c, 0xeb, 0x40, + 0x17, 0x3f + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0xc2, 0x66, 0x2c, 0x97, 0x48, 0x8b, 0x07, + 0xb6, 0xe8, 0x19, 0x12, 0x4b, 0x89, 0x89, 0x84, + 0x92, 0x06, 0x33, 0x4a, 0x4c, 0x2f, 0xbd, 0xf6, + 0x91, 0xf7, 0xb3, 0x4d, 0x2b, 0x16, 0xe9, 0xc2, + 0x93 + }; + const unsigned char decryption_key[32] = { + 0x0b, 0x2a, 0xba, 0x63, 0xb8, 0x85, 0xa0, 0xf0, + 0xe9, 0x6f, 0xa0, 0xf3, 0x03, 0x92, 0x0c, 0x7f, + 0xb7, 0x43, 0x1d, 0xdf, 0xa9, 0x43, 0x76, 0xad, + 0x94, 0xd9, 0x69, 0xfb, 0xf4, 0x10, 0x9d, 0xc8 + }; + const unsigned char signature[64] = { + 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, 0x8a, + 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, 0x12, + 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, 0x7a, + 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, 0x73, + 0x29, 0xe8, 0x0e, 0x0e, 0xe6, 0x0e, 0x57, 0xaf, + 0x3e, 0x62, 0x5b, 0xba, 0xe1, 0x67, 0x2b, 0x1e, + 0xca, 0xa5, 0x8e, 0xff, 0xe6, 0x13, 0x42, 0x6b, + 0x02, 0x4f, 0xa1, 0x62, 0x1d, 0x90, 0x33, 0x94 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 1); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 1 */ + /* verification test */ + /* the decrypted signature is high so it must be negated first + * AND the extracted decryption key must be negated */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x60, 0x35, 0xc8, 0x98, 0x60, 0xec, 0x62, + 0xad, 0x15, 0x3f, 0x69, 0xb5, 0xb3, 0x07, 0x7b, + 0xcd, 0x08, 0xfb, 0xb0, 0xd2, 0x8d, 0xc7, 0xf7, + 0xf6, 0xdf, 0x4a, 0x05, 0xcc, 0xa3, 0x54, 0x55, + 0xbe, 0x03, 0x70, 0x43, 0xb6, 0x3c, 0x56, 0xf6, + 0x31, 0x7d, 0x99, 0x28, 0xe8, 0xf9, 0x10, 0x07, + 0x33, 0x57, 0x48, 0xc4, 0x98, 0x24, 0x22, 0x0d, + 0xb1, 0x4a, 0xd1, 0x0d, 0x80, 0xa5, 0xd0, 0x0a, + 0x96, 0x54, 0xaf, 0x09, 0x96, 0xc1, 0x82, 0x4c, + 0x64, 0xc9, 0x0b, 0x95, 0x1b, 0xb2, 0x73, 0x4a, + 0xae, 0xcf, 0x78, 0xd4, 0xb3, 0x61, 0x31, 0xa4, + 0x72, 0x38, 0xc3, 0xfa, 0x2b, 0xa2, 0x5e, 0x2c, + 0xed, 0x54, 0x25, 0x5b, 0x06, 0xdf, 0x69, 0x6d, + 0xe1, 0x48, 0x3c, 0x37, 0x67, 0x24, 0x2a, 0x37, + 0x28, 0x82, 0x6e, 0x05, 0xf7, 0x9e, 0x39, 0x81, + 0xe1, 0x25, 0x53, 0x35, 0x5b, 0xba, 0x8a, 0x01, + 0x31, 0xcd, 0x37, 0x0e, 0x63, 0xe3, 0xda, 0x73, + 0x10, 0x6f, 0x63, 0x85, 0x76, 0xa5, 0xaa, 0xb0, + 0xea, 0x6d, 0x45, 0xc0, 0x42, 0x57, 0x4c, 0x0c, + 0x8d, 0x0b, 0x14, 0xb8, 0xc7, 0xc0, 0x1c, 0xfe, + 0x90, 0x72 + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0x4e, 0xee, 0x18, 0xbe, 0x9a, 0x5a, 0x52, + 0x24, 0x00, 0x0f, 0x91, 0x6c, 0x80, 0xb3, 0x93, + 0x44, 0x79, 0x89, 0xe7, 0x19, 0x4b, 0xc0, 0xb0, + 0xf1, 0xad, 0x7a, 0x03, 0x36, 0x97, 0x02, 0xbb, + 0x51 + }; + const unsigned char decryption_key[32] = { + 0xdb, 0x2d, 0xeb, 0xdd, 0xb0, 0x02, 0x47, 0x3a, + 0x00, 0x1d, 0xd7, 0x0b, 0x06, 0xf6, 0xc9, 0x7b, + 0xdc, 0xd1, 0xc4, 0x6b, 0xa1, 0x00, 0x12, 0x37, + 0xfe, 0x0e, 0xe1, 0xae, 0xff, 0xb2, 0xb6, 0xc4 + }; + const unsigned char signature[64] = { + 0x60, 0x35, 0xc8, 0x98, 0x60, 0xec, 0x62, 0xad, + 0x15, 0x3f, 0x69, 0xb5, 0xb3, 0x07, 0x7b, 0xcd, + 0x08, 0xfb, 0xb0, 0xd2, 0x8d, 0xc7, 0xf7, 0xf6, + 0xdf, 0x4a, 0x05, 0xcc, 0xa3, 0x54, 0x55, 0xbe, + 0x4c, 0xea, 0xcf, 0x92, 0x15, 0x46, 0xc0, 0x3d, + 0xd1, 0xbe, 0x59, 0x67, 0x23, 0xad, 0x1e, 0x76, + 0x91, 0xbd, 0xac, 0x73, 0xd8, 0x8c, 0xc3, 0x6c, + 0x42, 0x1c, 0x5e, 0x7f, 0x08, 0x38, 0x43, 0x05 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 1); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 2 */ + /* verification test */ + /* proof is wrong */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xf9, 0x4d, 0xca, 0x20, 0x6d, 0x75, 0x82, + 0xc0, 0x15, 0xfb, 0x9b, 0xff, 0xe4, 0xe4, 0x3b, + 0x14, 0x59, 0x1b, 0x30, 0xef, 0x7d, 0x2b, 0x46, + 0x4d, 0x10, 0x3e, 0xc5, 0xe1, 0x16, 0x59, 0x5d, + 0xba, 0x03, 0x12, 0x7f, 0x8a, 0xc3, 0x53, 0x3d, + 0x24, 0x92, 0x80, 0x33, 0x24, 0x74, 0x33, 0x90, + 0x00, 0x92, 0x2e, 0xb6, 0xa5, 0x8e, 0x3b, 0x9b, + 0xf4, 0xfc, 0x7e, 0x01, 0xe4, 0xb4, 0xdf, 0x2b, + 0x7a, 0x41, 0x00, 0xa1, 0xe0, 0x89, 0xf1, 0x6e, + 0x5d, 0x70, 0xbb, 0x89, 0xf9, 0x61, 0x51, 0x6f, + 0x1d, 0xe0, 0x68, 0x4c, 0xc7, 0x9d, 0xb9, 0x78, + 0x49, 0x5d, 0xf2, 0xf3, 0x99, 0xb0, 0xd0, 0x1e, + 0xd7, 0x24, 0x0f, 0xa6, 0xe3, 0x25, 0x2a, 0xed, + 0xb5, 0x8b, 0xdc, 0x6b, 0x58, 0x77, 0xb0, 0xc6, + 0x02, 0x62, 0x8a, 0x23, 0x5d, 0xd1, 0xcc, 0xae, + 0xbd, 0xdd, 0xcb, 0xe9, 0x61, 0x98, 0xc0, 0xc2, + 0x1b, 0xea, 0xd7, 0xb0, 0x5f, 0x42, 0x3b, 0x67, + 0x3d, 0x14, 0xd2, 0x06, 0xfa, 0x15, 0x07, 0xb2, + 0xdb, 0xe2, 0x72, 0x2a, 0xf7, 0x92, 0xb8, 0xc2, + 0x66, 0xfc, 0x25, 0xa2, 0xd9, 0x01, 0xd7, 0xe2, + 0xc3, 0x35 + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0x14, 0xcc, 0xb7, 0x56, 0x24, 0x9a, 0xd6, + 0xe7, 0x33, 0xc8, 0x02, 0x85, 0xea, 0x7a, 0xc2, + 0xee, 0x12, 0xff, 0xeb, 0xbc, 0xee, 0x4e, 0x55, + 0x6e, 0x68, 0x10, 0x79, 0x3a, 0x60, 0xc4, 0x5a, + 0xd4 + }; + const unsigned char decryption_key[32] = { + 0x1d, 0xfc, 0xfc, 0x08, 0x80, 0xe7, 0x25, 0x09, + 0x76, 0x8a, 0xb4, 0x6f, 0x25, 0x45, 0xb3, 0x31, + 0x68, 0xb8, 0xb8, 0xdf, 0x8e, 0x4f, 0x5f, 0xeb, + 0x50, 0x59, 0xaa, 0x37, 0x50, 0xee, 0x59, 0xd0 + }; + const unsigned char signature[64] = { + 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, 0x8a, + 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, 0x12, + 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, 0x7a, + 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, 0x73, + 0x29, 0xe8, 0x0e, 0x0e, 0xe6, 0x0e, 0x57, 0xaf, + 0x3e, 0x62, 0x5b, 0xba, 0xe1, 0x67, 0x2b, 0x1e, + 0xca, 0xa5, 0x8e, 0xff, 0xe6, 0x13, 0x42, 0x6b, + 0x02, 0x4f, 0xa1, 0x62, 0x1d, 0x90, 0x33, 0x94 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 0); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 0); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 0); + } + { + /* Test vector 3 */ + /* recovery test */ + /* plain recovery */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xf2, 0xdb, 0x6e, 0x9e, 0xd3, 0x30, 0x92, + 0xcc, 0x0b, 0x89, 0x8f, 0xd6, 0xb2, 0x82, 0xe9, + 0x9b, 0xda, 0xec, 0xcb, 0x3d, 0xe8, 0x5c, 0x2d, + 0x25, 0x12, 0xd8, 0xd5, 0x07, 0xf9, 0xab, 0xab, + 0x29, 0x02, 0x10, 0xc0, 0x1b, 0x5b, 0xed, 0x70, + 0x94, 0xa1, 0x26, 0x64, 0xae, 0xaa, 0xb3, 0x40, + 0x2d, 0x87, 0x09, 0xa8, 0xf3, 0x62, 0xb1, 0x40, + 0x32, 0x8d, 0x1b, 0x36, 0xdd, 0x7c, 0xb4, 0x20, + 0xd0, 0x2f, 0xb6, 0x6b, 0x12, 0x30, 0xd6, 0x1c, + 0x16, 0xd0, 0xcd, 0x0a, 0x2a, 0x02, 0x24, 0x6d, + 0x5a, 0xc7, 0x84, 0x8d, 0xcd, 0x6f, 0x04, 0xfe, + 0x62, 0x70, 0x53, 0xcd, 0x3c, 0x70, 0x15, 0xa7, + 0xd4, 0xaa, 0x6a, 0xc2, 0xb0, 0x43, 0x47, 0x34, + 0x8b, 0xd6, 0x7d, 0xa4, 0x3b, 0xe8, 0x72, 0x25, + 0x15, 0xd9, 0x9a, 0x79, 0x85, 0xfb, 0xfa, 0x66, + 0xf0, 0x36, 0x5c, 0x70, 0x1d, 0xe7, 0x6f, 0xf0, + 0x40, 0x0d, 0xff, 0xdc, 0x9f, 0xa8, 0x4d, 0xdd, + 0xf4, 0x13, 0xa7, 0x29, 0x82, 0x3b, 0x16, 0xaf, + 0x60, 0xaa, 0x63, 0x61, 0xbc, 0x32, 0xe7, 0xcf, + 0xd6, 0x70, 0x1e, 0x32, 0x95, 0x7c, 0x72, 0xac, + 0xe6, 0x7b + }; + const unsigned char encryption_key[33] = { + 0x02, 0x7e, 0xe4, 0xf8, 0x99, 0xbc, 0x9c, 0x5f, + 0x2b, 0x62, 0x6f, 0xa1, 0xa9, 0xb3, 0x7c, 0xe2, + 0x91, 0xc0, 0x38, 0x8b, 0x52, 0x27, 0xe9, 0x0b, + 0x0f, 0xd8, 0xf4, 0xfa, 0x57, 0x61, 0x64, 0xed, + 0xe7 + }; + const unsigned char decryption_key[32] = { + 0x9c, 0xf3, 0xea, 0x9b, 0xe5, 0x94, 0x36, 0x6b, + 0x78, 0xc4, 0x57, 0x16, 0x29, 0x08, 0xaf, 0x3c, + 0x2e, 0xa1, 0x77, 0x05, 0x81, 0x77, 0xe9, 0xc6, + 0xbf, 0x99, 0x04, 0x79, 0x27, 0x77, 0x3a, 0x06 + }; + const unsigned char signature[64] = { + 0xf2, 0xdb, 0x6e, 0x9e, 0xd3, 0x30, 0x92, 0xcc, + 0x0b, 0x89, 0x8f, 0xd6, 0xb2, 0x82, 0xe9, 0x9b, + 0xda, 0xec, 0xcb, 0x3d, 0xe8, 0x5c, 0x2d, 0x25, + 0x12, 0xd8, 0xd5, 0x07, 0xf9, 0xab, 0xab, 0x29, + 0x21, 0x81, 0x1f, 0xe7, 0xb5, 0x3b, 0xec, 0xf3, + 0xb7, 0xaf, 0xfa, 0x94, 0x42, 0xab, 0xaa, 0x93, + 0xc0, 0xab, 0x8a, 0x8e, 0x45, 0xcd, 0x7e, 0xe2, + 0xea, 0x8d, 0x25, 0x8b, 0xfc, 0x25, 0xd4, 0x64 + }; + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 4 */ + /* recovery test */ + /* the R value of the signature does not match */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xaa, 0x86, 0xd7, 0x80, 0x59, 0xa9, 0x10, + 0x59, 0xc2, 0x9e, 0xc1, 0xa7, 0x57, 0xc4, 0xdc, + 0x02, 0x9f, 0xf6, 0x36, 0xa1, 0xe6, 0xc1, 0x14, + 0x2f, 0xef, 0xe1, 0xe9, 0xd7, 0x33, 0x96, 0x17, + 0xc0, 0x03, 0xa8, 0x15, 0x3e, 0x50, 0xc0, 0xc8, + 0x57, 0x4a, 0x38, 0xd3, 0x89, 0xe6, 0x1b, 0xbb, + 0x0b, 0x58, 0x15, 0x16, 0x9e, 0x06, 0x09, 0x24, + 0xe4, 0xb5, 0xf2, 0xe7, 0x8f, 0xf1, 0x3a, 0xa7, + 0xad, 0x85, 0x8e, 0x0c, 0x27, 0xc4, 0xb9, 0xee, + 0xd9, 0xd6, 0x05, 0x21, 0xb3, 0xf5, 0x4f, 0xf8, + 0x3c, 0xa4, 0x77, 0x4b, 0xe5, 0xfb, 0x3a, 0x68, + 0x0f, 0x82, 0x0a, 0x35, 0xe8, 0x84, 0x0f, 0x4a, + 0xaf, 0x2d, 0xe8, 0x8e, 0x7c, 0x5c, 0xff, 0x38, + 0xa3, 0x7b, 0x78, 0x72, 0x59, 0x04, 0xef, 0x97, + 0xbb, 0x82, 0x34, 0x13, 0x28, 0xd5, 0x59, 0x87, + 0x01, 0x9b, 0xd3, 0x8a, 0xe1, 0x74, 0x5e, 0x3e, + 0xfe, 0x0f, 0x8e, 0xa8, 0xbd, 0xfe, 0xde, 0x0d, + 0x37, 0x8f, 0xc1, 0xf9, 0x6e, 0x94, 0x4a, 0x75, + 0x05, 0x24, 0x9f, 0x41, 0xe9, 0x37, 0x81, 0x50, + 0x9e, 0xe0, 0xba, 0xde, 0x77, 0x29, 0x0d, 0x39, + 0xcd, 0x12 + }; + const unsigned char encryption_key[33] = { + 0x03, 0x51, 0x76, 0xd2, 0x41, 0x29, 0x74, 0x1b, + 0x0f, 0xca, 0xa5, 0xfd, 0x67, 0x50, 0x72, 0x7c, + 0xe3, 0x08, 0x60, 0x44, 0x7e, 0x0a, 0x92, 0xc9, + 0xeb, 0xeb, 0xde, 0xb7, 0xc3, 0xf9, 0x39, 0x95, + 0xed + }; + const unsigned char signature[64] = { + 0xf7, 0xf7, 0xfe, 0x6b, 0xd0, 0x56, 0xfc, 0x4a, + 0xbd, 0x70, 0xd3, 0x35, 0xf7, 0x2d, 0x0a, 0xa1, + 0xe8, 0x40, 0x6b, 0xba, 0x68, 0xf3, 0xe5, 0x79, + 0xe4, 0x78, 0x94, 0x75, 0x32, 0x35, 0x64, 0xa4, + 0x52, 0xc4, 0x61, 0x76, 0xc7, 0xfb, 0x40, 0xaa, + 0x37, 0xd5, 0x65, 0x13, 0x41, 0xf5, 0x56, 0x97, + 0xda, 0xb2, 0x7d, 0x84, 0xa2, 0x13, 0xb3, 0x0c, + 0x93, 0x01, 0x1a, 0x77, 0x90, 0xba, 0xce, 0x8c + }; + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, NULL, signature, 0); + } + { + /* Test vector 5 */ + /* recovery test */ + /* recovery from high s signature */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x2c, 0x63, 0x7c, 0xd7, 0x97, 0xdd, 0x8c, + 0x2c, 0xe2, 0x61, 0x90, 0x7e, 0xd4, 0x3e, 0x82, + 0xd6, 0xd1, 0xa4, 0x8c, 0xba, 0xbb, 0xbe, 0xce, + 0x80, 0x11, 0x33, 0xdd, 0x8d, 0x70, 0xa0, 0x1b, + 0x14, 0x03, 0xeb, 0x61, 0x5a, 0x3e, 0x59, 0xb1, + 0xcb, 0xbf, 0x4f, 0x87, 0xac, 0xaf, 0x64, 0x5b, + 0xe1, 0xed, 0xa3, 0x2a, 0x06, 0x66, 0x11, 0xf3, + 0x5d, 0xd5, 0x55, 0x78, 0x02, 0x80, 0x2b, 0x14, + 0xb1, 0x9c, 0x81, 0xc0, 0x4c, 0x3f, 0xef, 0xac, + 0x57, 0x83, 0xb2, 0x07, 0x7b, 0xd4, 0x3f, 0xa0, + 0xa3, 0x9a, 0xb8, 0xa6, 0x4d, 0x4d, 0x78, 0x33, + 0x2a, 0x5d, 0x62, 0x1e, 0xa2, 0x3e, 0xca, 0x46, + 0xbc, 0x01, 0x10, 0x11, 0xab, 0x82, 0xdd, 0xa6, + 0xde, 0xb8, 0x56, 0x99, 0xf5, 0x08, 0x74, 0x4d, + 0x70, 0xd4, 0x13, 0x4b, 0xea, 0x03, 0xf7, 0x84, + 0xd2, 0x85, 0xb5, 0xc6, 0xc1, 0x5a, 0x56, 0xe4, + 0xe1, 0xfa, 0xb4, 0xbc, 0x35, 0x6a, 0xbb, 0xde, + 0xbb, 0x3b, 0x8f, 0xe1, 0xe5, 0x5e, 0x6d, 0xd6, + 0xd2, 0xa9, 0xea, 0x45, 0x7e, 0x91, 0xb2, 0xe6, + 0x64, 0x2f, 0xae, 0x69, 0xf9, 0xdb, 0xb5, 0x25, + 0x88, 0x54 + }; + const unsigned char encryption_key[33] = { + 0x02, 0x04, 0x25, 0x37, 0xe9, 0x13, 0xad, 0x74, + 0xc4, 0xbb, 0xd8, 0xda, 0x96, 0x07, 0xad, 0x3b, + 0x9c, 0xb2, 0x97, 0xd0, 0x8e, 0x01, 0x4a, 0xfc, + 0x51, 0x13, 0x30, 0x83, 0xf1, 0xbd, 0x68, 0x7a, + 0x62 + }; + const unsigned char decryption_key[32] = { + 0x32, 0x47, 0x19, 0xb5, 0x1f, 0xf2, 0x47, 0x4c, + 0x94, 0x38, 0xeb, 0x76, 0x49, 0x4b, 0x0d, 0xc0, + 0xbc, 0xce, 0xeb, 0x52, 0x9f, 0x0a, 0x54, 0x28, + 0xfd, 0x19, 0x8a, 0xd8, 0xf8, 0x86, 0xe9, 0x9c + }; + const unsigned char signature[64] = { + 0x2c, 0x63, 0x7c, 0xd7, 0x97, 0xdd, 0x8c, 0x2c, + 0xe2, 0x61, 0x90, 0x7e, 0xd4, 0x3e, 0x82, 0xd6, + 0xd1, 0xa4, 0x8c, 0xba, 0xbb, 0xbe, 0xce, 0x80, + 0x11, 0x33, 0xdd, 0x8d, 0x70, 0xa0, 0x1b, 0x14, + 0xb5, 0xf2, 0x43, 0x21, 0xf5, 0x50, 0xb7, 0xb9, + 0xdd, 0x06, 0xee, 0x4f, 0xcf, 0xd8, 0x2b, 0xda, + 0xd8, 0xb1, 0x42, 0xff, 0x93, 0xa7, 0x90, 0xcc, + 0x4d, 0x9f, 0x79, 0x62, 0xb3, 0x8c, 0x6a, 0x3b + }; + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 0); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 6 */ + /* serialization test */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 7 */ + /* serialization test */ + /* R can be above curve order */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, + 0x2c, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 8 */ + /* serialization test */ + /* R_a can be above curve order */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, + 0xfc, 0x2c, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 9 */ + /* serialization test */ + /* s_a cannot be zero */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 0); + } + { + /* Test vector 10 */ + /* serialization test */ + /* s_a too high */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, + 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, + 0x41, 0x41, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 0); + } +} + +/* Nonce function that returns constant 0 */ +static int ecdsa_adaptor_nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int ecdsa_adaptor_nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0, 32); + return 1; +} + +/* Nonce function that sets nonce to 0xFF...0xFF */ +static int ecdsa_adaptor_nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0xFF, 32); + return 1; +} + +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function + */ +static void nonce_function_ecdsa_adaptor_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t algolen) { + unsigned char nonces[2][32]; + CHECK(nonce_function_ecdsa_adaptor(nonces[0], args[0], args[1], args[2], args[3], algolen, args[4]) == 1); + testrand_flip(args[n_flip], n_bytes); + CHECK(nonce_function_ecdsa_adaptor(nonces[1], args[0], args[1], args[2], args[3], algolen, args[4]) == 1); + CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); +} + +static void run_nonce_function_ecdsa_adaptor_tests(void) { + static const unsigned char tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; + static const unsigned char aux_tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'a', 'u', 'x'}; + unsigned char algo[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; + size_t algolen = sizeof(algo); + static const unsigned char dleq_tag[] = {'D', 'L', 'E', 'Q'}; + secp256k1_sha256 sha_optimized; + unsigned char nonce[32]; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char pk[33]; + unsigned char aux_rand[32]; + unsigned char *args[5]; + int i; + + /* Check that hash initialized by + * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged has the expected + * state. */ + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha_optimized); + test_sha256_tag_midstate(&sha_optimized, tag, sizeof(tag)); + + /* Check that hash initialized by + * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux has the expected + * state. */ + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha_optimized); + test_sha256_tag_midstate(&sha_optimized, aux_tag, sizeof(aux_tag)); + + /* Check that hash initialized by + * secp256k1_nonce_function_dleq_sha256_tagged_aux has the expected + * state. */ + secp256k1_nonce_function_dleq_sha256_tagged(&sha_optimized); + test_sha256_tag_midstate(&sha_optimized, dleq_tag, sizeof(dleq_tag)); + + testrand_bytes_test(msg, sizeof(msg)); + testrand_bytes_test(key, sizeof(key)); + testrand_bytes_test(pk, sizeof(pk)); + testrand_bytes_test(aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = msg; + args[1] = key; + args[2] = pk; + args[3] = algo; + args[4] = aux_rand; + for (i = 0; i < COUNT; i++) { + nonce_function_ecdsa_adaptor_bitflip(args, 0, sizeof(msg), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 1, sizeof(key), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 2, sizeof(pk), algolen); + /* Flip algo special case "ECDSAadaptor/non" */ + nonce_function_ecdsa_adaptor_bitflip(args, 3, sizeof(algo), algolen); + /* Flip algo again */ + nonce_function_ecdsa_adaptor_bitflip(args, 3, sizeof(algo), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 4, sizeof(aux_rand), algolen); + } + + /* NULL algo is disallowed */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, NULL, 0, NULL) == 0); + /* Empty algo is fine */ + memset(algo, 0x00, algolen); + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); + /* Other algo is fine */ + memset(algo, 0xFF, algolen); + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); + /* dleq algo is fine */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, dleq_algo, sizeof(dleq_algo), NULL) == 1); + + /* Different algolen gives different nonce */ + for (i = 0; i < COUNT; i++) { + unsigned char nonce2[32]; + uint32_t offset = testrand_int(algolen - 1); + size_t algolen_tmp = (algolen + offset) % algolen; + + CHECK(nonce_function_ecdsa_adaptor(nonce2, msg, key, pk, algo, algolen_tmp, NULL) == 1); + CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); + } + + /* NULL aux_rand argument is allowed. */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); +} + +static void test_ecdsa_adaptor_api(void) { + secp256k1_pubkey pubkey; + secp256k1_pubkey enckey; + secp256k1_pubkey zero_pk; + secp256k1_ecdsa_signature sig; + unsigned char sk[32]; + unsigned char msg[32]; + unsigned char asig[162]; + unsigned char deckey[32]; + unsigned char zeros162[162] = { 0 }; + + /** setup **/ + testrand256(sk); + testrand256(msg); + testrand256(deckey); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, sk) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &enckey, deckey) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** main test body **/ + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, NULL, NULL) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ecdsa_adaptor_encrypt(STATIC_CTX, asig, sk, &enckey, msg, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, NULL, sk, &enckey, msg, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, NULL, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, asig, NULL, &enckey, msg, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, NULL, msg, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &zero_pk, msg, NULL, NULL)); + + /* Test bad nonce functions */ + memset(asig, 1, sizeof(asig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, ecdsa_adaptor_nonce_function_failing, NULL) == 0); + CHECK(secp256k1_memcmp_var(asig, zeros162, sizeof(asig)) == 0); + memset(asig, 1, sizeof(asig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, ecdsa_adaptor_nonce_function_0, NULL) == 0); + CHECK(secp256k1_memcmp_var(asig, zeros162, sizeof(asig)) == 0); + + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, asig, &pubkey, msg, &enckey) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, NULL, &pubkey, msg, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, asig, &pubkey, NULL, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, asig, &pubkey, msg, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, asig, NULL, msg, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, asig, &zero_pk, msg, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, asig, &pubkey, msg, &zero_pk)); + + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &sig, deckey, asig) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_decrypt(CTX, NULL, deckey, asig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_decrypt(CTX, &sig, NULL, asig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_decrypt(CTX, &sig, deckey, NULL)); + + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, asig, &enckey) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ecdsa_adaptor_recover(STATIC_CTX, deckey, &sig, asig, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, NULL, &sig, asig, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, NULL, asig, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, NULL, &enckey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, asig, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, asig, &zero_pk)); +} + +static void adaptor_tests_internal_impl(secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void* ndata) { + unsigned char seckey[32]; + secp256k1_pubkey pubkey; + unsigned char msg[32]; + unsigned char deckey[32]; + secp256k1_pubkey enckey; + unsigned char adaptor_sig[162]; + secp256k1_ecdsa_signature sig; + unsigned char zeros162[162] = { 0 }; + unsigned char zeros64[64] = { 0 }; + unsigned char big[32]; + + testrand256(seckey); + testrand256(msg); + testrand256(deckey); + + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, seckey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &enckey, deckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, seckey, &enckey, msg, noncefp, ndata) == 1); + + { + unsigned char adaptor_sig_tmp[162] = { 0 }; + + /* Test overflowing seckey */ + memset(big, 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig_tmp, big, &enckey, msg, NULL, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, zeros162, sizeof(adaptor_sig)) == 0); + } + { + /* Test adaptor_sig_serialize roundtrip */ + secp256k1_ge r, rp; + secp256k1_scalar sigr; + secp256k1_scalar sp; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); + + secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)) == 0); + } + { + /* Test adaptor_sig_deserialize */ + secp256k1_ge r, rp; + secp256k1_scalar sigr; + secp256k1_scalar sp; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); + + /* r */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[0], 0xFF, 33); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* sigr */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[1], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 1); + memset(&adaptor_sig_tmp[1], 0, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* rp */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, &rp, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[33], 0xFF, 33); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, &rp, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* sp */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, &sp, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[66], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, &sp, NULL, NULL, adaptor_sig_tmp) == 0); + + /* dleq_proof_e */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, &dleq_proof_e, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[98], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, &dleq_proof_e, NULL, adaptor_sig_tmp) == 1); + + /* dleq_proof_s */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, NULL, &dleq_proof_s, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[130], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, NULL, &dleq_proof_s, adaptor_sig_tmp) == 0); + } + + /* Test adaptor_sig_verify */ + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig, &pubkey, msg, &enckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig, &enckey, msg, &enckey) == 0); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig, &pubkey, msg, &pubkey) == 0); + { + /* Test failed adaptor sig deserialization */ + unsigned char adaptor_sig_tmp[162]; + memset(&adaptor_sig_tmp, 0xFF, 162); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig_tmp, &pubkey, msg, &enckey) == 0); + } + { + /* Test that any flipped bit in the adaptor signature will make + * verification fail */ + unsigned char adaptor_sig_tmp[162]; + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + rand_flip_bit(&adaptor_sig_tmp[1], sizeof(adaptor_sig_tmp) - 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig_tmp, &pubkey, msg, &enckey) == 0); + } + { + unsigned char msg_tmp[32]; + memcpy(msg_tmp, msg, sizeof(msg_tmp)); + rand_flip_bit(msg_tmp, sizeof(msg_tmp)); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig, &pubkey, msg_tmp, &enckey) == 0); + } + { + /* Verification must check that the derived R' is not equal to the point at + * infinity before negating it. R' is derived as follows: + * + * R' == s'⁻¹(m * G + R.x * X) + * + * When the base point, G, is multiplied by the subgroup order, q, the + * result is the point at infinity, 0: + * + * q * G = 0 + * + * Thus, if we set s' equal to R.x, m equal to (q - 1) * R.x, and X equal to + * G, then our derived R' will be 0: + * + * R' = R.x⁻¹((q - 1 * R.x) * G + R.x * G) = q * G = 0 */ + + /* t := q - 1 */ + const unsigned char target[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 + }; + unsigned char seckey_tmp[32] = { 0 }; + unsigned char msg_tmp[32]; + unsigned char adaptor_sig_tmp[162]; + secp256k1_pubkey pubkey_tmp; + secp256k1_scalar sigr, t, m; + + /* m := t * sigr */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + secp256k1_scalar_set_b32(&t, target, NULL); + secp256k1_scalar_mul(&m, &t, &sigr); + secp256k1_scalar_get_b32(msg_tmp, &m); + + /* X := G */ + seckey_tmp[31] = 1; + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey_tmp, seckey_tmp) == 1); + + /* sp := sigr */ + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memcpy(&adaptor_sig_tmp[66], &adaptor_sig_tmp[1], 32); + + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig_tmp, &pubkey_tmp, msg_tmp, &enckey) == 0); + } + + /* Test decryption */ + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &sig, deckey, adaptor_sig) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg, &pubkey) == 1); + + { + /* Test overflowing decryption key */ + secp256k1_ecdsa_signature s; + memset(big, 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &s, big, adaptor_sig) == 0); + CHECK(secp256k1_memcmp_var(&s.data[0], zeros64, sizeof(&s.data[0])) == 0); + } + { + /* Test key recover */ + unsigned char decryption_key_tmp[32]; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_recover(CTX, decryption_key_tmp, &sig, adaptor_sig, &enckey) == 1); + CHECK(secp256k1_memcmp_var(deckey, decryption_key_tmp, sizeof(deckey)) == 0); + + /* Test failed sp deserialization */ + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[66], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_recover(CTX, decryption_key_tmp, &sig, adaptor_sig_tmp, &enckey) == 0); + } +} + +static void adaptor_tests_internal(void) { + adaptor_tests_internal_impl(NULL, NULL); + /* Since the same nonce function with different algo arguments is used + * both for the adaptor sig secret nonce and the dleq secret nonce, + * but ecdsa_adaptor_nonce_function_overflowing ignores the algo arg + * (in violation of the documented API contract), the resulting secret + * nonces will be the same. */ + adaptor_tests_internal_impl(ecdsa_adaptor_nonce_function_overflowing, NULL); +} + +static void multi_hop_lock_tests_internal(void) { + unsigned char seckey_a[32]; + unsigned char seckey_b[32]; + unsigned char pop[32]; + unsigned char tx_ab[32]; + unsigned char tx_bc[32]; + unsigned char buf[32]; + unsigned char asig_ab[162]; + unsigned char asig_bc[162]; + secp256k1_pubkey pubkey_pop; + secp256k1_pubkey pubkey_a, pubkey_b; + secp256k1_pubkey l, r; + secp256k1_ge l_ge, r_ge; + secp256k1_scalar t1, t2, tp; + secp256k1_scalar deckey; + secp256k1_ecdsa_signature sig_ab, sig_bc; + + testrand256(seckey_a); + testrand256(seckey_b); + + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey_a, seckey_a)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey_b, seckey_b)); + + /* Carol setup */ + /* Proof of payment */ + testrand256(pop); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey_pop, pop)); + + /* Alice setup */ + testrand256(tx_ab); + rand_scalar(&t1); + rand_scalar(&t2); + secp256k1_scalar_add(&tp, &t1, &t2); + /* Left lock */ + secp256k1_pubkey_load(CTX, &l_ge, &pubkey_pop); + CHECK(secp256k1_eckey_pubkey_tweak_add(&l_ge, &t1)); + secp256k1_pubkey_save(&l, &l_ge); + /* Right lock */ + secp256k1_pubkey_load(CTX, &r_ge, &pubkey_pop); + CHECK(secp256k1_eckey_pubkey_tweak_add(&r_ge, &tp)); + secp256k1_pubkey_save(&r, &r_ge); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig_ab, seckey_a, &l, tx_ab, NULL, NULL)); + + /* Bob setup */ + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, asig_ab, &pubkey_a, tx_ab, &l)); + testrand256(tx_bc); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig_bc, seckey_b, &r, tx_bc, NULL, NULL)); + + /* Carol decrypt */ + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, asig_bc, &pubkey_b, tx_bc, &r)); + secp256k1_scalar_set_b32(&deckey, pop, NULL); + secp256k1_scalar_add(&deckey, &deckey, &tp); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &sig_bc, buf, asig_bc)); + CHECK(secp256k1_ecdsa_verify(CTX, &sig_bc, tx_bc, &pubkey_b)); + + /* Bob recover and decrypt */ + CHECK(secp256k1_ecdsa_adaptor_recover(CTX, buf, &sig_bc, asig_bc, &r)); + secp256k1_scalar_set_b32(&deckey, buf, NULL); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&deckey, &deckey, &t2); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_ecdsa_adaptor_decrypt(CTX, &sig_ab, buf, asig_ab)); + CHECK(secp256k1_ecdsa_verify(CTX, &sig_ab, tx_ab, &pubkey_a)); + + /* Alice recover and derive proof of payment */ + CHECK(secp256k1_ecdsa_adaptor_recover(CTX, buf, &sig_ab, asig_ab, &l)); + secp256k1_scalar_set_b32(&deckey, buf, NULL); + secp256k1_scalar_negate(&t1, &t1); + secp256k1_scalar_add(&deckey, &deckey, &t1); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0); +} + +static void adaptor_test_issue335(void) { + /* Inputs that will trigger R1==infinity in secp256k1_dleq_verify. */ + unsigned char adaptor_sig[162] = { + 0x03, 0x63, 0x3D, 0x56, 0xAB, 0xEE, 0x6F, 0x36, 0xE6, 0x07, 0xC6, 0x04, + 0x2C, 0x68, 0xB4, 0x09, 0xBE, 0x4F, 0x3D, 0x56, 0x3A, 0x51, 0x7B, 0xCA, + 0x95, 0xE6, 0xD9, 0x48, 0x1E, 0x95, 0xD0, 0xD6, 0xC6, 0x03, 0x91, 0x66, + 0xC2, 0x89, 0xB9, 0xF9, 0x05, 0xE5, 0x5F, 0x9E, 0x3D, 0xF9, 0xF6, 0x9D, + 0x7F, 0x35, 0x6B, 0x4A, 0x22, 0x09, 0x5F, 0x89, 0x4F, 0x47, 0x15, 0x71, + 0x4A, 0xA4, 0xB5, 0x66, 0x06, 0xAF, 0x84, 0x40, 0xB2, 0x83, 0x34, 0xF6, + 0x74, 0x18, 0xD8, 0x3D, 0x5C, 0xDC, 0x14, 0x0A, 0xAB, 0x22, 0x2B, 0x19, + 0x15, 0x13, 0xC3, 0x5D, 0x9C, 0xBC, 0x6D, 0x89, 0x1C, 0xB5, 0x38, 0x74, + 0xB0, 0xCE, 0x5F, 0x34, 0xD7, 0xA0, 0xA9, 0x89, 0x7A, 0x19, 0x45, 0x77, + 0xBD, 0x5F, 0x0F, 0x31, 0xD8, 0x3B, 0x50, 0xC6, 0x2A, 0x4D, 0xCF, 0x4D, + 0xCB, 0x91, 0x71, 0x8C, 0x66, 0xAE, 0xB8, 0xE2, 0x1A, 0x01, 0x65, 0x05, + 0x2D, 0x93, 0x73, 0x97, 0xB7, 0x66, 0xC4, 0xEB, 0x23, 0x8D, 0x3B, 0x55, + 0xA2, 0x3D, 0xF8, 0x8E, 0x56, 0x84, 0x87, 0x10, 0x76, 0x18, 0xC2, 0xE8, + 0x35, 0xF9, 0x4E, 0x2A, 0x29, 0xB2 + }; + unsigned char msg[32] = { + 0x38, 0x9C, 0x43, 0x7B, 0x37, 0xBB, 0x6F, 0x74, 0x09, 0x3D, 0x69, + 0x3E, 0x3D, 0x9B, 0x4F, 0xC7, 0x9D, 0xDF, 0xA9, 0x33, 0x39, 0x8C, + 0x90, 0x03, 0x95, 0x2D, 0x67, 0xCD, 0xD9, 0x99, 0xDC, 0x55 + }; + unsigned char deckey[32] = { + 0x4A, 0x0B, 0x45, 0xA7, 0x4F, 0xBF, 0x49, 0xC3, 0x4B, 0x7C, 0xE0, + 0x8E, 0x34, 0x89, 0xFB, 0xEA, 0xD5, 0x41, 0xA1, 0x2E, 0xBE, 0x13, + 0x3F, 0xD6, 0x8E, 0x24, 0x86, 0x60, 0x1B, 0x19, 0xC1, 0xB5 + }; + unsigned char seckey[32] = { + 0x12, 0xDB, 0x27, 0x33, 0x51, 0x3D, 0xD9, 0xDF, 0x6A, 0x3C, 0x5A, + 0xEC, 0x3C, 0xA9, 0xF5, 0xDA, 0xA7, 0x3E, 0xB4, 0x61, 0xC8, 0xBB, + 0x12, 0xB7, 0xD4, 0xAA, 0xF5, 0x9A, 0xE9, 0xE5, 0x8B, 0xB7 + }; + secp256k1_pubkey pubkey; + secp256k1_pubkey enckey; + + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, seckey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &enckey, deckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig, &pubkey, msg, &enckey) == 0); + + /* This explains how the inputs were obtained. */ + { + unsigned char adaptor_sig_tmp[sizeof(adaptor_sig)]; + /* Since the same nonce function with different algo arguments is used + * both for the adaptor sig secret nonce and the dleq secret nonce, + * but ecdsa_adaptor_nonce_function_overflowing ignores the algo arg + * (in violation of the documented API contract), the resulting secret + * nonces will be the same. */ + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig_tmp, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_overflowing, NULL) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig_tmp, &pubkey, msg, &enckey) == 1); + /* Increment the last least significant bit of e. */ + adaptor_sig_tmp[129] = 0x01; + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig)) == 0); + } +} + +/* --- Test registry --- */ +REPEAT_TEST(dleq_tests) +REPEAT_TEST(adaptor_tests) +REPEAT_TEST(multi_hop_lock_tests) + +static const struct tf_test_entry tests_ecdsa_adaptor[] = { + CASE1(run_nonce_function_ecdsa_adaptor_tests), + CASE1(test_ecdsa_adaptor_api), + CASE1(test_ecdsa_adaptor_spec_vectors), + CASE1(dleq_tests), + CASE1(adaptor_tests), + CASE1(multi_hop_lock_tests), + CASE1(adaptor_test_issue335), +}; + +#endif /* SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H */ diff --git a/src/modules/ecdsa_s2c/Makefile.am.include b/src/modules/ecdsa_s2c/Makefile.am.include new file mode 100644 index 00000000..b4939a91 --- /dev/null +++ b/src/modules/ecdsa_s2c/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_ecdsa_s2c.h +noinst_HEADERS += src/modules/ecdsa_s2c/main_impl.h +noinst_HEADERS += src/modules/ecdsa_s2c/tests_impl.h diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h new file mode 100644 index 00000000..cdc54737 --- /dev/null +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -0,0 +1,187 @@ +/********************************************************************** + * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_S2C_MAIN_H +#define SECP256K1_MODULE_ECDSA_S2C_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ecdsa_s2c.h" + +static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) { + secp256k1_pubkey_save((secp256k1_pubkey*) opening, ge); +} + +static int secp256k1_ecdsa_s2c_opening_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_ecdsa_s2c_opening* opening) { + return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey*) opening); +} + +int secp256k1_ecdsa_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* input33) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(input33 != NULL); + return secp256k1_ec_pubkey_parse(ctx, (secp256k1_pubkey*) opening, input33, 33); +} + +int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char* output33, const secp256k1_ecdsa_s2c_opening* opening) { + size_t out_len = 33; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output33 != NULL); + ARG_CHECK(opening != NULL); + return secp256k1_ec_pubkey_serialize(ctx, output33, &out_len, (const secp256k1_pubkey*) opening, SECP256K1_EC_COMPRESSED); +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("s2c/ecdsa/point")||SHA256("s2c/ecdsa/point"). */ +static void secp256k1_s2c_ecdsa_point_sha256_tagged(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0xa9b21c7bul, 0x358c3e3eul, 0x0b6863d1ul, 0xc62b2035ul, + 0xb44b40ceul, 0x254a8912ul, 0x0f85d0d4ul, 0x8a5bf91cul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("s2c/ecdsa/data")||SHA256("s2c/ecdsa/data"). */ +static void secp256k1_s2c_ecdsa_data_sha256_tagged(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0xfeefd675ul, 0x73166c99ul, 0xe2309cb8ul, 0x6d458113ul, + 0x01d3a512ul, 0x00e18112ul, 0x37ee0874ul, 0x421fc55ful + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* signature, secp256k1_ecdsa_s2c_opening* s2c_opening, const unsigned char + *msg32, const unsigned char *seckey, const unsigned char* s2c_data32) { + secp256k1_scalar r, s; + int ret; + unsigned char ndata[32]; + secp256k1_sha256 s2c_sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(s2c_data32 != NULL); + + /* Provide `s2c_data32` to the nonce function as additional data to + * derive the nonce. It is first hashed because it should be possible + * to derive nonces even if only a SHA256 commitment to the data is + * known. This is important in the ECDSA anti-exfil protocol. */ + secp256k1_s2c_ecdsa_data_sha256_tagged(&s2c_sha); + secp256k1_sha256_write(&s2c_sha, s2c_data32, 32); + secp256k1_sha256_finalize(&s2c_sha, ndata); + secp256k1_sha256_clear(&s2c_sha); + + secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha); + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, &s2c_sha, s2c_opening, s2c_data32, msg32, seckey, NULL, ndata); + secp256k1_scalar_cmov(&r, &secp256k1_scalar_zero, !ret); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_zero, !ret); + secp256k1_ecdsa_signature_save(signature, &r, &s); + return ret; +} + +int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp256k1_ecdsa_signature* sig, const unsigned char* data32, const secp256k1_ecdsa_s2c_opening* opening) { + secp256k1_ge commitment_ge; + secp256k1_ge original_pubnonce_ge; + unsigned char x_bytes[32]; + secp256k1_scalar sigr, sigs, x_scalar; + secp256k1_sha256 s2c_sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(data32 != NULL); + ARG_CHECK(opening != NULL); + + if (!secp256k1_ecdsa_s2c_opening_load(ctx, &original_pubnonce_ge, opening)) { + return 0; + } + secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha); + if (!secp256k1_ec_commit(&commitment_ge, &original_pubnonce_ge, &s2c_sha, data32, 32)) { + return 0; + } + + /* Check that sig_r == commitment_x (mod n) + * sig_r is the x coordinate of R represented by a scalar. + * commitment_x is the x coordinate of the commitment (field element). + * + * Note that we are only checking the x-coordinate -- this is because the y-coordinate + * is not part of the ECDSA signature (and therefore not part of the commitment!) + */ + secp256k1_ecdsa_signature_load(ctx, &sigr, &sigs, sig); + + secp256k1_fe_normalize(&commitment_ge.x); + secp256k1_fe_get_b32(x_bytes, &commitment_ge.x); + /* Do not check overflow; overflowing a scalar does not affect whether + * or not the R value is a cryptographic commitment, only whether it + * is a valid R value for an ECDSA signature. If users care about that + * they should use `ecdsa_verify` or `anti_exfil_host_verify`. In other + * words, this check would be (at best) unnecessary, and (at worst) + * insufficient. */ + secp256k1_scalar_set_b32(&x_scalar, x_bytes, NULL); + return secp256k1_scalar_eq(&sigr, &x_scalar); +} + +/*** anti-exfil ***/ +int secp256k1_ecdsa_anti_exfil_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) { + secp256k1_sha256 sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(rand_commitment32 != NULL); + ARG_CHECK(rand32 != NULL); + + secp256k1_s2c_ecdsa_data_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, rand32, 32); + secp256k1_sha256_finalize(&sha, rand_commitment32); + secp256k1_sha256_clear(&sha); + return 1; +} + +int secp256k1_ecdsa_anti_exfil_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) { + unsigned char nonce32[32]; + secp256k1_scalar k; + secp256k1_gej rj; + secp256k1_ge r; + unsigned int count = 0; + int is_nonce_valid = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(opening != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(rand_commitment32 != NULL); + + memset(nonce32, 0, 32); + while (!is_nonce_valid) { + /* cast to void* removes const qualifier, but secp256k1_nonce_function_default does not modify it */ + if (!secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, (void*)rand_commitment32, count)) { + secp256k1_callback_call(&ctx->error_callback, "(cryptographically unreachable) generated bad nonce"); + } + is_nonce_valid = secp256k1_scalar_set_b32_seckey(&k, nonce32); + /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */ + secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); + count++; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + secp256k1_ecdsa_s2c_opening_save(opening, &r); + secp256k1_memclear_explicit(nonce32, 32); + secp256k1_scalar_clear(&k); + return 1; +} + +int secp256k1_anti_exfil_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) { + return secp256k1_ecdsa_s2c_sign(ctx, sig, NULL, msg32, seckey, host_data32); +} + +int secp256k1_anti_exfil_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) { + return secp256k1_ecdsa_s2c_verify_commit(ctx, sig, host_data32, opening) && + secp256k1_ecdsa_verify(ctx, sig, msg32, pubkey); +} + +#endif /* SECP256K1_ECDSA_S2C_MAIN_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h new file mode 100644 index 00000000..d13e5164 --- /dev/null +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -0,0 +1,338 @@ +/********************************************************************** + * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_S2C_TESTS_H +#define SECP256K1_MODULE_ECDSA_S2C_TESTS_H + +#include "../../../include/secp256k1_ecdsa_s2c.h" +#include "../../unit_test.h" + +static void test_ecdsa_s2c_tagged_hash(void) { + unsigned char tag_data[] = {'s', '2', 'c', '/', 'e', 'c', 'd', 's', 'a', '/', 'd', 'a', 't', 'a'}; + unsigned char tag_point[] = {'s', '2', 'c', '/', 'e', 'c', 'd', 's', 'a', '/', 'p', 'o', 'i', 'n', 't'}; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char output[32]; + unsigned char output_optimized[32]; + + secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data)); + secp256k1_s2c_ecdsa_data_sha256_tagged(&sha_optimized); + secp256k1_sha256_finalize(&sha, output); + secp256k1_sha256_finalize(&sha_optimized, output_optimized); + CHECK(secp256k1_memcmp_var(output, output_optimized, 32) == 0); + + secp256k1_sha256_initialize_tagged(&sha, tag_point, sizeof(tag_point)); + secp256k1_s2c_ecdsa_point_sha256_tagged(&sha_optimized); + secp256k1_sha256_finalize(&sha, output); + secp256k1_sha256_finalize(&sha_optimized, output_optimized); + CHECK(secp256k1_memcmp_var(output, output_optimized, 32) == 0); +} + +static void run_s2c_opening_test(void) { + int i = 0; + unsigned char output[33]; + unsigned char input[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + secp256k1_ecdsa_s2c_opening opening; + + /* First parsing, then serializing works */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, input) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(CTX, output, &opening) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, input) == 1); + + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_opening_parse(CTX, NULL, input)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, NULL)); + CHECK(secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, input) == 1); + + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_opening_serialize(CTX, NULL, &opening)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_opening_serialize(CTX, output, NULL)); + + /* Invalid pubkey makes parsing fail but they are not API errors */ + input[0] = 0; /* bad oddness bit */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, input) == 0); + input[0] = 2; + input[31] = 1; /* point not on the curve */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, input) == 0); + + /* Try parsing and serializing a bunch of openings */ + for (i = 0; i < COUNT; i++) { + /* This is expected to fail in about 50% of iterations because the + * points' x-coordinates are uniformly random */ + if (secp256k1_ecdsa_s2c_opening_parse(CTX, &opening, input) == 1) { + CHECK(secp256k1_ecdsa_s2c_opening_serialize(CTX, output, &opening) == 1); + CHECK(secp256k1_memcmp_var(output, input, sizeof(output)) == 0); + } + testrand256(&input[1]); + /* Set pubkey oddness tag to first bit of input[1] */ + input[0] = (input[1] & 1) + 2; + } +} + +static void test_ecdsa_s2c_api(void) { + secp256k1_ecdsa_s2c_opening s2c_opening; + secp256k1_ecdsa_signature sig; + const unsigned char msg[] = {'m', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm'}; + const unsigned char sec[] = {'s', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's'}; + const unsigned char s2c_data[] = {'d', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'}; + const unsigned char hostrand[] = {'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r'}; + unsigned char hostrand_commitment[32]; + secp256k1_pubkey pk; + + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sec)); + + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_sign(CTX, NULL, &s2c_opening, msg, sec, s2c_data)); + /* NULL opening is not an API error */ + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &sig, NULL, msg, sec, s2c_data) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_sign(CTX, &sig, &s2c_opening, NULL, sec, s2c_data)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_sign(CTX, &sig, &s2c_opening, msg, NULL, s2c_data)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_sign(CTX, &sig, &s2c_opening, msg, sec, NULL)); + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &sig, &s2c_opening, msg, sec, s2c_data) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ecdsa_s2c_sign(STATIC_CTX, &sig, &s2c_opening, msg, sec, s2c_data)); + + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg, &pk) == 1); + + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_verify_commit(CTX, NULL, s2c_data, &s2c_opening)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_verify_commit(CTX, &sig, NULL, &s2c_opening)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_s2c_verify_commit(CTX, &sig, s2c_data, NULL)); + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &sig, s2c_data, &s2c_opening) == 1); + /* wrong data is not an API error */ + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &sig, sec, &s2c_opening) == 0); + + /* Signing with NULL s2c_opening gives the same result */ + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &sig, NULL, msg, sec, s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &sig, s2c_data, &s2c_opening) == 1); + + /* anti-exfil */ + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_anti_exfil_host_commit(CTX, NULL, hostrand)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_anti_exfil_host_commit(CTX, hostrand_commitment, NULL)); + CHECK(secp256k1_ecdsa_anti_exfil_host_commit(CTX, hostrand_commitment, hostrand) == 1); + + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_anti_exfil_signer_commit(CTX, NULL, msg, sec, hostrand_commitment)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_anti_exfil_signer_commit(CTX, &s2c_opening, NULL, sec, hostrand_commitment)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_anti_exfil_signer_commit(CTX, &s2c_opening, msg, NULL, hostrand_commitment)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_anti_exfil_signer_commit(CTX, &s2c_opening, msg, sec, NULL)); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(CTX, &s2c_opening, msg, sec, hostrand_commitment) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ecdsa_anti_exfil_signer_commit(STATIC_CTX, &s2c_opening, msg, sec, hostrand_commitment)); + + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_sign(CTX, NULL, msg, sec, hostrand)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_sign(CTX, &sig, NULL, sec, hostrand)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_sign(CTX, &sig, msg, NULL, hostrand)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_sign(CTX, &sig, msg, sec, NULL)); + CHECK(secp256k1_anti_exfil_sign(CTX, &sig, msg, sec, hostrand) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_anti_exfil_sign(STATIC_CTX, &sig, msg, sec, hostrand)); + + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_host_verify(CTX, NULL, msg, &pk, hostrand, &s2c_opening)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_host_verify(CTX, &sig, NULL, &pk, hostrand, &s2c_opening)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_host_verify(CTX, &sig, msg, NULL, hostrand, &s2c_opening)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_host_verify(CTX, &sig, msg, &pk, NULL, &s2c_opening)); + CHECK_ILLEGAL(CTX, secp256k1_anti_exfil_host_verify(CTX, &sig, msg, &pk, hostrand, NULL)); + CHECK(secp256k1_anti_exfil_host_verify(CTX, &sig, msg, &pk, hostrand, &s2c_opening) == 1); +} + +/* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */ +typedef struct { + /* Data to commit to */ + unsigned char s2c_data[32]; + /* Original nonce */ + unsigned char expected_s2c_opening[33]; + /* Original nonce (anti-exfil protocol, which mixes in host randomness) */ + unsigned char expected_s2c_exfil_opening[33]; +} ecdsa_s2c_test; + +static ecdsa_s2c_test ecdsa_s2c_tests[] = { + { + {0x1b, 0xf6, 0xfb, 0x42, 0xf4, 0x1e, 0xb8, 0x76, 0xc4, 0xd7, 0xaa, 0x0d, 0x67, 0x24, 0x2b, 0x00, 0xba, 0xab, 0x99, 0xdc, 0x20, 0x84, 0x49, 0x3e, 0x4e, 0x63, 0x27, 0x7f, 0xa1, 0xf7, 0x7f, 0x22}, + {0x03, 0xf0, 0x30, 0xde, 0xf3, 0x18, 0x8c, 0x0f, 0x56, 0xfc, 0xea, 0x87, 0x43, 0x5b, 0x30, 0x76, 0x43, 0xf4, 0x5d, 0xaf, 0xe2, 0x2c, 0xbc, 0x82, 0xfd, 0x56, 0x03, 0x4f, 0xae, 0x97, 0x41, 0x7d, 0x3a}, + {0x02, 0xdf, 0x63, 0x75, 0x5d, 0x1f, 0x32, 0x92, 0xbf, 0xfe, 0xd8, 0x29, 0x86, 0xb1, 0x06, 0x49, 0x7c, 0x93, 0xb1, 0xf8, 0xbd, 0xc0, 0x45, 0x4b, 0x6b, 0x0b, 0x0a, 0x47, 0x79, 0xc0, 0xef, 0x71, 0x88}, + }, + { + {0x35, 0x19, 0x9a, 0x8f, 0xbf, 0x84, 0xad, 0x6e, 0xf6, 0x9a, 0x18, 0x4c, 0x1b, 0x19, 0x28, 0x5b, 0xef, 0xbe, 0x06, 0xe6, 0x0b, 0x62, 0x64, 0xe6, 0xd3, 0x73, 0x89, 0x3f, 0x68, 0x55, 0xe2, 0x4a}, + {0x03, 0x90, 0x17, 0x17, 0xce, 0x7c, 0x74, 0x84, 0xa2, 0xce, 0x1b, 0x7d, 0xc7, 0x40, 0x3b, 0x14, 0xe0, 0x35, 0x49, 0x71, 0x39, 0x3e, 0xc0, 0x92, 0xa7, 0xf3, 0xe0, 0xc8, 0xe4, 0xe2, 0xd2, 0x63, 0x9d}, + {0x02, 0xc0, 0x4a, 0xc7, 0xf7, 0x71, 0xe8, 0xeb, 0xdb, 0xf3, 0x15, 0xff, 0x5e, 0x58, 0xb7, 0xfe, 0x95, 0x16, 0x10, 0x21, 0x03, 0x50, 0x00, 0x66, 0x17, 0x2c, 0x4f, 0xac, 0x5b, 0x20, 0xf9, 0xe0, 0xea}, + }, +}; + +static void test_ecdsa_s2c_fixed_vectors(void) { + const unsigned char privkey[32] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + }; + const unsigned char message[32] = { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + }; + size_t i; + + for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) { + secp256k1_ecdsa_s2c_opening s2c_opening; + unsigned char opening_ser[33]; + const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; + secp256k1_ecdsa_signature signature; + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(CTX, opening_ser, &s2c_opening) == 1); + CHECK(secp256k1_memcmp_var(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0); + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &signature, test->s2c_data, &s2c_opening) == 1); + } +} + +static void test_ecdsa_s2c_sign_verify(void) { + unsigned char privkey[32]; + secp256k1_pubkey pubkey; + unsigned char message[32]; + unsigned char noncedata[32]; + unsigned char s2c_data[32]; + unsigned char s2c_data2[32]; + secp256k1_ecdsa_signature signature; + secp256k1_ecdsa_s2c_opening s2c_opening; + + /* Generate a random key, message, noncedata and s2c_data. */ + { + secp256k1_scalar key; + testutil_random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); + + testrand256_test(message); + testrand256_test(noncedata); + testrand256_test(s2c_data); + testrand256_test(s2c_data2); + } + + { /* invalid privkeys */ + unsigned char zero_privkey[32] = {0}; + unsigned char overflow_privkey[32] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, NULL, message, zero_privkey, s2c_data) == 0); + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, NULL, message, overflow_privkey, s2c_data) == 0); + } + /* Check that the sign-to-contract signature is valid, with s2c_data. Also check the commitment. */ + { + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, &s2c_opening, message, privkey, s2c_data) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature, message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &signature, s2c_data, &s2c_opening) == 1); + } + /* Check that an invalid commitment does not verify */ + { + unsigned char sigbytes[64]; + size_t i; + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, &s2c_opening, message, privkey, s2c_data) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature, message, &pubkey) == 1); + + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, sigbytes, &signature) == 1); + for(i = 0; i < 32; i++) { + /* change one byte */ + sigbytes[i] = (((int)sigbytes[i]) + 1) % 256; + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &signature, sigbytes) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &signature, s2c_data, &s2c_opening) == 0); + /* revert */ + sigbytes[i] = (((int)sigbytes[i]) + 255) % 256; + } + } +} + +static void test_ecdsa_anti_exfil_signer_commit(void) { + size_t i; + unsigned char privkey[32] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + }; + unsigned char message[32] = { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + }; + /* Check that original pubnonce is derived from s2c_data */ + for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) { + secp256k1_ecdsa_s2c_opening s2c_opening; + unsigned char buf[33]; + const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(CTX, &s2c_opening, message, privkey, test->s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(CTX, buf, &s2c_opening) == 1); + CHECK(secp256k1_memcmp_var(test->expected_s2c_exfil_opening, buf, sizeof(buf)) == 0); + } +} + +/* This tests the full ECDSA Anti-Exfil Protocol */ +static void test_ecdsa_anti_exfil(void) { + unsigned char signer_privkey[32]; + unsigned char host_msg[32]; + unsigned char host_commitment[32]; + unsigned char host_nonce_contribution[32]; + secp256k1_pubkey signer_pubkey; + secp256k1_ecdsa_signature signature; + secp256k1_ecdsa_s2c_opening s2c_opening; + + /* Generate a random key, message. */ + { + secp256k1_scalar key; + testutil_random_scalar_order_test(&key); + secp256k1_scalar_get_b32(signer_privkey, &key); + CHECK(secp256k1_ec_pubkey_create(CTX, &signer_pubkey, signer_privkey) == 1); + testrand256_test(host_msg); + testrand256_test(host_nonce_contribution); + } + + /* Protocol step 1. */ + CHECK(secp256k1_ecdsa_anti_exfil_host_commit(CTX, host_commitment, host_nonce_contribution) == 1); + /* Protocol step 2. */ + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(CTX, &s2c_opening, host_msg, signer_privkey, host_commitment) == 1); + /* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */ + /* Protocol step 4. */ + CHECK(secp256k1_anti_exfil_sign(CTX, &signature, host_msg, signer_privkey, host_nonce_contribution) == 1); + /* Protocol step 5. */ + CHECK(secp256k1_anti_exfil_host_verify(CTX, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + /* Protocol step 5 (explicitly) */ + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &signature, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature, host_msg, &signer_pubkey) == 1); + + { /* host_verify: commitment does not match */ + unsigned char sigbytes[64]; + size_t i; + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, sigbytes, &signature) == 1); + for(i = 0; i < 32; i++) { + /* change one byte */ + sigbytes[i] += 1; + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &signature, sigbytes) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(CTX, &signature, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(CTX, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + /* revert */ + sigbytes[i] -= 1; + } + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &signature, sigbytes) == 1); + } + { /* host_verify: message does not match */ + unsigned char bad_msg[32]; + testrand256_test(bad_msg); + CHECK(secp256k1_anti_exfil_host_verify(CTX, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_anti_exfil_host_verify(CTX, &signature, bad_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + } + { /* s2c_sign: host provided data that didn't match commitment */ + secp256k1_ecdsa_s2c_opening orig_opening = s2c_opening; + unsigned char bad_nonce_contribution[32] = { 1, 2, 3, 4 }; + CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, &s2c_opening, host_msg, signer_privkey, bad_nonce_contribution) == 1); + /* good signature but the opening (original public nonce does not match the original */ + CHECK(secp256k1_ecdsa_verify(CTX, &signature, host_msg, &signer_pubkey) == 1); + CHECK(secp256k1_anti_exfil_host_verify(CTX, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(CTX, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_memcmp_var(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0); + } +} + +/* --- Test registry --- */ +static const struct tf_test_entry tests_ecdsa_s2c[] = { + CASE1(run_s2c_opening_test), + CASE1(test_ecdsa_s2c_tagged_hash), + CASE1(test_ecdsa_s2c_api), + CASE1(test_ecdsa_s2c_fixed_vectors), + CASE1(test_ecdsa_s2c_sign_verify), + CASE1(test_ecdsa_anti_exfil_signer_commit), + CASE1(test_ecdsa_anti_exfil) +}; + +#endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/modules/extrakeys/Makefile.am.include b/src/modules/extrakeys/Makefile.am.include index 0d901ec1..622d8bb4 100644 --- a/src/modules/extrakeys/Makefile.am.include +++ b/src/modules/extrakeys/Makefile.am.include @@ -1,4 +1,4 @@ include_HEADERS += include/secp256k1_extrakeys.h 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 +noinst_HEADERS += src/modules/extrakeys/main_impl.h \ No newline at end of file diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include new file mode 100644 index 00000000..f1bee7b3 --- /dev/null +++ b/src/modules/generator/Makefile.am.include @@ -0,0 +1,11 @@ +include_HEADERS += include/secp256k1_generator.h +noinst_HEADERS += src/modules/generator/pedersen.h +noinst_HEADERS += src/modules/generator/pedersen_impl.h +noinst_HEADERS += src/modules/generator/main_impl.h +noinst_HEADERS += src/modules/generator/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_generator +bench_generator_SOURCES = src/bench_generator.c +bench_generator_LDADD = libsecp256k1.la +bench_generator_CPPFLAGS = $(SECP_CONFIG_DEFINES) +endif diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h new file mode 100644 index 00000000..c58d66fe --- /dev/null +++ b/src/modules/generator/main_impl.h @@ -0,0 +1,466 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra & Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_GENERATOR_MAIN_H +#define SECP256K1_MODULE_GENERATOR_MAIN_H + +#include + +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../scalar.h" + +#include "../generator/pedersen_impl.h" + +/** Alternative generator for secp256k1. + * This is the sha256 of 'g' after standard encoding (without compression), + * which happens to be a point on the curve. More precisely, the generator is + * derived by running the following script with the sage mathematics software. + + import hashlib + F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' + H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(bytes.fromhex(G)).hexdigest(),16))) + print('%x %x' % H.xy()) + */ +static const secp256k1_generator secp256k1_generator_h_internal = {{ + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, + 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, + 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 +}}; + +const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; + + +static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { + secp256k1_fe x, y; + int succeed; + succeed = secp256k1_fe_set_b32_limit(&x, &gen->data[0]); + VERIFY_CHECK(succeed != 0); + succeed = secp256k1_fe_set_b32_limit(&y, &gen->data[32]); + VERIFY_CHECK(succeed != 0); + secp256k1_ge_set_xy(ge, &x, &y); + (void) succeed; +} + +static void secp256k1_generator_save(secp256k1_generator *gen, secp256k1_ge* ge) { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(&gen->data[0], &ge->x); + secp256k1_fe_get_b32(&gen->data[32], &ge->y); +} + +int secp256k1_generator_parse(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(input != NULL); + + if ((input[0] & 0xFE) != 10 || + !secp256k1_fe_set_b32_limit(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { + return 0; + } + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_generator_save(gen, &ge); + return 1; +} + +int secp256k1_generator_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_generator* gen) { + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(gen != NULL); + + secp256k1_generator_load(&ge, gen); + + output[0] = 11 ^ secp256k1_fe_is_square_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); + return 1; +} + +static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { + /* Implements the algorithm from: + * Indifferentiable Hashing to Barreto-Naehrig Curves + * Pierre-Alain Fouque and Mehdi Tibouchi + * Latincrypt 2012 + */ + + /* Basic algorithm: + + c = sqrt(-3) + d = (c - 1)/2 + + w = c * t / (1 + b + t^2) [with b = 7] + x1 = d - t*w + x2 = -(x1 + 1) + x3 = 1 + 1/w^2 + + To avoid the 2 divisions, compute the joint denominator j = wd * x3d, where + wd = 1 + b + t^2 + x3d = c^2 * t^2 = -3 * t^2 + + so that if j != 0, then + + 1 / wd = 1/j * x3d + 1 / x3d = 1/j * wd + + x1 = d - c * t^2 * x3d / j + x3 = 1 + wd^3 / j + + If j = 0, the function outputs the point (d, f(d)). This point is equal + to (x1, f(x1)) as defined above if division by 0 is defined to be 0. In + below code this is not special-cased because secp256k1_fe_inv returns 0 + on input 0. + + j = 0 happens only when t = 0 (since wd != 0 as -8 is not a square). + */ + + static const secp256k1_fe negc = SECP256K1_FE_CONST(0xf5d2d456, 0xcaf80e20, 0xdcc88f3d, 0x586869d3, 0x39e092ea, 0x25eb132b, 0x8272d850, 0xe32a03dd); + static const secp256k1_fe d = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40); + + secp256k1_fe wd, x3d, jinv, tmp, x1, x2, x3, alphain, betain, gammain, y1, y2, y3; + int alphaquad, betaquad; + + /* wd = t^2 */ + secp256k1_fe_sqr(&wd, t); /* mag 1 */ + /* x1 = -c * t^2 */ + secp256k1_fe_mul(&x1, &negc, &wd); /* mag 1 */ + /* x3d = t^2 */ + x3d = wd; /* mag 1 */ + /* x3d = 3 * t^2 */ + secp256k1_fe_mul_int(&x3d, 3); /* mag 3 */ + /* x3d = -3 * t^2 */ + secp256k1_fe_negate(&x3d, &x3d, 3); /* mag 4 */ + /* wd = 1 + b + t^2 */ + secp256k1_fe_add_int(&wd, SECP256K1_B + 1); /* mag 2 */ + /* jinv = wd * x3d */ + secp256k1_fe_mul(&jinv, &wd, &x3d); /* mag 1 */ + /* jinv = 1/(wd * x3d) */ + secp256k1_fe_inv(&jinv, &jinv); /* mag 1 */ + /* x1 = -c * t^2 * x3d */ + secp256k1_fe_mul(&x1, &x1, &x3d); /* mag 1 */ + /* x1 = -c * t^2 * x3d * 1/j */ + secp256k1_fe_mul(&x1, &x1, &jinv); /* mag 1 */ + /* x1 = d + -c * t^2 * x3d * 1/j */ + secp256k1_fe_add(&x1, &d); /* mag 2 */ + /* x2 = x1 */ + x2 = x1; /* mag 2 */ + /* x2 = x1 + 1 */ + secp256k1_fe_add_int(&x2, 1); /* mag 3 */ + /* x2 = - (x1 + 1) */ + secp256k1_fe_negate(&x2, &x2, 3); /* mag 4 */ + /* x3 = wd^2 */ + secp256k1_fe_sqr(&x3, &wd); /* mag 1 */ + /* x3 = wd^3 */ + secp256k1_fe_mul(&x3, &x3, &wd); /* mag 1 */ + /* x3 = wd^3 * 1/j */ + secp256k1_fe_mul(&x3, &x3, &jinv); /* mag 1 */ + /* x3 = 1 + (wd^3 * 1/j) */ + secp256k1_fe_add_int(&x3, 1); /* mag 2 */ + + secp256k1_fe_sqr(&alphain, &x1); /* mag 1 */ + secp256k1_fe_mul(&alphain, &alphain, &x1); /* mag 1 */ + secp256k1_fe_add_int(&alphain, SECP256K1_B); /* mag 2 */ + secp256k1_fe_sqr(&betain, &x2); /* mag 1 */ + secp256k1_fe_mul(&betain, &betain, &x2); /* mag 1 */ + secp256k1_fe_add_int(&betain, SECP256K1_B); /* mag 2 */ + secp256k1_fe_sqr(&gammain, &x3); /* mag 1 */ + secp256k1_fe_mul(&gammain, &gammain, &x3); /* mag 1 */ + secp256k1_fe_add_int(&gammain, SECP256K1_B); /* mag 2 */ + + alphaquad = secp256k1_fe_sqrt(&y1, &alphain); + betaquad = secp256k1_fe_sqrt(&y2, &betain); + secp256k1_fe_sqrt(&y3, &gammain); + + secp256k1_fe_cmov(&x1, &x2, (!alphaquad) & betaquad); + secp256k1_fe_cmov(&y1, &y2, (!alphaquad) & betaquad); + secp256k1_fe_cmov(&x1, &x3, (!alphaquad) & !betaquad); + secp256k1_fe_cmov(&y1, &y3, (!alphaquad) & !betaquad); + + secp256k1_ge_set_xy(ge, &x1, &y1); + + /* The linked algorithm from the paper uses the Jacobi symbol of t to + * determine the Jacobi symbol of the produced y coordinate. Since the + * rest of the algorithm only uses t^2, we can safely use another criterion + * as long as negation of t results in negation of the y coordinate. Here + * we choose to use t's oddness, as it is faster to determine. */ + secp256k1_fe_negate(&tmp, &ge->y, 1); + secp256k1_fe_cmov(&ge->y, &tmp, secp256k1_fe_is_odd(t)); +} + +static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) { + static const unsigned char prefix1[17] = "1st generation: "; + static const unsigned char prefix2[17] = "2nd generation: "; + secp256k1_fe t = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 4); + secp256k1_ge add; + secp256k1_gej accum; + int overflow; + secp256k1_sha256 sha256; + unsigned char b32[32]; + int ret = 1; + + if (blind32) { + secp256k1_scalar blind; + secp256k1_scalar_set_b32(&blind, blind32, &overflow); + ret = !overflow; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &accum, &blind); + } + + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, prefix1, 16); + secp256k1_sha256_write(&sha256, key32, 32); + secp256k1_sha256_finalize(&sha256, b32); + secp256k1_sha256_clear(&sha256); + ret &= secp256k1_fe_set_b32_limit(&t, b32); + shallue_van_de_woestijne(&add, &t); + if (blind32) { + secp256k1_gej_add_ge(&accum, &accum, &add); + } else { + secp256k1_gej_set_ge(&accum, &add); + } + + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, prefix2, 16); + secp256k1_sha256_write(&sha256, key32, 32); + secp256k1_sha256_finalize(&sha256, b32); + secp256k1_sha256_clear(&sha256); + ret &= secp256k1_fe_set_b32_limit(&t, b32); + shallue_van_de_woestijne(&add, &t); + secp256k1_gej_add_ge(&accum, &accum, &add); + + secp256k1_ge_set_gej(&add, &accum); + secp256k1_generator_save(gen, &add); + return ret; +} + +int secp256k1_generator_generate(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(key32 != NULL); + return secp256k1_generator_generate_internal(ctx, gen, key32, NULL); +} + +int secp256k1_generator_generate_blinded(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(key32 != NULL); + ARG_CHECK(blind32 != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + return secp256k1_generator_generate_internal(ctx, gen, key32, blind32); +} + +static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { + secp256k1_fe fe; + secp256k1_fe_set_b32_mod(&fe, &commit->data[1]); + secp256k1_ge_set_xquad(ge, &fe); + if (commit->data[0] & 1) { + secp256k1_ge_neg(ge, ge); + } +} + +static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { + secp256k1_fe_normalize(&ge->x); + secp256k1_fe_get_b32(&commit->data[1], &ge->x); + commit->data[0] = 9 ^ secp256k1_fe_is_square_var(&ge->y); +} + +int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { + secp256k1_fe x; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(input != NULL); + (void) ctx; + + if ((input[0] & 0xFE) != 8 || + !secp256k1_fe_set_b32_limit(&x, &input[1]) || + !secp256k1_ge_x_on_curve_var(&x)) { + return 0; + } + + memcpy(commit->data, input, 33); + return 1; +} + +int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(commit != NULL); + + memcpy(output, commit->data, 33); + return 1; +} + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { + secp256k1_ge genp; + secp256k1_gej rj; + secp256k1_ge r; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(gen != NULL); + secp256k1_generator_load(&genp, gen); + secp256k1_scalar_set_b32(&sec, blind, &overflow); + if (!overflow) { + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); + if (!secp256k1_gej_is_infinity(&rj)) { + secp256k1_ge_set_gej(&r, &rj); + secp256k1_pedersen_commitment_save(commit, &r); + ret = 1; + } + secp256k1_gej_clear(&rj); + secp256k1_ge_clear(&r); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest + * negative, then calculates an additional blinding value that adds to zero. + */ +int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { + secp256k1_scalar acc; + secp256k1_scalar x; + size_t i; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(blind_out != NULL); + ARG_CHECK(blinds != NULL); + for (i = 0; i < n; i++) { + ARG_CHECK(blinds[i] != NULL); + } + ARG_CHECK(npositive <= n); + (void) ctx; + secp256k1_scalar_set_int(&acc, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&x, blinds[i], &overflow); + if (overflow) { + return 0; + } + if (i >= npositive) { + secp256k1_scalar_negate(&x, &x); + } + secp256k1_scalar_add(&acc, &acc, &x); + } + secp256k1_scalar_get_b32(blind_out, &acc); + secp256k1_scalar_clear(&acc); + secp256k1_scalar_clear(&x); + return 1; +} + +/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { + secp256k1_gej accj; + secp256k1_ge add; + size_t i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(!pcnt || (commits != NULL)); + ARG_CHECK(!ncnt || (ncommits != NULL)); + for (i = 0; i < pcnt; i++) { + ARG_CHECK(commits[i] != NULL); + } + for (i = 0; i < ncnt; i++) { + ARG_CHECK(ncommits[i] != NULL); + } + (void) ctx; + secp256k1_gej_set_infinity(&accj); + for (i = 0; i < ncnt; i++) { + secp256k1_pedersen_commitment_load(&add, ncommits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + secp256k1_gej_neg(&accj, &accj); + for (i = 0; i < pcnt; i++) { + secp256k1_pedersen_commitment_load(&add, commits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + return secp256k1_gej_is_infinity(&accj); +} + +int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { + secp256k1_scalar sum; + secp256k1_scalar tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(n_total == 0 || value != NULL); + ARG_CHECK(n_total == 0 || generator_blind != NULL); + ARG_CHECK(n_total == 0 || blinding_factor != NULL); + ARG_CHECK(n_total > n_inputs); + for (i = 0; i < n_total; i++) { + ARG_CHECK(generator_blind[i] != NULL); + ARG_CHECK(blinding_factor[i] != NULL); + } + (void) ctx; + + if (n_total == 0) { + return 1; + } + + secp256k1_scalar_set_int(&sum, 0); + secp256k1_scalar_set_int(&tmp, 0); + + /* Here, n_total > 0. Thus the loop runs at least once. + Thus we may use a do-while loop, which checks the loop + condition only at the end. + + The do-while loop helps GCC prove that the loop runs at least + once and suppresses a -Wmaybe-uninitialized warning. */ + i = 0; + do { + int overflow = 0; + secp256k1_scalar addend; + secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ + + secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ + + secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ + secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ + secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ + secp256k1_scalar_clear(&addend); + + i++; + } while (i < n_total); + + /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ + secp256k1_scalar_negate(&sum, &sum); + secp256k1_scalar_add(&tmp, &tmp, &sum); + secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); + + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&sum); + return 1; +} + +#endif diff --git a/src/modules/generator/pedersen.h b/src/modules/generator/pedersen.h new file mode 100644 index 00000000..a09f4a5e --- /dev/null +++ b/src/modules/generator/pedersen.h @@ -0,0 +1,22 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_PEDERSEN_H +#define SECP256K1_PEDERSEN_H + +#include "../../ecmult_gen.h" +#include "../../group.h" +#include "../../scalar.h" + +#include + +/** Multiply a small number with the generator: r = gn*G2 */ +static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp); + +/* sec * G + value * G2. */ +static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp); + +#endif diff --git a/src/modules/generator/pedersen_impl.h b/src/modules/generator/pedersen_impl.h new file mode 100644 index 00000000..7d2a8a27 --- /dev/null +++ b/src/modules/generator/pedersen_impl.h @@ -0,0 +1,51 @@ +/*********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php. * + ***********************************************************************/ + +#ifndef SECP256K1_PEDERSEN_IMPL_H +#define SECP256K1_PEDERSEN_IMPL_H + +#include + +#include "../../eckey.h" +#include "../../ecmult_const.h" +#include "../../ecmult_gen.h" +#include "../../group.h" +#include "../../field.h" +#include "../../scalar.h" +#include "../../util.h" + +static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t value) { + unsigned char data[32]; + int i; + for (i = 0; i < 24; i++) { + data[i] = 0; + } + for (; i < 32; i++) { + data[i] = value >> 56; + value <<= 8; + } + secp256k1_scalar_set_b32(sec, data, NULL); + secp256k1_memclear_explicit(data, 32); +} + +static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) { + secp256k1_scalar s; + secp256k1_pedersen_scalar_set_u64(&s, gn); + secp256k1_ecmult_const(r, genp, &s); + secp256k1_scalar_clear(&s); +} + +/* sec * G + value * G2. */ +SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp) { + secp256k1_gej vj; + secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec); + secp256k1_pedersen_ecmult_small(&vj, value, genp); + /* FIXME: constant time. */ + secp256k1_gej_add_var(rj, rj, &vj, NULL); + secp256k1_gej_clear(&vj); +} + +#endif diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h new file mode 100644 index 00000000..14ec95dc --- /dev/null +++ b/src/modules/generator/tests_impl.h @@ -0,0 +1,356 @@ +/********************************************************************** + * Copyright (c) 2016 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_GENERATOR_TESTS_H +#define SECP256K1_MODULE_GENERATOR_TESTS_H + +#include +#include + +#include "../../group.h" +#include "../../scalar.h" +#include "../../testrand.h" +#include "../../util.h" +#include "../../unit_test.h" + +#include "../../../include/secp256k1_generator.h" + +static void test_generator_api(void) { + unsigned char key[32]; + unsigned char blind[32]; + unsigned char sergen[33]; + secp256k1_generator gen; + + testrand256(key); + testrand256(blind); + + CHECK(secp256k1_generator_generate(CTX, &gen, key) == 1); + CHECK_ILLEGAL(CTX, secp256k1_generator_generate(CTX, NULL, key)); + CHECK_ILLEGAL(CTX, secp256k1_generator_generate(CTX, &gen, NULL)); + + CHECK(secp256k1_generator_generate_blinded(CTX, &gen, key, blind) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_generator_generate_blinded(STATIC_CTX, &gen, key, blind)); + CHECK_ILLEGAL(CTX, secp256k1_generator_generate_blinded(CTX, NULL, key, blind)); + CHECK_ILLEGAL(CTX, secp256k1_generator_generate_blinded(CTX, &gen, NULL, blind)); + CHECK_ILLEGAL(CTX, secp256k1_generator_generate_blinded(CTX, &gen, key, NULL)); + + CHECK(secp256k1_generator_serialize(CTX, sergen, &gen) == 1); + CHECK_ILLEGAL(CTX, secp256k1_generator_serialize(CTX, NULL, &gen)); + CHECK_ILLEGAL(CTX, secp256k1_generator_serialize(CTX, sergen, NULL)); + + CHECK(secp256k1_generator_serialize(CTX, sergen, &gen) == 1); + CHECK(secp256k1_generator_parse(CTX, &gen, sergen) == 1); + CHECK_ILLEGAL(CTX, secp256k1_generator_parse(CTX, NULL, sergen)); + CHECK_ILLEGAL(CTX, secp256k1_generator_parse(CTX, &gen, NULL)); +} + +static void test_shallue_van_de_woestijne(void) { + /* Matches with the output of the shallue_van_de_woestijne.sage SAGE program */ + static const secp256k1_ge_storage results[34] = { + SECP256K1_GE_STORAGE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40, 0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), + SECP256K1_GE_STORAGE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40, 0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0xa98e9f96, 0x97e7d794, 0x8d0fe5c1, 0xa1735358, 0xc9db6e9f, 0x38212962, 0x2ae6ec3b, 0xfc5d0198), + SECP256K1_GE_STORAGE_CONST(0x531f7239, 0xaebc780e, 0x179fbf8d, 0x412a1b01, 0x511f0abc, 0xe0c46151, 0x8b38db84, 0xcc2467f3, 0x82387d45, 0xec7bd5cc, 0x61fcb9df, 0x41cddd7b, 0x217d8114, 0x3577dc8f, 0x23de356a, 0x7e97704e), + SECP256K1_GE_STORAGE_CONST(0x531f7239, 0xaebc780e, 0x179fbf8d, 0x412a1b01, 0x511f0abc, 0xe0c46151, 0x8b38db84, 0xcc2467f3, 0x7dc782ba, 0x13842a33, 0x9e034620, 0xbe322284, 0xde827eeb, 0xca882370, 0xdc21ca94, 0x81688be1), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0xa98e9f96, 0x97e7d794, 0x8d0fe5c1, 0xa1735358, 0xc9db6e9f, 0x38212962, 0x2ae6ec3b, 0xfc5d0198), + SECP256K1_GE_STORAGE_CONST(0x5e5936b1, 0x81db0b65, 0x8e33a8c6, 0x1aa687dd, 0x31d11e15, 0x85e35664, 0x6b4c2071, 0xcde7e942, 0x88bb5332, 0xa8e05654, 0x78d4f60c, 0x0cd979ec, 0x938558f2, 0xcac11216, 0x7c387a56, 0xe3a6d5f3), + SECP256K1_GE_STORAGE_CONST(0x5e5936b1, 0x81db0b65, 0x8e33a8c6, 0x1aa687dd, 0x31d11e15, 0x85e35664, 0x6b4c2071, 0xcde7e942, 0x7744accd, 0x571fa9ab, 0x872b09f3, 0xf3268613, 0x6c7aa70d, 0x353eede9, 0x83c785a8, 0x1c59263c), + SECP256K1_GE_STORAGE_CONST(0x657d438f, 0xfac34a50, 0x463fd07c, 0x3f09f320, 0x4c98e8ed, 0x6927e330, 0xc0c7735f, 0x76d32f6d, 0x577c2b11, 0xcaca2f6f, 0xd60bcaf0, 0x3e7cebe9, 0x5da6e1f4, 0xbb557f12, 0x2a397331, 0x81df897f), + SECP256K1_GE_STORAGE_CONST(0x657d438f, 0xfac34a50, 0x463fd07c, 0x3f09f320, 0x4c98e8ed, 0x6927e330, 0xc0c7735f, 0x76d32f6d, 0xa883d4ee, 0x3535d090, 0x29f4350f, 0xc1831416, 0xa2591e0b, 0x44aa80ed, 0xd5c68ccd, 0x7e2072b0), + SECP256K1_GE_STORAGE_CONST(0xbe0bc11b, 0x2bc639cb, 0xc28f72a8, 0xd07c21cc, 0xbc06cfa7, 0x4c2ff25e, 0x630c9740, 0x23128eab, 0x6f062fc8, 0x75148197, 0xd10375c3, 0xcc3fadb6, 0x20277e9c, 0x00579c55, 0xeddd7f95, 0xe95604db), + SECP256K1_GE_STORAGE_CONST(0xbe0bc11b, 0x2bc639cb, 0xc28f72a8, 0xd07c21cc, 0xbc06cfa7, 0x4c2ff25e, 0x630c9740, 0x23128eab, 0x90f9d037, 0x8aeb7e68, 0x2efc8a3c, 0x33c05249, 0xdfd88163, 0xffa863aa, 0x12228069, 0x16a9f754), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a), + SECP256K1_GE_STORAGE_CONST(0xaee172d4, 0xce7c5010, 0xdb20a88f, 0x469598c1, 0xd7f7926f, 0xabb85cb5, 0x339f1403, 0x87e6b494, 0x38065980, 0x4de81b35, 0x098c7190, 0xe3380f9d, 0x95b2ed6c, 0x6c869e85, 0xc772bc5a, 0x7bc3d9d5), + SECP256K1_GE_STORAGE_CONST(0xaee172d4, 0xce7c5010, 0xdb20a88f, 0x469598c1, 0xd7f7926f, 0xabb85cb5, 0x339f1403, 0x87e6b494, 0xc7f9a67f, 0xb217e4ca, 0xf6738e6f, 0x1cc7f062, 0x6a4d1293, 0x9379617a, 0x388d43a4, 0x843c225a), + SECP256K1_GE_STORAGE_CONST(0xc28f5c28, 0xf5c28f5c, 0x28f5c28f, 0x5c28f5c2, 0x8f5c28f5, 0xc28f5c28, 0xf5c28f5b, 0x6666635a, 0x0c4da840, 0x1b2cf5be, 0x4604e6ec, 0xf92b2780, 0x063a5351, 0xe294bf65, 0xbb2f8b61, 0x00902db7), + SECP256K1_GE_STORAGE_CONST(0xc28f5c28, 0xf5c28f5c, 0x28f5c28f, 0x5c28f5c2, 0x8f5c28f5, 0xc28f5c28, 0xf5c28f5b, 0x6666635a, 0xf3b257bf, 0xe4d30a41, 0xb9fb1913, 0x06d4d87f, 0xf9c5acae, 0x1d6b409a, 0x44d0749d, 0xff6fce78), + SECP256K1_GE_STORAGE_CONST(0xecf56be6, 0x9c8fde26, 0x152832c6, 0xe043b3d5, 0xaf9a723f, 0x789854a0, 0xcb1b810d, 0xe2614ece, 0x66127ae4, 0xe4c17a75, 0x60a727e6, 0xffd2ea7f, 0xaed99088, 0xbec465c6, 0xbde56791, 0x37ed5572), + SECP256K1_GE_STORAGE_CONST(0xecf56be6, 0x9c8fde26, 0x152832c6, 0xe043b3d5, 0xaf9a723f, 0x789854a0, 0xcb1b810d, 0xe2614ece, 0x99ed851b, 0x1b3e858a, 0x9f58d819, 0x002d1580, 0x51266f77, 0x413b9a39, 0x421a986d, 0xc812a6bd), + SECP256K1_GE_STORAGE_CONST(0xba72860f, 0x10fcd142, 0x23f71e3c, 0x228deb9a, 0xc46c5ff5, 0x90b884e5, 0xcc60d51e, 0x0629d16e, 0x67999f31, 0x5a74ada3, 0x526832cf, 0x76b9fec3, 0xa348cc97, 0x33c3aa67, 0x02bd2516, 0x7814f635), + SECP256K1_GE_STORAGE_CONST(0xba72860f, 0x10fcd142, 0x23f71e3c, 0x228deb9a, 0xc46c5ff5, 0x90b884e5, 0xcc60d51e, 0x0629d16e, 0x986660ce, 0xa58b525c, 0xad97cd30, 0x8946013c, 0x5cb73368, 0xcc3c5598, 0xfd42dae8, 0x87eb05fa), + SECP256K1_GE_STORAGE_CONST(0x92ef5657, 0xdba51cc7, 0xf3e1b442, 0xa6a0916b, 0x8ce03079, 0x2ef5657d, 0xba51cc7e, 0xab2beb65, 0x782c65d2, 0x3f1e0eb2, 0x9179a994, 0xe5e8ff80, 0x5a0d50d9, 0xdeeaed90, 0xcec96ca5, 0x973e2ad3), + SECP256K1_GE_STORAGE_CONST(0x92ef5657, 0xdba51cc7, 0xf3e1b442, 0xa6a0916b, 0x8ce03079, 0x2ef5657d, 0xba51cc7e, 0xab2beb65, 0x87d39a2d, 0xc0e1f14d, 0x6e86566b, 0x1a17007f, 0xa5f2af26, 0x2115126f, 0x31369359, 0x68c1d15c), + SECP256K1_GE_STORAGE_CONST(0x9468ad22, 0xf921fc78, 0x8de3f1b0, 0x586c58eb, 0x5e6f0270, 0xe950b602, 0x7ada90d9, 0xd71ae323, 0x922a0c6a, 0x9ccc31d9, 0xc3bf87fd, 0x88381739, 0x35fe393f, 0xa64dfdec, 0x29f2846d, 0x12918d86), + SECP256K1_GE_STORAGE_CONST(0x9468ad22, 0xf921fc78, 0x8de3f1b0, 0x586c58eb, 0x5e6f0270, 0xe950b602, 0x7ada90d9, 0xd71ae323, 0x6dd5f395, 0x6333ce26, 0x3c407802, 0x77c7e8c6, 0xca01c6c0, 0x59b20213, 0xd60d7b91, 0xed6e6ea9), + SECP256K1_GE_STORAGE_CONST(0x76ddc7f5, 0xe029e59e, 0x22b0e54f, 0xa811db94, 0x5a209c4f, 0x5e912ca2, 0x8b4da6a7, 0x4c1e00a2, 0x1e8f516c, 0x91c20437, 0x50f6e24e, 0x8c2cf202, 0xacf68291, 0xbf8b66eb, 0xf7335b62, 0xec2c88fe), + SECP256K1_GE_STORAGE_CONST(0x76ddc7f5, 0xe029e59e, 0x22b0e54f, 0xa811db94, 0x5a209c4f, 0x5e912ca2, 0x8b4da6a7, 0x4c1e00a2, 0xe170ae93, 0x6e3dfbc8, 0xaf091db1, 0x73d30dfd, 0x53097d6e, 0x40749914, 0x08cca49c, 0x13d37331), + SECP256K1_GE_STORAGE_CONST(0xf75763bc, 0x2907e79b, 0x125e33c3, 0x9a027f48, 0x0f8c6409, 0x2153432f, 0x967bc2b1, 0x1d1f5cf0, 0xb4a8edc6, 0x36391b39, 0x9bc219c0, 0x3d033128, 0xdbcd463e, 0xd2506394, 0x061b87a5, 0x9e510235), + SECP256K1_GE_STORAGE_CONST(0xf75763bc, 0x2907e79b, 0x125e33c3, 0x9a027f48, 0x0f8c6409, 0x2153432f, 0x967bc2b1, 0x1d1f5cf0, 0x4b571239, 0xc9c6e4c6, 0x643de63f, 0xc2fcced7, 0x2432b9c1, 0x2daf9c6b, 0xf9e47859, 0x61aef9fa), + }; + + secp256k1_ge ge; + secp256k1_fe fe; + secp256k1_ge_storage ges; + int i, s; + for (i = 0; i <= 16; i++) { + secp256k1_fe_set_int(&fe, i); + + for (s = 0; s < 2; s++) { + if (s) { + secp256k1_fe_negate(&fe, &fe, 1); + secp256k1_fe_normalize(&fe); + } + shallue_van_de_woestijne(&ge, &fe); + CHECK(secp256k1_ge_is_valid_var(&ge)); + secp256k1_ge_to_storage(&ges, &ge); + CHECK(secp256k1_memcmp_var(&ges, &results[i * 2 + s], sizeof(secp256k1_ge_storage)) == 0); + } + } +} + +static void test_generator_generate(void) { + static const secp256k1_ge_storage results[32] = { + SECP256K1_GE_STORAGE_CONST(0x806cd8ed, 0xd6c153e3, 0x4aa9b9a0, 0x8755c4be, 0x4718b1ef, 0xb26cb93f, 0xfdd99e1b, 0x21f2af8e, 0xc7062208, 0xcc649a03, 0x1bdc1a33, 0x9d01f115, 0x4bcd0dca, 0xfe0b875d, 0x62f35f73, 0x28673006), + SECP256K1_GE_STORAGE_CONST(0xd91b15ec, 0x47a811f4, 0xaa189561, 0xd13f5c4d, 0x4e81f10d, 0xc7dc551f, 0x4fea9b84, 0x610314c4, 0x9b0ada1e, 0xb38efd67, 0x8bff0b6c, 0x7d7315f7, 0xb49b8cc5, 0xa679fad4, 0xc94f9dc6, 0x9da66382), + SECP256K1_GE_STORAGE_CONST(0x11c00de6, 0xf885035e, 0x76051430, 0xa3c38b2a, 0x5f86ab8c, 0xf66dae58, 0x04ea7307, 0x348b19bf, 0xe0858ae7, 0x61dcb1ba, 0xff247e37, 0xd38fcd88, 0xf3bd7911, 0xaa4ed6e0, 0x28d792dd, 0x3ee1ac09), + SECP256K1_GE_STORAGE_CONST(0x986b99eb, 0x3130e7f0, 0xe779f674, 0xb85cb514, 0x46a676bf, 0xb1dfb603, 0x4c4bb639, 0x7c406210, 0xdf900609, 0x8b3ef1e0, 0x30e32fb0, 0xd97a4329, 0xff98aed0, 0xcd278c3f, 0xe6078467, 0xfbd12f35), + SECP256K1_GE_STORAGE_CONST(0xae528146, 0x03fdf91e, 0xc592977e, 0x12461dc7, 0xb9e038f8, 0x048dcb62, 0xea264756, 0xd459ae42, 0x80ef658d, 0x92becb84, 0xdba8e4f9, 0x560d7a72, 0xbaf4c393, 0xfbcf6007, 0x11039f1c, 0x224faaad), + SECP256K1_GE_STORAGE_CONST(0x00df3d91, 0x35975eee, 0x91fab903, 0xe3128e4a, 0xca071dde, 0x270814e5, 0xcbda69ec, 0xcad58f46, 0x11b590aa, 0x92d89969, 0x2dbd932f, 0x08013b8b, 0x45afabc6, 0x43677db2, 0x143e0c0f, 0x5865fb03), + SECP256K1_GE_STORAGE_CONST(0x1168155b, 0x987e9bc8, 0x84c5f3f4, 0x92ebf784, 0xcc8c6735, 0x39d8e5e8, 0xa967115a, 0x2949da9b, 0x0858a470, 0xf403ca97, 0xb1827f6f, 0x544c2c67, 0x08f6cb83, 0xc510c317, 0x96c981ed, 0xb9f61780), + SECP256K1_GE_STORAGE_CONST(0xe8d7c0cf, 0x2bb4194c, 0x97bf2a36, 0xbd115ba0, 0x81a9afe8, 0x7663fa3c, 0x9c3cd253, 0x79fe2571, 0x2028ad04, 0xefa00119, 0x5a25d598, 0x67e79502, 0x49de7c61, 0x4751cd9d, 0x4fb317f6, 0xf76f1110), + SECP256K1_GE_STORAGE_CONST(0x9532c491, 0xa64851dd, 0xcd0d3e5a, 0x93e17267, 0xa10aca95, 0xa23781aa, 0x5087f340, 0xc45fecc3, 0xb691ddc2, 0x3143a7b6, 0x09969302, 0x258affb8, 0x5bbf8666, 0xe1192319, 0xeb174d88, 0x308bd57a), + SECP256K1_GE_STORAGE_CONST(0x6b20b6e2, 0x1ba6cc44, 0x3f2c3a0c, 0x5283ba44, 0xbee43a0a, 0x2799a6cf, 0xbecc0f8a, 0xf8c583ac, 0xf7021e76, 0xd51291a6, 0xf9396215, 0x686f25aa, 0xbec36282, 0x5e11eeea, 0x6e51a6e6, 0xd7d7c006), + SECP256K1_GE_STORAGE_CONST(0xde27e6ff, 0x219b3ab1, 0x2b0a9e4e, 0x51fc6092, 0x96e55af6, 0xc6f717d6, 0x12cd6cce, 0x65d6c8f2, 0x48166884, 0x4dc13fd2, 0xed7a7d81, 0x66a0839a, 0x8a960863, 0xfe0001c1, 0x35d206fd, 0x63b87c09), + SECP256K1_GE_STORAGE_CONST(0x79a96fb8, 0xd88a08d3, 0x055d38d1, 0x3346b0d4, 0x47d838ca, 0xfcc8fa40, 0x6d3a7157, 0xef84e7e3, 0x6bab9c45, 0x2871b51d, 0xb0df2369, 0xe7860e01, 0x2e37ffea, 0x6689fd1a, 0x9c6fe9cf, 0xb940acea), + SECP256K1_GE_STORAGE_CONST(0x06c4d4cb, 0xd32c0ddb, 0x67e988c6, 0x2bdbe6ad, 0xa39b80cc, 0x61afb347, 0x234abe27, 0xa689618c, 0x5b355949, 0xf904fe08, 0x569b2313, 0xe8f19f8d, 0xc5b79e27, 0x70da0832, 0x5fb7a229, 0x238ca6b6), + SECP256K1_GE_STORAGE_CONST(0x7027e566, 0x3e727c28, 0x42aa14e5, 0x52c2d2ec, 0x1d8beaa9, 0x8a22ceab, 0x15ccafc3, 0xb4f06249, 0x9b3dffbc, 0xdbd5e045, 0x6931fd03, 0x8b1c6a9b, 0x4c168c6d, 0xa6553897, 0xfe11ce49, 0xac728139), + SECP256K1_GE_STORAGE_CONST(0xee3520c3, 0x9f2b954d, 0xf8e15547, 0xdaeb6cc8, 0x04c8f3b0, 0x9301f53e, 0xe0c11ea1, 0xeace539d, 0x244ff873, 0x7e060c98, 0xe843c353, 0xcd35d2e4, 0x3cd8b082, 0xcffbc9ae, 0x81eafa70, 0x332f9748), + SECP256K1_GE_STORAGE_CONST(0xdaecd756, 0xf5b706a4, 0xc14e1095, 0x3e2f70df, 0xa81276e7, 0x71806b89, 0x4d8a5502, 0xa0ef4998, 0xbac906c0, 0x948b1d48, 0xe023f439, 0xfd3770b8, 0x837f60cc, 0x40552a51, 0x433d0b79, 0x6610da27), + SECP256K1_GE_STORAGE_CONST(0x55e1ca28, 0x750fe2d0, 0x57f7449b, 0x3f49d999, 0x3b9616dd, 0x5387bc2e, 0x6e6698f8, 0xc4ea49f4, 0xe339e0e9, 0xa4c7fa99, 0xd063e062, 0x6582bce2, 0x33c6b1ee, 0x17a5b47f, 0x6d43ecf8, 0x98b40120), + SECP256K1_GE_STORAGE_CONST(0xdd82cac2, 0x9e0e0135, 0x4964d3bc, 0x27469233, 0xf13bbd5e, 0xd7aff24b, 0x4902fca8, 0x17294b12, 0x561ab1d6, 0xcd9bcb6e, 0x805585cf, 0x3df8714c, 0x1bfa6304, 0x5efbf122, 0x1a3d8fd9, 0x3827764a), + SECP256K1_GE_STORAGE_CONST(0xda5cbfb7, 0x3522e9c7, 0xcb594436, 0x83677038, 0x0eaa64a9, 0x2eca3888, 0x0fe4c9d6, 0xdeb22dbf, 0x4f46de68, 0x0447c780, 0xc54a314b, 0x5389a926, 0xbba8910b, 0x869fc6cd, 0x42ee82e8, 0x5895e42a), + SECP256K1_GE_STORAGE_CONST(0x4e09830e, 0xc8894c58, 0x4e6278de, 0x167a96b0, 0x20d60463, 0xee48f788, 0x4974d66e, 0x871e35e9, 0x21259c4d, 0x332ca932, 0x2e187df9, 0xe7afbc23, 0x9d171ebc, 0x7d9e2560, 0x503f50b1, 0x9fe45834), + SECP256K1_GE_STORAGE_CONST(0xabfff6ca, 0x41dcfd17, 0x03cae629, 0x9d127971, 0xf19ee000, 0x2db332e6, 0x5cc209a3, 0xc21b8f54, 0x65991d60, 0xee54f5cc, 0xddf7a732, 0xa76b0303, 0xb9f519a6, 0x22ea0390, 0x8af23ffa, 0x35ae6632), + SECP256K1_GE_STORAGE_CONST(0xc6c9b92c, 0x91e045a5, 0xa1913277, 0x44d6fce2, 0x11b12c7c, 0x9b3112d6, 0xc61e14a6, 0xd6b1ae12, 0x04ab0396, 0xebdc4c6a, 0xc213cc3e, 0x077a2e80, 0xb4ba7b2b, 0x33907d56, 0x2c98ccf7, 0xb82a2e9f), + SECP256K1_GE_STORAGE_CONST(0x66f6e6d9, 0xc4bb9a5f, 0x99085781, 0x83cb9362, 0x2ea437d8, 0xccd31969, 0xffadca3a, 0xff1d3935, 0x50a5b06e, 0x39e039d7, 0x1dfb2723, 0x18db74e5, 0x5af64da1, 0xdfc34586, 0x6aac3bd0, 0x5792a890), + SECP256K1_GE_STORAGE_CONST(0x58ded03c, 0x98e1a890, 0x63fc7793, 0xe3ecd896, 0x235e75c9, 0x82e7008f, 0xddbf3ca8, 0x5b7e9ecb, 0x34594776, 0x58ab6821, 0xaf43a453, 0xa946fda9, 0x13d24999, 0xccf22df8, 0xd291ef59, 0xb08975c0), + SECP256K1_GE_STORAGE_CONST(0x74557864, 0x4f2b0486, 0xd5beea7c, 0x2d258ccb, 0x78a870e1, 0x848982d8, 0xed3f91a4, 0x9db83a36, 0xd84e940e, 0x1d33c28a, 0x62398ec8, 0xc493aee7, 0x7c2ba722, 0x42dee7ae, 0x3c35c256, 0xad00cf42), + SECP256K1_GE_STORAGE_CONST(0x7fc7963a, 0x16abc8fb, 0x5d61eb61, 0x0fc50a68, 0x754470d2, 0xf43df3be, 0x52228f66, 0x522fe61b, 0x499f9e7f, 0x462c6545, 0x29687af4, 0x9f7c732d, 0x48801ce5, 0x21acd546, 0xc6fb903c, 0x7c265032), + SECP256K1_GE_STORAGE_CONST(0xb2f6257c, 0xc58df82f, 0xb9ba4f36, 0x7ededf03, 0xf8ea10f3, 0x104d7ae6, 0x233b7ac4, 0x725e11de, 0x9c7a32df, 0x4842f33d, 0xaad84f0b, 0x62e88b40, 0x46ddcbde, 0xbbeec6f8, 0x93bfde27, 0x0561dc73), + SECP256K1_GE_STORAGE_CONST(0xe2cdfd27, 0x8a8e22be, 0xabf08b79, 0x1bc6ae38, 0x41d22a9a, 0x9472e266, 0x1a7c6e83, 0xa2f74725, 0x0e26c103, 0xe0dd93b2, 0x3724f3b7, 0x8bb7366e, 0x2c245768, 0xd64f3283, 0xd8316e8a, 0x1383b977), + SECP256K1_GE_STORAGE_CONST(0x757c13e7, 0xe866017e, 0xe6af61d7, 0x161d208a, 0xc438f712, 0x242fcd23, 0x63a10e59, 0xd67e41fb, 0xb550c6a9, 0x4ddb15f3, 0xfeea4bfe, 0xd2faa19f, 0x2aa2fbd3, 0x0c6ae785, 0xe357f365, 0xb30d12e0), + SECP256K1_GE_STORAGE_CONST(0x528d525e, 0xac30095b, 0x5e5f83ca, 0x4d3dea63, 0xeb608f2d, 0x18dd25a7, 0x2529c8e5, 0x1ae5f9f1, 0xfde2860b, 0x492a4106, 0x9f356c05, 0x3ebc045e, 0x4ad08b79, 0x3e264935, 0xf25785a9, 0x8690b5ee), + SECP256K1_GE_STORAGE_CONST(0x150df593, 0x5b6956a0, 0x0cfed843, 0xb9d6ffce, 0x4f790022, 0xea18730f, 0xc495111d, 0x91568e55, 0x6700a2ca, 0x9ff4ed32, 0xc1697312, 0x4eb51ce3, 0x5656344b, 0x65a1e3d5, 0xd6c1f7ce, 0x29233f82), + SECP256K1_GE_STORAGE_CONST(0x38e02eaf, 0x2c8774fd, 0x58b8b373, 0x732457f1, 0x16dbe53b, 0xea5683d9, 0xada20dd7, 0x14ce20a6, 0x6ac5362e, 0xbb425416, 0x8250f43f, 0xa4ee2b63, 0x0406324f, 0x1c876d60, 0xebe5be2c, 0x6eb1515b), + }; + secp256k1_generator gen; + secp256k1_ge ge; + secp256k1_ge_storage ges; + int i; + unsigned char v[32]; + unsigned char s[32] = {0}; + secp256k1_scalar sc; + secp256k1_scalar_set_b32(&sc, s, NULL); + for (i = 1; i <= 32; i++) { + memset(v, 0, 31); + v[31] = i; + CHECK(secp256k1_generator_generate_blinded(CTX, &gen, v, s)); + secp256k1_generator_load(&ge, &gen); + secp256k1_ge_to_storage(&ges, &ge); + CHECK(secp256k1_memcmp_var(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); + CHECK(secp256k1_generator_generate(CTX, &gen, v)); + secp256k1_generator_load(&ge, &gen); + secp256k1_ge_to_storage(&ges, &ge); + CHECK(secp256k1_memcmp_var(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); + } + + /* There is no range restriction on the value, but the blinder must be a + * valid scalar. Check that an invalid blinder causes the call to fail + * but not crash. */ + memset(v, 0xff, 32); + CHECK(secp256k1_generator_generate(CTX, &gen, v)); + memset(s, 0xff, 32); + CHECK(!secp256k1_generator_generate_blinded(CTX, &gen, v, s)); +} + +static void test_generator_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x0b, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_generator parse; + + CHECK(secp256k1_generator_parse(CTX, &parse, two_g)); + CHECK(secp256k1_generator_serialize(CTX, result, &parse)); + CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); + + result[0] = 0x0a; + CHECK(secp256k1_generator_parse(CTX, &parse, result)); + result[0] = 0x08; + CHECK(!secp256k1_generator_parse(CTX, &parse, result)); +} + +static void test_pedersen_api(void) { + secp256k1_pedersen_commitment commit; + const secp256k1_pedersen_commitment *commit_ptr = &commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + const unsigned char *blind_ptr = blind; + unsigned char *blind_out_ptr = blind_out; + uint64_t val = testrand32(); + + testrand256(blind); + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_pedersen_commit(STATIC_CTX, &commit, blind, val, secp256k1_generator_h)); + + CHECK_ILLEGAL(CTX, secp256k1_pedersen_commit(CTX, NULL, blind, val, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_commit(CTX, &commit, NULL, val, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_commit(CTX, &commit, blind, val, NULL)); + + CHECK(secp256k1_pedersen_blind_sum(CTX, blind_out, &blind_ptr, 1, 1) != 0); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_sum(CTX, NULL, &blind_ptr, 1, 1)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_sum(CTX, blind_out, NULL, 1, 1)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_sum(CTX, blind_out, &blind_ptr, 0, 1)); + CHECK(secp256k1_pedersen_blind_sum(CTX, blind_out, &blind_ptr, 0, 0) != 0); + + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_verify_tally(CTX, &commit_ptr, 1, &commit_ptr, 1) != 0); + CHECK(secp256k1_pedersen_verify_tally(CTX, NULL, 0, &commit_ptr, 1) == 0); + CHECK(secp256k1_pedersen_verify_tally(CTX, &commit_ptr, 1, NULL, 0) == 0); + CHECK(secp256k1_pedersen_verify_tally(CTX, NULL, 0, NULL, 0) != 0); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_verify_tally(CTX, NULL, 1, &commit_ptr, 1)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_verify_tally(CTX, &commit_ptr, 1, NULL, 1)); + + CHECK(secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 1, 1)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 0, 0)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, NULL, &blind_ptr, &blind_out_ptr, 1, 0)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, NULL, &blind_out_ptr, 1, 0)); + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, NULL, 1, 0)); + /* check that NULL in array of generator_blind pointers is not allowed */ + blind_ptr = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 1, 0)); + blind_ptr = blind; + /* check that NULL in array of blinding_factor pointers is not allowed */ + blind_out_ptr = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 1, 0)); +} + +static void test_pedersen_internal(void) { + secp256k1_pedersen_commitment commits[19]; + const secp256k1_pedersen_commitment *cptr[19]; + unsigned char blinds[32*19]; + const unsigned char *bptr[19]; + secp256k1_scalar s; + uint64_t values[19]; + int64_t totalv; + int i; + int inputs; + int outputs; + int total; + inputs = (testrand32() & 7) + 1; + outputs = (testrand32() & 7) + 2; + total = inputs + outputs; + for (i = 0; i < 19; i++) { + cptr[i] = &commits[i]; + bptr[i] = &blinds[i * 32]; + } + totalv = 0; + for (i = 0; i < inputs; i++) { + values[i] = testrandi64(0, INT64_MAX - totalv); + totalv += values[i]; + } + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = testrandi64(0, totalv); + totalv -= values[i + inputs]; + } + values[total - 1] = totalv; + + for (i = 0; i < total - 1; i++) { + testutil_random_scalar_order(&s); + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + CHECK(secp256k1_pedersen_blind_sum(CTX, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); + /* check that NULL in array of blind pointers is not allowed */ + for (i = 0; i < total - 1; i++) { + unsigned char blind_out[32]; + const unsigned char *original_ptr = bptr[i]; + bptr[i] = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_sum(CTX, blind_out, bptr, total - 1, inputs)); + bptr[i] = original_ptr; + } + for (i = 0; i < total; i++) { + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + + CHECK(secp256k1_pedersen_commit(CTX, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commitment_serialize(CTX, result, &commits[i])); + CHECK(secp256k1_pedersen_commitment_parse(CTX, &parse, result)); + CHECK(secp256k1_memcmp_var(&commits[i], &parse, 33) == 0); + } + CHECK(secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); + CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[inputs], outputs, cptr, inputs)); + /* check that NULL in array of commits pointers is not allowed */ + for (i = 0; i < inputs; i++) { + const secp256k1_pedersen_commitment *original_ptr = cptr[i]; + cptr[i] = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); + cptr[i] = original_ptr; + } + /* check that NULL in array of ncommits pointers is not allowed */ + for (i = 0; i < outputs; i++) { + const secp256k1_pedersen_commitment *original_ptr = cptr[inputs + i]; + cptr[inputs + i] = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); + cptr[inputs + i] = original_ptr; + } + if (inputs > 0 && values[0] > 0) { + CHECK(!secp256k1_pedersen_verify_tally(CTX, cptr, inputs - 1, &cptr[inputs], outputs)); + } + testutil_random_scalar_order(&s); + for (i = 0; i < 4; i++) { + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + values[0] = INT64_MAX; + values[1] = 0; + values[2] = 1; + for (i = 0; i < 3; i++) { + CHECK(secp256k1_pedersen_commit(CTX, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + } + CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[0], 1, &cptr[0], 1)); + CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[1], 1, &cptr[1], 1)); +} + +static void test_pedersen_commitment_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x09, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + + CHECK(secp256k1_pedersen_commitment_parse(CTX, &parse, two_g)); + CHECK(secp256k1_pedersen_commitment_serialize(CTX, result, &parse)); + CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); + + result[0] = 0x08; + CHECK(secp256k1_pedersen_commitment_parse(CTX, &parse, result)); + result[0] = 0x0c; + CHECK(!secp256k1_pedersen_commitment_parse(CTX, &parse, result)); +} + +/* --- Test registry --- */ +REPEAT_TEST(test_pedersen) + +static const struct tf_test_entry tests_generator[] = { + CASE1(test_shallue_van_de_woestijne), + CASE1(test_generator_fixed_vector), + CASE1(test_generator_api), + CASE1(test_generator_generate), + CASE1(test_pedersen), + CASE1(test_pedersen_api), + CASE1(test_pedersen_commitment_fixed_vector), +}; + +#endif diff --git a/src/modules/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include index 796443c9..cd60e67f 100644 --- a/src/modules/musig/Makefile.am.include +++ b/src/modules/musig/Makefile.am.include @@ -4,5 +4,6 @@ noinst_HEADERS += src/modules/musig/keyagg.h noinst_HEADERS += src/modules/musig/keyagg_impl.h noinst_HEADERS += src/modules/musig/session.h noinst_HEADERS += src/modules/musig/session_impl.h +noinst_HEADERS += src/modules/musig/adaptor_impl.h noinst_HEADERS += src/modules/musig/tests_impl.h noinst_HEADERS += src/modules/musig/vectors.h diff --git a/src/modules/musig/adaptor_impl.h b/src/modules/musig/adaptor_impl.h new file mode 100644 index 00000000..3830e8a2 --- /dev/null +++ b/src/modules/musig/adaptor_impl.h @@ -0,0 +1,101 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H +#define SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_musig.h" + +#include "session.h" +#include "../../scalar.h" + +int secp256k1_musig_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_musig_session *session) { + secp256k1_musig_session_internal session_i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce_parity != NULL); + ARG_CHECK(session != NULL); + + if (!secp256k1_musig_session_load(ctx, &session_i, session)) { + return 0; + } + *nonce_parity = session_i.fin_nonce_parity; + return 1; +} + +int secp256k1_musig_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + ret &= !overflow; + + /* Determine if the secret adaptor should be negated. + * + * The musig_session stores the X-coordinate and the parity of the "final nonce" + * (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor. + * + * Since a BIP340 signature requires an x-only public nonce, in the case where + * (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce + * corresponding to the signature is actually (-r - t)*G. Thus adapting a + * pre-signature requires negating t in this case. + */ + if (nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(&sig64[32], &s); + memmove(sig64, pre_sig64, 32); + secp256k1_scalar_clear(&t); + return ret; +} + +int secp256k1_musig_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); + ret &= !overflow; + secp256k1_scalar_negate(&t, &t); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + + if (!nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return ret; +} + +#endif diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index a1311e41..044bd30e 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -8,5 +8,6 @@ #include "keyagg_impl.h" #include "session_impl.h" +#include "adaptor_impl.h" #endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 6a37bfdf..2c89ba20 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -585,7 +585,7 @@ static void secp256k1_musig_nonce_process_internal(const secp256k1_context *ctx, *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); } -int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache) { +int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey *adaptor) { secp256k1_keyagg_cache_internal cache_i; secp256k1_ge aggnonce_pts[2]; unsigned char fin_nonce[32]; @@ -607,6 +607,30 @@ int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_ return 0; } + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_ge adaptorp; + secp256k1_gej tmp; + if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { + return 0; + } + secp256k1_gej_set_ge(&tmp, &aggnonce_pts[0]); + secp256k1_gej_add_ge_var(&tmp, &tmp, &adaptorp, NULL); + secp256k1_ge_set_gej(&aggnonce_pts[0], &tmp); + } + + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_ge adaptorp; + secp256k1_gej tmp; + if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { + return 0; + } + secp256k1_gej_set_ge(&tmp, &aggnonce_pts[0]); + secp256k1_gej_add_ge_var(&tmp, &tmp, &adaptorp, NULL); + secp256k1_ge_set_gej(&aggnonce_pts[0], &tmp); + } + secp256k1_musig_nonce_process_internal(ctx, &session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_pts, agg_pk32, msg32); secp256k1_schnorrsig_challenge(secp256k1_get_hash_context(ctx), &session_i.challenge, fin_nonce, msg32, 32, agg_pk32); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index cc644916..51b48039 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -35,7 +35,7 @@ static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_pubkey *p return ret; } -/* Just a simple (non-tweaked) 2-of-2 MuSig aggregate, sign, verify +/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig aggregate, sign, verify * test. */ static void musig_simple_test_internal(void) { unsigned char sk[2][32]; @@ -75,7 +75,7 @@ static void musig_simple_test_internal(void) { CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); for (i = 0; i < 2; i++) { CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[i], &secnonce[i], &keypair[i], &keyagg_cache, &session) == 1); @@ -124,6 +124,7 @@ static void musig_api_tests(void) { const secp256k1_musig_partial_sig *partial_sig_ptr[2]; secp256k1_musig_partial_sig invalid_partial_sig; const secp256k1_musig_partial_sig *invalid_partial_sig_ptr[2]; + unsigned char final_sig[64]; unsigned char pre_sig[64]; unsigned char buf[32]; unsigned char sk[2][32]; @@ -158,6 +159,10 @@ static void musig_api_tests(void) { const secp256k1_pubkey *invalid_pk_ptr2[2]; const secp256k1_pubkey *invalid_pk_ptr3[3]; unsigned char tweak[32]; + int nonce_parity; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor1[32]; + secp256k1_pubkey adaptor; int i; /** setup **/ @@ -175,8 +180,10 @@ static void musig_api_tests(void) { memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce)); memset(&invalid_session, 0, sizeof(invalid_session)); + testrand256(sec_adaptor); testrand256(msg); testrand256(tweak); + CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1); for (i = 0; i < 2; i++) { pk_ptr[i] = &pk[i]; invalid_pk_ptr2[i] = &invalid_pk; @@ -403,15 +410,17 @@ static void musig_api_tests(void) { } /** Process nonces **/ - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache, &adaptor)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, (secp256k1_pubkey *)&invalid_pk)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1); @@ -503,6 +512,40 @@ static void musig_api_tests(void) { CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 0)); CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 1) == 1); CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 2) == 1); + + /** Adaptor signature verification */ + CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity, &session) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, NULL, &session)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, &invalid_session)); + + CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, NULL, pre_sig, sec_adaptor, 0)); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, NULL, sec_adaptor, 0)); + CHECK(secp256k1_musig_adapt(CTX, final_sig, max64, sec_adaptor, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, NULL, 0)); + CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, max64, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, 2)); + /* sig and pre_sig argument point to the same location */ + memcpy(final_sig, pre_sig, sizeof(final_sig)); + CHECK(secp256k1_musig_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); + + CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); + + /** Secret adaptor can be extracted from signature */ + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1); + CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) == 0); + /* wrong nonce parity */ + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1); + CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) != 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, NULL, final_sig, pre_sig, 0)); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, NULL, pre_sig, 0)); + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, max64, pre_sig, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, NULL, 0)); + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, max64, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, 2)); } static void musig_nonce_bitflip(const secp256k1_hash_ctx *hash_ctx, unsigned char **args, size_t n_flip, size_t n_bytes) { @@ -571,6 +614,111 @@ static void musig_nonce_test(void) { } } +static void scriptless_atomic_swap_internal(void) { + /* Throughout this test "a" and "b" refer to two hypothetical blockchains, + * while the indices 0 and 1 refer to the two signers. Here signer 0 is + * sending a-coins to signer 1, while signer 1 is sending b-coins to signer + * 0. Signer 0 produces the adaptor signatures. */ + unsigned char pre_sig_a[64]; + unsigned char final_sig_a[64]; + unsigned char pre_sig_b[64]; + unsigned char final_sig_b[64]; + secp256k1_musig_partial_sig partial_sig_a[2]; + const secp256k1_musig_partial_sig *partial_sig_a_ptr[2]; + secp256k1_musig_partial_sig partial_sig_b[2]; + const secp256k1_musig_partial_sig *partial_sig_b_ptr[2]; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor_extracted[32]; + secp256k1_pubkey pub_adaptor; + unsigned char sk_a[2][32]; + unsigned char sk_b[2][32]; + secp256k1_keypair keypair_a[2]; + secp256k1_keypair keypair_b[2]; + secp256k1_pubkey pk_a[2]; + const secp256k1_pubkey *pk_a_ptr[2]; + secp256k1_pubkey pk_b[2]; + const secp256k1_pubkey *pk_b_ptr[2]; + secp256k1_musig_keyagg_cache keyagg_cache_a; + secp256k1_musig_keyagg_cache keyagg_cache_b; + secp256k1_xonly_pubkey agg_pk_a; + secp256k1_xonly_pubkey agg_pk_b; + secp256k1_musig_secnonce secnonce_a[2]; + secp256k1_musig_secnonce secnonce_b[2]; + secp256k1_musig_pubnonce pubnonce_a[2]; + secp256k1_musig_pubnonce pubnonce_b[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr_a[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr_b[2]; + secp256k1_musig_aggnonce aggnonce_a; + secp256k1_musig_aggnonce aggnonce_b; + secp256k1_musig_session session_a, session_b; + int nonce_parity_a; + int nonce_parity_b; + unsigned char seed_a[2][32] = { "a0", "a1" }; + unsigned char seed_b[2][32] = { "b0", "b1" }; + const unsigned char msg32_a[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'a'}; + const unsigned char msg32_b[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'b'}; + int i; + + /* Step 1: key setup */ + for (i = 0; i < 2; i++) { + pk_a_ptr[i] = &pk_a[i]; + pk_b_ptr[i] = &pk_b[i]; + pubnonce_ptr_a[i] = &pubnonce_a[i]; + pubnonce_ptr_b[i] = &pubnonce_b[i]; + partial_sig_a_ptr[i] = &partial_sig_a[i]; + partial_sig_b_ptr[i] = &partial_sig_b[i]; + + testrand256(sk_a[i]); + testrand256(sk_b[i]); + CHECK(create_keypair_and_pk(&keypair_a[i], &pk_a[i], sk_a[i]) == 1); + CHECK(create_keypair_and_pk(&keypair_b[i], &pk_b[i], sk_b[i]) == 1); + } + testrand256(sec_adaptor); + CHECK(secp256k1_ec_pubkey_create(CTX, &pub_adaptor, sec_adaptor) == 1); + + CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk_a, &keyagg_cache_a, pk_a_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1); + + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], &pk_a[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_a[1], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], &pk_b[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], &pk_b[1], NULL, NULL, NULL) == 1); + + /* Step 2: Exchange nonces */ + CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_a, pubnonce_ptr_a, 2) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session_a, &aggnonce_a, msg32_a, &keyagg_cache_a, &pub_adaptor) == 1); + CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_a, &session_a) == 1); + CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_b, pubnonce_ptr_b, 2) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session_b, &aggnonce_b, msg32_b, &keyagg_cache_b, &pub_adaptor) == 1); + CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_b, &session_b) == 1); + + /* Step 3: Signer 0 produces partial signatures for both chains. */ + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[0], &secnonce_a[0], &keypair_a[0], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[0], &secnonce_b[0], &keypair_b[0], &keyagg_cache_b, &session_b) == 1); + + /* Step 4: Signer 1 receives partial signatures, verifies them and creates a + * partial signature to send B-coins to signer 0. */ + CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_a[0], &pubnonce_a[0], &pk_a[0], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_b[0], &pubnonce_b[0], &pk_b[0], &keyagg_cache_b, &session_b) == 1); + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[1], &secnonce_b[1], &keypair_b[1], &keyagg_cache_b, &session_b) == 1); + + /* Step 5: Signer 0 aggregates its own partial signature with the partial + * signature from signer 1 and adapts it. This results in a complete + * signature which is broadcasted by signer 0 to take B-coins. */ + CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_b, &session_b, partial_sig_b_ptr, 2) == 1); + CHECK(secp256k1_musig_adapt(CTX, final_sig_b, pre_sig_b, sec_adaptor, nonce_parity_b) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_b, msg32_b, sizeof(msg32_b), &agg_pk_b) == 1); + + /* Step 6: Signer 1 signs, extracts adaptor from the published signature, + * and adapts the signature to take A-coins. */ + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[1], &secnonce_a[1], &keypair_a[1], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_a, &session_a, partial_sig_a_ptr, 2) == 1); + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor_extracted, final_sig_b, pre_sig_b, nonce_parity_b) == 1); + CHECK(secp256k1_memcmp_var(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ + CHECK(secp256k1_musig_adapt(CTX, final_sig_a, pre_sig_a, sec_adaptor_extracted, nonce_parity_a) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1); +} + /* Checks that the initialized tagged hashes have the expected * state. */ static void sha256_tag_test(void) { @@ -639,7 +787,7 @@ static void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_secrand[1], sk1, &pk[1], NULL, NULL, NULL) == 1); CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache, NULL) == 1); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce[0], &keypair[0], keyagg_cache, &session) == 1); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[1], &secnonce[1], &keypair[1], keyagg_cache, &session) == 1); @@ -905,7 +1053,7 @@ static void musig_test_vectors_signverify(void) { CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonces[c->aggnonce_index])); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); musig_test_set_secnonce(&secnonce, vector->secnonces[0], &pubkey); @@ -948,7 +1096,7 @@ static void musig_test_vectors_signverify(void) { if (!expected) { continue; } - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); expected = c->error != MUSIG_SECNONCE; CHECK(!expected); @@ -977,7 +1125,7 @@ static void musig_test_vectors_signverify(void) { CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, c->nonce_indices_len) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[c->signer_index], sizeof(vector->pubkeys[0]))); @@ -1032,7 +1180,7 @@ static void musig_test_vectors_tweak(void) { CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); CHECK(secp256k1_musig_partial_sig_serialize(CTX, partial_sig32, &partial_sig)); @@ -1068,7 +1216,7 @@ static void musig_test_vectors_sigagg(void) { CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk32, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, c->aggnonce)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); for (j = 0; j < c->psig_indices_len; j++) { CHECK(secp256k1_musig_partial_sig_parse(CTX, &partial_sig[j], vector->psigs[c->psig_indices[j]])); partial_sig_ptr[j] = &partial_sig[j]; @@ -1141,12 +1289,14 @@ static void musig_test_static_nonce_gen_counter(void) { /* --- Test registry --- */ REPEAT_TEST(musig_simple_test) /* Run multiple times to ensure that pk and nonce have different y parities */ +REPEAT_TEST(scriptless_atomic_swap) REPEAT_TEST(musig_tweak_test) static const struct tf_test_entry tests_musig[] = { CASE1(musig_simple_test), CASE1(musig_api_tests), CASE1(musig_nonce_test), + CASE1(scriptless_atomic_swap), CASE1(musig_tweak_test), CASE1(sha256_tag_test), CASE1(musig_test_vectors_keyagg), diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include new file mode 100644 index 00000000..bc727d5b --- /dev/null +++ b/src/modules/rangeproof/Makefile.am.include @@ -0,0 +1,13 @@ +include_HEADERS += include/secp256k1_rangeproof.h +noinst_HEADERS += src/modules/rangeproof/main_impl.h +noinst_HEADERS += src/modules/rangeproof/borromean.h +noinst_HEADERS += src/modules/rangeproof/borromean_impl.h +noinst_HEADERS += src/modules/rangeproof/rangeproof.h +noinst_HEADERS += src/modules/rangeproof/rangeproof_impl.h +noinst_HEADERS += src/modules/rangeproof/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_rangeproof +bench_rangeproof_SOURCES = src/bench_rangeproof.c +bench_rangeproof_LDADD = libsecp256k1.la +bench_rangeproof_CPPFLAGS = $(SECP_CONFIG_DEFINES) +endif diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h new file mode 100644 index 00000000..5620deac --- /dev/null +++ b/src/modules/rangeproof/borromean.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef SECP256K1_BORROMEAN_H +#define SECP256K1_BORROMEAN_H + +#include "../../scalar.h" +#include "../../field.h" +#include "../../group.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" + +static int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e0, const secp256k1_scalar *s, + const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen); + +static int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, + unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, + const size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen); + +#endif diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h new file mode 100644 index 00000000..2fff3c28 --- /dev/null +++ b/src/modules/rangeproof/borromean_impl.h @@ -0,0 +1,197 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef SECP256K1_BORROMEAN_IMPL_H +#define SECP256K1_BORROMEAN_IMPL_H + +#include "../../scalar.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../eckey.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" +#include "borromean.h" + +#include +#include + +SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, + size_t ridx, size_t eidx) { + unsigned char ring[4]; + unsigned char epos[4]; + secp256k1_sha256 sha256_en; + secp256k1_sha256_initialize(&sha256_en); + secp256k1_write_be32(ring, (uint32_t)ridx); + secp256k1_write_be32(epos, (uint32_t)eidx); + secp256k1_sha256_write(&sha256_en, e, elen); + secp256k1_sha256_write(&sha256_en, m, mlen); + secp256k1_sha256_write(&sha256_en, ring, 4); + secp256k1_sha256_write(&sha256_en, epos, 4); + secp256k1_sha256_finalize(&sha256_en, hash); + secp256k1_sha256_clear(&sha256_en); +} + +/** "Borromean" ring signature. + * Verifies nrings concurrent ring signatures all sharing a challenge value. + * Signature is one s value per pubkey and a hash. + * Verification equation: + * | m = H(P_{0..}||message) (Message must contain pubkeys or a pubkey commitment) + * | For each ring i: + * | | en = to_scalar(H(e0||m||i||0)) + * | | For each pubkey j: + * | | | r = s_i_j G + en * P_i_j + * | | | e = H(r||m||i||j) + * | | | en = to_scalar(e) + * | | r_i = r + * | return e_0 ==== H(r_{0..i}||m) + */ +int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e0, + const secp256k1_scalar *s, const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen) { + secp256k1_gej rgej; + secp256k1_ge rge; + secp256k1_scalar ens; + secp256k1_sha256 sha256_e0; + unsigned char tmp[33]; + size_t i; + size_t j; + size_t count; + int overflow; + VERIFY_CHECK(e0 != NULL); + VERIFY_CHECK(s != NULL); + VERIFY_CHECK(pubs != NULL); + VERIFY_CHECK(rsizes != NULL); + VERIFY_CHECK(nrings > 0); + VERIFY_CHECK(m != NULL); + count = 0; + secp256k1_sha256_initialize(&sha256_e0); + for (i = 0; i < nrings; i++) { + VERIFY_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + for (j = 0; j < rsizes[i]; j++) { + if (overflow || secp256k1_scalar_is_zero(&s[count]) || secp256k1_scalar_is_zero(&ens) || secp256k1_gej_is_infinity(&pubs[count])) { + return 0; + } + if (evalues) { + /*If requested, save the challenges for proof rewind.*/ + evalues[count] = ens; + } + secp256k1_ecmult(&rgej, &pubs[count], &ens, &s[count]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + /* OPT: loop can be hoisted and split to use batch inversion across all the rings; this would make it much faster. */ + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); + if (j != rsizes[i] - 1) { + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + } else { + secp256k1_sha256_write(&sha256_e0, tmp, 33); + } + count++; + } + } + secp256k1_sha256_write(&sha256_e0, m, mlen); + secp256k1_sha256_finalize(&sha256_e0, tmp); + secp256k1_sha256_clear(&sha256_e0); + return secp256k1_memcmp_var(e0, tmp, 32) == 0; +} + +int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, + unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, + const size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen) { + secp256k1_gej rgej; + secp256k1_ge rge; + secp256k1_scalar ens; + secp256k1_sha256 sha256_e0; + unsigned char tmp[33]; + size_t i; + size_t j; + size_t count; + int overflow; + VERIFY_CHECK(ecmult_gen_ctx != NULL); + VERIFY_CHECK(e0 != NULL); + VERIFY_CHECK(s != NULL); + VERIFY_CHECK(pubs != NULL); + VERIFY_CHECK(k != NULL); + VERIFY_CHECK(sec != NULL); + VERIFY_CHECK(rsizes != NULL); + VERIFY_CHECK(secidx != NULL); + VERIFY_CHECK(nrings > 0); + VERIFY_CHECK(m != NULL); + secp256k1_sha256_initialize(&sha256_e0); + count = 0; + for (i = 0; i < nrings; i++) { + VERIFY_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &k[i]); + secp256k1_ge_set_gej(&rge, &rgej); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(&rge, tmp); + for (j = secidx[i] + 1; j < rsizes[i]; j++) { + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + /** The signing algorithm as a whole is not memory uniform so there is likely a cache sidechannel that + * leaks which members are non-forgeries. That the forgeries themselves are variable time may leave + * an additional privacy impacting timing side-channel, but not a key loss one. + */ + secp256k1_ecmult(&rgej, &pubs[count + j], &ens, &s[count + j]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); + } + secp256k1_sha256_write(&sha256_e0, tmp, 33); + count += rsizes[i]; + } + secp256k1_sha256_write(&sha256_e0, m, mlen); + secp256k1_sha256_finalize(&sha256_e0, e0); + secp256k1_sha256_clear(&sha256_e0); + count = 0; + for (i = 0; i < nrings; i++) { + VERIFY_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + for (j = 0; j < secidx[i]; j++) { + secp256k1_ecmult(&rgej, &pubs[count + j], &ens, &s[count + j]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + } + secp256k1_scalar_mul(&s[count + j], &ens, &sec[i]); + secp256k1_scalar_negate(&s[count + j], &s[count + j]); + secp256k1_scalar_add(&s[count + j], &s[count + j], &k[i]); + if (secp256k1_scalar_is_zero(&s[count + j])) { + return 0; + } + count += rsizes[i]; + } + secp256k1_scalar_clear(&ens); + secp256k1_ge_clear(&rge); + secp256k1_gej_clear(&rgej); + secp256k1_memclear_explicit(tmp, 33); + return 1; +} + +#endif diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h new file mode 100644 index 00000000..32614caa --- /dev/null +++ b/src/modules/rangeproof/main_impl.h @@ -0,0 +1,103 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RANGEPROOF_MAIN_H +#define SECP256K1_MODULE_RANGEPROOF_MAIN_H + +#include "../../group.h" + +#include "../generator/main_impl.h" +#include "../rangeproof/borromean_impl.h" +#include "../rangeproof/rangeproof_impl.h" + +int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { + size_t offset; + uint64_t scale; + ARG_CHECK(exp != NULL); + ARG_CHECK(mantissa != NULL); + ARG_CHECK(min_value != NULL); + ARG_CHECK(max_value != NULL); + ARG_CHECK(proof != NULL); + offset = 0; + scale = 1; + (void)ctx; + return secp256k1_rangeproof_getheader_impl(&offset, exp, mantissa, &scale, min_value, max_value, proof, plen); +} + +int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, + unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { + secp256k1_ge commitp; + secp256k1_ge genp; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(min_value != NULL); + ARG_CHECK(max_value != NULL); + ARG_CHECK(message_out != NULL || outlen == NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(gen != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_gen_ctx, + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp); +} + +int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { + secp256k1_ge commitp; + secp256k1_ge genp; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(min_value != NULL); + ARG_CHECK(max_value != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(gen != NULL); + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); + return secp256k1_rangeproof_verify_impl(NULL, + NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp); +} + +int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value, + const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, + const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen){ + secp256k1_ge commitp; + secp256k1_ge genp; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(plen != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(message != NULL || msg_len == 0); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(gen != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); + return secp256k1_rangeproof_sign_impl(&ctx->ecmult_gen_ctx, + proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp); +} + +size_t secp256k1_rangeproof_max_size(const secp256k1_context* ctx, uint64_t max_value, int min_bits) { + const int val_mantissa = max_value > 0 ? 64 - secp256k1_clz64_var(max_value) : 1; + const int mantissa = min_bits > val_mantissa ? min_bits : val_mantissa; + const size_t rings = (mantissa + 1) / 2; + const size_t npubs = rings * 4 - 2 * (mantissa % 2); + + VERIFY_CHECK(ctx != NULL); + (void) ctx; + + return 10 + 32 * (npubs + rings - 1) + 32 + ((rings - 1 + 7) / 8); +} + +#endif diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h new file mode 100644 index 00000000..fa118e66 --- /dev/null +++ b/src/modules/rangeproof/rangeproof.h @@ -0,0 +1,20 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_RANGEPROOF_H +#define SECP256K1_RANGEPROOF_H + +#include "../../scalar.h" +#include "../../group.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" + +static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp); + +#endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h new file mode 100644 index 00000000..476da5eb --- /dev/null +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -0,0 +1,685 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_RANGEPROOF_IMPL_H +#define SECP256K1_RANGEPROOF_IMPL_H + +#include "../../eckey.h" +#include "../../scalar.h" +#include "../../group.h" +#include "../../hash_impl.h" +#include "../../util.h" + +#include "../generator/pedersen.h" +#include "../rangeproof/borromean.h" +#include "../rangeproof/rangeproof.h" + +SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, + int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { + secp256k1_gej base; + size_t i; + size_t j; + size_t npub; + VERIFY_CHECK(exp < 19); + if (exp < 0) { + exp = 0; + } + secp256k1_gej_set_ge(&base, genp); + secp256k1_gej_neg(&base, &base); + while (exp--) { + /* Multiplication by 10 */ + secp256k1_gej tmp; + secp256k1_gej_double_var(&tmp, &base, NULL); + secp256k1_gej_double_var(&base, &tmp, NULL); + secp256k1_gej_double_var(&base, &base, NULL); + secp256k1_gej_add_var(&base, &base, &tmp, NULL); + } + npub = 0; + for (i = 0; i < rings; i++) { + for (j = 1; j < rsizes[i]; j++) { + secp256k1_gej_add_var(&pubs[npub + j], &pubs[npub + j - 1], &base, NULL); + } + if (i < rings - 1) { + secp256k1_gej_double_var(&base, &base, NULL); + secp256k1_gej_double_var(&base, &base, NULL); + } + npub += rsizes[i]; + } +} + +SECP256K1_INLINE static void secp256k1_rangeproof_serialize_point(unsigned char* data, const secp256k1_ge *point) { + secp256k1_fe pointx; + pointx = point->x; + secp256k1_fe_normalize(&pointx); + data[0] = !secp256k1_fe_is_square_var(&point->y); + secp256k1_fe_get_b32(data + 1, &pointx); +} + +SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_scalar *s, unsigned char *message, + size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge* genp) { + unsigned char tmp[32]; + unsigned char rngseed[32 + 33 + 33 + 10]; + secp256k1_rfc6979_hmac_sha256 rng; + secp256k1_scalar acc; + int overflow; + int ret; + size_t i; + size_t j; + int b; + size_t npub; + VERIFY_CHECK(len <= 10); + memcpy(rngseed, nonce, 32); + secp256k1_rangeproof_serialize_point(rngseed + 32, commit); + secp256k1_rangeproof_serialize_point(rngseed + 32 + 33, genp); + memcpy(rngseed + 33 + 33 + 32, proof, len); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + 33 + len); + secp256k1_scalar_set_int(&acc, 0); + npub = 0; + ret = 1; + for (i = 0; i < rings; i++) { + if (i < rings - 1) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + secp256k1_scalar_set_b32(&sec[i], tmp, &overflow); + } while (overflow || secp256k1_scalar_is_zero(&sec[i])); + secp256k1_scalar_add(&acc, &acc, &sec[i]); + } else { + secp256k1_scalar_negate(&acc, &acc); + sec[i] = acc; + } + for (j = 0; j < rsizes[i]; j++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + if (message) { + for (b = 0; b < 32; b++) { + tmp[b] ^= message[(i * 4 + j) * 32 + b]; + message[(i * 4 + j) * 32 + b] = tmp[b]; + } + } + secp256k1_scalar_set_b32(&s[npub], tmp, &overflow); + ret &= !(overflow || secp256k1_scalar_is_zero(&s[npub])); + npub++; + } + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + secp256k1_rfc6979_hmac_sha256_clear(&rng); + secp256k1_scalar_clear(&acc); + secp256k1_memclear_explicit(tmp, 32); + return ret; +} + +SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rings, size_t *rsizes, size_t *npub, size_t *secidx, uint64_t *min_value, + int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) { + size_t i; + *rings = 1; + rsizes[0] = 1; + secidx[0] = 0; + *scale = 1; + *mantissa = 0; + *npub = 0; + if (*min_value == UINT64_MAX) { + /* If the minimum value is the maximal representable value, then we cannot code a range. */ + *exp = -1; + } + if (*exp >= 0) { + int max_bits; + uint64_t v2; + if ((*min_value && value > INT64_MAX) || (value && *min_value >= INT64_MAX)) { + /* If either value or min_value is >= 2^63-1 then the other must by zero to avoid overflowing the proven range. */ + return 0; + } + max_bits = *min_value ? secp256k1_clz64_var(*min_value) : 64; + if (*min_bits > max_bits) { + *min_bits = max_bits; + } + if (*min_bits > 61 || value > INT64_MAX) { + /** Ten is not a power of two, so dividing by ten and then representing in base-2 times ten + * expands the representable range. The verifier requires the proven range is within 0..2**64. + * For very large numbers (all over 2**63) we must change our exponent to compensate. + * Rather than handling it precisely, this just disables use of the exponent for big values. + */ + *exp = 0; + } + /* Mask off the least significant digits, as requested. */ + *v = value - *min_value; + /* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */ + v2 = *min_bits ? (UINT64_MAX>>(64-*min_bits)) : 0; + for (i = 0; (int) i < *exp && (v2 <= UINT64_MAX / 10); i++) { + *v /= 10; + v2 *= 10; + } + *exp = i; + v2 = *v; + for (i = 0; (int) i < *exp; i++) { + v2 *= 10; + *scale *= 10; + } + /* If the masked number isn't precise, compute the public offset. */ + *min_value = value - v2; + /* How many bits do we need to represent our value? */ + *mantissa = *v ? 64 - secp256k1_clz64_var(*v) : 1; + if (*min_bits > *mantissa) { + /* If the user asked for more precision, give it to them. */ + *mantissa = *min_bits; + } + /* Digits in radix-4, except for the last digit if our mantissa length is odd. */ + *rings = (*mantissa + 1) >> 1; + for (i = 0; i < *rings; i++) { + rsizes[i] = ((i < *rings - 1) | (!(*mantissa&1))) ? 4 : 2; + *npub += rsizes[i]; + secidx[i] = (*v >> (i*2)) & 3; + } + VERIFY_CHECK(*mantissa>0); + VERIFY_CHECK((*v & ~(UINT64_MAX>>(64-*mantissa))) == 0); /* Did this get all the bits? */ + } else { + /* A proof for an exact value. */ + *exp = 0; + *min_value = value; + *v = 0; + *npub = 2; + } + VERIFY_CHECK(*v * *scale + *min_value == value); + VERIFY_CHECK(*rings > 0); + VERIFY_CHECK(*rings <= 32); + VERIFY_CHECK(*npub <= 128); + return 1; +} + +/* strawman interface, writes proof in proof, a buffer of plen, proves with respect to min_value the range for commit which has the provided blinding factor and value. */ +SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + unsigned char *proof, size_t *plen, uint64_t min_value, + const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, + const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp){ + secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ + secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ + secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ + secp256k1_scalar k[32]; /* Nonces for our non-forged signatures. */ + secp256k1_scalar stmp; + secp256k1_sha256 sha256_m; + unsigned char prep[4096]; + unsigned char tmp[33]; + unsigned char *signs; /* Location of sign flags in the proof. */ + uint64_t v; + uint64_t scale; /* scale = 10^exp. */ + int mantissa; /* Number of bits proven in the blinded value. */ + size_t rings; /* How many digits will our proof cover. */ + size_t rsizes[32]; /* How many possible values there are for each place. */ + size_t secidx[32]; /* Which digit is the correct one. */ + size_t len; /* Number of bytes used so far. */ + size_t i; + int overflow; + size_t npub; + len = 0; + if (*plen < 65 || min_value > value || min_bits > 64 || min_bits < 0 || exp < -1 || exp > 18) { + return 0; + } + if (!secp256k1_range_proveparams(&v, &rings, rsizes, &npub, secidx, &min_value, &mantissa, &scale, &exp, &min_bits, value)) { + return 0; + } + proof[len] = (rsizes[0] > 1 ? (64 | exp) : 0) | (min_value ? 32 : 0); + len++; + if (rsizes[0] > 1) { + VERIFY_CHECK(mantissa > 0 && mantissa <= 64); + proof[len] = mantissa - 1; + len++; + } + if (min_value) { + for (i = 0; i < 8; i++) { + proof[len + i] = (min_value >> ((7-i) * 8)) & 255; + } + len += 8; + } + /* Do we have enough room in the proof for the message? Each ring gives us 128 bytes, but the + * final ring is used to encode the blinding factor and the value, so we can't use that. (Well, + * technically there are 64 bytes available if we avoided the other data, but this is difficult + * because it's not always in the same place. */ + if (msg_len > 0 && msg_len > 128 * (rings - 1)) { + return 0; + } + /* Do we have enough room for the proof? */ + if (*plen - len < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + return 0; + } + secp256k1_sha256_initialize(&sha256_m); + secp256k1_rangeproof_serialize_point(tmp, commit); + secp256k1_sha256_write(&sha256_m, tmp, 33); + secp256k1_rangeproof_serialize_point(tmp, genp); + secp256k1_sha256_write(&sha256_m, tmp, 33); + secp256k1_sha256_write(&sha256_m, proof, len); + + memset(prep, 0, 4096); + if (message != NULL) { + memcpy(prep, message, msg_len); + } + /* Note, the data corresponding to the blinding factors must be zero. */ + if (rsizes[rings - 1] > 1) { + size_t idx; + /* Value encoding sidechannel. */ + idx = rsizes[rings - 1] - 1; + idx -= secidx[rings - 1] == idx; + idx = ((rings - 1) * 4 + idx) * 32; + for (i = 0; i < 8; i++) { + prep[8 + i + idx] = prep[16 + i + idx] = prep[24 + i + idx] = (v >> (56 - i * 8)) & 255; + prep[i + idx] = 0; + } + prep[idx] = 128; + } + if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len, genp)) { + return 0; + } + secp256k1_memclear_explicit(prep, 4096); + for (i = 0; i < rings; i++) { + /* Sign will overwrite the non-forged signature, move that random value into the nonce. */ + k[i] = s[i * 4 + secidx[i]]; + secp256k1_scalar_clear(&s[i * 4 + secidx[i]]); + } + /** Genrand returns the last blinding factor as -sum(rest), + * adding in the blinding factor for our commitment, results in the blinding factor for + * the commitment to the last digit that the verifier can compute for itself by subtracting + * all the digits in the proof from the commitment. This lets the prover skip sending the + * blinded value for one digit. + */ + secp256k1_scalar_set_b32(&stmp, blind, &overflow); + secp256k1_scalar_add(&sec[rings - 1], &sec[rings - 1], &stmp); + if (overflow || secp256k1_scalar_is_zero(&sec[rings - 1])) { + return 0; + } + signs = &proof[len]; + /* We need one sign bit for each blinded value we send. */ + for (i = 0; i < (rings + 6) >> 3; i++) { + signs[i] = 0; + len++; + } + npub = 0; + for (i = 0; i < rings; i++) { + /*OPT: Use the precomputed gen2 basis?*/ + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2), genp); + if (secp256k1_gej_is_infinity(&pubs[npub])) { + return 0; + } + if (i < rings - 1) { + unsigned char tmpc[33]; + secp256k1_ge c; + unsigned char quadness; + /*OPT: split loop and batch invert.*/ + /*OPT: do not compute full pubs[npub] in ge form; we only need x */ + secp256k1_ge_set_gej_var(&c, &pubs[npub]); + secp256k1_rangeproof_serialize_point(tmpc, &c); + quadness = tmpc[0]; + secp256k1_sha256_write(&sha256_m, tmpc, 33); + signs[i>>3] |= quadness << (i&7); + memcpy(&proof[len], tmpc + 1, 32); + len += 32; + } + npub += rsizes[i]; + } + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); + if (extra_commit != NULL) { + secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); + } + secp256k1_sha256_finalize(&sha256_m, tmp); + secp256k1_sha256_clear(&sha256_m); + if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { + return 0; + } + len += 32; + for (i = 0; i < npub; i++) { + secp256k1_scalar_get_b32(&proof[len],&s[i]); + len += 32; + } + VERIFY_CHECK(len <= *plen); + *plen = len; + secp256k1_memclear_explicit(prep, 4096); + return 1; +} + +/* Computes blinding factor x given k, s, and the challenge e. */ +SECP256K1_INLINE static void secp256k1_rangeproof_recover_x(secp256k1_scalar *x, const secp256k1_scalar *k, const secp256k1_scalar *e, + const secp256k1_scalar *s) { + secp256k1_scalar stmp; + secp256k1_scalar_negate(x, s); + secp256k1_scalar_add(x, x, k); + secp256k1_scalar_inverse(&stmp, e); + secp256k1_scalar_mul(x, x, &stmp); +} + +/* Computes ring's nonce given the blinding factor x, the challenge e, and the signature s. */ +SECP256K1_INLINE static void secp256k1_rangeproof_recover_k(secp256k1_scalar *k, const secp256k1_scalar *x, const secp256k1_scalar *e, + const secp256k1_scalar *s) { + secp256k1_scalar stmp; + secp256k1_scalar_mul(&stmp, x, e); + secp256k1_scalar_add(k, s, &stmp); +} + +SECP256K1_INLINE static void secp256k1_rangeproof_ch32xor(unsigned char *x, const unsigned char *y) { + int i; + for (i = 0; i < 32; i++) { + x[i] ^= y[i]; + } +} + +SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar *blind, uint64_t *v, + unsigned char *m, size_t *mlen, secp256k1_scalar *ev, secp256k1_scalar *s, + size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge *genp) { + secp256k1_scalar s_orig[128]; + secp256k1_scalar sec[32]; + secp256k1_scalar stmp; + unsigned char prep[4096]; + unsigned char tmp[32]; + uint64_t value = 0; + size_t offset; + size_t i; + size_t j; + int b; + size_t skip1; + size_t skip2; + size_t npub; + npub = ((rings - 1) << 2) + rsizes[rings-1]; + VERIFY_CHECK(npub <= 128); + VERIFY_CHECK(npub >= 1); + memset(prep, 0, 4096); + /* Reconstruct the provers random values. */ + secp256k1_rangeproof_genrand(sec, s_orig, prep, rsizes, rings, nonce, commit, proof, len, genp); + *v = UINT64_MAX; + secp256k1_scalar_clear(blind); + if (rings == 1 && rsizes[0] == 1) { + /* With only a single proof, we can only recover the blinding factor. */ + secp256k1_rangeproof_recover_x(blind, &s_orig[0], &ev[0], &s[0]); + if (v) { + *v = 0; + } + if (mlen) { + *mlen = 0; + } + return 1; + } + npub = (rings - 1) << 2; + for (j = 0; j < 2; j++) { + size_t idx; + /* Look for a value encoding in the last ring. */ + idx = npub + rsizes[rings - 1] - 1 - j; + secp256k1_scalar_get_b32(tmp, &s[idx]); + secp256k1_rangeproof_ch32xor(tmp, &prep[idx * 32]); + if ((tmp[0] & 128) && (secp256k1_memcmp_var(&tmp[16], &tmp[24], 8) == 0) && (secp256k1_memcmp_var(&tmp[8], &tmp[16], 8) == 0)) { + value = 0; + for (i = 0; i < 8; i++) { + value = (value << 8) + tmp[24 + i]; + } + if (v) { + *v = value; + } + memcpy(&prep[idx * 32], tmp, 32); + break; + } + } + if (j > 1) { + /* Couldn't extract a value. */ + if (mlen) { + *mlen = 0; + } + return 0; + } + skip1 = rsizes[rings - 1] - 1 - j; + skip2 = ((value >> ((rings - 1) << 1)) & 3); + if (skip1 == skip2) { + /*Value is in wrong position.*/ + if (mlen) { + *mlen = 0; + } + return 0; + } + skip1 += (rings - 1) << 2; + skip2 += (rings - 1) << 2; + /* Like in the rsize[] == 1 case, Having figured out which s is the one which was not forged, we can recover the blinding factor. */ + secp256k1_rangeproof_recover_x(&stmp, &s_orig[skip2], &ev[skip2], &s[skip2]); + secp256k1_scalar_negate(&sec[rings - 1], &sec[rings - 1]); + secp256k1_scalar_add(blind, &stmp, &sec[rings - 1]); + if (!m || !mlen || *mlen == 0) { + if (mlen) { + *mlen = 0; + } + /* FIXME: cleanup in early out/failure cases. */ + return 1; + } + offset = 0; + npub = 0; + for (i = 0; i < rings; i++) { + size_t idx; + idx = (value >> (i << 1)) & 3; + for (j = 0; j < rsizes[i]; j++) { + if (npub == skip1 || npub == skip2) { + npub++; + continue; + } + if (idx == j) { + /** For the non-forged signatures the signature is calculated instead of random, instead we recover the prover's nonces. + * this could just as well recover the blinding factors and messages could be put there as is done for recovering the + * blinding factor in the last ring, but it takes an inversion to recover x so it's faster to put the message data in k. + */ + secp256k1_rangeproof_recover_k(&stmp, &sec[i], &ev[npub], &s[npub]); + } else { + stmp = s[npub]; + } + secp256k1_scalar_get_b32(tmp, &stmp); + secp256k1_rangeproof_ch32xor(tmp, &prep[npub * 32]); + for (b = 0; b < 32 && offset < *mlen; b++) { + m[offset] = tmp[b]; + offset++; + } + npub++; + } + } + *mlen = offset; + secp256k1_memclear_explicit(prep, 4096); + for (i = 0; i < 128; i++) { + secp256k1_scalar_clear(&s_orig[i]); + } + for (i = 0; i < 32; i++) { + secp256k1_scalar_clear(&sec[i]); + } + secp256k1_scalar_clear(&stmp); + return 1; +} + +SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(size_t *offset, int *exp, int *mantissa, uint64_t *scale, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { + int i; + int has_nz_range; + int has_min; + if (plen < 65 || ((proof[*offset] & 128) != 0)) { + return 0; + } + has_nz_range = proof[*offset] & 64; + has_min = proof[*offset] & 32; + *exp = -1; + *mantissa = 0; + if (has_nz_range) { + *exp = proof[*offset] & 31; + *offset += 1; + if (*exp > 18) { + return 0; + } + *mantissa = proof[*offset] + 1; + if (*mantissa > 64) { + return 0; + } + *max_value = UINT64_MAX>>(64-*mantissa); + } else { + *max_value = 0; + } + *offset += 1; + *scale = 1; + for (i = 0; i < *exp; i++) { + if (*max_value > UINT64_MAX / 10) { + return 0; + } + *max_value *= 10; + *scale *= 10; + } + *min_value = 0; + if (has_min) { + if(plen - *offset < 8) { + return 0; + } + /*FIXME: Compact minvalue encoding?*/ + for (i = 0; i < 8; i++) { + *min_value = (*min_value << 8) | proof[*offset + i]; + } + *offset += 8; + } + if (*max_value > UINT64_MAX - *min_value) { + return 0; + } + *max_value += *min_value; + return 1; +} + +/* Verifies range proof (len plen) for commit, the min/max values proven are put in the min/max arguments; returns 0 on failure 1 on success.*/ +SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp) { + secp256k1_gej accj; + secp256k1_gej pubs[128]; + secp256k1_ge c; + secp256k1_scalar s[128]; + secp256k1_scalar evalues[128]; /* Challenges, only used during proof rewind. */ + secp256k1_sha256 sha256_m; + size_t rsizes[32]; + int ret; + size_t i; + int exp; + int mantissa; + size_t offset; + size_t rings; + int overflow; + size_t npub; + int offset_post_header; + uint64_t scale; + unsigned char signs[31]; + unsigned char m[33]; + const unsigned char *e0; + offset = 0; + if (!secp256k1_rangeproof_getheader_impl(&offset, &exp, &mantissa, &scale, min_value, max_value, proof, plen)) { + return 0; + } + offset_post_header = offset; + rings = 1; + rsizes[0] = 1; + npub = 1; + if (mantissa != 0) { + rings = (mantissa >> 1); + for (i = 0; i < rings; i++) { + rsizes[i] = 4; + } + npub = (mantissa >> 1) << 2; + if (mantissa & 1) { + rsizes[rings] = 2; + npub += rsizes[rings]; + rings++; + } + } + VERIFY_CHECK(rings <= 32); + if (plen - offset < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + return 0; + } + secp256k1_sha256_initialize(&sha256_m); + secp256k1_rangeproof_serialize_point(m, commit); + secp256k1_sha256_write(&sha256_m, m, 33); + secp256k1_rangeproof_serialize_point(m, genp); + secp256k1_sha256_write(&sha256_m, m, 33); + secp256k1_sha256_write(&sha256_m, proof, offset); + for(i = 0; i < rings - 1; i++) { + signs[i] = (proof[offset + ( i>> 3)] & (1 << (i & 7))) != 0; + } + offset += (rings + 6) >> 3; + if ((rings - 1) & 7) { + /* Number of coded blinded points is not a multiple of 8, force extra sign bits to 0 to reject mutation. */ + if ((proof[offset - 1] >> ((rings - 1) & 7)) != 0) { + return 0; + } + } + npub = 0; + secp256k1_gej_set_infinity(&accj); + if (*min_value) { + secp256k1_pedersen_ecmult_small(&accj, *min_value, genp); + } + for(i = 0; i < rings - 1; i++) { + secp256k1_fe fe; + if (!secp256k1_fe_set_b32_limit(&fe, &proof[offset]) || + !secp256k1_ge_set_xquad(&c, &fe)) { + return 0; + } + if (signs[i]) { + secp256k1_ge_neg(&c, &c); + } + /* Not using secp256k1_rangeproof_serialize_point as we almost have it + * serialized form already. */ + secp256k1_sha256_write(&sha256_m, &signs[i], 1); + secp256k1_sha256_write(&sha256_m, &proof[offset], 32); + secp256k1_gej_set_ge(&pubs[npub], &c); + secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL); + offset += 32; + npub += rsizes[i]; + } + secp256k1_gej_neg(&accj, &accj); + secp256k1_gej_add_ge_var(&pubs[npub], &accj, commit, NULL); + if (secp256k1_gej_is_infinity(&pubs[npub])) { + return 0; + } + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); + npub += rsizes[rings - 1]; + e0 = &proof[offset]; + offset += 32; + for (i = 0; i < npub; i++) { + secp256k1_scalar_set_b32(&s[i], &proof[offset], &overflow); + if (overflow) { + return 0; + } + offset += 32; + } + if (offset != plen) { + /*Extra data found, reject.*/ + return 0; + } + if (extra_commit != NULL) { + secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); + } + secp256k1_sha256_finalize(&sha256_m, m); + secp256k1_sha256_clear(&sha256_m); + ret = secp256k1_borromean_verify(nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); + if (ret && nonce) { + /* Given the nonce, try rewinding the witness to recover its initial state. */ + secp256k1_scalar blind; + uint64_t vv; + if (!ecmult_gen_ctx) { + return 0; + } + if (!secp256k1_rangeproof_rewind_inner(&blind, &vv, message_out, outlen, evalues, s, rsizes, rings, nonce, commit, proof, offset_post_header, genp)) { + return 0; + } + /* Unwind apparently successful, see if the commitment can be reconstructed. */ + /* FIXME: should check vv is in the mantissa's range. */ + vv = (vv * scale) + *min_value; + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv, genp); + if (secp256k1_gej_is_infinity(&accj)) { + return 0; + } + secp256k1_gej_neg(&accj, &accj); + secp256k1_gej_add_ge_var(&accj, &accj, commit, NULL); + if (!secp256k1_gej_is_infinity(&accj)) { + return 0; + } + if (blindout) { + secp256k1_scalar_get_b32(blindout, &blind); + } + if (value_out) { + *value_out = vv; + } + } + return ret; +} + +#endif diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h new file mode 100644 index 00000000..7538558e --- /dev/null +++ b/src/modules/rangeproof/tests_impl.h @@ -0,0 +1,1371 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RANGEPROOF_TESTS_H +#define SECP256K1_MODULE_RANGEPROOF_TESTS_H + +#include + +#include "../../group.h" +#include "../../scalar.h" +#include "../../testrand.h" +#include "../../util.h" +#include "../../unit_test.h" + +#include "../../../include/secp256k1_rangeproof.h" + +static void test_rangeproof_api_internal(void) { + unsigned char proof[5134]; + unsigned char blind[32]; + secp256k1_pedersen_commitment commit; + uint64_t vmin = testrand32(); + uint64_t val = vmin + testrand32(); + size_t len = sizeof(proof); + /* we'll switch to dylan thomas for this one */ + const unsigned char message[68] = "My tears are like the quiet drift / Of petals from some magic rose;"; + size_t mlen = sizeof(message); + const unsigned char ext_commit[72] = "And all my grief flows from the rift / Of unremembered skies and snows."; + size_t ext_commit_len = sizeof(ext_commit); + + testrand256(blind); + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, val, secp256k1_generator_h)); + + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_rangeproof_sign(STATIC_CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, NULL, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, NULL, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, &len, vmin, NULL, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, NULL, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, NULL, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, vmin - 1, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, mlen, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, secp256k1_generator_h) != 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, NULL)); + + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + { + int exp; + int mantissa; + uint64_t min_value; + uint64_t max_value; + CHECK(secp256k1_rangeproof_info(CTX, &exp, &mantissa, &min_value, &max_value, proof, len) != 0); + CHECK(exp == 0); + CHECK(((uint64_t) 1 << mantissa) > val - vmin); + CHECK(((uint64_t) 1 << (mantissa - 1)) <= val - vmin); + CHECK(min_value == vmin); + CHECK(max_value >= val); + + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_info(CTX, NULL, &mantissa, &min_value, &max_value, proof, len)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_info(CTX, &exp, NULL, &min_value, &max_value, proof, len)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_info(CTX, &exp, &mantissa, NULL, &max_value, proof, len)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_info(CTX, &exp, &mantissa, &min_value, NULL, proof, len)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_info(CTX, &exp, &mantissa, &min_value, &max_value, NULL, len)); + CHECK(secp256k1_rangeproof_info(CTX, &exp, &mantissa, &min_value, &max_value, proof, 0) == 0); + } + { + uint64_t min_value; + uint64_t max_value; + CHECK(secp256k1_rangeproof_verify(CTX, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 1); + + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_verify(CTX, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_verify(CTX, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_verify(CTX, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_verify(CTX, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(CTX, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_verify(CTX, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(CTX, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_verify(CTX, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL)); + } + { + unsigned char blind_out[32]; + unsigned char message_out[68]; + uint64_t value_out; + uint64_t min_value; + uint64_t max_value; + size_t message_len = sizeof(message_out); + + CHECK(secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_rangeproof_rewind(STATIC_CTX, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + + CHECK(min_value == vmin); + CHECK(max_value >= val); + CHECK(value_out == val); + CHECK(message_len == sizeof(message_out)); + CHECK(secp256k1_memcmp_var(message, message_out, sizeof(message_out)) == 0); + + /* blindout may be NULL */ + CHECK(secp256k1_rangeproof_rewind(CTX, NULL, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + /* valueout may be NULL */ + CHECK(secp256k1_rangeproof_rewind(CTX, blind_out, NULL, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, NULL, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK_ILLEGAL(CTX, secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL)); + } + + /* This constant is hardcoded in these tests and elsewhere, so we + * consider it to be part of the API and test it here. */ + CHECK(secp256k1_rangeproof_max_size(CTX, 0, 64) == 5134); + CHECK(secp256k1_rangeproof_max_size(CTX, UINT64_MAX, 0) == 5134); +} + +static void test_borromean_internal(void) { + unsigned char e0[32]; + secp256k1_scalar s[64]; + secp256k1_gej pubs[64]; + secp256k1_scalar k[8]; + secp256k1_scalar sec[8]; + secp256k1_ge ge; + secp256k1_scalar one; + unsigned char m[32]; + size_t rsizes[8]; + size_t secidx[8]; + size_t nrings; + size_t i; + size_t j; + int c; + testrand256_test(m); + nrings = 1 + (testrand32()&7); + c = 0; + secp256k1_scalar_set_int(&one, 1); + if (testrand32()&1) { + secp256k1_scalar_negate(&one, &one); + } + for (i = 0; i < nrings; i++) { + rsizes[i] = 1 + (testrand32()&7); + secidx[i] = testrand32() % rsizes[i]; + testutil_random_scalar_order(&sec[i]); + testutil_random_scalar_order(&k[i]); + if(testrand32()&7) { + sec[i] = one; + } + if(testrand32()&7) { + k[i] = one; + } + for (j = 0; j < rsizes[i]; j++) { + testutil_random_scalar_order(&s[c + j]); + if(testrand32()&7) { + s[i] = one; + } + if (j == secidx[i]) { + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubs[c + j], &sec[i]); + } else { + testutil_random_ge_test(&ge); + testutil_random_ge_jacobian_test(&pubs[c + j],&ge); + } + } + c += rsizes[i]; + } + CHECK(secp256k1_borromean_sign(&CTX->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, secidx, nrings, m, 32)); + CHECK(secp256k1_borromean_verify(NULL, e0, s, pubs, rsizes, nrings, m, 32)); + i = testrand32() % c; + secp256k1_scalar_negate(&s[i],&s[i]); + CHECK(!secp256k1_borromean_verify(NULL, e0, s, pubs, rsizes, nrings, m, 32)); + secp256k1_scalar_negate(&s[i],&s[i]); + secp256k1_scalar_set_int(&one, 1); + for(j = 0; j < 4; j++) { + i = testrand32() % c; + if (testrand32() & 1) { + secp256k1_gej_double_var(&pubs[i],&pubs[i], NULL); + } else { + secp256k1_scalar_add(&s[i],&s[i],&one); + } + CHECK(!secp256k1_borromean_verify(NULL, e0, s, pubs, rsizes, nrings, m, 32)); + } +} + +static void test_rangeproof(void) { + const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; + secp256k1_pedersen_commitment commit; + secp256k1_pedersen_commitment commit2; + unsigned char proof[5134 + 1]; /* One additional byte to test if trailing bytes are rejected */ + unsigned char blind[32]; + unsigned char blindout[32]; + unsigned char message[4096]; + size_t mlen; + uint64_t v; + uint64_t vout; + uint64_t vmin; + uint64_t minv; + uint64_t maxv; + size_t len; + size_t i; + size_t j; + size_t k; + /* Short message is a Simone de Beauvoir quote */ + const unsigned char message_short[120] = "When I see my own likeness in the depths of someone else's consciousness, I always experience a moment of panic."; + /* Long message is 0xA5 with a bunch of this quote in the middle */ + unsigned char message_long[3968]; + memset(message_long, 0xa5, sizeof(message_long)); + for (i = 1200; i < 3600; i += 120) { + memcpy(&message_long[i], message_short, sizeof(message_short)); + } + + testrand256(blind); + for (i = 0; i < 11; i++) { + v = testvs[i]; + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, v, secp256k1_generator_h)); + for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { + const unsigned char *input_message = NULL; + size_t input_message_len = 0; + /* vmin is always either 0 or 1; if it is 1, then we have no room for a message. + * If it's 0, we use "minimum encoding" and only have room for a small message when + * `testvs[i]` is >= 4; for a large message when it's >= 2^32. */ + if (vmin == 0 && i > 2) { + input_message = message_short; + input_message_len = sizeof(message_short); + } + if (vmin == 0 && i > 7) { + input_message = message_long; + input_message_len = sizeof(message_long); + } + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, NULL, 0, secp256k1_generator_h)); + CHECK(len <= 5134); + CHECK(len <= secp256k1_rangeproof_max_size(CTX, v, 0)); + mlen = 4096; + CHECK(secp256k1_rangeproof_rewind(CTX, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + if (input_message != NULL) { + CHECK(secp256k1_memcmp_var(message, input_message, input_message_len) == 0); + } + for (j = input_message_len; j < mlen; j++) { + CHECK(message[j] == 0); + } + CHECK(mlen <= 4096); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv <= v); + CHECK(maxv >= v); + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(len <= 73); + CHECK(len <= secp256k1_rangeproof_max_size(CTX, v, 0)); + CHECK(secp256k1_rangeproof_rewind(CTX, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv == v); + CHECK(maxv == v); + + /* Check with a committed message */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), secp256k1_generator_h)); + CHECK(len <= 73); + CHECK(len <= secp256k1_rangeproof_max_size(CTX, v, 0)); + CHECK(!secp256k1_rangeproof_rewind(CTX, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_rewind(CTX, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(CTX, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv == v); + CHECK(maxv == v); + } + } + testrand256(blind); + v = INT64_MAX - 1; + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, v, secp256k1_generator_h)); + for (i = 0; i < 19; i++) { + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(len <= secp256k1_rangeproof_max_size(CTX, v, 0)); + CHECK(secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(len <= 5134); + CHECK(minv <= v); + CHECK(maxv >= v); + /* Make sure it fails when validating with a committed message */ + CHECK(!secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); + } + testrand256(blind); + { + /*Malleability test.*/ + v = testrandi64(0, 255); + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, v, secp256k1_generator_h)); + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(len <= 5134); + CHECK(len <= secp256k1_rangeproof_max_size(CTX, v, 3)); + /* Test if trailing bytes are rejected. */ + proof[len] = v; + CHECK(!secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len + 1, NULL, 0, secp256k1_generator_h)); + for (i = 0; i < len*8; i++) { + proof[i >> 3] ^= 1 << (i & 7); + CHECK(!secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + proof[i >> 3] ^= 1 << (i & 7); + } + CHECK(secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(minv <= v); + CHECK(maxv >= v); + } + memcpy(&commit2, &commit, sizeof(commit)); + for (i = 0; i < (size_t) COUNT; i++) { + int exp; + int min_bits; + v = testrandi64(0, UINT64_MAX >> (testrand32()&63)); + vmin = 0; + if ((v < INT64_MAX) && (testrand32()&1)) { + vmin = testrandi64(0, v); + } + testrand256(blind); + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, v, secp256k1_generator_h)); + len = 5134; + exp = (int)testrandi64(0,18)-(int)testrandi64(0,18); + if (exp < 0) { + exp = -exp; + } + min_bits = (int)testrandi64(0,64)-(int)testrandi64(0,64); + if (min_bits < 0) { + min_bits = -min_bits; + } + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(len <= 5134); + CHECK(len <= secp256k1_rangeproof_max_size(CTX, v, min_bits)); + mlen = 4096; + CHECK(secp256k1_rangeproof_rewind(CTX, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + for (j = 0; j < mlen; j++) { + CHECK(message[j] == 0); + } + CHECK(mlen <= 4096); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); + + CHECK(minv <= v); + CHECK(maxv >= v); + CHECK(secp256k1_rangeproof_rewind(CTX, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + memcpy(&commit2, &commit, sizeof(commit)); + } + for (j = 0; j < 3; j++) { + for (i = 0; i < 96; i++) { + testrand256(&proof[i * 32]); + } + for (k = 0; k < 128; k += 3) { + len = k; + CHECK(!secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); + } + len = testrandi64(0, 3072); + CHECK(!secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); + } +} + +static void test_rangeproof_null_blinder(void) { + unsigned char proof[5134]; + const unsigned char blind[32] = { 0 }; + const uint64_t v = 1111; + uint64_t minv, maxv; + secp256k1_pedersen_commitment commit; + size_t len; + + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, v, secp256k1_generator_h)); + + /* Try a 32-bit proof; should work */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, 1, &commit, blind, commit.data, 0, 32, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(minv == 1); + CHECK(maxv == 1ULL << 32); + + /* Try a 3-bit proof; should work */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, v - 1, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(CTX, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(minv == 1110); + CHECK(maxv == 1117); + + /* But a 2-bits will not because then it does not have any subcommitments (which rerandomize + * the blinding factors that get passed into the borromean logic ... passing 0s will fail) */ + len = 5134; + CHECK(!secp256k1_rangeproof_sign(CTX, proof, &len, v - 1, &commit, blind, commit.data, 0, 2, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + + /* Rewinding with 3-bits works */ + { + uint64_t value_out; + unsigned char msg[128]; + unsigned char msg_out[128]; + unsigned char blind_out[32]; + size_t msg_len = sizeof(msg); + + len = 1000; + testrand256(msg); + testrand256(&msg[32]); + testrand256(&msg[64]); + testrand256(&msg[96]); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &len, v, &commit, blind, commit.data, 0, 3, v, msg, sizeof(msg), NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(CTX, blind_out, &value_out, msg_out, &msg_len, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h) != 0); + CHECK(secp256k1_memcmp_var(blind, blind_out, sizeof(blind)) == 0); + CHECK(secp256k1_memcmp_var(msg, msg_out, sizeof(msg)) == 0); + CHECK(value_out == v); + CHECK(minv == v); + CHECK(maxv == v + 7); + } +} + +static void test_single_value_proof(uint64_t val) { + unsigned char proof[5000]; + secp256k1_pedersen_commitment commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + unsigned char nonce[32]; + const unsigned char message[] = { ' ' }; /* no message will fit into a single-value proof */ + unsigned char message_out[sizeof(proof)] = { 0 }; + size_t plen = sizeof(proof); + uint64_t min_val_out = 0; + uint64_t max_val_out = 0; + + uint64_t val_out = 0; + size_t m_len_out = 0; + size_t i; + + testrand256(blind); + testrand256(nonce); + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, val, secp256k1_generator_h)); + + CHECK(secp256k1_rangeproof_sign( + CTX, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + message, sizeof(message), /* Will cause this to fail */ + NULL, 0, + secp256k1_generator_h + ) == 0); + + plen = sizeof(proof); + CHECK(secp256k1_rangeproof_sign( + CTX, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + NULL, 0, + NULL, 0, + secp256k1_generator_h + ) == 1); + CHECK(plen <= secp256k1_rangeproof_max_size(CTX, val, 0)); + + /* Different proof sizes are unfortunate but is caused by `min_value` of + * zero being special-cased and encoded more efficiently. */ + if (val == 0) { + CHECK(plen == 65); + } else { + CHECK(plen == 73); + } + + /* Test if trailing bytes are rejected. */ + proof[plen] = 0; + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_val_out, &max_val_out, + &commit, + proof, plen + 1, + NULL, 0, + secp256k1_generator_h + ) == 0); + /* Test if single-bit malleation is caught */ + for (i = 0; i < plen*8; i++) { + proof[i >> 3] ^= 1 << (i & 7); + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + ) == 0); + proof[i >> 3] ^= 1 << (i & 7); + } + /* Test if unchanged proof is accepted. */ + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + ) == 1); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + + memset(message_out, 0, sizeof(message_out)); + m_len_out = sizeof(message_out); + CHECK(secp256k1_rangeproof_rewind( + CTX, + blind_out, &val_out, + message_out, &m_len_out, + nonce, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + )); + CHECK(val_out == val); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + CHECK(m_len_out == 0); + CHECK(secp256k1_memcmp_var(blind, blind_out, 32) == 0); + for (m_len_out = 0; m_len_out < sizeof(message_out); m_len_out++) { + CHECK(message_out[m_len_out] == 0); + } +} + +#define MAX_N_GENS 30 +static void test_multiple_generators(void) { + const size_t n_inputs = (testrand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_outputs = (testrand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_generators = n_inputs + n_outputs; + unsigned char *generator_blind[MAX_N_GENS]; + unsigned char *pedersen_blind[MAX_N_GENS]; + secp256k1_generator generator[MAX_N_GENS]; + secp256k1_pedersen_commitment commit[MAX_N_GENS]; + const secp256k1_pedersen_commitment *commit_ptr[MAX_N_GENS]; + size_t i; + int64_t total_value; + uint64_t value[MAX_N_GENS]; + + secp256k1_scalar s; + + unsigned char generator_seed[32]; + testutil_random_scalar_order(&s); + secp256k1_scalar_get_b32(generator_seed, &s); + /* Create all the needed generators */ + for (i = 0; i < n_generators; i++) { + generator_blind[i] = malloc(32); + pedersen_blind[i] = malloc(32); + + testutil_random_scalar_order(&s); + secp256k1_scalar_get_b32(generator_blind[i], &s); + testutil_random_scalar_order(&s); + secp256k1_scalar_get_b32(pedersen_blind[i], &s); + + CHECK(secp256k1_generator_generate_blinded(CTX, &generator[i], generator_seed, generator_blind[i])); + + commit_ptr[i] = &commit[i]; + } + + /* Compute all the values -- can be positive or negative */ + total_value = 0; + for (i = 0; i < n_outputs; i++) { + value[n_inputs + i] = testrandi64(0, INT64_MAX - total_value); + total_value += value[n_inputs + i]; + } + for (i = 0; i < n_inputs - 1; i++) { + value[i] = testrandi64(0, total_value); + total_value -= value[i]; + } + value[i] = total_value; + + /* Correct for blinding factors and do the commitments */ + CHECK(secp256k1_pedersen_blind_generator_blind_sum(CTX, value, (const unsigned char * const *) generator_blind, pedersen_blind, n_generators, n_inputs)); + for (i = 0; i < n_generators; i++) { + CHECK(secp256k1_pedersen_commit(CTX, &commit[i], pedersen_blind[i], value[i], &generator[i])); + } + + /* Verify */ + CHECK(secp256k1_pedersen_verify_tally(CTX, &commit_ptr[0], n_inputs, &commit_ptr[n_inputs], n_outputs)); + + /* Cleanup */ + for (i = 0; i < n_generators; i++) { + free(generator_blind[i]); + free(pedersen_blind[i]); + } +} + +static void test_rangeproof_fixed_vectors(void) { + size_t i; + unsigned char blind[32]; + uint64_t value; + uint64_t min_value; + uint64_t max_value; + secp256k1_pedersen_commitment pc; + unsigned char message[4000] = {0}; + size_t m_len = sizeof(message); + + /* Vector 1: no message */ +{ + static const unsigned char vector_1[] = { + 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x02, 0x2a, 0x5c, 0x42, 0x0e, 0x1d, + 0x51, 0xe1, 0xb7, 0xf3, 0x69, 0x04, 0xb5, 0xbb, 0x9b, 0x41, 0x66, 0x14, 0xf3, 0x64, 0x42, 0x26, + 0xe3, 0xa7, 0x6a, 0x06, 0xbb, 0xa8, 0x5a, 0x49, 0x6f, 0x19, 0x76, 0xfb, 0xe5, 0x75, 0x77, 0x88, + 0xab, 0xa9, 0x66, 0x44, 0x80, 0xea, 0x29, 0x95, 0x7f, 0xdf, 0x72, 0x4a, 0xaf, 0x02, 0xbe, 0xdd, + 0x5d, 0x15, 0xd8, 0xae, 0xff, 0x74, 0xc9, 0x8c, 0x1a, 0x67, 0x0e, 0xb2, 0x57, 0x22, 0x99, 0xc3, + 0x21, 0x46, 0x6f, 0x15, 0x58, 0x0e, 0xdb, 0xe6, 0x6e, 0xc4, 0x0d, 0xfe, 0x6f, 0x04, 0x6b, 0x0d, + 0x18, 0x3d, 0x78, 0x40, 0x98, 0x56, 0x4e, 0xe4, 0x4a, 0x74, 0x90, 0xa7, 0xac, 0x9c, 0x16, 0xe0, + 0x3e, 0x81, 0xaf, 0x0f, 0xe3, 0x4f, 0x34, 0x99, 0x52, 0xf7, 0xa7, 0xf6, 0xd3, 0x83, 0xa0, 0x17, + 0x4b, 0x2d, 0xa7, 0xd4, 0xfd, 0xf7, 0x84, 0x45, 0xc4, 0x11, 0x71, 0x3d, 0x4a, 0x22, 0x34, 0x09, + 0x9c, 0xa7, 0xe5, 0xc8, 0xba, 0x04, 0xbf, 0xfd, 0x25, 0x11, 0x7d, 0xa4, 0x43, 0x45, 0xc7, 0x62, + 0x9e, 0x7b, 0x80, 0xf6, 0x09, 0xbb, 0x1b, 0x2e, 0xf3, 0xcd, 0x23, 0xe0, 0xed, 0x81, 0x43, 0x42, + 0xbe, 0xc4, 0x9f, 0x58, 0x8a, 0x0d, 0x66, 0x79, 0x09, 0x70, 0x11, 0x68, 0x3d, 0x87, 0x38, 0x1c, + 0x3c, 0x85, 0x52, 0x5b, 0x62, 0xf7, 0x3e, 0x7e, 0x87, 0xa2, 0x99, 0x24, 0xd0, 0x7d, 0x18, 0x63, + 0x56, 0x48, 0xa4, 0x3a, 0xfe, 0x65, 0xfa, 0xa4, 0xd0, 0x67, 0xaa, 0x98, 0x65, 0x4d, 0xe4, 0x22, + 0x75, 0x45, 0x52, 0xe8, 0x41, 0xc7, 0xed, 0x38, 0xeb, 0xf5, 0x02, 0x90, 0xc9, 0x45, 0xa3, 0xb0, + 0x4d, 0x03, 0xd7, 0xab, 0x43, 0xe4, 0x21, 0xfc, 0x83, 0xd6, 0x12, 0x1d, 0x76, 0xb1, 0x3c, 0x67, + 0x63, 0x1f, 0x52, 0x9d, 0xc3, 0x23, 0x5c, 0x4e, 0xa6, 0x8d, 0x01, 0x4a, 0xba, 0x9a, 0xf4, 0x16, + 0x5b, 0x67, 0xc8, 0xe1, 0xd2, 0x42, 0x6d, 0xdf, 0xcd, 0x08, 0x6a, 0x73, 0x41, 0x6a, 0xc2, 0x84, + 0xc6, 0x31, 0xbe, 0x57, 0xcb, 0x0e, 0xde, 0xbf, 0x71, 0xd5, 0x8a, 0xf7, 0x24, 0xb2, 0xa7, 0x89, + 0x96, 0x62, 0x4f, 0xd9, 0xf7, 0xc3, 0xde, 0x4c, 0xab, 0x13, 0x72, 0xb4, 0xb3, 0x35, 0x04, 0x82, + 0xa8, 0x75, 0x1d, 0xde, 0x46, 0xa8, 0x0d, 0xb8, 0x23, 0x44, 0x00, 0x44, 0xfa, 0x53, 0x6c, 0x2d, + 0xce, 0xd3, 0xa6, 0x80, 0xa1, 0x20, 0xca, 0xd1, 0x63, 0xbb, 0xbe, 0x39, 0x5f, 0x9d, 0x27, 0x69, + 0xb3, 0x33, 0x1f, 0xdb, 0xda, 0x67, 0x05, 0x37, 0xbe, 0x65, 0xe9, 0x7e, 0xa9, 0xc3, 0xff, 0x37, + 0x8a, 0xb4, 0x2d, 0xfe, 0xf2, 0x16, 0x85, 0xc7, 0x0f, 0xd9, 0xbe, 0x14, 0xd1, 0x80, 0x14, 0x9f, + 0x58, 0x56, 0x98, 0x41, 0xf6, 0x26, 0xf7, 0xa2, 0x71, 0x66, 0xb4, 0x7a, 0x9c, 0x12, 0x73, 0xd3, + 0xdf, 0x77, 0x2b, 0x49, 0xe5, 0xca, 0x50, 0x57, 0x44, 0x6e, 0x3f, 0x58, 0x56, 0xbc, 0x21, 0x70, + 0x4f, 0xc6, 0xaa, 0x12, 0xff, 0x7c, 0xa7, 0x3d, 0xed, 0x46, 0xc1, 0x40, 0xe6, 0x58, 0x09, 0x2a, + 0xda, 0xb3, 0x76, 0xab, 0x44, 0xb5, 0x4e, 0xb3, 0x12, 0xe0, 0x26, 0x8a, 0x52, 0xac, 0x49, 0x1d, + 0xe7, 0x06, 0x53, 0x3a, 0x01, 0x35, 0x21, 0x2e, 0x86, 0x48, 0xc5, 0x75, 0xc1, 0xa2, 0x7d, 0x22, + 0x53, 0xf6, 0x3f, 0x41, 0xc5, 0xb3, 0x08, 0x7d, 0xa3, 0x67, 0xc0, 0xbb, 0xb6, 0x8d, 0xf0, 0xd3, + 0x01, 0x72, 0xd3, 0x63, 0x82, 0x01, 0x1a, 0xe7, 0x1d, 0x22, 0xfa, 0x95, 0x33, 0xf6, 0xf2, 0xde, + 0xa2, 0x53, 0x86, 0x55, 0x5a, 0xb4, 0x2e, 0x75, 0x75, 0xc6, 0xd5, 0x93, 0x9c, 0x57, 0xa9, 0x1f, + 0xb9, 0x3e, 0xe8, 0x1c, 0xbf, 0xac, 0x1c, 0x54, 0x6f, 0xf5, 0xab, 0x41, 0xee, 0xb3, 0x0e, 0xd0, + 0x76, 0xc4, 0x1a, 0x45, 0xcd, 0xf1, 0xd6, 0xcc, 0xb0, 0x83, 0x70, 0x73, 0xbc, 0x88, 0x74, 0xa0, + 0x5b, 0xe7, 0x98, 0x10, 0x36, 0xbf, 0xec, 0x23, 0x1c, 0xc2, 0xb5, 0xba, 0x4b, 0x9d, 0x7f, 0x8c, + 0x8a, 0xe2, 0xda, 0x18, 0xdd, 0xab, 0x27, 0x8a, 0x15, 0xeb, 0xb0, 0xd4, 0x3a, 0x8b, 0x77, 0x00, + 0xc7, 0xbb, 0xcc, 0xfa, 0xba, 0xa4, 0x6a, 0x17, 0x5c, 0xf8, 0x51, 0x5d, 0x8d, 0x16, 0xcd, 0xa7, + 0x0e, 0x71, 0x97, 0x98, 0x78, 0x5a, 0x41, 0xb3, 0xf0, 0x1f, 0x87, 0x2d, 0x65, 0xcd, 0x29, 0x49, + 0xd2, 0x87, 0x2c, 0x91, 0xa9, 0x5f, 0xcc, 0xa9, 0xd8, 0xbb, 0x53, 0x18, 0xe7, 0xd6, 0xec, 0x65, + 0xa6, 0x45, 0xf6, 0xce, 0xcf, 0x48, 0xf6, 0x1e, 0x3d, 0xd2, 0xcf, 0xcb, 0x3a, 0xcd, 0xbb, 0x92, + 0x29, 0x24, 0x16, 0x7f, 0x8a, 0xa8, 0x5c, 0x0c, 0x45, 0x71, 0x33 + }; + static const unsigned char commit_1[] = { + 0x08, + 0xf5, 0x1e, 0x0d, 0xc5, 0x86, 0x78, 0x51, 0xa9, 0x00, 0x00, 0xef, 0x4d, 0xe2, 0x94, 0x60, 0x89, + 0x83, 0x04, 0xb4, 0x0e, 0x90, 0x10, 0x05, 0x1c, 0x7f, 0xd7, 0x33, 0x92, 0x1f, 0xe7, 0x74, 0x59 + }; + static const unsigned char blind_1[] = { + 0x98, 0x44, 0xfc, 0x7a, 0x64, 0xa9, 0xca, 0xdf, 0xf3, 0x2f, 0x9f, 0x02, 0xba, 0x46, 0xc7, 0xd9, + 0x77, 0x47, 0xa4, 0xd3, 0x53, 0x17, 0xc6, 0x44, 0x30, 0x73, 0x84, 0xeb, 0x1f, 0xbe, 0xa1, 0xfb + }; + + CHECK(secp256k1_pedersen_commitment_parse(CTX, &pc, commit_1)); + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_value, &max_value, + &pc, + vector_1, sizeof(vector_1), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == 86); + CHECK(max_value == 25586); + + CHECK(secp256k1_rangeproof_rewind( + CTX, + blind, &value, + message, &m_len, + pc.data, + &min_value, &max_value, + &pc, + vector_1, sizeof(vector_1), + NULL, 0, + secp256k1_generator_h + )); + + CHECK(secp256k1_memcmp_var(blind, blind_1, 32) == 0); + CHECK(value == 86); + CHECK(min_value == 86); + CHECK(max_value == 25586); + CHECK(m_len == 448); /* length of the sidechannel in the proof */ + for (i = 0; i < m_len; i++) { + /* No message encoded in this vector */ + CHECK(message[i] == 0); + } +} + + /* Vector 2: embedded message */ +{ + static const unsigned char vector_2[] = { + 0x40, 0x03, 0x00, 0x90, 0x1a, 0x61, 0x64, 0xbb, 0x85, 0x1a, 0x78, 0x35, 0x1e, 0xe0, 0xd5, 0x96, + 0x71, 0x0f, 0x18, 0x8e, 0xf3, 0x33, 0xf0, 0x75, 0xfe, 0xd6, 0xc6, 0x11, 0x6b, 0x42, 0x89, 0xea, + 0xa2, 0x0c, 0x89, 0x25, 0x37, 0x81, 0x10, 0xf9, 0xf0, 0x9b, 0xda, 0x68, 0x2a, 0xd9, 0x2e, 0x0c, + 0x45, 0x17, 0x54, 0x6d, 0x02, 0xd2, 0x21, 0x5d, 0xbc, 0x10, 0xf8, 0x8f, 0xf1, 0x92, 0x40, 0xa9, + 0xc7, 0x24, 0x00, 0x1b, 0xc8, 0x75, 0x0f, 0xf6, 0x8f, 0x93, 0x8b, 0x78, 0x62, 0x73, 0x3c, 0x86, + 0x4b, 0x61, 0x7c, 0x0f, 0xc6, 0x41, 0xc9, 0xb3, 0xc1, 0x30, 0x7f, 0xd4, 0xee, 0x9f, 0x37, 0x08, + 0x9b, 0x64, 0x23, 0xd5, 0xe6, 0x1a, 0x03, 0x54, 0x74, 0x9b, 0x0b, 0xae, 0x6f, 0x2b, 0x1e, 0xf5, + 0x40, 0x44, 0xaa, 0x12, 0xe8, 0xbd, 0xe0, 0xa6, 0x85, 0x89, 0xf1, 0xa9, 0xd0, 0x3f, 0x2e, 0xc6, + 0x1f, 0x11, 0xf5, 0x44, 0x69, 0x99, 0x31, 0x10, 0x2e, 0x64, 0xc6, 0x44, 0xdb, 0x47, 0x06, 0x6d, + 0xd5, 0xf2, 0x8d, 0x19, 0x00, 0x39, 0xb8, 0xca, 0xda, 0x5c, 0x1d, 0x83, 0xbd, 0xa3, 0x6d, 0xbf, + 0x97, 0xdd, 0x83, 0x86, 0xc9, 0x56, 0xe2, 0xbb, 0x37, 0x4b, 0x2d, 0xb5, 0x9d, 0xf2, 0x7a, 0x6a, + 0x25, 0x47, 0xfa, 0x03, 0x05, 0xc5, 0xda, 0x73, 0xe1, 0x96, 0x15, 0x21, 0x23, 0xe5, 0xef, 0x55, + 0x36, 0xdd, 0xf1, 0xb1, 0x3f, 0x33, 0x1a, 0x91, 0x6c, 0x73, 0x64, 0xd3, 0x88, 0xe7, 0xc6, 0xc9, + 0x04, 0x29, 0xae, 0x55, 0x27, 0xa0, 0x80, 0x60, 0xaf, 0x0c, 0x09, 0x2f, 0xc8, 0x1b, 0xe6, 0x16, + 0x9e, 0xed, 0x29, 0xc7, 0x93, 0xce, 0xc7, 0x0d, 0xdf, 0x1f, 0x28, 0xba, 0xf3, 0x38, 0xc3, 0xaa, + 0x99, 0xd9, 0x21, 0x41, 0xb8, 0x10, 0xa5, 0x48, 0x37, 0xec, 0x60, 0xda, 0x64, 0x5a, 0x73, 0x55, + 0xd7, 0xff, 0x23, 0xfa, 0xf6, 0xc6, 0xf4, 0xe2, 0xca, 0x99, 0x2f, 0x30, 0x36, 0x48, 0x73, 0x8b, + 0x57, 0xa6, 0x62, 0x12, 0xa3, 0xe7, 0x5c, 0xa8, 0xd1, 0xe6, 0x85, 0x05, 0x59, 0xfe, 0x2b, 0x44, + 0xe4, 0x73, 0x1c, 0xc3, 0x56, 0x32, 0x07, 0x65, 0x4a, 0x58, 0xaf, 0x2b, 0x3f, 0x36, 0xca, 0xb4, + 0x1d, 0x5c, 0x2a, 0x46, 0x1f, 0xf7, 0x63, 0x59, 0x4f, 0x2b, 0xd0, 0xf6, 0xfc, 0xcf, 0x04, 0x09, + 0xb7, 0x65, 0x1b + }; + static const unsigned char commit_2[] = { + 0x09, + 0x25, 0xa4, 0xbd, 0xc4, 0x57, 0x69, 0xeb, 0x4f, 0x34, 0x0f, 0xea, 0xb8, 0xe4, 0x72, 0x04, 0x54, + 0x06, 0xe5, 0xd6, 0x85, 0x15, 0x42, 0xea, 0x6e, 0x1d, 0x11, 0x11, 0x9c, 0x56, 0xf8, 0x10, 0x45 + }; + static const unsigned char blind_2[] = { + 0xdc, 0x79, 0x07, 0x89, 0x2d, 0xc4, 0xe3, 0x76, 0xf9, 0x13, 0x38, 0xd6, 0x4b, 0x46, 0xed, 0x9d, + 0x9b, 0xf6, 0x70, 0x3d, 0x04, 0xcf, 0x96, 0x8c, 0xfd, 0xb5, 0xff, 0x0a, 0x06, 0xc7, 0x08, 0x8b + }; + static const unsigned char message_2[] = "When I see my own likeness in the depths of someone else's consciousness, I always experience a moment of panic."; + + CHECK(secp256k1_pedersen_commitment_parse(CTX, &pc, commit_2)); + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_value, &max_value, + &pc, + vector_2, sizeof(vector_2), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == 0); + CHECK(max_value == 15); + + CHECK(secp256k1_rangeproof_rewind( + CTX, + blind, &value, + message, &m_len, + pc.data, + &min_value, &max_value, + &pc, + vector_2, sizeof(vector_2), + NULL, 0, + secp256k1_generator_h + )); + + CHECK(secp256k1_memcmp_var(blind, blind_2, 32) == 0); + CHECK(value == 11); + CHECK(min_value == 0); + CHECK(max_value == 15); + CHECK(m_len == 192); /* length of the sidechannel in the proof */ + CHECK(secp256k1_memcmp_var(message, message_2, sizeof(message_2)) == 0); + for (i = sizeof(message_2); i < m_len; i++) { + CHECK(message[i] == 0); + } +} + + /* Vector 3: single-value proof of UINT64_MAX */ +{ + static const unsigned char vector_3[] = { + 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0x7d, 0x0b, 0x79, 0x0e, 0xaf, 0x41, + 0xa5, 0x8e, 0x9b, 0x0c, 0x5b, 0xa3, 0xee, 0x7d, 0xfd, 0x3d, 0x6b, 0xf3, 0xac, 0x04, 0x8a, 0x43, + 0x75, 0xb0, 0xb7, 0x0e, 0x92, 0xd7, 0xdf, 0xf0, 0x76, 0xc4, 0xa5, 0xb6, 0x2f, 0xf1, 0xb5, 0xfb, + 0xb4, 0xb6, 0x29, 0xea, 0x34, 0x9b, 0x16, 0x30, 0x0d, 0x06, 0xf1, 0xb4, 0x3f, 0x0d, 0x73, 0x59, + 0x75, 0xbf, 0x5d, 0x19, 0x59, 0xef, 0x11, 0xf0, 0xbf + }; + static const unsigned char commit_3[] = { + 0x08, + 0xc7, 0xea, 0x40, 0x7d, 0x26, 0x38, 0xa2, 0x99, 0xb9, 0x40, 0x22, 0x78, 0x17, 0x57, 0x65, 0xb3, + 0x36, 0x82, 0x18, 0x42, 0xc5, 0x57, 0x04, 0x5e, 0x58, 0x5e, 0xf6, 0x40, 0x8b, 0x24, 0x73, 0x10 + }; + static const unsigned char nonce_3[] = { + 0x84, 0x50, 0x94, 0x69, 0xa3, 0x4b, 0x6c, 0x62, 0x1a, 0xc7, 0xe2, 0x0e, 0x07, 0x9a, 0x6f, 0x85, + 0x5f, 0x26, 0x50, 0xcd, 0x88, 0x5a, 0x9f, 0xaa, 0x23, 0x5e, 0x0a, 0xe0, 0x7e, 0xc5, 0xe9, 0xf1 + }; + static const unsigned char blind_3[] = { + 0x68, 0x89, 0x47, 0x8c, 0x77, 0xec, 0xcc, 0x2b, 0x65, 0x01, 0x78, 0x6b, 0x06, 0x8b, 0x38, 0x94, + 0xc0, 0x6b, 0x9b, 0x4c, 0x02, 0xa6, 0xc8, 0xf6, 0xc0, 0x34, 0xea, 0x35, 0x57, 0xf4, 0xe1, 0x37 + }; + + CHECK(secp256k1_pedersen_commitment_parse(CTX, &pc, commit_3)); + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_value, &max_value, + &pc, + vector_3, sizeof(vector_3), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == UINT64_MAX); + CHECK(max_value == UINT64_MAX); + + CHECK(secp256k1_rangeproof_rewind( + CTX, + blind, &value, + message, &m_len, + nonce_3, + &min_value, &max_value, + &pc, + vector_3, sizeof(vector_3), + NULL, 0, + secp256k1_generator_h + )); + CHECK(secp256k1_memcmp_var(blind, blind_3, 32) == 0); + CHECK(value == UINT64_MAX); + CHECK(min_value == UINT64_MAX); + CHECK(max_value == UINT64_MAX); + CHECK(m_len == 0); +} +} + +static void print_vector_helper(unsigned char *buf, size_t buf_len) { + size_t j; + printf(" "); + for (j = 0; j < buf_len; j++) { + printf("0x%02x", buf[j]); + if (j == buf_len-1) { + printf(",\n"); + } else if ((j+1) % 16 != 0) { + printf(", "); + } else { + printf(",\n"); + printf(" "); + } + } + printf("};\n"); +} + +static void print_vector(int i, unsigned char *proof, size_t p_len, secp256k1_pedersen_commitment *commit) { + unsigned char commit_output[33]; + + printf("unsigned char vector_%d[] = {\n", i); + print_vector_helper(proof, p_len); + + CHECK(secp256k1_pedersen_commitment_serialize(CTX, commit_output, commit)); + printf("unsigned char commit_%d[] = {\n", i); + print_vector_helper(commit_output, sizeof(commit_output)); +} + + +/* Use same nonce and blinding value for all "reproducible" test vectors */ +static unsigned char vector_blind[] = { + 0x48, 0x26, 0xad, 0x41, 0x37, 0x4c, 0x25, 0x62, 0x52, 0x14, 0x78, 0x82, 0x89, 0x9c, 0x86, 0x27, + 0xa1, 0x19, 0xf6, 0xe1, 0xfa, 0x44, 0xe4, 0x29, 0x08, 0xa7, 0xb3, 0x45, 0xad, 0x35, 0xb2, 0xd9, +}; +static unsigned char vector_nonce[] = { + 0xc8, 0x5c, 0x7e, 0x6c, 0xa1, 0xfa, 0x11, 0x35, 0xc7, 0x45, 0x24, 0x8a, 0xb5, 0x28, 0x6d, 0x1a, + 0x88, 0x00, 0xff, 0xca, 0x96, 0x0f, 0xc7, 0x77, 0xa5, 0x96, 0x7a, 0x5e, 0xf8, 0x88, 0x2d, 0xd4, +}; + +/* Maximum length of a message that can be embedded into a rangeproof */ +static void test_rangeproof_fixed_vectors_reproducible_helper(unsigned char *vector, size_t vector_len, unsigned char *commit, uint64_t *value_r, uint64_t *min_value_r, uint64_t *max_value_r, unsigned char *message_r, size_t *m_len_r) { + secp256k1_pedersen_commitment pc; + unsigned char blind_r[32]; + + CHECK(secp256k1_pedersen_commitment_parse(CTX, &pc, commit)); + CHECK(secp256k1_rangeproof_verify( + CTX, + min_value_r, max_value_r, + &pc, + vector, vector_len, + NULL, 0, + secp256k1_generator_h + )); + + *m_len_r = SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN; + CHECK(secp256k1_rangeproof_rewind( + CTX, + blind_r, value_r, + message_r, m_len_r, + vector_nonce, + min_value_r, max_value_r, + &pc, + vector, vector_len, + NULL, 0, + secp256k1_generator_h + )); + CHECK(secp256k1_memcmp_var(blind_r, vector_blind, sizeof(vector_blind)) == 0); +} + +static void test_rangeproof_fixed_vectors_reproducible(void) { + uint64_t value_r; + uint64_t min_value_r; + uint64_t max_value_r; + unsigned char message[SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN], message_r[SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN]; + size_t m_len_r; + memset(message, 0xFF, sizeof(message)); + + /* Test maximum values for value, min_bits, m_len and exp */ + { + uint64_t value = UINT64_MAX; + uint64_t min_value = 0; + size_t m_len = sizeof(message); /* maximum message length */ + + int min_bits = 64; + int exp = 18; + unsigned char proof[5126]; + size_t p_len = sizeof(proof); + secp256k1_pedersen_commitment pc; + + unsigned char vector_0[] = { + 0x40, 0x3f, 0xd1, 0x77, 0x65, 0x05, 0x87, 0x88, 0xd0, 0x3d, 0xb2, 0x24, 0x60, 0x7a, 0x08, 0x76, + 0xf8, 0x9f, 0x5a, 0x00, 0x73, 0x32, 0x6b, 0x5b, 0x0b, 0x59, 0xda, 0xa0, 0x6d, 0x2b, 0x66, 0xb8, + 0xfa, 0xa4, 0x8c, 0xf9, 0x78, 0x5e, 0xe3, 0xc7, 0x30, 0xea, 0xb4, 0x31, 0x77, 0x3a, 0xe4, 0xe3, + 0xf0, 0x76, 0x15, 0x21, 0x07, 0xb3, 0x6e, 0x84, 0x36, 0xdb, 0x45, 0xe6, 0x2b, 0x14, 0x50, 0xf3, + 0x53, 0x5d, 0x79, 0xf8, 0x6d, 0xc4, 0x99, 0x36, 0xa4, 0x7c, 0xc1, 0x14, 0x90, 0x99, 0xa8, 0x4b, + 0xf0, 0x01, 0x9f, 0xe7, 0xd4, 0xf9, 0xf1, 0x74, 0xb0, 0x7f, 0xf5, 0x90, 0x8d, 0x27, 0x9e, 0x61, + 0x9e, 0xc5, 0xd0, 0xa6, 0x32, 0xe8, 0x64, 0x4a, 0x02, 0x8b, 0xbf, 0xf7, 0xb8, 0x31, 0xa3, 0x4d, + 0x99, 0xbe, 0x12, 0x77, 0x4b, 0x07, 0x4a, 0xef, 0x75, 0xb4, 0xb3, 0x6e, 0x96, 0x95, 0xff, 0xe9, + 0xf7, 0xfc, 0x27, 0x17, 0x62, 0xfa, 0x99, 0xed, 0x00, 0x3c, 0xdd, 0xaa, 0xae, 0x9e, 0x80, 0xc1, + 0x29, 0x73, 0x4d, 0xfc, 0x41, 0xe6, 0xb4, 0x21, 0xe2, 0x62, 0x78, 0xf5, 0x46, 0xef, 0xcd, 0xcf, + 0x15, 0x2a, 0x05, 0x80, 0xb9, 0x95, 0xaa, 0xa5, 0xe9, 0x69, 0x5f, 0xfd, 0x58, 0x12, 0x00, 0x51, + 0xc4, 0x8f, 0xa2, 0xce, 0x03, 0x7f, 0x16, 0x19, 0xb1, 0x77, 0xc2, 0x98, 0xbe, 0xaa, 0x18, 0x6a, + 0x80, 0x0b, 0x4a, 0x81, 0x85, 0xc0, 0xc2, 0x62, 0xb3, 0xec, 0xae, 0xe7, 0x95, 0xbf, 0xd3, 0xe0, + 0xcd, 0xa3, 0xdd, 0x02, 0x70, 0x98, 0x6c, 0xf3, 0x4b, 0x43, 0xec, 0x8d, 0x07, 0xf4, 0x3e, 0xb0, + 0x00, 0x7c, 0xb7, 0x1a, 0x85, 0x9a, 0x94, 0xe8, 0x57, 0xc9, 0x7e, 0x24, 0xb4, 0x7a, 0x84, 0x17, + 0x08, 0xe6, 0xae, 0x91, 0x14, 0xcb, 0x94, 0xf3, 0xe9, 0x13, 0x25, 0x35, 0x54, 0xbf, 0x22, 0xe9, + 0xab, 0x8e, 0xa4, 0xa1, 0x18, 0x78, 0x6c, 0xee, 0x13, 0x26, 0xfc, 0x79, 0xf1, 0xe5, 0x51, 0x1e, + 0x0c, 0xac, 0xa5, 0xef, 0x0d, 0xee, 0xe8, 0x5f, 0x93, 0xa4, 0x88, 0x4a, 0x32, 0x95, 0x8a, 0x61, + 0x76, 0xd8, 0xac, 0x2d, 0x36, 0x9a, 0x6b, 0xa4, 0x7c, 0x30, 0xa3, 0x09, 0x38, 0xbb, 0xbc, 0x51, + 0x1a, 0x10, 0xae, 0x9e, 0x18, 0x9f, 0xd8, 0xc8, 0xce, 0xfa, 0x63, 0xab, 0x28, 0xc9, 0x76, 0x28, + 0x32, 0x61, 0x39, 0x83, 0x99, 0x0a, 0x41, 0xc0, 0x55, 0x1c, 0x65, 0x6c, 0xcf, 0xc3, 0x72, 0x47, + 0xe7, 0xb1, 0x99, 0xb5, 0x04, 0x44, 0xb9, 0xde, 0x4b, 0x83, 0x37, 0x66, 0xb2, 0xee, 0x9f, 0x07, + 0xf1, 0x4f, 0x4d, 0x59, 0xee, 0x37, 0x79, 0x47, 0x0e, 0x31, 0x70, 0x3a, 0xfa, 0xe0, 0xa1, 0xef, + 0xa2, 0x1f, 0xeb, 0xe8, 0xd7, 0x4f, 0xcb, 0xc2, 0xce, 0xdc, 0x82, 0xa6, 0x36, 0xed, 0x1d, 0xdd, + 0xa6, 0x40, 0x10, 0x38, 0x4f, 0x28, 0x90, 0xc3, 0xe3, 0xb6, 0xa4, 0x74, 0xbb, 0x56, 0x23, 0x01, + 0x3a, 0xb6, 0xb1, 0xad, 0x94, 0x4b, 0x52, 0x42, 0x0a, 0x9d, 0xd6, 0x89, 0xdd, 0xa7, 0x0f, 0x66, + 0xdb, 0x4e, 0x5b, 0xa4, 0xc2, 0x11, 0xd7, 0xd5, 0xf7, 0x0a, 0xf1, 0xc8, 0x35, 0x16, 0xc0, 0x7d, + 0x29, 0x5d, 0x5c, 0x62, 0x6b, 0xe0, 0x1b, 0x74, 0x8d, 0x14, 0x9e, 0x08, 0xb6, 0x18, 0x0d, 0x2b, + 0x3a, 0xfb, 0x22, 0x9e, 0xd6, 0x77, 0x05, 0x1b, 0xd4, 0x5d, 0x25, 0x27, 0x97, 0x40, 0x93, 0x58, + 0x35, 0xad, 0xc5, 0x19, 0x96, 0x62, 0xbb, 0x10, 0x2d, 0x4e, 0x24, 0x62, 0xc0, 0x1a, 0xe6, 0x12, + 0x84, 0xac, 0x1b, 0x5e, 0x25, 0xa9, 0xc7, 0x5d, 0xa0, 0x34, 0x85, 0x70, 0xf1, 0x08, 0xd8, 0xf9, + 0x1c, 0x1b, 0x71, 0xcc, 0x72, 0xec, 0xce, 0x30, 0x91, 0x67, 0xac, 0xe5, 0x4e, 0x51, 0xa6, 0x47, + 0x74, 0x09, 0x19, 0xee, 0x9d, 0x2d, 0x3f, 0xf2, 0x49, 0x5a, 0xf0, 0x6c, 0x8f, 0xe9, 0x1f, 0x10, + 0xcc, 0x32, 0x2f, 0x8d, 0x3e, 0xfa, 0xba, 0x0c, 0x37, 0x51, 0xe2, 0x3e, 0xcc, 0xdd, 0x81, 0xd5, + 0xef, 0xef, 0x56, 0x3a, 0xd2, 0x96, 0x10, 0xfb, 0x19, 0xce, 0x90, 0xbf, 0x82, 0x11, 0xf6, 0xe6, + 0x28, 0x7e, 0x4d, 0x16, 0x61, 0x87, 0xdd, 0xc5, 0x61, 0x5d, 0x85, 0xa9, 0x02, 0xea, 0xdf, 0x7a, + 0x2c, 0x92, 0xdd, 0xc3, 0xa3, 0xc9, 0xec, 0xd9, 0xc0, 0x54, 0xbd, 0xf2, 0x77, 0x00, 0x5e, 0x18, + 0x8c, 0x0a, 0x63, 0xdf, 0x8f, 0xf9, 0x1e, 0x45, 0x1c, 0xd1, 0xf7, 0x11, 0x1e, 0xbd, 0x20, 0x30, + 0x1c, 0x27, 0x45, 0xfe, 0xb7, 0xe6, 0x93, 0xae, 0x2a, 0xc1, 0xdc, 0xe2, 0xf6, 0xd1, 0x54, 0x42, + 0xa3, 0x26, 0x79, 0xd6, 0xb3, 0x5c, 0xb0, 0xd2, 0x97, 0x3e, 0xc0, 0x0e, 0xce, 0xf4, 0x82, 0x42, + 0x99, 0x49, 0xb7, 0xad, 0x33, 0xd6, 0x67, 0x81, 0xaf, 0x40, 0xa2, 0xbd, 0x71, 0x7e, 0x82, 0x66, + 0x8d, 0x97, 0x3b, 0x9d, 0x30, 0xe8, 0x4b, 0x4d, 0xcd, 0xf0, 0x1e, 0xfb, 0x33, 0xbd, 0xcd, 0xb2, + 0xca, 0x7e, 0x5d, 0xa2, 0xe5, 0x6a, 0xa1, 0xc0, 0xbd, 0xae, 0x8c, 0x65, 0x93, 0x9d, 0x53, 0x6f, + 0x8d, 0xce, 0x4f, 0xc2, 0xb1, 0x54, 0x7d, 0x7a, 0x59, 0x08, 0xc0, 0xaa, 0xfc, 0x12, 0xa9, 0x41, + 0x9b, 0x11, 0x3c, 0x24, 0x3a, 0x89, 0x8f, 0x2f, 0x5a, 0x49, 0x44, 0x64, 0x83, 0x17, 0xbe, 0xa2, + 0xff, 0x6f, 0xcb, 0x65, 0x04, 0x6f, 0x75, 0x4c, 0x5d, 0x15, 0x06, 0x7b, 0x3b, 0x3e, 0xc1, 0xed, + 0xcb, 0xdc, 0xde, 0x04, 0x03, 0x30, 0x74, 0xc2, 0x6b, 0x50, 0x53, 0x6b, 0x8c, 0xcd, 0x1b, 0xe7, + 0xd5, 0xf8, 0x42, 0xe5, 0x07, 0x16, 0xc3, 0x70, 0x83, 0x49, 0x25, 0xa4, 0x1e, 0x61, 0xa8, 0xc4, + 0x13, 0x82, 0xfe, 0x56, 0xd7, 0x04, 0xbe, 0x4d, 0x59, 0x05, 0xb8, 0x35, 0xcc, 0xd2, 0xaf, 0x40, + 0x24, 0xf3, 0xbc, 0x48, 0x58, 0x0e, 0x01, 0xe6, 0x47, 0x19, 0x9d, 0xb6, 0xe3, 0x6f, 0x17, 0x5e, + 0xab, 0x6c, 0xa3, 0x5a, 0x9a, 0xdd, 0x95, 0x2a, 0x18, 0x22, 0x42, 0x36, 0x07, 0xa0, 0x47, 0xb1, + 0x2e, 0x8f, 0xc8, 0x78, 0xcc, 0x7d, 0x16, 0x61, 0x5f, 0x34, 0x66, 0xee, 0x01, 0x17, 0x60, 0xa3, + 0x3f, 0x4d, 0xb1, 0xcc, 0xcc, 0x13, 0x99, 0x51, 0x3e, 0x78, 0x69, 0x7b, 0x83, 0x49, 0x5f, 0xf3, + 0x89, 0xa9, 0x9e, 0x24, 0x18, 0x08, 0x4d, 0xdb, 0x8a, 0xb1, 0xd8, 0xd7, 0xae, 0x30, 0x82, 0x4d, + 0x3d, 0x4f, 0xce, 0xbe, 0x17, 0xe5, 0x47, 0x5d, 0xa6, 0x03, 0x8c, 0xae, 0xe7, 0xa2, 0x63, 0xf3, + 0xe8, 0x88, 0x21, 0xf4, 0xfd, 0xa9, 0x32, 0x15, 0x93, 0x0c, 0xbe, 0x61, 0xe8, 0x35, 0x6c, 0xb5, + 0xc9, 0xa9, 0xec, 0x1c, 0x7f, 0x34, 0x5b, 0xb0, 0x80, 0x6d, 0x0a, 0x52, 0x87, 0x74, 0x12, 0x90, + 0x3a, 0xf7, 0x40, 0x41, 0xe1, 0x62, 0xa5, 0xb7, 0xf0, 0x5d, 0x45, 0x3e, 0x55, 0x1a, 0x30, 0xec, + 0x5d, 0x52, 0x00, 0x76, 0x38, 0x10, 0x0d, 0xf0, 0x2f, 0x7f, 0xf2, 0x3e, 0x34, 0x1f, 0x1a, 0xd9, + 0xf8, 0xb9, 0x86, 0xf9, 0xdc, 0x05, 0xe0, 0xcf, 0x28, 0x49, 0xfd, 0x21, 0x64, 0xf6, 0xa1, 0xc4, + 0xf7, 0xce, 0x91, 0xb2, 0x15, 0xdf, 0x82, 0x39, 0x30, 0x60, 0xf3, 0xd1, 0xa6, 0x18, 0xc4, 0x3b, + 0xf7, 0xd2, 0x64, 0xe8, 0xab, 0x67, 0x23, 0xb8, 0x2c, 0x57, 0x84, 0x17, 0xc0, 0x2c, 0x21, 0xfc, + 0x55, 0x8b, 0xb6, 0x06, 0xbf, 0x79, 0x7e, 0x29, 0x5a, 0xfb, 0x5c, 0xa5, 0x5a, 0xe4, 0x46, 0xac, + 0x16, 0x8e, 0xf4, 0x03, 0xb7, 0xbb, 0xb0, 0x7b, 0xbf, 0xd3, 0x84, 0xbe, 0xb5, 0x6a, 0xc8, 0x28, + 0xe8, 0x2c, 0x2d, 0x0b, 0x7d, 0x0a, 0x65, 0xd3, 0xee, 0x54, 0x8c, 0xbf, 0xd9, 0xda, 0x84, 0x21, + 0x80, 0x07, 0x68, 0x09, 0x75, 0xbd, 0xa8, 0xd0, 0xbf, 0xa0, 0xf3, 0xc7, 0xc5, 0xb5, 0xf2, 0xf8, + 0xf1, 0x74, 0x6e, 0x7d, 0xad, 0x80, 0xbd, 0x87, 0xe9, 0x83, 0x2e, 0xda, 0x61, 0x28, 0x03, 0x74, + 0xe5, 0x45, 0x74, 0x0d, 0x1f, 0x47, 0x46, 0x10, 0x7f, 0xef, 0x12, 0x9f, 0x78, 0xec, 0x03, 0xed, + 0x22, 0x86, 0x6f, 0x1a, 0x31, 0x14, 0xf4, 0x3a, 0x7f, 0xef, 0x98, 0x3e, 0x64, 0x86, 0xb9, 0x0e, + 0x5b, 0x0d, 0x55, 0xba, 0xcc, 0x6d, 0x04, 0xc7, 0x9c, 0x1e, 0xd4, 0xf7, 0xf0, 0x60, 0x6d, 0x54, + 0x75, 0x70, 0x3b, 0x99, 0xd3, 0x01, 0x9f, 0x34, 0x44, 0x98, 0xab, 0xd1, 0x6b, 0xc8, 0xaa, 0xfd, + 0xd9, 0x5a, 0xd3, 0xee, 0x5d, 0x2c, 0x54, 0x38, 0x06, 0x1f, 0x93, 0xb3, 0x5c, 0x87, 0x10, 0x5e, + 0xcb, 0xcd, 0xd3, 0x4a, 0x89, 0xd2, 0x0d, 0xac, 0xeb, 0x42, 0x67, 0x2b, 0xd1, 0x75, 0x12, 0x58, + 0xdd, 0x19, 0xe3, 0x21, 0x33, 0x75, 0xf6, 0x51, 0x25, 0xeb, 0xa6, 0x43, 0x44, 0x82, 0x64, 0xae, + 0xb8, 0x97, 0x01, 0xff, 0x17, 0x8f, 0xce, 0x96, 0xc8, 0xc3, 0x86, 0xa0, 0x05, 0xb2, 0x2c, 0x33, + 0x01, 0x27, 0x25, 0x84, 0x83, 0x85, 0x33, 0xe2, 0xd0, 0xc5, 0x65, 0x89, 0x85, 0x45, 0x81, 0x3f, + 0x2d, 0xb4, 0xb1, 0x8b, 0x1d, 0x04, 0x5d, 0x4c, 0xd8, 0x46, 0x8a, 0x04, 0x3a, 0x3b, 0xa7, 0x76, + 0x47, 0x5e, 0xcc, 0xc0, 0x16, 0xb2, 0x3a, 0x38, 0x9a, 0x6a, 0x50, 0x3a, 0x8b, 0x82, 0xb7, 0x6b, + 0xf2, 0x60, 0x53, 0x4e, 0xdf, 0x8a, 0x02, 0x9f, 0xc6, 0x27, 0x4f, 0xf5, 0x2a, 0xf1, 0xf1, 0x2f, + 0x4a, 0xaa, 0xc7, 0x94, 0xc0, 0xdc, 0xdb, 0x8c, 0x41, 0xd9, 0x16, 0x13, 0xa2, 0xae, 0x37, 0x2a, + 0x7e, 0x26, 0x6f, 0xdf, 0x46, 0x07, 0x74, 0x88, 0x62, 0xab, 0x28, 0x64, 0x12, 0x7c, 0xca, 0xd5, + 0xbb, 0x6f, 0x7f, 0x3b, 0x44, 0x99, 0x20, 0x93, 0x9a, 0xa0, 0xac, 0x17, 0xed, 0x82, 0xf9, 0x43, + 0xc2, 0x98, 0x6b, 0xcf, 0x54, 0x91, 0xfe, 0x3c, 0x9e, 0xfa, 0x7b, 0x57, 0x38, 0xe2, 0x64, 0x58, + 0x9c, 0xe0, 0x41, 0x95, 0x8a, 0xa0, 0xa3, 0x3d, 0x7b, 0x3e, 0x99, 0xea, 0xc7, 0xec, 0x82, 0xc8, + 0xa8, 0xae, 0xbd, 0xf9, 0x5c, 0x7e, 0xa2, 0x20, 0x78, 0xce, 0x4a, 0x6c, 0x74, 0x2a, 0xe7, 0x31, + 0xdb, 0xc1, 0x02, 0x49, 0x4b, 0x83, 0x0b, 0x0e, 0x7e, 0xeb, 0x69, 0x59, 0xf9, 0x3c, 0x13, 0x47, + 0xaf, 0xbd, 0x58, 0xec, 0x7f, 0xae, 0x7e, 0x4b, 0xf3, 0x3d, 0x18, 0xbf, 0xb0, 0x79, 0x92, 0x59, + 0x9e, 0x5f, 0x03, 0x30, 0x15, 0xba, 0xec, 0xd1, 0xaf, 0x2e, 0xf7, 0x88, 0xde, 0x50, 0xae, 0x9e, + 0x59, 0x14, 0xf3, 0xa5, 0x78, 0x25, 0xd7, 0xd9, 0x1a, 0x33, 0x81, 0x29, 0x8b, 0x93, 0xf6, 0xfa, + 0x90, 0x3d, 0x13, 0xaa, 0x0d, 0xa7, 0x8e, 0x79, 0xc0, 0x36, 0x45, 0x29, 0xa5, 0xf1, 0xfe, 0x92, + 0x1b, 0x57, 0x42, 0x58, 0xe0, 0x85, 0x15, 0xa2, 0xc9, 0xb1, 0x50, 0x08, 0x6a, 0x02, 0x46, 0xc6, + 0x1d, 0xd0, 0xf0, 0xb4, 0x5a, 0xcc, 0xd5, 0x54, 0x9d, 0xab, 0x13, 0x47, 0x9f, 0x82, 0x60, 0x9b, + 0x11, 0x64, 0x35, 0xfa, 0xef, 0x89, 0xb4, 0x87, 0x43, 0x48, 0x6e, 0x78, 0x64, 0x01, 0xbe, 0x09, + 0xd1, 0xd0, 0x40, 0xdf, 0x77, 0x6b, 0xee, 0x92, 0xc5, 0xff, 0x77, 0xcf, 0x20, 0x95, 0x36, 0x78, + 0x35, 0x1f, 0x1e, 0xaf, 0x4b, 0xa7, 0x66, 0x71, 0x9d, 0x5e, 0xb7, 0xd9, 0x70, 0x6e, 0xaa, 0x35, + 0x49, 0x3c, 0x9a, 0x23, 0x53, 0xaf, 0x3e, 0x9d, 0x60, 0x72, 0xb5, 0x27, 0x33, 0x80, 0x33, 0xd2, + 0x11, 0x4b, 0xff, 0xfb, 0x53, 0xab, 0x14, 0x4c, 0xe4, 0xe7, 0xbc, 0x2f, 0x5c, 0xd8, 0xbf, 0x81, + 0x5c, 0xf7, 0x4d, 0x5d, 0xb8, 0x84, 0x62, 0xf3, 0xd2, 0x0a, 0x53, 0x66, 0xd3, 0x13, 0xff, 0xb0, + 0xeb, 0x4b, 0x1f, 0x10, 0x1d, 0xa9, 0xba, 0x5c, 0xad, 0xe2, 0x52, 0x91, 0xae, 0xbe, 0x5d, 0x05, + 0x54, 0x6d, 0x72, 0x1e, 0xc1, 0x14, 0xb5, 0x9b, 0x22, 0x3f, 0x78, 0x73, 0x5e, 0x99, 0x50, 0xde, + 0xa8, 0x41, 0x31, 0xd0, 0x44, 0xf3, 0x2f, 0x31, 0xd9, 0x0b, 0x72, 0x1a, 0xd3, 0x70, 0xbe, 0x84, + 0xfc, 0xe1, 0xed, 0x15, 0xb9, 0xe9, 0x69, 0xe2, 0xbe, 0x50, 0xa2, 0xda, 0x4e, 0x7a, 0x83, 0xc5, + 0x56, 0xea, 0xaf, 0x2c, 0xc9, 0x8f, 0xcc, 0x83, 0x1a, 0xa5, 0x0e, 0x74, 0xaa, 0x64, 0x96, 0xb5, + 0x5a, 0x4a, 0x72, 0xad, 0x86, 0x5a, 0xb8, 0x5a, 0x04, 0x0d, 0x68, 0x21, 0x63, 0x23, 0x7b, 0x17, + 0x9d, 0xfd, 0x1b, 0x3f, 0xac, 0x80, 0x98, 0x97, 0xb5, 0xd4, 0xb7, 0x08, 0x50, 0xf4, 0x18, 0xf9, + 0x16, 0xb1, 0x57, 0xfa, 0xff, 0xa8, 0x02, 0x6e, 0x62, 0x7b, 0x15, 0x8d, 0xc0, 0x17, 0xdd, 0xa3, + 0x45, 0x98, 0x5a, 0xa0, 0xda, 0xe2, 0xd1, 0x17, 0x91, 0x3d, 0xda, 0x18, 0x16, 0x19, 0x3a, 0xfb, + 0xfd, 0x44, 0x72, 0x03, 0x97, 0x72, 0x1d, 0xbf, 0x11, 0xca, 0x95, 0x05, 0x6a, 0x9e, 0x41, 0x3d, + 0x85, 0xb4, 0xd9, 0xb3, 0x88, 0xd4, 0xd9, 0xfb, 0x1c, 0xd2, 0x35, 0x31, 0x12, 0xcc, 0xf3, 0x03, + 0x50, 0x8a, 0xfb, 0x0e, 0x72, 0xeb, 0x86, 0x65, 0xd5, 0x96, 0x0f, 0x53, 0x1e, 0x13, 0x99, 0x91, + 0xa7, 0x70, 0xab, 0x4c, 0xa6, 0x1a, 0xb7, 0x0d, 0x71, 0x0e, 0xb6, 0x17, 0x85, 0xdf, 0x9b, 0x74, + 0x19, 0x4a, 0xc7, 0x55, 0x0e, 0xe9, 0x22, 0x6b, 0x8e, 0xa8, 0x5e, 0x45, 0xc4, 0x0f, 0x36, 0xbb, + 0x21, 0xda, 0x97, 0xcf, 0xed, 0x41, 0xb5, 0x00, 0x26, 0xb1, 0x70, 0x43, 0x5c, 0x60, 0x59, 0x23, + 0x19, 0xc5, 0xdb, 0x49, 0xef, 0xdd, 0x5d, 0x19, 0x5f, 0x58, 0xaa, 0x20, 0xd9, 0x09, 0x17, 0x09, + 0xc7, 0x5f, 0xb9, 0x65, 0x8f, 0x0a, 0xe8, 0x4d, 0x7d, 0x60, 0x88, 0x7a, 0x53, 0x9e, 0xf1, 0x25, + 0xcd, 0xa1, 0x3e, 0xc6, 0xc5, 0x86, 0xf2, 0xee, 0x60, 0x0f, 0x11, 0x3e, 0xe3, 0x90, 0x4d, 0xff, + 0x49, 0x0b, 0x2f, 0x85, 0x7f, 0x18, 0x53, 0x4e, 0xe2, 0x5c, 0x06, 0x61, 0x51, 0x08, 0xca, 0x55, + 0x80, 0x83, 0xa4, 0x80, 0x05, 0x26, 0xe7, 0x29, 0xdb, 0xab, 0x94, 0x66, 0xe9, 0xbf, 0xf8, 0xd2, + 0x79, 0x71, 0x61, 0x05, 0x33, 0xc8, 0x6b, 0x5c, 0x62, 0x01, 0x7f, 0x82, 0xef, 0x5f, 0xa3, 0xf6, + 0x29, 0x86, 0xb6, 0x0a, 0xa5, 0xed, 0x3a, 0xae, 0x34, 0x12, 0xba, 0xb0, 0x80, 0xd4, 0x53, 0x79, + 0x57, 0x7d, 0xa2, 0x38, 0xfd, 0x39, 0xc1, 0xaf, 0x07, 0x8d, 0x23, 0x6b, 0xcd, 0x97, 0xe0, 0xf6, + 0x83, 0x74, 0x7c, 0x3c, 0x95, 0xdb, 0xb1, 0xf0, 0xf7, 0x9e, 0xe9, 0xe4, 0x68, 0x67, 0x18, 0x0e, + 0x00, 0x12, 0x70, 0x79, 0xc2, 0xfe, 0xde, 0x00, 0x12, 0x87, 0x00, 0xd3, 0x8a, 0xc5, 0x1b, 0xb4, + 0xd7, 0x46, 0x1d, 0x73, 0x5c, 0x51, 0x93, 0x19, 0x83, 0x1b, 0x28, 0xb9, 0x70, 0x87, 0x20, 0x34, + 0xb6, 0x7e, 0xb9, 0x1f, 0x9d, 0x15, 0xdb, 0xd7, 0xfb, 0x30, 0x92, 0xe8, 0x21, 0x6c, 0x5c, 0xdb, + 0xaf, 0x5c, 0xd5, 0xa5, 0x76, 0x4b, 0x5f, 0xc3, 0xa8, 0x73, 0x9a, 0x42, 0x04, 0x21, 0xf1, 0x49, + 0xbc, 0x47, 0xa0, 0xa7, 0x4a, 0xda, 0x16, 0x34, 0xcd, 0x62, 0xe1, 0xe0, 0x9a, 0x35, 0xf2, 0xf9, + 0xb2, 0x3c, 0xcf, 0xa6, 0x4f, 0xd7, 0x23, 0xc2, 0xd2, 0xa5, 0x5a, 0xf9, 0xfd, 0x7d, 0x25, 0x84, + 0xda, 0xda, 0x66, 0xb9, 0x99, 0x7f, 0xec, 0xfc, 0x82, 0x7c, 0xea, 0x6b, 0x23, 0x89, 0x6e, 0xab, + 0xe9, 0x43, 0xcb, 0x88, 0xcd, 0x18, 0x6e, 0xae, 0x2d, 0xf5, 0xc7, 0xe3, 0x92, 0x55, 0x0b, 0xc0, + 0x4d, 0xc8, 0xee, 0x4b, 0x7e, 0xfe, 0x84, 0x8c, 0x32, 0x33, 0xc8, 0xf1, 0xfb, 0xd8, 0x11, 0x22, + 0xd9, 0x0d, 0x2b, 0x16, 0x19, 0xba, 0x65, 0xe4, 0x99, 0x26, 0x7b, 0xa7, 0x04, 0x11, 0xfc, 0xb8, + 0x05, 0x48, 0x36, 0x43, 0x82, 0x14, 0xb7, 0x31, 0x50, 0xfd, 0x38, 0x89, 0xc1, 0x36, 0xa1, 0xd5, + 0x8e, 0x52, 0x76, 0x99, 0xc8, 0x38, 0x49, 0xb4, 0x94, 0x02, 0x96, 0x35, 0x8c, 0xc1, 0x9c, 0x7c, + 0x2b, 0xbe, 0x73, 0x62, 0x0a, 0xd3, 0x57, 0x3d, 0xdb, 0x81, 0x14, 0x7c, 0xd0, 0x4a, 0xe5, 0x2f, + 0x63, 0xbd, 0xac, 0xcf, 0x83, 0x10, 0xfd, 0x06, 0x54, 0xc0, 0x5c, 0xba, 0x96, 0x72, 0x0b, 0xcf, + 0x0a, 0x74, 0xe2, 0xbf, 0xbc, 0x1c, 0xc6, 0xd8, 0x9e, 0x7f, 0x5f, 0xbb, 0x00, 0xfe, 0x2a, 0xbd, + 0x36, 0x02, 0x56, 0x5b, 0xa2, 0x30, 0x75, 0x44, 0x62, 0xf8, 0x22, 0x24, 0x14, 0x04, 0x30, 0x26, + 0xe5, 0xb4, 0x06, 0x3d, 0xfe, 0x5c, 0x3a, 0xc7, 0xd8, 0x1d, 0x1b, 0xc9, 0x99, 0xbb, 0xa5, 0x2c, + 0x92, 0x3b, 0xaa, 0x92, 0xf2, 0x12, 0x59, 0xc1, 0xd7, 0xec, 0xae, 0x89, 0x45, 0xfb, 0xe6, 0x15, + 0xa7, 0xe8, 0xad, 0x26, 0xf9, 0xb3, 0xe0, 0xd5, 0x57, 0xab, 0x4c, 0xab, 0xda, 0xe0, 0xc2, 0x9d, + 0xb1, 0x12, 0x6f, 0xc9, 0x84, 0x2b, 0x66, 0x89, 0x05, 0x37, 0x2a, 0x6b, 0x8d, 0xe8, 0x21, 0xa4, + 0xbb, 0x28, 0xc4, 0xb4, 0xa6, 0x93, 0xc2, 0xc9, 0x54, 0x39, 0x38, 0x84, 0xae, 0x70, 0x9b, 0xcf, + 0xc5, 0xc5, 0x3c, 0x56, 0x21, 0xb4, 0x95, 0xb0, 0xa7, 0x2a, 0x30, 0xd8, 0xcb, 0x18, 0x22, 0x31, + 0x59, 0x01, 0x62, 0x43, 0xe2, 0x65, 0xb9, 0xf3, 0xc6, 0x5c, 0x9c, 0xe1, 0xea, 0x48, 0x6f, 0x10, + 0xc2, 0x27, 0x3a, 0xd6, 0xd1, 0x15, 0xeb, 0x7f, 0xc9, 0x2b, 0x21, 0x25, 0xae, 0x91, 0x34, 0xd0, + 0x6b, 0xfe, 0xe3, 0x79, 0x52, 0xb9, 0xb2, 0x17, 0xd6, 0x6b, 0xf0, 0xfa, 0x3f, 0x15, 0xb5, 0x74, + 0x10, 0xf9, 0xd9, 0xb0, 0xc5, 0xdb, 0x72, 0x1a, 0x76, 0xeb, 0x41, 0x6f, 0xb5, 0x9b, 0x8d, 0xb9, + 0x8f, 0x75, 0x6d, 0xc8, 0x25, 0xfa, 0xee, 0xdb, 0x1c, 0x3c, 0x01, 0x80, 0x38, 0x5b, 0x83, 0x01, + 0xc0, 0x02, 0xa1, 0x1c, 0x71, 0xef, 0xbc, 0x58, 0xa5, 0xf6, 0x49, 0xb7, 0xef, 0x9c, 0xa7, 0x7d, + 0x39, 0xcc, 0x2a, 0x0b, 0xdb, 0x78, 0xb7, 0x5d, 0x22, 0x36, 0xb1, 0x36, 0x72, 0x56, 0x62, 0x19, + 0xf1, 0x5c, 0x0a, 0x63, 0x02, 0x6a, 0x59, 0x8e, 0x24, 0xb6, 0x32, 0x21, 0x11, 0x96, 0xd2, 0x8c, + 0xf1, 0xaf, 0x84, 0x3f, 0xf5, 0x98, 0x0d, 0x22, 0x14, 0x15, 0xda, 0x9f, 0x44, 0x0b, 0x08, 0x33, + 0x13, 0x11, 0x53, 0x78, 0xe1, 0xf1, 0x4b, 0x40, 0x1c, 0x43, 0x73, 0x5d, 0x86, 0x06, 0xe1, 0x02, + 0x79, 0x3f, 0x68, 0x00, 0xb7, 0xb0, 0xcb, 0x3b, 0x2e, 0x12, 0x4e, 0x81, 0x77, 0x2a, 0xc5, 0x74, + 0xc4, 0xd9, 0xa0, 0x1e, 0xa3, 0x74, 0x17, 0x61, 0x15, 0xaf, 0xa7, 0xb4, 0x25, 0x61, 0x85, 0x60, + 0x70, 0x85, 0xfb, 0x43, 0xb9, 0x43, 0x4f, 0x87, 0x44, 0x1a, 0x7b, 0xee, 0x68, 0xbb, 0x74, 0x99, + 0xff, 0x81, 0x22, 0xb4, 0x8d, 0x94, 0xf0, 0x4a, 0x3e, 0x28, 0xf5, 0xbd, 0x5c, 0xa1, 0x7f, 0x5c, + 0xea, 0xe6, 0xba, 0xf2, 0x5d, 0x85, 0xf2, 0xed, 0xd7, 0xfd, 0x93, 0xf0, 0xbb, 0xba, 0x86, 0x23, + 0xb5, 0xee, 0xb5, 0x5b, 0xd9, 0x91, 0xbf, 0xdc, 0xe3, 0xdc, 0xbe, 0x82, 0x92, 0xce, 0xb4, 0x0f, + 0xbf, 0xf1, 0x80, 0x0f, 0xe8, 0xc3, 0x04, 0x49, 0x3e, 0x95, 0x72, 0xf8, 0x36, 0x44, 0xce, 0xf4, + 0x3a, 0x6a, 0xeb, 0xdd, 0x88, 0x28, 0x8d, 0xf3, 0x04, 0x65, 0xd8, 0xfd, 0x9d, 0xb1, 0x22, 0xbd, + 0x44, 0xe7, 0xcf, 0x0f, 0x2f, 0xfc, 0xb6, 0xd6, 0x60, 0x7a, 0x6e, 0x6a, 0x93, 0x83, 0xf1, 0x86, + 0xf4, 0xf1, 0x23, 0xb2, 0xfd, 0x4a, 0xb6, 0xcf, 0x97, 0x70, 0xae, 0xa9, 0x44, 0x94, 0x7a, 0x4e, + 0xf9, 0x1e, 0xf8, 0xdc, 0x1f, 0xf0, 0x06, 0x16, 0xdf, 0x69, 0xde, 0x70, 0xea, 0x64, 0x98, 0xe6, + 0x1f, 0x9f, 0x3a, 0xcf, 0xf6, 0xb6, 0x10, 0xb1, 0x3c, 0xd1, 0x76, 0x67, 0x6c, 0xaf, 0x3c, 0x0a, + 0xc3, 0x46, 0x31, 0x89, 0xf7, 0x7a, 0x0f, 0x9b, 0x7d, 0xd8, 0x9a, 0x81, 0x57, 0x0c, 0x37, 0xda, + 0xf7, 0xc3, 0xe5, 0x4b, 0x14, 0xe9, 0x0e, 0x8c, 0x27, 0xdc, 0x68, 0xdb, 0xd8, 0xdb, 0x8b, 0xee, + 0x4e, 0x9f, 0x93, 0x18, 0x4c, 0x75, 0xec, 0x41, 0x6f, 0xbe, 0x89, 0xe3, 0x77, 0x64, 0xbd, 0x22, + 0xa1, 0x0a, 0x16, 0x93, 0xbc, 0x7a, 0xec, 0x6e, 0x3c, 0x2d, 0x97, 0x4a, 0xb1, 0x7d, 0xb6, 0xfc, + 0x01, 0x8b, 0x87, 0x2b, 0x60, 0xe4, 0x53, 0xde, 0x72, 0xef, 0xa5, 0xdf, 0xeb, 0x2d, 0x3c, 0x01, + 0xdc, 0xfd, 0xd8, 0x3b, 0x2a, 0x16, 0x74, 0x1b, 0x26, 0x40, 0x5c, 0x4d, 0x11, 0x7f, 0xa8, 0x3f, + 0xc9, 0xe8, 0x8f, 0x6b, 0xe4, 0x96, 0x3f, 0x2b, 0x4e, 0xfa, 0x1f, 0xa5, 0x73, 0x85, 0xbb, 0xe5, + 0x6f, 0xd7, 0x0c, 0xf4, 0x46, 0x81, 0xfd, 0x0d, 0x0f, 0x70, 0x3c, 0x51, 0x46, 0x47, 0x5d, 0x37, + 0x02, 0x94, 0x52, 0x44, 0x4b, 0xac, 0x30, 0xc3, 0x72, 0xd1, 0x50, 0x5e, 0x29, 0xa2, 0x4a, 0xfb, + 0xbe, 0x8e, 0x60, 0x16, 0xb0, 0xb1, 0x82, 0xd8, 0x29, 0xf5, 0x3c, 0x72, 0x3c, 0x79, 0x9d, 0xa8, + 0x64, 0x15, 0x57, 0x7a, 0x10, 0x0f, 0xb4, 0xc0, 0x02, 0xdb, 0xc6, 0x6c, 0x37, 0xa1, 0xf3, 0x52, + 0x5c, 0xb5, 0xcd, 0x42, 0xf1, 0x43, 0x31, 0xfe, 0x0b, 0xaa, 0xcb, 0xd5, 0x0a, 0xf4, 0x69, 0x64, + 0xf5, 0x98, 0x41, 0x53, 0xed, 0x04, 0x27, 0x56, 0x4a, 0xe9, 0x4c, 0x96, 0x64, 0x29, 0xff, 0xe2, + 0xe1, 0x79, 0xf5, 0x78, 0x69, 0x43, 0xe6, 0x4e, 0x4e, 0xa8, 0x8a, 0x50, 0x15, 0x37, 0x44, 0x73, + 0x41, 0xe8, 0x21, 0xe4, 0x0a, 0x2e, 0x43, 0x2d, 0x4b, 0xd1, 0xfe, 0xf5, 0xc5, 0x30, 0x31, 0x39, + 0xfa, 0x53, 0x7a, 0x3c, 0xd5, 0x63, 0x7a, 0x5c, 0xff, 0x18, 0xf8, 0x79, 0x84, 0x73, 0x84, 0x55, + 0x61, 0x17, 0x2f, 0xad, 0x34, 0xd3, 0xf2, 0xf3, 0x2b, 0xe6, 0xe3, 0xc2, 0x0e, 0x2b, 0x28, 0xc4, + 0x92, 0xd0, 0xd3, 0x14, 0x06, 0x58, 0xbb, 0x2f, 0xdf, 0xe6, 0x3d, 0x0d, 0x6b, 0x71, 0x40, 0xc2, + 0xbf, 0x6b, 0x41, 0xae, 0x85, 0x39, 0x1c, 0xdf, 0x02, 0xf3, 0x6e, 0x85, 0x15, 0x1d, 0xb8, 0xe4, + 0xbe, 0x32, 0x77, 0x70, 0x22, 0x5b, 0xe7, 0x03, 0xd6, 0x1c, 0xab, 0x90, 0x9b, 0x0d, 0x03, 0x40, + 0x0f, 0x3d, 0x82, 0x9b, 0xb8, 0x32, 0x03, 0x99, 0xee, 0xe4, 0xa9, 0x63, 0x35, 0xec, 0xe2, 0x29, + 0xb3, 0xda, 0x4c, 0x3c, 0x4c, 0x6b, 0x4f, 0x67, 0x0a, 0x8e, 0x35, 0xe2, 0xe5, 0x3d, 0x97, 0xe6, + 0xec, 0xb2, 0x99, 0xa2, 0x7d, 0x1c, 0xf0, 0xb0, 0x6d, 0xc7, 0x14, 0xe6, 0xb1, 0x77, 0x75, 0x2d, + 0x73, 0x0e, 0xa6, 0x3b, 0x4b, 0x90, 0xac, 0xe4, 0x34, 0x60, 0x6d, 0x03, 0xba, 0xb2, 0xb4, 0xb8, + 0x11, 0xe1, 0x1c, 0xb2, 0x27, 0xbb, 0x39, 0x61, 0x47, 0x53, 0x3e, 0xb0, 0x5b, 0x0d, 0x5a, 0xc9, + 0x36, 0xd5, 0xd3, 0x99, 0xa8, 0xdb, 0x74, 0x32, 0x5b, 0x05, 0x95, 0x0d, 0x2f, 0x19, 0xa7, 0x99, + 0x2e, 0x57, 0x19, 0xde, 0x4b, 0xb9, 0x08, 0x57, 0xdc, 0x37, 0x15, 0x7d, 0x1f, 0xe5, 0x12, 0x33, + 0x91, 0xa1, 0xdd, 0xe3, 0x30, 0x2b, 0x84, 0x4c, 0xe9, 0xbd, 0x0c, 0x8b, 0x6d, 0xe4, 0x75, 0x12, + 0x41, 0x98, 0x4b, 0xab, 0x25, 0xda, 0x4b, 0xc0, 0x92, 0xb1, 0x4a, 0x32, 0x06, 0x5a, 0xac, 0x93, + 0xf2, 0x6f, 0x01, 0x4d, 0xc1, 0xac, 0xf2, 0x37, 0x8b, 0x4e, 0x03, 0xbb, 0xd0, 0x06, 0x4f, 0xfa, + 0x7d, 0xb5, 0xdc, 0x1e, 0xf3, 0x2d, 0x05, 0x29, 0x1b, 0x7e, 0x6e, 0x0e, 0x26, 0x3c, 0x11, 0x5c, + 0xaf, 0x97, 0x55, 0x5a, 0xc9, 0x4f, 0x75, 0xb5, 0x24, 0x72, 0xa7, 0x07, 0x0b, 0x02, 0xd9, 0xa2, + 0xa3, 0xb0, 0xde, 0x30, 0x22, 0xfc, 0x54, 0x14, 0xfa, 0x7b, 0x19, 0x58, 0x19, 0x59, 0x9f, 0x7d, + 0x8a, 0x63, 0xd7, 0x27, 0x66, 0xe0, 0x52, 0x05, 0x3d, 0x4e, 0x14, 0xad, 0xf5, 0xaa, 0xc5, 0x79, + 0x1a, 0x02, 0x78, 0x03, 0x2c, 0xe4, 0x86, 0x14, 0x51, 0xbc, 0xc7, 0x31, 0x58, 0x04, 0x39, 0xe9, + 0xac, 0xae, 0xaf, 0x74, 0x97, 0xef, 0x9a, 0x5a, 0x97, 0x7c, 0xf5, 0xcd, 0xa1, 0x19, 0xd6, 0x31, + 0xc2, 0x0b, 0x5e, 0x66, 0x9a, 0x7f, 0x96, 0x09, 0x59, 0xc5, 0x17, 0x5c, 0x39, 0x66, 0xa7, 0x29, + 0x31, 0xb9, 0x6d, 0xad, 0x4e, 0xc1, 0x90, 0xb9, 0x94, 0x02, 0xc3, 0xe0, 0x69, 0xb8, 0x44, 0x6b, + 0xa3, 0xa6, 0xab, 0x7e, 0xfe, 0xfa, 0x8f, 0x89, 0x60, 0xf5, 0x7e, 0x68, 0x62, 0xcf, 0x58, 0xa2, + 0x96, 0xca, 0xe5, 0x36, 0x23, 0xd9, 0x88, 0x6e, 0x8b, 0x17, 0x7e, 0xf3, 0xe5, 0xd7, 0x63, 0xb0, + 0x96, 0x82, 0x18, 0xf3, 0x10, 0x13, 0x09, 0xce, 0x7b, 0xe3, 0xd8, 0xb6, 0x5b, 0x6f, 0x0e, 0x13, + 0x1b, 0xd3, 0x49, 0x57, 0x8b, 0xa1, 0xf8, 0xbd, 0xda, 0xed, 0x0d, 0x9c, 0x6c, 0x32, 0x61, 0x03, + 0xdd, 0x39, 0x63, 0x9c, 0x6f, 0xe3, 0xe5, 0xc1, 0xd6, 0xdb, 0x26, 0x1d, 0xa2, 0x60, 0x7e, 0x7b, + 0xb2, 0x51, 0x80, 0x8f, 0x78, 0x89, 0x43, 0xaa, 0x9d, 0x76, 0xef, 0x1e, 0xdb, 0xd0, 0x51, 0xb4, + 0x8e, 0xed, 0x14, 0x42, 0xfb, 0x63, 0x5d, 0x21, 0x99, 0x30, 0x99, 0x05, 0x53, 0x25, 0xe8, 0xec, + 0xe7, 0xf1, 0x40, 0x8c, 0xba, 0xb6, 0x6a, 0xe0, 0xc7, 0x73, 0x68, 0x6c, 0x15, 0x4b, 0x42, 0xe3, + 0xda, 0x97, 0x0e, 0x40, 0xc5, 0xff, 0x98, 0x2b, 0xf2, 0x19, 0xca, 0x3d, 0x49, 0x72, 0x7a, 0x96, + 0xd5, 0x16, 0xd4, 0x2a, 0xf4, 0x5d, 0x1d, 0xd2, 0x1d, 0x22, 0x60, 0xe0, 0x18, 0xf7, 0x38, 0x04, + 0x01, 0x94, 0x45, 0xb2, 0x5f, 0xc0, 0x10, 0xce, 0x65, 0x62, 0x33, 0x61, 0x6d, 0x65, 0x15, 0x7d, + 0x39, 0xc0, 0xcf, 0xcb, 0x4c, 0xa2, 0x23, 0x1c, 0xa8, 0x42, 0x46, 0x99, 0x81, 0xdc, 0x28, 0xad, + 0xd6, 0xcc, 0x92, 0xdd, 0x06, 0x58, 0x02, 0x0c, 0xe1, 0x67, 0xf2, 0x4f, 0x7a, 0xe3, 0xde, 0xb9, + 0x65, 0x81, 0xb4, 0x59, 0x13, 0xfc, 0xa4, 0x12, 0x13, 0x41, 0x9f, 0xd2, 0xe2, 0x9e, 0xea, 0x87, + 0x6a, 0x95, 0xba, 0xae, 0x27, 0x4d, 0x12, 0xc4, 0x58, 0x2a, 0x4c, 0x18, 0x68, 0x6b, 0x3b, 0xd6, + 0xfa, 0xc5, 0x47, 0x0c, 0xfe, 0x40, 0x66, 0x5b, 0x0e, 0xe9, 0xe9, 0x01, 0x4f, 0x1c, 0x48, 0x52, + 0x19, 0xc8, 0x9d, 0xa3, 0xbe, 0x81, 0x04, 0xa1, 0xd8, 0x4e, 0x6f, 0x6e, 0x80, 0xcd, 0xf7, 0x38, + 0x63, 0x16, 0x1f, 0x27, 0x4e, 0x5e, 0x4a, 0xcb, 0xc8, 0x15, 0x62, 0x15, 0x9e, 0x4e, 0xbc, 0xfc, + 0xa3, 0x26, 0x85, 0x54, 0xe0, 0x56, 0xb9, 0xef, 0xc0, 0x6c, 0x42, 0x87, 0x35, 0x9a, 0x28, 0xc1, + 0x90, 0x2c, 0xa2, 0xc6, 0xbe, 0x9e, 0xbb, 0x11, 0x1b, 0x88, 0xa7, 0xea, 0x03, 0x82, 0x8b, 0xf5, + 0xb5, 0x72, 0x76, 0xb8, 0x30, 0x2d, 0x07, 0xb3, 0x2f, 0x35, 0x48, 0xf5, 0xc5, 0x6c, 0xd3, 0x30, + 0x98, 0x55, 0xe1, 0x68, 0x0d, 0x47, 0xcf, 0xbe, 0x29, 0x8f, 0xcf, 0x48, 0x7b, 0x79, 0x2d, 0xd8, + 0xbf, 0x52, 0xce, 0x1d, 0xdc, 0xb2, 0x22, 0xe6, 0x86, 0x30, 0x98, 0x50, 0xf4, 0x56, 0x53, 0x49, + 0x38, 0x5f, 0x6d, 0xce, 0xe9, 0x35, 0x2f, 0xcf, 0x44, 0x8a, 0x96, 0xe5, 0x0a, 0xd8, 0x62, 0x64, + 0x1b, 0xfe, 0x38, 0x83, 0x36, 0x60, 0xd4, 0x31, 0xa4, 0x63, 0x4a, 0xd7, 0xa0, 0x9b, 0x1a, 0xca, + 0x84, 0xaa, 0x30, 0xba, 0xea, 0xcc, 0x48, 0xb2, 0xb0, 0x51, 0xc5, 0x0e, 0xd6, 0x3b, 0x1b, 0xab, + 0x20, 0x4a, 0xd0, 0xd9, 0xc8, 0xf3, 0x19, 0x63, 0x07, 0x10, 0x7c, 0xcd, 0xa6, 0x5a, 0xa5, 0xcd, + 0x3b, 0x8e, 0x09, 0x7f, 0xf0, 0x54, 0xba, 0xc8, 0x2a, 0xbb, 0x1d, 0x0d, 0x76, 0xdd, 0xb1, 0x47, + 0xed, 0xb9, 0x41, 0x25, 0x43, 0x97, 0x2d, 0x27, 0xc0, 0x89, 0x81, 0x69, 0xfb, 0x02, 0x21, 0xd3, + 0xc6, 0x59, 0x65, 0x11, 0x42, 0xbc, 0x88, 0x98, 0xa0, 0x0a, 0xa1, 0xdc, 0xb1, 0xb7, 0x03, 0xc1, + 0x64, 0x8f, 0x95, 0xb3, 0x37, 0x67, 0x87, 0x5f, 0xe5, 0xf1, 0x93, 0x27, 0xe7, 0x12, 0x62, 0x5e, + 0x87, 0x52, 0x4b, 0x44, 0x5f, 0xe5, 0x45, 0x58, 0xc0, 0xfb, 0x06, 0xbc, 0x8a, 0xe0, 0xd4, 0xe5, + 0x9f, 0xf3, 0x2c, 0x20, 0x30, 0x64, 0xfe, 0x46, 0xf5, 0xf3, 0xda, 0x53, 0x42, 0x8f, 0x99, 0x7d, + 0xfb, 0x47, 0xb7, 0x4a, 0xbe, 0x66, 0xf6, 0xe1, 0x2e, 0x62, 0x87, 0x26, 0xce, 0x02, 0x9a, 0xe2, + 0xeb, 0xca, 0x64, 0xc4, 0xaa, 0x0d, 0xbc, 0x9b, 0x1e, 0x32, 0xc3, 0xf8, 0xec, 0xaf, 0x1b, 0xdb, + 0xff, 0x0c, 0x71, 0x69, 0xc6, 0xb2, 0xc0, 0x3a, 0x66, 0xee, 0xd2, 0xc9, 0x16, 0x4d, 0xaf, 0x24, + 0xfa, 0x65, 0x18, 0x75, 0xfb, 0x45, 0x25, 0xa6, 0x26, 0xfb, 0x66, 0x2a, 0x15, 0x9c, 0x85, 0xf4, + 0xda, 0x1d, 0x4f, 0x3b, 0xe2, 0x4d, 0x23, 0xeb, 0x8c, 0x57, 0xb1, 0xb5, 0x4c, 0x90, 0x23, 0x7f, + 0x01, 0xfa, 0x06, 0x9d, 0x35, 0x4c, 0x6a, 0xb5, 0x79, 0xb9, 0x00, 0x17, 0x46, 0x46, 0x54, 0x45, + 0x60, 0xed, 0xe8, 0x86, 0xc3, 0xde, 0x65, 0xdf, 0x5b, 0x40, 0x97, 0xcd, 0x77, 0x25, 0x41, 0xcd, + 0xf2, 0x89, 0xbf, 0xfe, 0x55, 0xa1, 0xf9, 0xf5, 0x7a, 0x18, 0x2d, 0x6c, 0xec, 0x28, 0xed, 0xd1, + 0x44, 0xa1, 0x98, 0x77, 0x73, 0x6f, 0xc0, 0x4c, 0x7f, 0x05, 0xc0, 0x9c, 0x19, 0xed, 0x90, 0x3c, + 0x56, 0xb6, 0xf4, 0x86, 0x2f, 0x60, 0xb3, 0x1b, 0x95, 0x58, 0x26, 0x40, 0xc2, 0xc0, 0xda, 0x24, + 0x26, 0xcb, 0x06, 0x26, 0x53, 0x30, 0x12, 0x35, 0x0b, 0xe5, 0x39, 0x80, 0x3a, 0xf7, 0xd5, 0xae, + 0xb8, 0xcb, 0xea, 0xf6, 0x4c, 0x3d, 0x81, 0xb4, 0x1f, 0x97, 0x88, 0x2c, 0x9f, 0x9c, 0x70, 0x70, + 0xd5, 0x34, 0xe8, 0x65, 0xb7, 0xdb, 0xb3, 0x33, 0x26, 0xcf, 0x95, 0xa3, 0x31, 0x6a, 0x90, 0x9f, + 0xde, 0xd3, 0x17, 0x8a, 0x24, 0xdc, 0xe1, 0x57, 0xb8, 0x28, 0x70, 0x79, 0x52, 0xd7, 0x9d, 0x6b, + 0x99, 0xbb, 0xf7, 0x14, 0x5e, 0xc0, 0x05, 0xff, 0xbc, 0x82, 0xc2, 0x20, 0x57, 0x6c, 0xbe, 0xbe, + 0x71, 0xf7, 0xdb, 0x24, 0xc3, 0x33, 0xe6, 0x26, 0x50, 0x9a, 0xfc, 0xd6, 0x35, 0xb7, 0x42, 0xc4, + 0x63, 0x22, 0x3d, 0x4c, 0x52, 0x1b, 0xf3, 0x6a, 0x38, 0x5c, 0x9b, 0xf5, 0x9b, 0xfb, 0x5f, 0x0b, + 0x37, 0x9a, 0xf2, 0x25, 0xd3, 0xd9, 0xa7, 0xc5, 0x02, 0x0f, 0x86, 0xaa, 0xee, 0x71, 0x49, 0xa2, + 0x22, 0xc2, 0x9d, 0x92, 0xc2, 0x3e, 0x8f, 0x26, 0x7f, 0x5c, 0x23, 0x62, 0xe4, 0xb5, 0xf5, 0x9e, + 0xea, 0x2f, 0xbc, 0xe8, 0x4b, 0x4d, 0xd1, 0xbd, 0x2e, 0x39, 0x04, 0x56, 0xfc, 0x0f, 0xd3, 0xd6, + 0x10, 0x16, 0xe5, 0x02, 0x11, 0x5c, 0xbc, 0x66, 0x90, 0xf1, 0xb7, 0xf6, 0x4f, 0x56, 0x0f, 0x87, + 0x2c, 0xa8, 0xb6, 0xa9, 0x30, 0xf6, 0x17, 0x1b, 0xda, 0x2c, 0x2a, 0x75, 0x09, 0xcc, 0x32, 0xe2, + 0x77, 0xc8, 0xd8, 0x98, 0x7b, 0xd4, 0x8a, 0x73, 0xda, 0xe2, 0x76, 0x78, 0x04, 0x82, 0xab, 0x11, + 0x71, 0xe3, 0x73, 0x79, 0x5e, 0xdb, 0x86, 0x79, 0x52, 0x1d, 0x28, 0xc1, 0x87, 0xa8, 0x2f, 0xca, + 0xbc, 0xb9, 0xba, 0x58, 0x4e, 0xb3, 0x89, 0x4d, 0x74, 0x88, 0x37, 0x36, 0xbb, 0x1d, 0x1d, 0xe8, + 0xc9, 0xd4, 0xe8, 0xa4, 0x31, 0x17, 0x56, 0xf4, 0x72, 0xc0, 0x00, 0x58, 0x1c, 0x76, 0xab, 0x2d, + 0x75, 0xe6, 0x90, 0x85, 0xbf, 0xdc, 0x57, 0x2f, 0xbe, 0xb1, 0xa9, 0x3c, 0xd1, 0xf6, 0x58, 0x80, + 0xcf, 0x43, 0x60, 0xb4, 0x1b, 0x82, 0x02, 0x90, 0x3c, 0xad, 0xfc, 0xef, 0xc4, 0xce, 0x9a, 0xec, + 0xea, 0x26, 0x57, 0xb9, 0x7e, 0xf7, 0x69, 0xa3, 0x06, 0x52, 0xc2, 0x51, 0x83, 0x8f, 0xf4, 0x0d, + 0xb7, 0xa3, 0xfc, 0xae, 0x29, 0x81, 0xcc, 0xdc, 0xbb, 0x2d, 0xed, 0x38, 0xf4, 0x40, 0x7a, 0x16, + 0x56, 0x11, 0x7f, 0x33, 0x50, 0x44, 0xc0, 0xb9, 0x77, 0x66, 0x6b, 0xce, 0x18, 0xd6, 0xdf, 0x39, + 0x9e, 0xa6, 0xa1, 0xbd, 0xed, 0x0e, 0xd7, 0x14, 0xe1, 0xb9, 0x6a, 0x51, 0x4b, 0x5c, 0x00, 0xdc, + 0xd7, 0xdb, 0x76, 0x69, 0xef, 0xfa, 0x2e, 0xcf, 0x4f, 0xa8, 0x2e, 0x59, 0x26, 0xa0, 0x55, 0x7e, + 0x9b, 0x55, 0xfc, 0x7c, 0x32, 0xff, 0xbf, 0x08, 0x90, 0xb5, 0x7a, 0x4f, 0xf2, 0x26, 0x05, 0x85, + 0xf6, 0x56, 0x7b, 0x56, 0x41, 0xdc, 0x95, 0xcf, 0x03, 0x45, 0xef, 0x78, 0x34, 0x30, 0x61, 0x9a, + 0x44, 0x8f, 0xbc, 0xb5, 0x5c, 0xf4, 0x64, 0x2c, 0x13, 0x21, 0x3c, 0xa5, 0x1b, 0xe7, 0x9c, 0x60, + 0x9b, 0x49, 0xe1, 0x34, 0x8a, 0xef, 0x72, 0xa7, 0xa9, 0x71, 0x6f, 0x32, 0x52, 0x64, 0x0a, 0xa0, + 0x4a, 0xf3, 0xa2, 0xee, 0x16, 0x1a, 0xbe, 0x93, 0x0a, 0xa2, 0xd8, 0xb0, 0x82, 0x3b, 0x4d, 0x7d, + 0x24, 0xe2, 0x09, 0xef, 0x6b, 0xdd, 0x92, 0x00, 0x6f, 0x76, 0xd1, 0x41, 0xde, 0xdb, 0xab, 0x6c, + 0x39, 0x4d, 0x26, 0x54, 0xb4, 0x56, 0xce, 0x4c, 0x19, 0x95, 0x2a, 0x51, 0xa9, 0xf1, 0xad, 0x58, + 0xf8, 0x6d, 0xeb, 0x32, 0xc2, 0x30, 0x03, 0x15, 0x0e, 0x9d, 0x76, 0x74, 0x5d, 0xe0, 0x62, 0xd9, + 0xc3, 0x83, 0xe4, 0xd5, 0x33, 0x1d, 0x7b, 0xfe, 0x6f, 0x16, 0x5c, 0xaf, 0x34, 0xe9, 0x2c, 0x20, + 0xca, 0x99, 0xac, 0x57, 0x47, 0x81, 0x0d, 0x57, 0x65, 0x33, 0x1a, 0x5b, 0x0c, 0xaa, 0x13, 0x00, + 0xf9, 0x3d, 0x02, 0x2c, 0xc1, 0x23, 0x7d, 0x55, 0x47, 0xdb, 0x8b, 0xcb, 0x50, 0xc1, 0xd2, 0x95, + 0xff, 0x0b, 0x8a, 0xb3, 0x38, 0x43, 0x51, 0xe1, 0x3b, 0x3b, 0xc3, 0x4b, 0x5e, 0xc9, 0xac, 0x0f, + 0xb3, 0x81, 0x32, 0xf1, 0x2e, 0xa2, 0x51, 0xea, 0xb2, 0x85, 0x1a, 0x48, 0xee, 0x35, 0xa0, 0x86, + 0x05, 0x14, 0x05, 0xc3, 0xf5, 0xe2, 0xa1, 0xdf, 0x47, 0xb0, 0xe2, 0x04, 0x21, 0x85, 0xbd, 0x0b, + 0x00, 0x98, 0xe4, 0xdb, 0xe1, 0x7a, 0xf7, 0xfd, 0x7a, 0x92, 0x45, 0x15, 0x57, 0xd7, 0xcf, 0x18, + 0xb7, 0xa3, 0xf2, 0xd5, 0xcf, 0x18, 0x7a, 0xe5, 0xa4, 0x4b, 0x0c, 0xe1, 0x66, 0xd9, 0x1b, 0x3f, + 0x60, 0x47, 0x90, 0xd1, 0xe9, 0xb8, 0xf5, 0xa8, 0x36, 0x1f, 0xab, 0x7d, 0x85, 0x9a, 0x57, 0xc6, + 0xdb, 0x9c, 0xf8, 0x90, 0xbf, 0xc7, 0xb7, 0xf5, 0xa3, 0x1d, 0x69, 0x6d, 0xe8, 0xda, 0x3d, 0xa6, + 0xaa, 0x38, 0x55, 0x30, 0x9d, 0x7a, 0xc0, 0x6d, 0x42, 0xeb, 0xc6, 0x97, 0xf3, 0x84, 0xa1, 0x0a, + 0x4c, 0x78, 0xc3, 0xdb, 0x9f, 0x29, 0x85, 0x08, 0x4d, 0x8f, 0xbe, 0x8e, 0xba, 0x68, 0x1a, 0x97, + 0x74, 0xa7, 0xd2, 0xec, 0xb9, 0x8b, 0xbf, 0x8c, 0xd7, 0xb6, 0x2e, 0x3d, 0x3b, 0x7d, 0xf8, 0x84, + 0x2b, 0x30, 0xe6, 0x40, 0x13, 0x53, 0xbe, 0x47, 0xd9, 0x14, 0x16, 0xdf, 0x05, 0xeb, 0xa3, 0x6c, + 0x74, 0xd4, 0xba, 0x8d, 0xf5, 0x80, 0xab, 0xc8, 0x40, 0x3c, 0xd5, 0x00, 0x47, 0x86, 0x66, 0x73, + 0x6b, 0x36, 0xc5, 0xa2, 0x81, 0x92, 0xd8, 0xfd, 0xde, 0x61, 0x34, 0xc3, 0x34, 0xaa, 0x1a, 0x40, + 0x3c, 0x95, 0x8f, 0x75, 0x2f, 0xc6, 0x22, 0xbb, 0x45, 0xfa, 0x68, 0x10, 0xd7, 0x22, 0x09, 0x59, + 0x36, 0x25, 0x45, 0x06, 0x9b, 0xa2, 0x33, 0xf9, 0x34, 0x63, 0xe2, 0x2b, 0x18, 0xa7, 0xbe, 0x25, + 0xf2, 0xe6, 0x9d, 0x99, 0x97, 0xb1, 0x0d, 0x64, 0x3a, 0x53, 0xcb, 0xe5, 0x73, 0xc6, 0x47, 0x3c, + 0x76, 0x87, 0xae, 0x74, 0x1f, 0x4f, 0x84, 0x2e, 0x4f, 0x10, 0xda, 0x4e, 0x32, 0x40, 0x71, 0xc4, + 0xd9, 0xac, 0x85, 0x4c, 0x6e, 0x10, 0x37, 0x66, 0xcd, 0x49, 0x83, 0x20, 0xa7, 0xe7, 0x47, 0x70, + 0xaf, 0x38, 0x6c, 0x95, 0x32, 0x6e, 0x7f, 0x21, 0x9e, 0x2b, 0xbd, 0x09, 0x6a, 0xe0, 0xd9, 0xdf, + 0x27, 0x1e, 0x41, 0x0b, 0x1a, 0xc3, 0x6c, 0x83, 0x9f, 0x1a, 0x57, 0x5e, 0x94, 0x72, 0xc5, 0x8d, + 0x9f, 0x61, 0xe4, 0x47, 0x56, 0xb1, 0x80, 0x32, 0x3c, 0x23, 0x4c, 0x21, 0x0d, 0xdd, 0x4e, 0x5f, + 0x61, 0x8a, 0xcf, 0xee, 0x59, 0x87, 0x36, 0xe4, 0x0a, 0x24, 0x7c, 0x03, 0xda, 0x64, 0x76, 0x3c, + 0x80, 0x04, 0x3c, 0x89, 0x91, 0x9e, 0x56, 0xba, 0x66, 0x98, 0xb2, 0xfc, 0x8d, 0x81, 0xdf, 0xf4, + 0x3c, 0x0c, 0x0c, 0x03, 0xee, 0xd9, 0xb4, 0xb0, 0x0a, 0xcf, 0x6d, 0x0b, 0xf6, 0xe7, 0xa6, 0x21, + 0x1c, 0xe7, 0x9f, 0xa5, 0x74, 0xea, 0x18, 0x3f, 0xf7, 0x2c, 0x3c, 0x09, 0x53, 0xd6, 0xcc, 0x71, + 0xd7, 0x07, 0x9d, 0x3d, 0x59, 0xb4, 0xec, 0x86, 0xe9, 0x8b, 0xa0, 0x14, 0x99, 0xf8, 0xa6, 0x9b, + 0x59, 0xe2, 0x6e, 0x73, 0x78, 0xe0, 0xf3, 0xcb, 0xce, 0x06, 0xd0, 0x1b, 0x70, 0xd8, 0x15, 0xc2, + 0xbf, 0x04, 0xe8, 0xcb, 0x31, 0x1d, 0x04, 0x9f, 0x9d, 0xf2, 0xa1, 0x60, 0x1f, 0x63, 0x49, 0x64, + 0x56, 0x3e, 0xa1, 0x64, 0xf2, 0xb0, 0xaa, 0xdc, 0x5f, 0xa3, 0x3b, 0x8d, 0x16, 0x07, 0xa1, 0xf3, + 0xec, 0xc5, 0x7f, 0xe2, 0x1c, 0xeb, 0xb7, 0x81, 0xd3, 0xdf, 0x5f, 0xee, 0xa0, 0xe1, 0x82, 0x25, + 0x7a, 0xe2, 0x3f, 0xce, 0x3b, 0x89, 0x1f, 0xbe, 0x73, 0x9e, 0xe4, 0x46, 0x11, 0x39, 0xfc, 0x6b, + 0xe6, 0x99, 0xd6, 0x98, 0x9f, 0x8f, 0x19, 0x41, 0x90, 0x5d, 0xf1, 0x85, 0x94, 0xe7, 0x13, 0x91, + 0xe3, 0x01, 0xfd, 0x41, 0x29, 0x1d, 0xcb, 0x11, 0x13, 0xcd, 0x4c, 0x92, 0x6c, 0x15, 0x7c, 0xd1, + 0xbc, 0x50, 0x68, 0x4c, 0x46, 0xe3, 0x0f, 0x25, 0xd5, 0x6c, 0x3b, 0x53, 0x0b, 0x2f, 0x1d, 0xd2, + 0x52, 0xca, 0x97, 0x29, 0x5c, 0xdf, 0x24, 0xa8, 0xc6, 0xbe, 0xd1, 0xc1, 0x14, 0x20, 0x24, 0x4f, + 0xfa, 0xd4, 0xe5, 0xb0, 0x93, 0x45, 0xe1, 0xc9, 0xf2, 0xbe, 0x0d, 0xc2, 0xd1, 0x4c, 0xab, 0x2f, + 0x85, 0xc1, 0x0b, 0x51, 0x40, 0x7c, 0xdf, 0x7f, 0x74, 0xa1, 0x3d, 0xb8, 0x4d, 0x4f, 0x26, 0x24, + 0x11, 0x61, 0x64, 0xd3, 0x0e, 0x5b, 0x29, 0x87, 0x05, 0xfe, 0xb9, 0x78, 0xde, 0xad, 0xf9, 0xe8, + 0xc4, 0x47, 0x4d, 0xfb, 0xa8, 0x54, 0x99, 0x50, 0x1d, 0xe7, 0xaf, 0x1a, 0x12, 0x20, 0x8d, 0xcd, + 0xde, 0x8e, 0xf8, 0x26, 0x65, 0x8b, + }; + unsigned char commit_0[] = { + 0x09, 0x9e, 0x56, 0x8d, 0x5b, 0x9d, 0x2a, 0xd6, 0x1f, 0xe0, 0x81, 0x21, 0xcc, 0x15, 0xb3, 0x66, + 0x6d, 0xb4, 0xbb, 0xac, 0xdd, 0x28, 0x08, 0xab, 0x21, 0x6e, 0x35, 0xac, 0xa7, 0xe0, 0x0a, 0xa8, + 0xef, + }; + + CHECK(secp256k1_pedersen_commit(CTX, &pc, vector_blind, value, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); + CHECK(p_len <= secp256k1_rangeproof_max_size(CTX, value, min_bits)); + CHECK(p_len == sizeof(proof)); + /* Uncomment the next line to print the test vector */ + /* print_vector(0, proof, p_len, &pc); */ + CHECK(p_len == sizeof(vector_0)); + CHECK(secp256k1_memcmp_var(proof, vector_0, p_len) == 0); + + test_rangeproof_fixed_vectors_reproducible_helper(vector_0, sizeof(vector_0), commit_0, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); + CHECK(value_r == value); + CHECK(m_len_r == m_len); + CHECK(secp256k1_memcmp_var(message_r, message, m_len_r) == 0); + CHECK(min_value_r == min_value); + CHECK(max_value_r == UINT64_MAX); + memset(message_r, 0, sizeof(message_r)); + } + + /* Test min_bits = 3 */ + { + uint64_t value = 13; + size_t m_len = 128; /* maximum message length with min_bits = 3 */ + + uint64_t min_value = 1; + int min_bits = 3; + int exp = 1; + unsigned char proof[267]; + size_t p_len = sizeof(proof); + secp256k1_pedersen_commitment pc; + + unsigned char vector_1[] = { + 0x61, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0xcb, 0xdc, 0xbe, 0x42, 0xe6, + 0x44, 0x1e, 0xc4, 0x63, 0x9d, 0xb1, 0x93, 0x7b, 0x49, 0xdc, 0xd5, 0x6e, 0x55, 0xdd, 0x3b, 0x1e, + 0x41, 0x1c, 0x0e, 0xd7, 0x47, 0xd7, 0xf0, 0x26, 0xf7, 0xe4, 0x36, 0xbd, 0x51, 0xb9, 0x77, 0x90, + 0x33, 0xdd, 0x64, 0xe7, 0x47, 0x38, 0x49, 0x29, 0x12, 0xa8, 0x12, 0x79, 0xbc, 0x62, 0xea, 0xf9, + 0xb5, 0x51, 0x8f, 0x51, 0xea, 0x28, 0x5d, 0x30, 0x9f, 0x30, 0xd5, 0x93, 0x31, 0x56, 0x61, 0x01, + 0xd7, 0x7f, 0xa4, 0xec, 0xfc, 0xe5, 0x83, 0x52, 0x5a, 0xe0, 0x80, 0x76, 0x40, 0xb8, 0x8d, 0x67, + 0x23, 0x46, 0x8c, 0xb8, 0x74, 0x2a, 0x20, 0x12, 0x86, 0x4d, 0xd8, 0x8c, 0x23, 0x73, 0x2f, 0xbe, + 0x99, 0xa5, 0xd5, 0x8c, 0x11, 0xc7, 0xb2, 0xf9, 0xd3, 0x7c, 0x88, 0x16, 0x4d, 0x21, 0x80, 0x10, + 0x70, 0xfc, 0x1f, 0x9b, 0x0b, 0x5e, 0xbe, 0xe3, 0x65, 0xe2, 0x4f, 0xbd, 0x1d, 0xb0, 0x64, 0x0a, + 0xc5, 0xe0, 0x94, 0x8b, 0x49, 0xf7, 0xc4, 0x88, 0x5e, 0xc0, 0x2d, 0xbb, 0x98, 0x60, 0x5f, 0xd2, + 0x7a, 0x9a, 0xff, 0x9e, 0x1c, 0x1f, 0x45, 0x34, 0x08, 0x96, 0xa9, 0xd3, 0xa5, 0x4d, 0x95, 0x9c, + 0x1f, 0xe6, 0xe5, 0xdc, 0x32, 0xbb, 0x18, 0x4a, 0x76, 0x22, 0xe9, 0x75, 0x1f, 0x45, 0x6b, 0x81, + 0x70, 0x4a, 0xc8, 0x00, 0x72, 0x7a, 0xc8, 0xee, 0xed, 0xc5, 0x19, 0x8f, 0xec, 0x7b, 0x4b, 0xfd, + 0x7f, 0xc8, 0x51, 0xda, 0x28, 0x0e, 0x95, 0xd3, 0xc6, 0xc1, 0x29, 0x28, 0x3f, 0xd8, 0x3d, 0x41, + 0xde, 0xdf, 0xfc, 0x2b, 0x71, 0x3a, 0xdb, 0x78, 0xa4, 0x0e, 0x50, 0xb8, 0xf9, 0xae, 0xdb, 0x7b, + 0xb1, 0x31, 0x81, 0xc2, 0xf2, 0xb5, 0x01, 0x64, 0x8e, 0x86, 0xe2, 0x8b, 0x67, 0x13, 0xec, 0x7e, + 0xf5, 0xad, 0x9d, 0x57, 0x2b, 0x5d, 0x0c, 0x94, 0xa9, 0x89, 0x92, + }; + unsigned char commit_1[] = { + 0x09, 0xe5, 0xb3, 0x27, 0x82, 0x88, 0xeb, 0x21, 0xcd, 0xb2, 0x56, 0x37, 0x61, 0x84, 0xce, 0xc1, + 0x66, 0x16, 0x2e, 0x44, 0xc8, 0x65, 0x8e, 0xe6, 0x3a, 0x1a, 0x57, 0x2c, 0xb9, 0x6c, 0x07, 0x85, + 0xf0, + }; + CHECK(secp256k1_pedersen_commit(CTX, &pc, vector_blind, value, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); + CHECK(p_len <= secp256k1_rangeproof_max_size(CTX, value, min_bits)); + CHECK(p_len == sizeof(proof)); + /* Uncomment the next line to print the test vector */ + /* print_vector(1, proof, p_len, &pc); */ + CHECK(p_len == sizeof(vector_1)); + CHECK(secp256k1_memcmp_var(proof, vector_1, p_len) == 0); + + test_rangeproof_fixed_vectors_reproducible_helper(vector_1, sizeof(vector_1), commit_1, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); + CHECK(value_r == value); + CHECK(m_len_r == m_len); + CHECK(secp256k1_memcmp_var(message_r, message, m_len_r) == 0); + CHECK(min_value_r == 3); + CHECK(max_value_r == 73); + memset(message_r, 0, sizeof(message_r)); + } + + /* Test large min_value */ + { + uint64_t value = INT64_MAX; + size_t m_len = 0; /* maximum message length with min_bits = 3 */ + + /* Uncomment this to recreate test vector */ + uint64_t min_value = INT64_MAX-1; + int min_bits = 1; + int exp = 0; + unsigned char proof[106]; + size_t p_len = sizeof(proof); + secp256k1_pedersen_commitment pc; + + unsigned char vector_2[] = { + 0x60, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x81, 0xd8, 0x21, 0x12, 0x4d, 0xa4, + 0x84, 0xdd, 0x2c, 0xd1, 0x04, 0xe7, 0x08, 0x9a, 0xd3, 0x6f, 0xa5, 0xd8, 0xfc, 0x52, 0x4c, 0xba, + 0xf0, 0x83, 0xeb, 0x76, 0x9f, 0x1c, 0x03, 0xe3, 0xcf, 0x23, 0x1e, 0x40, 0x18, 0xc6, 0x6d, 0xf9, + 0x25, 0x56, 0x80, 0x3c, 0x83, 0xdd, 0x58, 0x36, 0x43, 0xe3, 0x56, 0xa0, 0xb7, 0xf0, 0x0e, 0xf9, + 0xe2, 0x8b, 0x82, 0x5a, 0x77, 0xa7, 0xbe, 0x36, 0x98, 0x10, 0x99, 0x2e, 0xaa, 0x21, 0x24, 0xe6, + 0x78, 0xa8, 0xcc, 0xc7, 0x06, 0x1c, 0x06, 0xb0, 0x03, 0x87, 0x86, 0x89, 0xce, 0x85, 0x88, 0xea, + 0xa1, 0x9d, 0x4d, 0xfd, 0x8d, 0x65, 0xbd, 0xa9, 0xd0, 0x0f, + }; + unsigned char commit_2[] = { + 0x09, 0x2a, 0x74, 0xa1, 0x9c, 0xee, 0xcb, 0x6a, 0xd1, 0xa7, 0x97, 0xbe, 0x97, 0xe7, 0xb6, 0x37, + 0x90, 0x96, 0xc2, 0x5a, 0xe5, 0xfc, 0xed, 0x91, 0xff, 0x4c, 0x67, 0x07, 0x96, 0x1d, 0x2a, 0xb3, + 0x70, + }; + + CHECK(secp256k1_pedersen_commit(CTX, &pc, vector_blind, value, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(CTX, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); + CHECK(p_len <= secp256k1_rangeproof_max_size(CTX, value, min_bits)); + CHECK(p_len == sizeof(proof)); + /* Uncomment the next line to print the test vector */ + /* print_vector(2, proof, p_len, &pc); */ + CHECK(p_len == sizeof(vector_2)); + CHECK(secp256k1_memcmp_var(proof, vector_2, p_len) == 0); + + test_rangeproof_fixed_vectors_reproducible_helper(vector_2, sizeof(vector_2), commit_2, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); + CHECK(value_r == value); + CHECK(m_len_r == m_len); + CHECK(secp256k1_memcmp_var(message_r, message, m_len_r) == 0); + CHECK(min_value_r == INT64_MAX-1); + CHECK(max_value_r == INT64_MAX); + memset(message_r, 0, sizeof(message_r)); + } +} + +static void test_single_value_proof_all(void) { + test_single_value_proof(0); + test_single_value_proof(12345678); + test_single_value_proof(UINT64_MAX); +} + +/* --- Test registry --- */ +REPEAT_TEST(test_rangeproof_api) +REPEAT_TEST(test_borromean) + +static const struct tf_test_entry tests_rangeproof[] = { + CASE1(test_rangeproof_api), + CASE1(test_single_value_proof_all), + CASE1(test_rangeproof_fixed_vectors), + CASE1(test_rangeproof_fixed_vectors_reproducible), + CASE1(test_borromean), + CASE1(test_rangeproof), + CASE1(test_rangeproof_null_blinder), + CASE1(test_multiple_generators), +}; + +#endif diff --git a/src/modules/recovery/main_impl.h b/src/modules/recovery/main_impl.h index 76a005e0..a2bdf364 100644 --- a/src/modules/recovery/main_impl.h +++ b/src/modules/recovery/main_impl.h @@ -129,7 +129,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd ARG_CHECK(signature != NULL); ARG_CHECK(seckey != NULL); - ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msghash32, seckey, noncefp, noncedata); + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, NULL, NULL, NULL, msghash32, seckey, noncefp, noncedata); secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); return ret; } diff --git a/src/modules/schnorrsig_halfagg/Makefile.am.include b/src/modules/schnorrsig_halfagg/Makefile.am.include new file mode 100644 index 00000000..8cc04074 --- /dev/null +++ b/src/modules/schnorrsig_halfagg/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_schnorrsig_halfagg.h +noinst_HEADERS += src/modules/schnorrsig_halfagg/main_impl.h +noinst_HEADERS += src/modules/schnorrsig_halfagg/tests_impl.h diff --git a/src/modules/schnorrsig_halfagg/main_impl.h b/src/modules/schnorrsig_halfagg/main_impl.h new file mode 100644 index 00000000..5d424a38 --- /dev/null +++ b/src/modules/schnorrsig_halfagg/main_impl.h @@ -0,0 +1,198 @@ +#ifndef SECP256K1_MODULE_SCHNORRSIG_HALFAGG_MAIN_H +#define SECP256K1_MODULE_SCHNORRSIG_HALFAGG_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_schnorrsig.h" +#include "../../../include/secp256k1_schnorrsig_halfagg.h" +#include "../../hash.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("HalfAgg/randomizer")||SHA256("HalfAgg/randomizer"). */ +static void secp256k1_schnorrsig_sha256_tagged_aggregation(secp256k1_sha256 *sha) { + static const uint32_t midstate[8] = { + 0xd11f5532ul, 0xfa57f70ful, 0x5db0d728ul, 0xf806ffe1ul, + 0x1d4db069ul, 0xb4d587e1ul, 0x50451c2aul, 0x10fb63e9ul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); +} + +int secp256k1_schnorrsig_inc_aggregate(const secp256k1_context *ctx, unsigned char *aggsig, size_t *aggsig_len, const secp256k1_xonly_pubkey *all_pubkeys, const unsigned char *all_msgs32, const unsigned char *new_sigs64, size_t n_before, size_t n_new) { + size_t i; + size_t n; + secp256k1_sha256 hash; + secp256k1_scalar s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(aggsig != NULL); + ARG_CHECK(aggsig_len != NULL); + ARG_CHECK(new_sigs64 != NULL || n_new == 0); + + /* Check that aggsig_len is large enough, i.e. aggsig_len >= 32*(n+1) */ + n = n_before + n_new; + ARG_CHECK(n >= n_before); + ARG_CHECK(all_pubkeys != NULL || n == 0); + ARG_CHECK(all_msgs32 != NULL || n == 0); + if ((*aggsig_len / 32) <= 0 || ((*aggsig_len / 32) - 1) < n) { + return 0; + } + + /* Prepare hash with common prefix. The prefix is the tag and */ + /* r_0 || pk_0 || m_0 || .... || r_{n'-1} || pk_{n'-1} || m_{n'-1} */ + /* where n' = n_before */ + secp256k1_schnorrsig_sha256_tagged_aggregation(&hash); + for (i = 0; i < n_before; ++i) { + /* serialize pk_i */ + unsigned char pk_ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &all_pubkeys[i])) { + return 0; + } + /* write r_i */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + /* write pk_i */ + secp256k1_sha256_write(&hash, pk_ser, 32); + /* write m_i*/ + secp256k1_sha256_write(&hash, &all_msgs32[i*32], 32); + } + + /* Compute s = s_old + sum_{i = n_before}^{n} z_i*s_i */ + /* where s_old = 0 if n_before = 0 */ + secp256k1_scalar_set_int(&s, 0); + if (n_before > 0) secp256k1_scalar_set_b32(&s, &aggsig[n_before*32], NULL); + for (i = n_before; i < n; ++i) { + unsigned char pk_ser[32]; + unsigned char hashoutput[32]; + secp256k1_sha256 hashcopy; + secp256k1_scalar si; + secp256k1_scalar zi; + + /* Step 0: Serialize pk_i into pk_ser */ + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &all_pubkeys[i])) { + return 0; + } + + /* Step 1: z_i = TaggedHash(...) */ + /* 1.a) Write into hash r_i, pk_i, m_i, r_i */ + secp256k1_sha256_write(&hash, &new_sigs64[(i-n_before)*64], 32); + secp256k1_sha256_write(&hash, pk_ser, 32); + secp256k1_sha256_write(&hash, &all_msgs32[i*32], 32); + /* 1.b) Copy the hash */ + hashcopy = hash; + /* 1.c) Finalize the copy to get zi*/ + secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_sha256_clear(&hashcopy); + /* Note: No need to check overflow, comes from hash */ + secp256k1_scalar_set_b32(&zi, hashoutput, NULL); + + /* Step 2: s := s + zi*si */ + /* except if i == 0, then zi = 1 implicitly */ + secp256k1_scalar_set_b32(&si, &new_sigs64[(i-n_before)*64+32], NULL); + if (i != 0) secp256k1_scalar_mul(&si, &si, &zi); + secp256k1_scalar_add(&s, &s, &si); + } + + /* copy new rs into aggsig */ + for (i = n_before; i < n; ++i) { + memcpy(&aggsig[i*32], &new_sigs64[(i-n_before)*64], 32); + } + /* copy new s into aggsig */ + secp256k1_scalar_get_b32(&aggsig[n*32], &s); + *aggsig_len = 32 * (1 + n); + return 1; +} + +int secp256k1_schnorrsig_aggregate(const secp256k1_context *ctx, unsigned char *aggsig, size_t *aggsig_len, const secp256k1_xonly_pubkey *pubkeys, const unsigned char *msgs32, const unsigned char *sigs64, size_t n) { + return secp256k1_schnorrsig_inc_aggregate(ctx, aggsig, aggsig_len, pubkeys, msgs32, sigs64, 0, n); +} + +int secp256k1_schnorrsig_aggverify(const secp256k1_context *ctx, const secp256k1_xonly_pubkey *pubkeys, const unsigned char *msgs32, size_t n, const unsigned char *aggsig, size_t aggsig_len) { + size_t i; + secp256k1_gej lhs, rhs; + secp256k1_scalar s; + secp256k1_sha256 hash; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkeys != NULL || n == 0); + ARG_CHECK(msgs32 != NULL || n == 0); + ARG_CHECK(aggsig != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + + /* Check that aggsig_len is correct, i.e., aggsig_len = 32*(n+1) */ + if ((aggsig_len / 32) <= 0 || ((aggsig_len / 32)-1) != n || (aggsig_len % 32) != 0) { + return 0; + } + + /* Compute the rhs: */ + /* Set rhs = 0 */ + /* For each i in 0,.., n-1, do: */ + /* (1) z_i = TaggedHash(...) */ + /* (2) T_i = R_i+e_i*P_i */ + /* (3) rhs = rhs + z_i*T_i */ + secp256k1_gej_set_infinity(&rhs); + secp256k1_schnorrsig_sha256_tagged_aggregation(&hash); + for (i = 0; i < n; ++i) { + secp256k1_fe rx; + secp256k1_ge rp, pp; + secp256k1_scalar ei; + secp256k1_gej ppj, ti; + + unsigned char pk_ser[32]; + unsigned char hashoutput[32]; + secp256k1_sha256 hashcopy; + secp256k1_scalar zi; + + /* Step 0: Serialize pk_i into pk_ser */ + /* We need that in Step 1 and in Step 2 */ + if (!secp256k1_xonly_pubkey_load(ctx, &pp, &pubkeys[i])) { + return 0; + } + secp256k1_fe_get_b32(pk_ser, &pp.x); + + /* Step 1: z_i = TaggedHash(...) */ + /* 1.a) Write into hash r_i, pk_i, m_i, r_i */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + secp256k1_sha256_write(&hash, pk_ser, 32); + secp256k1_sha256_write(&hash, &msgs32[i*32], 32); + /* 1.b) Copy the hash */ + hashcopy = hash; + /* 1.c) Finalize the copy to get zi*/ + secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_sha256_clear(&hashcopy); + secp256k1_scalar_set_b32(&zi, hashoutput, NULL); + + /* Step 2: T_i = R_i+e_i*P_i */ + /* 2.a) R_i = lift_x(int(r_i)); fail if that fails */ + if (!secp256k1_fe_set_b32_limit(&rx, &aggsig[i*32])) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&rp, &rx, 0)) { + return 0; + } + + /* 2.b) e_i = int(hash_{BIP0340/challenge}(bytes(r_i) || pk_i || m_i)) mod n */ + secp256k1_schnorrsig_challenge(&ei, &aggsig[i*32], &msgs32[i*32], 32, pk_ser); + secp256k1_gej_set_ge(&ppj, &pp); + /* 2.c) T_i = R_i + e_i*P_i */ + secp256k1_ecmult(&ti, &ppj, &ei, NULL); + secp256k1_gej_add_ge_var(&ti, &ti, &rp, NULL); + + /* Step 3: rhs = rhs + zi*T_i */ + /* Note that if i == 0, then zi = 1 implicitly */ + if (i != 0) secp256k1_ecmult(&ti, &ti, &zi, NULL); + secp256k1_gej_add_var(&rhs, &rhs, &ti, NULL); + } + + /* Compute the lhs as lhs = s*G */ + secp256k1_scalar_set_b32(&s, &aggsig[n*32], &overflow); + if (overflow) { + return 0; + } + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &lhs, &s); + + /* Check that lhs == rhs */ + secp256k1_gej_neg(&lhs, &lhs); + secp256k1_gej_add_var(&lhs, &lhs, &rhs, NULL); + return secp256k1_gej_is_infinity(&lhs); +} + +#endif diff --git a/src/modules/schnorrsig_halfagg/tests_impl.h b/src/modules/schnorrsig_halfagg/tests_impl.h new file mode 100644 index 00000000..29d39b2c --- /dev/null +++ b/src/modules/schnorrsig_halfagg/tests_impl.h @@ -0,0 +1,338 @@ +#ifndef SECP256K1_MODULE_SCHNORRSIG_HALFAGG_TESTS_H +#define SECP256K1_MODULE_SCHNORRSIG_HALFAGG_TESTS_H + +#include "../../../include/secp256k1_schnorrsig_halfagg.h" +#include "../../unit_test.h" + +#define N_MAX 50 + +/* We test that the hash initialized by secp256k1_schnorrsig_sha256_tagged_aggregate + * has the expected state. */ +void test_schnorrsig_sha256_tagged_aggregate(void) { + static const unsigned char tag[] = {'H', 'a', 'l', 'f', 'A', 'g', 'g', '/', 'r', 'a', 'n', 'd', 'o', 'm', 'i', 'z', 'e', 'r'}; + secp256k1_sha256 sha_optimized; + + secp256k1_schnorrsig_sha256_tagged_aggregation(&sha_optimized); + test_sha256_tag_midstate(&sha_optimized, tag, sizeof(tag)); +} + +/* Create n many x-only pubkeys and sigs for random messages */ +void test_schnorrsig_aggregate_input_helper(secp256k1_xonly_pubkey *pubkeys, unsigned char *msgs32, unsigned char *sigs64, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + unsigned char sk[32]; + secp256k1_keypair keypair; + testrand256(sk); + testrand256(&msgs32[i*32]); + + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pubkeys[i], NULL, &keypair)); + CHECK(secp256k1_schnorrsig_sign(CTX, &sigs64[i*64], &msgs32[i*32], &keypair, NULL)); + } +} + +/* In this test we create a bunch of Schnorr signatures, + * aggregate some of them in one shot, and then + * aggregate the others incrementally to the already aggregated ones. + * The aggregate signature should verify after both steps. */ +void test_schnorrsig_aggregate_internal(void) { + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1) + 17]; + size_t aggsig_len = sizeof(aggsig); + + size_t n = testrand_int(N_MAX + 1); + size_t n_initial = testrand_int(n + 1); + size_t n_new = n - n_initial; + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + + /* Aggregate the first n_initial of them */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n_initial)); + /* Make sure that the aggregate signature verifies */ + CHECK(aggsig_len == 32*(n_initial + 1)); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n_initial, aggsig, aggsig_len)); + /* Aggregate the remaining n_new many signatures to the already existing ones */ + aggsig_len = sizeof(aggsig); + secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new); + /* Make sure that the aggregate signature verifies */ + CHECK(aggsig_len == 32*(n + 1)); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len)); + + /* Check that a direct aggregation of the n sigs yields an identical aggsig */ + { + unsigned char aggsig2[sizeof(aggsig)]; + size_t aggsig_len2 = sizeof(aggsig2); + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig2, &aggsig_len2, pubkeys, msgs32, sigs64, n)); + CHECK(aggsig_len == aggsig_len2); + CHECK(secp256k1_memcmp_var(aggsig, aggsig2, aggsig_len) == 0); + } +} + +/* This tests the verification test vectors from + * https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/hacspec-halfagg/tests/tests.rs#L78 . */ +void test_schnorrsig_aggverify_spec_vectors(void) { + /* Test vector 0 */ + { + size_t n = 0; + const unsigned char aggsig[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + size_t aggsig_len = sizeof(aggsig); + CHECK(secp256k1_schnorrsig_aggverify(CTX, NULL, NULL, n, aggsig, aggsig_len)); + } + /* Test vector 1 */ + { + size_t n = 1; + const unsigned char pubkeys_ser[1*32] = { + 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, + 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, + 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, + 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f + }; + secp256k1_xonly_pubkey pubkeys[1]; + const unsigned char msgs32[1*32] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 + }; + const unsigned char aggsig[1*32+32] = { + 0xb0, 0x70, 0xaa, 0xfc, 0xea, 0x43, 0x9a, 0x4f, + 0x6f, 0x1b, 0xbf, 0xc2, 0xeb, 0x66, 0xd2, 0x9d, + 0x24, 0xb0, 0xca, 0xb7, 0x4d, 0x6b, 0x74, 0x5c, + 0x3c, 0xfb, 0x00, 0x9c, 0xc8, 0xfe, 0x4a, 0xa8, + 0x0e, 0x06, 0x6c, 0x34, 0x81, 0x99, 0x36, 0x54, + 0x9f, 0xf4, 0x9b, 0x6f, 0xd4, 0xd4, 0x1e, 0xdf, + 0xc4, 0x01, 0xa3, 0x67, 0xb8, 0x7d, 0xdd, 0x59, + 0xfe, 0xe3, 0x81, 0x77, 0x96, 0x1c, 0x22, 0x5f, + }; + size_t aggsig_len = sizeof(aggsig); + size_t i; + for (i = 0; i < n; ++i) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pubkeys[i], &pubkeys_ser[i*32])); + } + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len)); + } + /* Test vector 2 */ + { + size_t n = 2; + const unsigned char pubkeys_ser[2*32] = { + 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, + 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, + 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, + 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, + + 0x46, 0x27, 0x79, 0xad, 0x4a, 0xad, 0x39, 0x51, + 0x46, 0x14, 0x75, 0x1a, 0x71, 0x08, 0x5f, 0x2f, + 0x10, 0xe1, 0xc7, 0xa5, 0x93, 0xe4, 0xe0, 0x30, + 0xef, 0xb5, 0xb8, 0x72, 0x1c, 0xe5, 0x5b, 0x0b, + }; + secp256k1_xonly_pubkey pubkeys[2]; + const unsigned char msgs32[2*32] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + }; + const unsigned char aggsig[2*32+32] = { + 0xb0, 0x70, 0xaa, 0xfc, 0xea, 0x43, 0x9a, 0x4f, + 0x6f, 0x1b, 0xbf, 0xc2, 0xeb, 0x66, 0xd2, 0x9d, + 0x24, 0xb0, 0xca, 0xb7, 0x4d, 0x6b, 0x74, 0x5c, + 0x3c, 0xfb, 0x00, 0x9c, 0xc8, 0xfe, 0x4a, 0xa8, + 0xa3, 0xaf, 0xbd, 0xb4, 0x5a, 0x6a, 0x34, 0xbf, + 0x7c, 0x8c, 0x00, 0xf1, 0xb6, 0xd7, 0xe7, 0xd3, + 0x75, 0xb5, 0x45, 0x40, 0xf1, 0x37, 0x16, 0xc8, + 0x7b, 0x62, 0xe5, 0x1e, 0x2f, 0x4f, 0x22, 0xff, + 0xbf, 0x89, 0x13, 0xec, 0x53, 0x22, 0x6a, 0x34, + 0x89, 0x2d, 0x60, 0x25, 0x2a, 0x70, 0x52, 0x61, + 0x4c, 0xa7, 0x9a, 0xe9, 0x39, 0x98, 0x68, 0x28, + 0xd8, 0x1d, 0x23, 0x11, 0x95, 0x73, 0x71, 0xad, + }; + size_t aggsig_len = sizeof(aggsig); + size_t i; + for (i = 0; i < n; ++i) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pubkeys[i], &pubkeys_ser[i*32])); + } + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len)); + } +} + +static void test_schnorrsig_aggregate_api_internal(void) { + size_t n = testrand_int(N_MAX + 1); + size_t n_initial = testrand_int(n + 1); + size_t n_new = n - n_initial; + + /* Test preparation. */ + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1)]; + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + + /* Test body 1: Check API of function aggregate. */ + { + /* Should not accept NULL for aggsig or aggsig length */ + size_t aggsig_len = sizeof(aggsig); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, NULL, &aggsig_len, pubkeys, msgs32, sigs64, n_initial)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, NULL, pubkeys, msgs32, sigs64, n_initial)); + /* Should not accept NULL for keys, messages, or signatures if n_initial is not 0 */ + if (n_initial != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, NULL, msgs32, sigs64, n_initial)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, NULL, sigs64, n_initial)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, NULL, n_initial)); + } + } + + /* Test body 2: Check API of function inc_aggregate. */ + { + size_t aggsig_len = sizeof(aggsig); + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n_initial)); + aggsig_len = 32*(n+1); + /* Should not accept NULL for aggsig or aggsig length */ + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, NULL, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, NULL, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + /* Should not accept NULL for keys or messages if n is not 0 */ + if (n != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, NULL, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, NULL, &sigs64[n_initial*64], n_initial, n_new)); + } + /* Should not accept NULL for new_sigs64 if n_new is not 0 */ + if (n_new != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, NULL, n_initial, n_new)); + } + /* Should not accept overflowing number of sigs. */ + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], SIZE_MAX, SIZE_MAX)); + if (n_initial > 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, SIZE_MAX)); + } + /* Should reject if aggsig_len is too small. */ + aggsig_len = 32*n; + CHECK(secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new) == 0); + aggsig_len = 32*(n+1) - 1; + CHECK(secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new) == 0); + } + + /* Test body 3: Check API of function aggverify. */ + { + size_t aggsig_len = sizeof(aggsig); + CHECK(secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + /* Should not accept NULL for keys or messages if n is not 0 */ + if (n != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggverify(CTX, NULL, msgs32, n, aggsig, aggsig_len)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggverify(CTX, pubkeys, NULL, n, aggsig, aggsig_len)); + } + /* Should never accept NULL the aggsig */ + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, NULL, aggsig_len)); + /* Should reject for invalid aggsig_len. */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len + 1) == 0); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len - 1) == 0); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len + 32) == 0); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len - 32) == 0); + } +} + +/* In this test, we make sure that trivial attempts to break + * the security of verification do not work. */ +static void test_schnorrsig_aggregate_unforge_internal(void) { + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1)]; + + size_t n = testrand_int(N_MAX + 1); + + /* Test 1: We fix a set of n messages and compute + * a random aggsig for them. This should not verify. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + { + size_t aggsig_len = sizeof(aggsig); + size_t i; + /* Sample aggsig randomly */ + for (i = 0; i < n + 1; ++i) { + testrand256(&aggsig[i*32]); + } + /* Make sure that it does not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } + + /* Test 2: We fix a set of n messages and compute valid + * signatures for all but one. The resulting aggregate signature + * should not verify. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + if (n > 0) { + size_t aggsig_len = sizeof(aggsig); + /* Replace a randomly chosen real sig with a random one. */ + size_t k = testrand_int(n); + testrand256(&sigs64[k*64]); + testrand256(&sigs64[k*64+32]); + /* Aggregate the n signatures */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n)); + /* Make sure the result does not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } + + /* Test 3: We generate a valid aggregate signature and then + * change one of the messages. This should not verify. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + if (n > 0) { + size_t aggsig_len = sizeof(aggsig); + size_t k; + /* Aggregate the n signatures */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n)); + /* Change one of the messages */ + k = testrand_int(32*n); + msgs32[k] = msgs32[k]^0xff; + /* Make sure the result does not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } +} + +/* In this test, we make sure that the algorithms properly reject + * for overflowing and non parseable values. */ +static void test_schnorrsig_aggregate_overflow_internal(void) { + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1)]; + size_t n = testrand_int(N_MAX + 1); + + /* We check that verification returns 0 if the s in aggsig overflows. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + { + size_t aggsig_len = sizeof(aggsig); + /* Aggregate */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n)); + /* Make s in the aggsig overflow */ + memset(&aggsig[n*32], 0xFF, 32); + /* Should not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } +} + +/* --- Test registry --- */ +REPEAT_TEST(test_schnorrsig_aggregate) +REPEAT_TEST(test_schnorrsig_aggregate_api) +REPEAT_TEST(test_schnorrsig_aggregate_unforge) +REPEAT_TEST(test_schnorrsig_aggregate_overflow) + +static const struct tf_test_entry tests_schnorrsig_halfagg[] = { + CASE1(test_schnorrsig_sha256_tagged_aggregate), + CASE1(test_schnorrsig_aggverify_spec_vectors), + CASE1(test_schnorrsig_aggregate), + CASE1(test_schnorrsig_aggregate_api), + CASE1(test_schnorrsig_aggregate_unforge), + CASE1(test_schnorrsig_aggregate_overflow), +}; + +#undef N_MAX + +#endif diff --git a/src/modules/surjection/Makefile.am.include b/src/modules/surjection/Makefile.am.include new file mode 100644 index 00000000..51ece21f --- /dev/null +++ b/src/modules/surjection/Makefile.am.include @@ -0,0 +1,6 @@ +include_HEADERS += include/secp256k1_surjectionproof.h +noinst_HEADERS += src/modules/surjection/main_impl.h +noinst_HEADERS += src/modules/surjection/surjection.h +noinst_HEADERS += src/modules/surjection/surjection_impl.h +noinst_HEADERS += src/modules/surjection/tests_impl.h + diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h new file mode 100644 index 00000000..248efe14 --- /dev/null +++ b/src/modules/surjection/main_impl.h @@ -0,0 +1,401 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#ifndef SECP256K1_MODULE_SURJECTION_MAIN_H +#define SECP256K1_MODULE_SURJECTION_MAIN_H + +#include +#include + +#include "../../../include/secp256k1_rangeproof.h" +#include "../../../include/secp256k1_surjectionproof.h" +#include "../rangeproof/borromean.h" +#include "surjection_impl.h" +#include "../../hash.h" + +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +#undef SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 +#endif + +static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) { + size_t ret = 0; + size_t i; + for (i = 0; i < count; i++) { +#ifdef HAVE_BUILTIN_POPCOUNT + ret += __builtin_popcount(data[i]); +#else + ret += !!(data[i] & 0x1); + ret += !!(data[i] & 0x2); + ret += !!(data[i] & 0x4); + ret += !!(data[i] & 0x8); + ret += !!(data[i] & 0x10); + ret += !!(data[i] & 0x20); + ret += !!(data[i] & 0x40); + ret += !!(data[i] & 0x80); +#endif + } + return ret; +} + +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif +int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surjectionproof *proof, const unsigned char *input, size_t inputlen) { + size_t n_inputs; + size_t signature_len; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(input != NULL); + (void) ctx; + + if (inputlen < 2) { + return 0; + } + n_inputs = ((size_t) (input[1] << 8)) + input[0]; + if (n_inputs > SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) { + return 0; + } + if (inputlen < 2 + (n_inputs + 7) / 8) { + return 0; + } + + /* Check that the bitvector of used inputs is of the claimed + * length; i.e. the final byte has no "padding bits" set */ + if (n_inputs % 8 != 0) { + const unsigned char padding_mask = (~0U) << (n_inputs % 8); + if ((input[2 + (n_inputs + 7) / 8 - 1] & padding_mask) != 0) { + return 0; + } + } + + signature_len = 32 * (1 + secp256k1_count_bits_set(&input[2], (n_inputs + 7) / 8)); + if (inputlen != 2 + (n_inputs + 7) / 8 + signature_len) { + return 0; + } + proof->n_inputs = n_inputs; + memcpy(proof->used_inputs, &input[2], (n_inputs + 7) / 8); + memcpy(proof->data, &input[2 + (n_inputs + 7) / 8], signature_len); + + return 1; +} + +int secp256k1_surjectionproof_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_surjectionproof *proof) { + size_t signature_len; + size_t serialized_len; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(proof != NULL); + (void) ctx; + + signature_len = 32 * (1 + secp256k1_count_bits_set(proof->used_inputs, (proof->n_inputs + 7) / 8)); + serialized_len = 2 + (proof->n_inputs + 7) / 8 + signature_len; + if (*outputlen < serialized_len) { + return 0; + } + + output[0] = proof->n_inputs % 0x100; + output[1] = proof->n_inputs / 0x100; + memcpy(&output[2], proof->used_inputs, (proof->n_inputs + 7) / 8); + memcpy(&output[2 + (proof->n_inputs + 7) / 8], proof->data, signature_len); + *outputlen = serialized_len; + + return 1; +} + +size_t secp256k1_surjectionproof_n_total_inputs(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + (void) ctx; + return proof->n_inputs; +} + +size_t secp256k1_surjectionproof_n_used_inputs(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + (void) ctx; + return secp256k1_count_bits_set(proof->used_inputs, (proof->n_inputs + 7) / 8); +} + +size_t secp256k1_surjectionproof_serialized_size(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + return 2 + (proof->n_inputs + 7) / 8 + 32 * (1 + secp256k1_surjectionproof_n_used_inputs(ctx, proof)); +} + +typedef struct { + unsigned char state[32]; + size_t state_i; +} secp256k1_surjectionproof_csprng; + +static void secp256k1_surjectionproof_csprng_init(secp256k1_surjectionproof_csprng *csprng, const unsigned char* state) { + memcpy(csprng->state, state, 32); + csprng->state_i = 0; +} + +static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_csprng *csprng, size_t rand_max) { + /* The number of random bytes to read for each random sample */ + const size_t increment = rand_max > 256 ? 2 : 1; + /* The maximum value expressable by the number of random bytes we read */ + const size_t selection_range = rand_max > 256 ? 0xffff : 0xff; + /* The largest multiple of rand_max that fits within selection_range */ + const size_t limit = ((selection_range + 1) / rand_max) * rand_max; + + while (1) { + size_t val; + if (csprng->state_i + increment >= 32) { + secp256k1_sha256 sha; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, csprng->state, 32); + secp256k1_sha256_finalize(&sha, csprng->state); + secp256k1_sha256_clear(&sha); + csprng->state_i = 0; + } + val = csprng->state[csprng->state_i]; + if (increment > 1) { + val = (val << 8) + csprng->state[csprng->state_i + 1]; + } + csprng->state_i += increment; + /* Accept only values below our limit. Values equal to or above the limit are + * biased because they comprise only a subset of the range (0, rand_max - 1) */ + if (val < limit) { + return val % rand_max; + } + } +} + +/* While '_allocate_initialized' may be a wordy suffix for this function, and '_create' + * may have been more appropriate, '_create' could be confused with '_generate', + * as the meanings for the words are close. Therefore, more wordy, but less + * ambiguous suffix was chosen. */ +int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, secp256k1_surjectionproof** proof_out_p, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) { + int ret = 0; + secp256k1_surjectionproof* proof; + + VERIFY_CHECK(ctx != NULL); + + ARG_CHECK(proof_out_p != NULL); + *proof_out_p = 0; + + proof = checked_malloc(&ctx->error_callback, sizeof(secp256k1_surjectionproof)); + if (proof != NULL) { + ret = secp256k1_surjectionproof_initialize(ctx, proof, input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, fixed_output_tag, n_max_iterations, random_seed32); + if (ret) { + *proof_out_p = proof; + } + else { + free(proof); + } + } + return ret; +} + +/* secp256k1_surjectionproof structure may also be allocated on the stack, + * and initialized explicitly via secp256k1_surjectionproof_initialize(). + * Supplying stack-allocated struct to _destroy() will result in calling + * free() with the pointer that points at the stack, with disasterous + * consequences. Thus, it is not advised to mix heap- and stack-allocating + * approaches to working with this struct. It is possible to detect this + * situation by using additional field in the struct that can be set to + * special value depending on the allocation path, and check it here. + * But currently, it is not seen as big enough concern to warrant this extra code .*/ +void secp256k1_surjectionproof_destroy(secp256k1_surjectionproof* proof) { + if (proof != NULL) { + VERIFY_CHECK(proof->n_inputs <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + free(proof); + } +} + +int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) { + secp256k1_surjectionproof_csprng csprng; + size_t n_iterations = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(input_index != NULL); + ARG_CHECK(fixed_input_tags != NULL); + ARG_CHECK(fixed_output_tag != NULL); + ARG_CHECK(random_seed32 != NULL); + ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + ARG_CHECK(n_input_tags_to_use <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); + ARG_CHECK(n_input_tags_to_use <= n_input_tags); + (void) ctx; + + secp256k1_surjectionproof_csprng_init(&csprng, random_seed32); + memset(proof->data, 0, sizeof(proof->data)); + proof->n_inputs = n_input_tags; + + while (1) { + int has_output_tag = 0; + size_t i; + + /* obtain a random set of indices */ + memset(proof->used_inputs, 0, sizeof(proof->used_inputs)); + for (i = 0; i < n_input_tags_to_use; i++) { + while (1) { + size_t next_input_index; + next_input_index = secp256k1_surjectionproof_csprng_next(&csprng, n_input_tags); + if (secp256k1_memcmp_var(&fixed_input_tags[next_input_index], fixed_output_tag, sizeof(*fixed_output_tag)) == 0) { + *input_index = next_input_index; + has_output_tag = 1; + } + + if (!(proof->used_inputs[next_input_index / 8] & (1 << (next_input_index % 8)))) { + proof->used_inputs[next_input_index / 8] |= (1 << (next_input_index % 8)); + break; + } + } + } + + /* Check if we succeeded */ + n_iterations++; + if (has_output_tag) { +#ifdef VERIFY + proof->initialized = 1; +#endif + return n_iterations; + } + if (n_iterations >= n_max_iterations) { +#ifdef VERIFY + proof->initialized = 0; +#endif + return 0; + } + } +} + +int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag, size_t input_index, const unsigned char *input_blinding_key, const unsigned char *output_blinding_key) { + secp256k1_scalar blinding_key; + secp256k1_scalar tmps; + secp256k1_scalar nonce; + int overflow = 0; + size_t rsizes[1]; /* array needed for borromean sig API */ + size_t indices[1]; /* array needed for borromean sig API */ + size_t i; + size_t n_total_pubkeys; + size_t n_used_pubkeys; + size_t ring_input_index = 0; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + unsigned char msg32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(proof != NULL); + ARG_CHECK(ephemeral_input_tags != NULL); + ARG_CHECK(ephemeral_output_tag != NULL); + ARG_CHECK(input_blinding_key != NULL); + ARG_CHECK(output_blinding_key != NULL); +#ifdef VERIFY + CHECK(proof->initialized == 1); +#endif + + n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof); + /* This must be true if the proof was created with surjectionproof_initialize */ + ARG_CHECK(n_used_pubkeys > 0); + + /* Compute secret key */ + secp256k1_scalar_set_b32(&tmps, input_blinding_key, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&blinding_key, output_blinding_key, &overflow); + if (overflow) { + return 0; + } + /* If any input tag is equal to an output tag, verification will fail, because our ring + * signature logic would receive a zero-key, which is illegal. This is unfortunate but + * it is deployed on Liquid and cannot be fixed without a hardfork. We should review + * this at the same time that we relax the max-256-inputs rule. */ + for (i = 0; i < n_ephemeral_input_tags; i++) { + if (secp256k1_memcmp_var(ephemeral_input_tags[i].data, ephemeral_output_tag->data, sizeof(ephemeral_output_tag->data)) == 0) { + return 0; + } + } + secp256k1_scalar_negate(&tmps, &tmps); + secp256k1_scalar_add(&blinding_key, &blinding_key, &tmps); + + /* Compute public keys */ + n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof); + + if (n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) { + return 0; + } + + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index) == 0) { + return 0; + } + + /* Produce signature */ + rsizes[0] = (int) n_used_pubkeys; + indices[0] = (int) ring_input_index; + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); + if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) { + return 0; + } + /* Borromean sign will overwrite one of the s values we just generated, so use + * it as a nonce instead. This avoids extra random generation and also is an + * homage to the rangeproof code which does this very cleverly to encode messages. */ + nonce = borromean_s[ring_input_index]; + secp256k1_scalar_clear(&borromean_s[ring_input_index]); + if (secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &nonce, &blinding_key, rsizes, indices, 1, msg32, 32) == 0) { + return 0; + } + for (i = 0; i < n_used_pubkeys; i++) { + secp256k1_scalar_get_b32(&proof->data[32 + 32 * i], &borromean_s[i]); + } + return 1; +} + +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif +int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag) { + size_t rsizes[1]; /* array needed for borromean sig API */ + size_t i; + size_t n_total_pubkeys; + size_t n_used_pubkeys; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + unsigned char msg32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(ephemeral_input_tags != NULL); + ARG_CHECK(ephemeral_output_tag != NULL); + + /* Compute public keys */ + n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof); + n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof); + if (n_used_pubkeys == 0 || n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) { + return 0; + } + + /* Reject proofs with too many used inputs in USE_REDUCED_SURJECTION_PROOF_SIZE mode */ + if (n_used_pubkeys > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { + return 0; + } + + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 0, NULL) == 0) { + return 0; + } + + /* Verify signature */ + rsizes[0] = (int) n_used_pubkeys; + for (i = 0; i < n_used_pubkeys; i++) { + int overflow = 0; + secp256k1_scalar_set_b32(&borromean_s[i], &proof->data[32 + 32 * i], &overflow); + if (overflow == 1) { + return 0; + } + } + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); + return secp256k1_borromean_verify(NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32); +} + +#endif diff --git a/src/modules/surjection/surjection.h b/src/modules/surjection/surjection.h new file mode 100644 index 00000000..ac7407d7 --- /dev/null +++ b/src/modules/surjection/surjection.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SURJECTION_H +#define SECP256K1_SURJECTION_H + +#include "../../group.h" +#include "../../scalar.h" + +SECP256K1_INLINE static int secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag); + +SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key); + +SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index); + +#endif diff --git a/src/modules/surjection/surjection.md b/src/modules/surjection/surjection.md new file mode 100644 index 00000000..e7bd4db1 --- /dev/null +++ b/src/modules/surjection/surjection.md @@ -0,0 +1,108 @@ +Surjection Proof Module +=========================== + +This module implements a scheme by which a given point can be proven to be +equal to one of a set of points, plus a known difference. This is used in +Confidential Assets when reblinding "asset commitments", which are NUMS +points, to prove that the underlying NUMS point does not change during +reblinding. + +Assets are represented, in general, by a 32-byte seed (a hash of some +transaction data) which is hashed to form a NUMS generator, which appears +on the blockchain only in blinded form. We refer to the seed as an +"asset ID" and the blinded generator as an "(ephemeral) asset commitment". +These asset commitments are unique per-output, and their NUMS components +are in general known only to the holder of the output. + +The result is that within a transaction, all outputs are able to have +a new uniformly-random asset commitment which cannot be associated with +any individual input asset id, but verifiers are nonetheless assured that +all assets coming out of a transaction are ones that went in. + +### Terminology + +Assets are identified by a 32-byte "asset ID". In this library these IDs +are used as input to a point-valued hash function `H`. We usually refer +to the hash output as `A`, since this output is the only thing that appears +in the algebra. + +Then transaction outputs have "asset commitments", which are curvepoints +of the form `A + rG`, where `A` is the hash of the asset ID and `r` is +some random "blinding factor". + +### Design Rationale + +Confidential Assets essentially works by replacing the second NUMS generator +`H` in Confidental Transactions with a per-asset unique NUMS generator. This +allows the same verification equation (the sum of all blinded inputs must +equal the sum of all blinded outputs) to imply that quantity of *every* asset +type is preserved in each transaction. + +It turns out that even if outputs are reblinded by the addition of `rG` for +some known `r`, this verification equation has the same meaning, with one +caveat: verifiers must be assured that the reblinding preserves the original +generators (and does not, for example, negate them). + +This assurance is what surjection proofs provide. + +### Limitations + +The naive scheme works as follows: every output asset is shown to have come +from some input asset. However, the proofs scale with the number of input +assets, so for all outputs the total size of all surjection proofs is `O(mn)` +for `m`, `n` the number of inputs and outputs. + +We therefore restrict the number of inputs that each output may have come +from to 3 (well, some fixed number, which is passed into the API), which +provides a weaker form of blinding, but gives `O(n)` scaling. Over many +transactions, the privacy afforded by this increases exponentially. + +### Our Scheme + +Our scheme works as follows. Proofs are generated in two steps, "initialization" +which selects a subset of inputs and "generation" which does the mathematical +part of proof generation. + +Every input has an asset commitment for which we know the blinding key and +underlying asset ID. + +#### Initialization + +The initialization function takes a list of input asset IDs and one output +asset ID. It chooses an input subset of some fixed size repeatedly until it +the output ID appears at least once in its subset. + +It stores a bitmap representing this subset in the proof object and returns +the number of iterations it needed to choose the subset. The reciprocal of +this represents the probability that a uniformly random input-output +mapping would correspond to the actual input-output mapping, and therefore +gives a measure of privacy. (Lower iteration counts are better.) + +It also informs the caller the index of the input whose ID matches the output. + +As the API works on only a single output at a time, the total probability +should be computed by multiplying together the counts for each output. + +#### Generation + +The generation function takes a list of input asset commitments, an output +asset commitment, the input index returned by the initialization step, and +blinding keys for (a) the output commitment, (b) the input commitment. Here +"the input commitment" refers specifically to the input whose index was +chosen during initialization. + +Next, it computes a ring signature over the differences between the output +commitment and every input commitment chosen during initialization. Since +the discrete log of one of these is the difference between the output and +input blinding keys, it is possible to create a ring signature over every +differences will be the blinding factor of the output. We create such a +signature, which completes the proof. + +#### Verification + +Verification takes a surjection proof object, a list of input commitments, +and an output commitment. The proof object contains a ring signature and +a bitmap describing which input commitments to use, and verification +succeeds iff the signature verifies. + + diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h new file mode 100644 index 00000000..0776e4c8 --- /dev/null +++ b/src/modules/surjection/surjection_impl.h @@ -0,0 +1,97 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SURJECTION_IMPL_H +#define SECP256K1_SURJECTION_IMPL_H + +#include +#include + +#include "../../eckey.h" +#include "../../group.h" +#include "../../scalar.h" +#include "../../hash.h" + +SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, const secp256k1_generator *ephemeral_input_tags, size_t n_input_tags, const secp256k1_generator *ephemeral_output_tag) { + /* compute message */ + size_t i; + unsigned char pk_ser[33]; + size_t pk_len = sizeof(pk_ser); + secp256k1_sha256 sha256_en; + + secp256k1_sha256_initialize(&sha256_en); + for (i = 0; i < n_input_tags; i++) { + pk_ser[0] = 2 + (ephemeral_input_tags[i].data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_input_tags[i].data[0], 32); + secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); + } + pk_ser[0] = 2 + (ephemeral_output_tag->data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_output_tag->data[0], 32); + secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); + secp256k1_sha256_finalize(&sha256_en, msg32); + secp256k1_sha256_clear(&sha256_en); +} + +SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key) { + size_t i; + unsigned char sec_input[36]; + secp256k1_sha256 sha256_en; + + /* compute s values */ + secp256k1_scalar_get_b32(&sec_input[4], blinding_key); + for (i = 0; i < ns; i++) { + int overflow = 0; + sec_input[0] = i; + sec_input[1] = i >> 8; + sec_input[2] = i >> 16; + sec_input[3] = i >> 24; + + secp256k1_sha256_initialize(&sha256_en); + secp256k1_sha256_write(&sha256_en, sec_input, 36); + secp256k1_sha256_finalize(&sha256_en, sec_input); + secp256k1_sha256_clear(&sha256_en); + secp256k1_scalar_set_b32(&s[i], sec_input, &overflow); + if (overflow == 1) { + secp256k1_memclear_explicit(sec_input, 32); + return 0; + } + } + secp256k1_memclear_explicit(sec_input, 32); + return 1; +} + +SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_generator *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_generator *output_tag, size_t input_index, size_t *ring_input_index) { + size_t i; + size_t j = 0; + for (i = 0; i < n_input_tags; i++) { + if (used_tags[i / 8] & (1 << (i % 8))) { + secp256k1_ge tmpge; + secp256k1_generator_load(&tmpge, &input_tags[i]); + secp256k1_ge_neg(&tmpge, &tmpge); + + VERIFY_CHECK(j < SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); + VERIFY_CHECK(j < n_pubkeys); + secp256k1_gej_set_ge(&pubkeys[j], &tmpge); + + secp256k1_generator_load(&tmpge, output_tag); + secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], &tmpge, NULL); + if (ring_input_index != NULL && input_index == i) { + *ring_input_index = j; + } + j++; + } + } +#ifdef VERIFY + /* Caller needs to ensure that the number of set bits in used_tags (which we counted in j) equals n_pubkeys. */ + VERIFY_CHECK(j == n_pubkeys); +#else + (void)n_pubkeys; +#endif + return 1; +} + + +#endif diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h new file mode 100644 index 00000000..d81ac624 --- /dev/null +++ b/src/modules/surjection/tests_impl.h @@ -0,0 +1,658 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS_H +#define SECP256K1_MODULE_SURJECTIONPROOF_TESTS_H + +#include "../../testrand.h" +#include "../../group.h" +#include "../../unit_test.h" +#include "../../../include/secp256k1_generator.h" +#include "../../../include/secp256k1_rangeproof.h" +#include "../../../include/secp256k1_surjectionproof.h" + +static void test_surjectionproof_api(void) { + unsigned char seed[32]; + secp256k1_fixed_asset_tag fixed_input_tags[10]; + secp256k1_fixed_asset_tag fixed_output_tag; + secp256k1_generator ephemeral_input_tags[10]; + secp256k1_generator ephemeral_output_tag; + unsigned char input_blinding_key[10][32]; + unsigned char output_blinding_key[32]; + unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + size_t serialized_len; + secp256k1_surjectionproof proof; + secp256k1_surjectionproof* proof_on_heap; + size_t n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]); + size_t input_index; + size_t i; + + testrand256(seed); + + for (i = 0; i < n_inputs; i++) { + testrand256(input_blinding_key[i]); + testrand256(fixed_input_tags[i].data); + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i])); + } + testrand256(output_blinding_key); + memcpy(&fixed_output_tag, &fixed_input_tags[0], sizeof(fixed_input_tags[0])); + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_output_tag, fixed_output_tag.data, output_blinding_key)); + + /* check allocate_initialized */ + CHECK(secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + CHECK(proof_on_heap != 0); + secp256k1_surjectionproof_destroy(proof_on_heap); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed)); + CHECK(proof_on_heap == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed)); + CHECK(proof_on_heap == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed)); + CHECK(proof_on_heap == 0); + CHECK(secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0); + CHECK(proof_on_heap != 0); + secp256k1_surjectionproof_destroy(proof_on_heap); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed)); + CHECK(proof_on_heap == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed)); + CHECK(proof_on_heap == 0); + CHECK((secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0); + CHECK(proof_on_heap == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_allocate_initialized(CTX, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL)); + CHECK(proof_on_heap == 0); + + /* we are now going to test essentially the same functions, just without + * heap allocation. */ + + /* check initialize */ + CHECK(secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0); + CHECK(secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, &proof, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed)); + CHECK(secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed)); + CHECK((secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL)); + + CHECK(secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + /* check generate */ + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_surjectionproof_generate(STATIC_CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key)); + + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &proof, NULL, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key)); + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, 0, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, NULL, 0, input_blinding_key[0], output_blinding_key)); + /* the below line "succeeds" but generates an invalid proof as the input_index is wrong. it is fairly expensive to detect this. should we? */ + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 1, input_blinding_key[0], output_blinding_key) != 0); + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, n_inputs + 1, input_blinding_key[0], output_blinding_key) != 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, NULL, output_blinding_key)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], NULL)); + + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0); + /* check verify */ + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 1); + + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_verify(CTX, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_verify(CTX, &proof, NULL, n_inputs, &ephemeral_output_tag)); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag) == 0); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag) == 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs, NULL)); + + /* Test how surjectionproof_generate fails when the proof was not created + * with surjectionproof_initialize */ + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 1); + { + secp256k1_surjectionproof tmp_proof = proof; + tmp_proof.n_inputs = 0; + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &tmp_proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key)); + } + + CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 1); + + /* Check serialize */ + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(CTX, serialized_proof, &serialized_len, &proof) != 0); + serialized_len = sizeof(serialized_proof); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_serialize(CTX, NULL, &serialized_len, &proof)); + serialized_len = sizeof(serialized_proof); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_serialize(CTX, serialized_proof, NULL, &proof)); + serialized_len = sizeof(serialized_proof); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_serialize(CTX, serialized_proof, &serialized_len, NULL)); + + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(CTX, serialized_proof, &serialized_len, &proof) != 0); + /* Check parse */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof, serialized_len) != 0); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_parse(CTX, NULL, serialized_proof, serialized_len)); + CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_parse(CTX, &proof, NULL, serialized_len)); + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof, 0) == 0); +} + +static void test_input_selection(size_t n_inputs) { + unsigned char seed[32]; + size_t i; + size_t result; + size_t input_index; + size_t try_count = n_inputs * 100; + secp256k1_surjectionproof proof; + secp256k1_fixed_asset_tag fixed_input_tags[1000]; + const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1; + + CHECK(n_inputs < max_n_inputs); + testrand256(seed); + + for (i = 0; i < n_inputs + 1; i++) { + testrand256(fixed_input_tags[i].data); + } + + /* cannot match output when told to use zero keys */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], try_count, seed); + CHECK(result == 0); + CHECK(secp256k1_surjectionproof_n_used_inputs(CTX, &proof) == 0); + CHECK(secp256k1_surjectionproof_n_total_inputs(CTX, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(CTX, &proof) == 34 + (n_inputs + 7) / 8); + if (n_inputs > 0) { + /* succeed in 100*n_inputs tries (probability of failure e^-100) */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 1, &fixed_input_tags[0], try_count, seed); + CHECK(result > 0); + CHECK(result < n_inputs * 10); + CHECK(secp256k1_surjectionproof_n_used_inputs(CTX, &proof) == 1); + CHECK(secp256k1_surjectionproof_n_total_inputs(CTX, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(CTX, &proof) == 66 + (n_inputs + 7) / 8); + CHECK(input_index == 0); + } + + if (n_inputs >= 3) { + /* succeed in 10*n_inputs tries (probability of failure e^-10) */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[1], try_count, seed); + CHECK(result > 0); + CHECK(secp256k1_surjectionproof_n_used_inputs(CTX, &proof) == 3); + CHECK(secp256k1_surjectionproof_n_total_inputs(CTX, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(CTX, &proof) == 130 + (n_inputs + 7) / 8); + CHECK(input_index == 1); + + /* fail, key not found */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[n_inputs], try_count, seed); + CHECK(result == 0); + + /* succeed on first try when told to use all keys */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], try_count, seed); + CHECK(result == 1); + CHECK(secp256k1_surjectionproof_n_used_inputs(CTX, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_n_total_inputs(CTX, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(CTX, &proof) == 2 + 32 * (n_inputs + 1) + (n_inputs + 7) / 8); + CHECK(input_index == 0); + + /* succeed in less than 64 tries when told to use half keys. (probability of failure 2^-64) */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs / 2, &fixed_input_tags[0], 64, seed); + CHECK(result > 0); + CHECK(result < 64); + CHECK(secp256k1_surjectionproof_n_used_inputs(CTX, &proof) == n_inputs / 2); + CHECK(secp256k1_surjectionproof_n_total_inputs(CTX, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(CTX, &proof) == 2 + 32 * (n_inputs / 2 + 1) + (n_inputs + 7) / 8); + CHECK(input_index == 0); + } +} + +/** Runs surjectionproof_initilize multiple times and records the number of times each input was used. + */ +static void test_input_selection_distribution_helper(const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, size_t *used_inputs) { + secp256k1_surjectionproof proof; + size_t input_index; + size_t i; + size_t j; + unsigned char seed[32]; + size_t result; + for (i = 0; i < n_input_tags; i++) { + used_inputs[i] = 0; + } + for(j = 0; j < 10000; j++) { + testrand256(seed); + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, &fixed_input_tags[0], 64, seed); + CHECK(result > 0); + + for (i = 0; i < n_input_tags; i++) { + if (proof.used_inputs[i / 8] & (1 << (i % 8))) { + used_inputs[i] += 1; + } + } + } +} + +/** Probabilistic test of the distribution of used_inputs after surjectionproof_initialize. + * Each confidence interval assertion fails incorrectly with a probability of 2^-128. + */ +static void test_input_selection_distribution(void) { + size_t i; + size_t n_input_tags_to_use; + const size_t n_inputs = 4; + secp256k1_fixed_asset_tag fixed_input_tags[4]; + size_t used_inputs[4]; + + for (i = 0; i < n_inputs; i++) { + testrand256(fixed_input_tags[i].data); + } + + /* If there is one input tag to use, initialize must choose the one equal to fixed_output_tag. */ + n_input_tags_to_use = 1; + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + CHECK(used_inputs[0] == 10000); + CHECK(used_inputs[1] == 0); + CHECK(used_inputs[2] == 0); + CHECK(used_inputs[3] == 0); + + n_input_tags_to_use = 2; + /* The input equal to the fixed_output_tag must be included in all used_inputs sets. + * For each fixed_input_tag != fixed_output_tag the probability that it's included + * in the used_inputs set is P(used_input|not fixed_output_tag) = 1/3. + */ + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + CHECK(used_inputs[0] == 10000); + CHECK(used_inputs[1] > 2725 && used_inputs[1] < 3961); + CHECK(used_inputs[2] > 2725 && used_inputs[2] < 3961); + CHECK(used_inputs[3] > 2725 && used_inputs[3] < 3961); + + n_input_tags_to_use = 3; + /* P(used_input|not fixed_output_tag) = 2/3 */ + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + CHECK(used_inputs[0] == 10000); + CHECK(used_inputs[1] > 6039 && used_inputs[1] < 7275); + CHECK(used_inputs[2] > 6039 && used_inputs[2] < 7275); + CHECK(used_inputs[3] > 6039 && used_inputs[3] < 7275); + + + n_input_tags_to_use = 1; + /* Create second input tag that is equal to the output tag. Therefore, when using only + * one input we have P(used_input|fixed_output_tag) = 1/2 and P(used_input|not fixed_output_tag) = 0 + */ + memcpy(fixed_input_tags[0].data, fixed_input_tags[1].data, 32); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + CHECK(used_inputs[0] > 4345 && used_inputs[0] < 5655); + CHECK(used_inputs[1] > 4345 && used_inputs[1] < 5655); + CHECK(used_inputs[2] == 0); + CHECK(used_inputs[3] == 0); + + n_input_tags_to_use = 2; + /* When choosing 2 inputs in initialization there are 5 possible combinations of + * input indexes {(0, 1), (1, 2), (0, 3), (1, 3), (0, 2)}. Therefore we have + * P(used_input|fixed_output_tag) = 3/5 and P(used_input|not fixed_output_tag) = 2/5. + */ + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + CHECK(used_inputs[0] > 5352 && used_inputs[0] < 6637); + CHECK(used_inputs[1] > 5352 && used_inputs[1] < 6637); + CHECK(used_inputs[2] > 3363 && used_inputs[2] < 4648); + CHECK(used_inputs[3] > 3363 && used_inputs[3] < 4648); + + n_input_tags_to_use = 3; + /* There are 4 combinations, each with all inputs except one. Therefore we have + * P(used_input|fixed_output_tag) = 3/4 and P(used_input|not fixed_output_tag) = 3/4. + */ + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + CHECK(used_inputs[0] > 6918 && used_inputs[0] < 8053); + CHECK(used_inputs[1] > 6918 && used_inputs[1] < 8053); + CHECK(used_inputs[2] > 6918 && used_inputs[2] < 8053); + CHECK(used_inputs[3] > 6918 && used_inputs[3] < 8053); +} + +static void test_gen_verify(size_t n_inputs, size_t n_used) { + unsigned char seed[32]; + secp256k1_surjectionproof proof; + unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + unsigned char serialized_proof_trailing[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX + 1]; + size_t serialized_len = SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX; + secp256k1_fixed_asset_tag fixed_input_tags[1000]; + secp256k1_generator ephemeral_input_tags[1000]; + unsigned char *input_blinding_key[1000]; + const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1; + size_t try_count = n_inputs * 100; + size_t key_index; + size_t input_index; + size_t i; + int result; + + /* setup */ + CHECK(n_used <= n_inputs); + CHECK(n_inputs < max_n_inputs); + testrand256(seed); + + key_index = (((size_t) seed[0] << 8) + seed[1]) % n_inputs; + + for (i = 0; i < n_inputs + 1; i++) { + input_blinding_key[i] = malloc(32); + testrand256(input_blinding_key[i]); + /* choose random fixed tag, except that for the output one copy from the key_index */ + if (i < n_inputs) { + testrand256(fixed_input_tags[i].data); + } else { + memcpy(&fixed_input_tags[i], &fixed_input_tags[key_index], sizeof(fixed_input_tags[i])); + } + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i])); + } + + /* test */ + result = secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, fixed_input_tags, n_inputs, n_used, &fixed_input_tags[key_index], try_count, seed); + if (n_used == 0) { + CHECK(result == 0); + return; + } + CHECK(result > 0); + CHECK(input_index == key_index); + + result = secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]); + CHECK(result == 1); + + CHECK(secp256k1_surjectionproof_serialize(CTX, serialized_proof, &serialized_len, &proof)); + CHECK(serialized_len == secp256k1_surjectionproof_serialized_size(CTX, &proof)); + CHECK(serialized_len == SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used)); + + /* trailing garbage */ + memcpy(&serialized_proof_trailing, &serialized_proof, serialized_len); + serialized_proof_trailing[serialized_len] = seed[0]; + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof_trailing, serialized_len + 1) == 0); + + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof, serialized_len)); + result = secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); + CHECK(result == 1); + + /* various fail cases */ + if (n_inputs > 1) { + result = secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); + CHECK(result == 0); + + /* number of entries in ephemeral_input_tags array is less than proof.n_inputs */ + n_inputs -= 1; + result = secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]); + CHECK(result == 0); + result = secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); + CHECK(result == 0); + n_inputs += 1; + } + + for (i = 0; i < n_inputs; i++) { + /* flip bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + result = secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); + CHECK(result == 0); + /* reset the bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + } + + /* cleanup */ + for (i = 0; i < n_inputs + 1; i++) { + free(input_blinding_key[i]); + } +} + +/* check that a proof with empty n_used_inputs is invalid */ +static void test_no_used_inputs_verify(void) { + secp256k1_surjectionproof proof; + secp256k1_fixed_asset_tag fixed_input_tag; + secp256k1_fixed_asset_tag fixed_output_tag; + secp256k1_generator ephemeral_input_tags[1]; + size_t n_ephemeral_input_tags = 1; + secp256k1_generator ephemeral_output_tag; + unsigned char blinding_key[32]; + secp256k1_ge output; + secp256k1_sha256 sha256_e0; + int result; + + /* Create proof that doesn't use inputs. secp256k1_surjectionproof_initialize + * will not work here since it insists on selecting an input that matches the output. */ + proof.n_inputs = 1; + memset(proof.used_inputs, 0, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8); + + /* create different fixed input and output tags */ + testrand256(fixed_input_tag.data); + testrand256(fixed_output_tag.data); + + /* blind fixed output tags with random blinding key */ + testrand256(blinding_key); + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_input_tags[0], fixed_input_tag.data, blinding_key)); + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_output_tag, fixed_output_tag.data, blinding_key)); + + /* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */ + secp256k1_generator_load(&output, &ephemeral_output_tag); + secp256k1_surjection_genmessage(proof.data, ephemeral_input_tags, 1, &ephemeral_output_tag); + secp256k1_sha256_initialize(&sha256_e0); + secp256k1_sha256_write(&sha256_e0, proof.data, 32); + secp256k1_sha256_finalize(&sha256_e0, proof.data); + + result = secp256k1_surjectionproof_verify(CTX, &proof, ephemeral_input_tags, n_ephemeral_input_tags, &ephemeral_output_tag); + CHECK(result == 0); +} + +static void test_bad_serialize(void) { + secp256k1_surjectionproof proof; + unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + size_t serialized_len; + + proof.n_inputs = 0; + memset(proof.used_inputs, 0, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8); + memset(proof.data, 0, 32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)); + + serialized_len = 2 + 31; + /* e0 is one byte too short */ + CHECK(secp256k1_surjectionproof_serialize(CTX, serialized_proof, &serialized_len, &proof) == 0); +} + +static void test_bad_parse(void) { + secp256k1_surjectionproof proof; + unsigned char serialized_proof0[] = { 0x00 }; + unsigned char serialized_proof1[] = { 0x01, 0x00 }; + unsigned char serialized_proof2[33] = { 0 }; + + /* Missing total input count */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof0, sizeof(serialized_proof0)) == 0); + /* Missing bitmap */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof1, sizeof(serialized_proof1)) == 0); + /* Missing e0 value */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0); +} + +static void test_input_eq_output(void) { + secp256k1_surjectionproof proof; + secp256k1_fixed_asset_tag fixed_tag; + secp256k1_generator ephemeral_tag; + unsigned char blinding_key[32]; + unsigned char entropy[32]; + size_t input_index; + + testrand256(fixed_tag.data); + testrand256(blinding_key); + testrand256(entropy); + + CHECK(secp256k1_surjectionproof_initialize(CTX, &proof, &input_index, &fixed_tag, 1, 1, &fixed_tag, 100, entropy) == 1); + CHECK(input_index == 0); + + /* Generation should fail */ + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_tag, fixed_tag.data, blinding_key)); + CHECK(!secp256k1_surjectionproof_generate(CTX, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); + + /* ...even when the blinding key is zero */ + memset(blinding_key, 0, 32); + CHECK(secp256k1_generator_generate_blinded(CTX, &ephemeral_tag, fixed_tag.data, blinding_key)); + CHECK(!secp256k1_surjectionproof_generate(CTX, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); +} + +static void test_fixed_vectors(void) { + const unsigned char tag0_ser[] = { + 0x0a, + 0x1c, 0xa3, 0xdd, 0x12, 0x48, 0xdd, 0x4d, 0xd0, 0x04, 0x30, 0x47, 0x48, 0x75, 0xf5, 0xf5, 0xff, + 0x2a, 0xd5, 0x0d, 0x1d, 0x86, 0x2b, 0xa4, 0xa4, 0x2f, 0x46, 0xe9, 0xb4, 0x54, 0x21, 0xf0, 0x85 + }; + const unsigned char tag1_ser[] = { + 0x0a, + 0x09, 0x0d, 0x5a, 0xd4, 0xed, 0xae, 0x9c, 0x0c, 0x69, 0x79, 0xf3, 0x8d, 0x22, 0x03, 0x0a, 0x3d, + 0x38, 0xd4, 0x78, 0xe1, 0x03, 0x0d, 0x70, 0x57, 0xd9, 0x9a, 0x23, 0x03, 0xf0, 0x7f, 0xfb, 0xef + }; + const unsigned char tag2_ser[] = { + 0x0a, + 0xfd, 0xed, 0xba, 0x15, 0x20, 0x8a, 0xb2, 0xaf, 0x0b, 0x76, 0x6d, 0xd2, 0x5f, 0xd4, 0x15, 0x11, + 0x90, 0xec, 0xcb, 0x3f, 0xcd, 0x08, 0xb5, 0x35, 0xd9, 0x24, 0x18, 0xb1, 0xd3, 0x47, 0x83, 0x54 + }; + const unsigned char tag3_ser[] = { + 0x0b, + 0x8b, 0x47, 0xca, 0xee, 0x20, 0x52, 0x17, 0xbf, 0xee, 0xcc, 0x84, 0xcd, 0x34, 0x32, 0x6c, 0x36, + 0xf1, 0xd9, 0x3f, 0xe1, 0x6f, 0x77, 0xfe, 0x89, 0x3e, 0x4a, 0xc8, 0x2a, 0x75, 0xfa, 0x2d, 0x36 + }; + const unsigned char tag4_ser[] = { + 0x0b, + 0x3c, 0x5c, 0xf4, 0x61, 0x45, 0xa8, 0x53, 0xc1, 0x64, 0x32, 0x0e, 0x92, 0x68, 0x52, 0xbd, 0x12, + 0xe9, 0x45, 0x31, 0xeb, 0x04, 0x4c, 0xf4, 0xe2, 0x9e, 0x9f, 0x60, 0x26, 0x50, 0xbf, 0xd6, 0x9f + }; + const unsigned char output_tag_ser[] = { + 0x0b, + 0xf7, 0x3c, 0x03, 0xed, 0xae, 0x83, 0xa1, 0xa6, 0x94, 0x8c, 0xe3, 0xb8, 0x54, 0x02, 0xa8, 0xbd, + 0x66, 0xca, 0x28, 0xef, 0x44, 0xf5, 0x3a, 0xcb, 0xc7, 0x5b, 0x16, 0xac, 0xce, 0x29, 0x4b, 0xc6 + }; + + const unsigned char total1_used1[] = { + 0x01, 0x00, 0x01, 0x8e, 0x6b, 0x8d, 0x8b, 0x96, 0x29, 0x10, 0x29, 0xcb, 0xf8, 0x48, 0xd9, 0xc8, + 0x5b, 0x77, 0xdc, 0xdf, 0x16, 0x67, 0x19, 0xfe, 0x8d, 0xee, 0x8f, 0x56, 0x6f, 0x9c, 0xe9, 0xae, + 0xb9, 0xd9, 0x12, 0xb8, 0x95, 0x6c, 0xf1, 0x48, 0x07, 0x7d, 0x49, 0xe4, 0x3e, 0x7f, 0xc1, 0x2c, + 0xe2, 0xe1, 0x94, 0x10, 0xb1, 0xda, 0x86, 0x5f, 0xbc, 0x03, 0x59, 0xe1, 0x09, 0xd2, 0x1b, 0x18, + 0xce, 0x58, 0x15 + }; + const size_t total1_used1_len = sizeof(total1_used1); + + const unsigned char total2_used1[] = { + 0x02, 0x00, 0x01, 0x35, 0x3a, 0x29, 0x4b, 0xe4, 0x99, 0xc6, 0xbf, 0x99, 0x4d, 0x6c, 0xc8, 0x18, + 0x14, 0xad, 0x10, 0x22, 0x3a, 0xb8, 0x1c, 0xb9, 0xc5, 0x77, 0xda, 0xe0, 0x8a, 0x71, 0x2d, 0x0d, + 0x8e, 0x80, 0xf5, 0x8d, 0x74, 0xf9, 0x01, 0x6b, 0x35, 0x88, 0xf4, 0x8e, 0x43, 0xa5, 0x9c, 0x0f, + 0x7e, 0x37, 0x86, 0x77, 0x44, 0x72, 0x7c, 0xaa, 0xff, 0x14, 0x5b, 0x7a, 0x42, 0x41, 0x75, 0xb2, + 0x5e, 0x3d, 0x6c + }; + const size_t total2_used1_len = sizeof(total2_used1); + + const unsigned char total3_used2[] = { + 0x03, 0x00, 0x03, 0xf2, 0x3f, 0xca, 0x49, 0x52, 0x05, 0xaf, 0x81, 0x83, 0x01, 0xd7, 0xf4, 0x92, + 0xc0, 0x50, 0xe3, 0x15, 0xfc, 0x94, 0xc1, 0x27, 0x10, 0xd7, 0x8f, 0x57, 0xb1, 0x23, 0xcf, 0x68, + 0x31, 0xf8, 0xcb, 0x58, 0x3d, 0xca, 0x2f, 0x7a, 0x3b, 0x0b, 0xb6, 0x10, 0x52, 0x94, 0xc8, 0x5f, + 0x0a, 0xf8, 0xca, 0x5d, 0x4c, 0x38, 0x44, 0x92, 0xb3, 0xc7, 0xe4, 0x46, 0x9f, 0x96, 0x64, 0xbd, + 0xd2, 0xda, 0x40, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91 + }; + const size_t total3_used2_len = sizeof(total3_used2); + + const unsigned char total5_used3[] = { + 0x05, 0x00, 0x15, 0x36, 0x3b, 0x92, 0x97, 0x84, 0x25, 0x75, 0xd6, 0xa6, 0xaf, 0xb7, 0x32, 0x5b, + 0x2c, 0xf8, 0x31, 0xe2, 0x15, 0x3a, 0x9b, 0xb7, 0x20, 0x14, 0xc0, 0x67, 0x96, 0x7d, 0xa9, 0xc4, + 0xa2, 0xb4, 0x22, 0x57, 0x5f, 0xb8, 0x20, 0xf1, 0xe8, 0x82, 0xaf, 0xbc, 0x8a, 0xbc, 0x01, 0xc9, + 0x35, 0xf2, 0x7f, 0x6f, 0x0c, 0x0d, 0xba, 0x87, 0xa4, 0xc3, 0xec, 0x60, 0x54, 0x49, 0x35, 0xeb, + 0x1e, 0x48, 0x2c, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b, + 0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee, + 0x9c, 0xf5, 0xb9 + }; + const size_t total5_used3_len = sizeof(total5_used3); + + const unsigned char total5_used5[] = { + 0x05, 0x00, 0x1f, 0xfd, 0xbb, 0xb6, 0xc2, 0x78, 0x82, 0xad, 0xe1, 0x66, 0x6d, 0x20, 0x4d, 0xfe, + 0x6b, 0xd2, 0x0b, 0x21, 0x6e, 0xa8, 0x5b, 0xc8, 0xe4, 0x88, 0x42, 0x11, 0x30, 0x3b, 0x6b, 0x02, + 0xc9, 0x7f, 0x44, 0x1c, 0xee, 0xd8, 0x37, 0x6a, 0xf8, 0xfd, 0xc8, 0x4b, 0x0b, 0xa1, 0x43, 0x1f, + 0x68, 0x77, 0x8d, 0x1b, 0xac, 0x9e, 0xc1, 0xc1, 0xda, 0x60, 0xa8, 0xcf, 0x10, 0x9d, 0x80, 0x07, + 0x90, 0x57, 0xb6, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b, + 0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee, + 0x9c, 0xf5, 0xb9, 0x5a, 0xc8, 0x03, 0x8d, 0x4f, 0xe3, 0x1d, 0x79, 0x38, 0x5a, 0xfa, 0xe5, 0xa8, + 0x9d, 0x56, 0x77, 0xb3, 0xf9, 0xa8, 0x70, 0x46, 0x27, 0x26, 0x6c, 0x6e, 0x54, 0xaf, 0xf9, 0xd0, + 0x37, 0xa4, 0x86, 0x68, 0x8f, 0xac, 0x3e, 0x78, 0xaa, 0x3d, 0x83, 0x1a, 0xca, 0x05, 0xfe, 0x10, + 0x95, 0xa4, 0x6a, 0x10, 0xc6, 0x62, 0xf3, 0xf7, 0xf3, 0x4d, 0x0b, 0xd4, 0x94, 0xe5, 0x51, 0x6c, + 0x85, 0xd7, 0xc7 + }; + const size_t total5_used5_len = sizeof(total5_used5); + + unsigned char bad[sizeof(total5_used5) + 32] = { 0 }; + + secp256k1_generator input_tags[5]; + secp256k1_generator output_tag; + secp256k1_surjectionproof proof; + + CHECK(secp256k1_generator_parse(CTX, &input_tags[0], tag0_ser)); + CHECK(secp256k1_generator_parse(CTX, &input_tags[1], tag1_ser)); + CHECK(secp256k1_generator_parse(CTX, &input_tags[2], tag2_ser)); + CHECK(secp256k1_generator_parse(CTX, &input_tags[3], tag3_ser)); + CHECK(secp256k1_generator_parse(CTX, &input_tags[4], tag4_ser)); + CHECK(secp256k1_generator_parse(CTX, &output_tag, output_tag_ser)); + + /* check 1-of-1 */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, total1_used1, total1_used1_len)); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, input_tags, 1, &output_tag)); + /* check 1-of-2 */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, total2_used1, total2_used1_len)); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, input_tags, 2, &output_tag)); + /* check 2-of-3 */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, total3_used2, total3_used2_len)); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, input_tags, 3, &output_tag)); + /* check 3-of-5 */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, total5_used3, total5_used3_len)); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, input_tags, 5, &output_tag)); + /* check 5-of-5 */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, total5_used5, total5_used5_len)); + CHECK(secp256k1_surjectionproof_verify(CTX, &proof, input_tags, 5, &output_tag)); + + /* check invalid length fails */ + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, total5_used5, total5_used3_len)); + /* check invalid keys fail */ + CHECK(secp256k1_surjectionproof_parse(CTX, &proof, total1_used1, total1_used1_len)); + CHECK(!secp256k1_surjectionproof_verify(CTX, &proof, &input_tags[1], 1, &output_tag)); + CHECK(!secp256k1_surjectionproof_verify(CTX, &proof, input_tags, 1, &input_tags[0])); + + /* Try setting 6 bits on the total5-used-5; check that parsing fails */ + memcpy(bad, total5_used5, total5_used5_len); + bad[2] = 0x3f; /* 0x1f -> 0x3f */ + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used5_len)); + /* Correct for the length */ + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used5_len + 32)); + /* Alternately just turn off one of the "legit" bits */ + bad[2] = 0x37; /* 0x1f -> 0x37 */ + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used5_len)); + + /* Similarly try setting 4 bits on the total5-used-3, with one bit out of range */ + memcpy(bad, total5_used3, total5_used3_len); + bad[2] = 0x35; /* 0x15 -> 0x35 */ + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used3_len)); + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used3_len + 32)); + bad[2] = 0x34; /* 0x15 -> 0x34 */ + CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used3_len)); +} + +static void test_input_selection_all(void) { + test_input_selection(0); + test_input_selection(1); + test_input_selection(5); + test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); +} + +static void test_gen_verify_all(void) { + test_gen_verify(10, 3); + test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); +} + +/* --- Test registry --- */ +static const struct tf_test_entry tests_surjection[] = { + CASE1(test_surjectionproof_api), + CASE1(test_input_eq_output), + CASE1(test_fixed_vectors), + CASE1(test_input_selection_all), + CASE1(test_input_selection_distribution), + CASE1(test_gen_verify_all), + CASE1(test_no_used_inputs_verify), + CASE1(test_bad_serialize), + CASE1(test_bad_parse), +}; + +#endif diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include new file mode 100644 index 00000000..41445112 --- /dev/null +++ b/src/modules/whitelist/Makefile.am.include @@ -0,0 +1,10 @@ +include_HEADERS += include/secp256k1_whitelist.h +noinst_HEADERS += src/modules/whitelist/whitelist_impl.h +noinst_HEADERS += src/modules/whitelist/main_impl.h +noinst_HEADERS += src/modules/whitelist/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_whitelist +bench_whitelist_SOURCES = src/bench_whitelist.c +bench_whitelist_LDADD = libsecp256k1.la +bench_generator_CPPFLAGS = $(SECP_CONFIG_DEFINES) +endif diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h new file mode 100644 index 00000000..301d2476 --- /dev/null +++ b/src/modules/whitelist/main_impl.h @@ -0,0 +1,168 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_WHITELIST_MAIN_H +#define SECP256K1_MODULE_WHITELIST_MAIN_H + +#include "../../../include/secp256k1_whitelist.h" +#include "whitelist_impl.h" + +#define MAX_KEYS SECP256K1_WHITELIST_MAX_N_KEYS /* shorter alias */ + +int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey, const unsigned char *online_seckey, const unsigned char *summed_seckey, const size_t index) { + secp256k1_gej pubs[MAX_KEYS]; + secp256k1_scalar s[MAX_KEYS]; + secp256k1_scalar sec, non; + unsigned char msg32[32]; + int ret; + + /* Sanity checks */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(online_pubkeys != NULL); + ARG_CHECK(offline_pubkeys != NULL); + ARG_CHECK(n_keys <= MAX_KEYS); + ARG_CHECK(sub_pubkey != NULL); + ARG_CHECK(online_seckey != NULL); + ARG_CHECK(summed_seckey != NULL); + ARG_CHECK(index < n_keys); + + /* Compute pubkeys: online_pubkey + tweaked(offline_pubkey + address), and message */ + ret = secp256k1_whitelist_compute_keys_and_message(ctx, msg32, pubs, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey); + + /* Compute signing key: online_seckey + tweaked(summed_seckey) */ + if (ret) { + ret = secp256k1_whitelist_compute_tweaked_privkey(ctx, &sec, online_seckey, summed_seckey); + } + /* Compute nonce and random s-values */ + if (ret) { + unsigned char seckey32[32]; + unsigned int count = 0; + int overflow = 0; + + secp256k1_scalar_get_b32(seckey32, &sec); + while (1) { + size_t i; + unsigned char nonce32[32]; + int done; + ret = secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, NULL, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + secp256k1_memclear_explicit(nonce32, 32); + if (overflow || secp256k1_scalar_is_zero(&non)) { + count++; + continue; + } + done = 1; + for (i = 0; i < n_keys; i++) { + msg32[0] ^= i + 1; + msg32[1] ^= (i + 1) / 0x100; + ret = secp256k1_nonce_function_default(&sig->data[32 * (i + 1)], msg32, seckey32, NULL, NULL, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&s[i], &sig->data[32 * (i + 1)], &overflow); + msg32[0] ^= i + 1; + msg32[1] ^= (i + 1) / 0x100; + if (overflow || secp256k1_scalar_is_zero(&s[i])) { + count++; + done = 0; + break; + } + } + if (done) { + break; + } + } + secp256k1_memclear_explicit(seckey32, 32); + } + /* Actually sign */ + if (ret) { + sig->n_keys = n_keys; + ret = secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &non, &sec, &n_keys, &index, 1, msg32, 32); + /* Signing will change s[index], so update in the sig structure */ + secp256k1_scalar_get_b32(&sig->data[32 * (index + 1)], &s[index]); + } + + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_whitelist_verify(const secp256k1_context* ctx, const secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey) { + secp256k1_scalar s[MAX_KEYS]; + secp256k1_gej pubs[MAX_KEYS]; + unsigned char msg32[32]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(online_pubkeys != NULL); + ARG_CHECK(offline_pubkeys != NULL); + ARG_CHECK(sub_pubkey != NULL); + + if (sig->n_keys > MAX_KEYS || sig->n_keys != n_keys) { + return 0; + } + for (i = 0; i < sig->n_keys; i++) { + int overflow = 0; + secp256k1_scalar_set_b32(&s[i], &sig->data[32 * (i + 1)], &overflow); + if (overflow || secp256k1_scalar_is_zero(&s[i])) { + return 0; + } + } + + /* Compute pubkeys: online_pubkey + tweaked(offline_pubkey + address), and message */ + if (!secp256k1_whitelist_compute_keys_and_message(ctx, msg32, pubs, online_pubkeys, offline_pubkeys, sig->n_keys, sub_pubkey)) { + return 0; + } + /* Do verification */ + return secp256k1_borromean_verify(NULL, &sig->data[0], s, pubs, &sig->n_keys, 1, msg32, 32); +} + +size_t secp256k1_whitelist_signature_n_keys(const secp256k1_whitelist_signature *sig) { + return sig->n_keys; +} + +int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const unsigned char *input, size_t input_len) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + if (input_len == 0) { + return 0; + } + + sig->n_keys = input[0]; + if (sig->n_keys > MAX_KEYS || input_len != 1 + 32 * (sig->n_keys + 1)) { + return 0; + } + memcpy(&sig->data[0], &input[1], 32 * (sig->n_keys + 1)); + + return 1; +} + +int secp256k1_whitelist_signature_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *output_len, const secp256k1_whitelist_signature *sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(output_len != NULL); + ARG_CHECK(sig != NULL); + + if (*output_len < 1 + 32 * (sig->n_keys + 1)) { + return 0; + } + + output[0] = sig->n_keys; + memcpy(&output[1], &sig->data[0], 32 * (sig->n_keys + 1)); + *output_len = 1 + 32 * (sig->n_keys + 1); + + return 1; +} + +#endif diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h new file mode 100644 index 00000000..dad8a479 --- /dev/null +++ b/src/modules/whitelist/tests_impl.h @@ -0,0 +1,168 @@ +/********************************************************************** + * Copyright (c) 2014-2016 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_WHITELIST_TESTS_H +#define SECP256K1_MODULE_WHITELIST_TESTS_H + +#include "../../../include/secp256k1_whitelist.h" +#include "../../unit_test.h" + +static void test_whitelist_end_to_end_internal(const unsigned char *summed_seckey, const unsigned char *online_seckey, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey, const size_t signer_i, const size_t n_keys) { + unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; + size_t slen = sizeof(serialized); + secp256k1_whitelist_signature sig; + secp256k1_whitelist_signature sig1; + + CHECK(secp256k1_whitelist_sign(CTX, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey, online_seckey, summed_seckey, signer_i)); + CHECK(secp256k1_whitelist_verify(CTX, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey) == 1); + /* Check that exchanging keys causes a failure */ + CHECK(secp256k1_whitelist_verify(CTX, &sig, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); + /* Serialization round trip */ + CHECK(secp256k1_whitelist_signature_serialize(CTX, serialized, &slen, &sig) == 1); + CHECK(slen == 33 + 32 * n_keys); + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig1, serialized, slen) == 1); + /* (Check various bad-length conditions) */ + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig1, serialized, slen + 32) == 0); + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig1, serialized, slen + 1) == 0); + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig1, serialized, slen - 1) == 0); + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig1, serialized, 0) == 0); + CHECK(secp256k1_whitelist_verify(CTX, &sig1, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey) == 1); + CHECK(secp256k1_whitelist_verify(CTX, &sig1, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); + + /* Test n_keys */ + CHECK(secp256k1_whitelist_signature_n_keys(&sig) == n_keys); + CHECK(secp256k1_whitelist_signature_n_keys(&sig1) == n_keys); + + /* Test bad number of keys in signature */ + sig.n_keys = n_keys + 1; + CHECK(secp256k1_whitelist_verify(CTX, &sig, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); + sig.n_keys = n_keys; +} + +static void test_whitelist_end_to_end(const size_t n_keys, int test_all_keys) { + unsigned char **online_seckey = malloc(n_keys * sizeof(*online_seckey)); + unsigned char **summed_seckey = malloc(n_keys * sizeof(*summed_seckey)); + secp256k1_pubkey *online_pubkeys = malloc(n_keys * sizeof(*online_pubkeys)); + secp256k1_pubkey *offline_pubkeys = malloc(n_keys * sizeof(*offline_pubkeys)); + + secp256k1_scalar ssub; + unsigned char csub[32]; + secp256k1_pubkey sub_pubkey; + + /* Generate random keys */ + size_t i; + /* Start with subkey */ + testutil_random_scalar_order_test(&ssub); + secp256k1_scalar_get_b32(csub, &ssub); + CHECK(secp256k1_ec_seckey_verify(CTX, csub) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &sub_pubkey, csub) == 1); + /* Then offline and online whitelist keys */ + for (i = 0; i < n_keys; i++) { + secp256k1_scalar son, soff; + + online_seckey[i] = malloc(32); + summed_seckey[i] = malloc(32); + + /* Create two keys */ + testutil_random_scalar_order_test(&son); + secp256k1_scalar_get_b32(online_seckey[i], &son); + CHECK(secp256k1_ec_seckey_verify(CTX, online_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &online_pubkeys[i], online_seckey[i]) == 1); + + testutil_random_scalar_order_test(&soff); + secp256k1_scalar_get_b32(summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(CTX, summed_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &offline_pubkeys[i], summed_seckey[i]) == 1); + + /* Make summed_seckey correspond to the sum of offline_pubkey and sub_pubkey */ + secp256k1_scalar_add(&soff, &soff, &ssub); + secp256k1_scalar_get_b32(summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(CTX, summed_seckey[i]) == 1); + } + + /* Sign/verify with each one */ + if (test_all_keys) { + for (i = 0; i < n_keys; i++) { + test_whitelist_end_to_end_internal(summed_seckey[i], online_seckey[i], online_pubkeys, offline_pubkeys, &sub_pubkey, i, n_keys); + } + } else { + uint32_t rand_idx = testrand_int(n_keys-1); + test_whitelist_end_to_end_internal(summed_seckey[0], online_seckey[0], online_pubkeys, offline_pubkeys, &sub_pubkey, 0, n_keys); + test_whitelist_end_to_end_internal(summed_seckey[rand_idx], online_seckey[rand_idx], online_pubkeys, offline_pubkeys, &sub_pubkey, rand_idx, n_keys); + test_whitelist_end_to_end_internal(summed_seckey[n_keys-1], online_seckey[n_keys-1], online_pubkeys, offline_pubkeys, &sub_pubkey, n_keys-1, n_keys); + } + + for (i = 0; i < n_keys; i++) { + free(online_seckey[i]); + free(summed_seckey[i]); + } + free(online_seckey); + free(summed_seckey); + free(online_pubkeys); + free(offline_pubkeys); +} + +static void test_whitelist_bad_parse(void) { + secp256k1_whitelist_signature sig; + + const unsigned char serialized0[] = { 1+32*(0+1) }; + const unsigned char serialized1[] = { + 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 + }; + const unsigned char serialized2[] = { + 0x01, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + + /* Empty input */ + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig, serialized0, 0) == 0); + /* Misses one byte of e0 */ + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig, serialized1, sizeof(serialized1)) == 0); + /* Enough bytes for e0, but there is no s value */ + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig, serialized2, sizeof(serialized2)) == 0); +} + +static void test_whitelist_bad_serialize(void) { + unsigned char serialized[] = { + 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + size_t serialized_len; + secp256k1_whitelist_signature sig; + + CHECK(secp256k1_whitelist_signature_parse(CTX, &sig, serialized, sizeof(serialized)) == 1); + serialized_len = sizeof(serialized) - 1; + /* Output buffer is one byte too short */ + CHECK(secp256k1_whitelist_signature_serialize(CTX, serialized, &serialized_len, &sig) == 0); +} + +static void test_whitelist_end_to_end_all_internal(void) { + test_whitelist_end_to_end(1, 1); + test_whitelist_end_to_end(10, 1); + test_whitelist_end_to_end(50, 1); + test_whitelist_end_to_end(SECP256K1_WHITELIST_MAX_N_KEYS, 0); +} + +/* --- Test registry --- */ +REPEAT_TEST(test_whitelist_end_to_end_all) + +static const struct tf_test_entry tests_whitelist[] = { + CASE1(test_whitelist_bad_parse), + CASE1(test_whitelist_bad_serialize), + CASE1(test_whitelist_end_to_end_all), +}; + +#endif diff --git a/src/modules/whitelist/whitelist.md b/src/modules/whitelist/whitelist.md new file mode 100644 index 00000000..28307f4b --- /dev/null +++ b/src/modules/whitelist/whitelist.md @@ -0,0 +1,107 @@ +Address Whitelisting Module +=========================== + +This module implements a scheme by which members of some group, having fixed +signing keys, can prove control of an arbitrary other key without associating +their own identity (only that they belong to the group) to the new key. The +application is to patch ring-signature-like behaviour onto systems such as +Bitcoin or PGP which do not directly support this. + +We refer to such delegation as "whitelisting" because we expect it to be used +to build a dynamic whitelist of authorized keys. + +For example, imagine a private sidechain with a fixed membership set but +stronger privacy properties than Bitcoin. When moving coins from this system +to Bitcoin, it is desirable that the destination Bitcoin addresses be provably +in control of some user of the sidechain. This prevents malicious or erroneous +behaviour on the sidechain, which can likely be resolved by its participants, +from translating to theft on the wider Bitcoin network, which is irreversible. + +### Unused Schemes and Design Rationale + +#### Direct Signing + +An obvious scheme for such delegation is to simply have participants sign the +key they want to whitelist. To avoid revealing their specific identity, they +could use a ring signature. The problem with this is that it really only proves +that a participant *signed off* on a key, not that they control it. Thus any +security failure that allows text substitution could be used to subvert this +and redirect coins to an attacker-controlled address. + +#### Signing with Difference-of-Keys + +A less obvious scheme is to have a participant sign an arbitrary message with +the sum of her key `P` and the whitelisted key `W`. Such a signature with the key +`P + W` proves knowledge of either (a) discrete logarithms of both `P` and `W`; +or (b) neither. This makes directly attacking participants' signing schemes much +harder, but allows an attacker to whitelist arbitrary "cancellation" keys by +computing `W` as the difference between an attacker-controlled key and `P`. +Because to spend the funds the attacker must produce a signature with `W`, the +coins will be unspendable until attacker and the legitimate participant owning +`P` cooperate. + +In an important sense, this "cancellation" attack is a good thing: it enables +*offline delegation*. That is, the key `P` does not need to be available at the +time of delegation. Instead, participants could choose `S = P + W`, sign with +this to delegate, and only later compute the discrete logarithm of `W = P - S`. +This allows `P` to be in cold storage or be otherwise inaccessible, improving +the overall system security. + +#### Signing with Tweaked-Difference-of-Keys + +A modification of this scheme, which prevents this "cancellation" attack, is to +instead have participants sign some message with the key `P + H(W)W`, for `H` +some random-oracle hash that maps group elements to scalars. This key, and its +discrete logarithm, cannot be known until after `W` is chosen, so `W` cannot +be selected as the difference between it and `P`. (Note that `P` could still +be some chosen difference; however `P` is a fixed key and must be verified +out-of-band to have come from a legitimate participant anyway.) + +This scheme is almost what we want, but it no longer supports offline +delegation. However, we can get this back by introducing a new key, `P'`, +and signing with the key `P + H(W + P')(W + P')`. This gives us the best +of both worlds: `P'` does not need to be online to delegate, allowing it +to be securely stored and preventing real-time attacks; `P` does need to +be online, but its compromise only allows an attacker to whitelist keys he does +not control alone. + +### Our Scheme + +Our scheme works as follows: each participant `i` chooses two keys, `P_i` and `Q_i`. +We refer to `P_i` as the "online key" and `Q_i` as the "offline key". To whitelist +a key `W`, the participant computes the key `L_j = P_j + H(W + Q_j)(W + Q_j)` for +every participant `j`. Then she will know the discrete logarithm of `L_i` for her +own `i`. + +Next, she signs a message containing every `P_i` and `Q_i` as well as `W` with +a ring signature over all the keys `L_j`. This proves that she knows the discrete +logarithm of some `L_i` (though it is zero-knowledge which one), and therefore +knows: +1. The discrete logarithms of all of `W`, `P_i` and `Q_i`; or +2. The discrete logarithm of `P_i` but of *neither* `W` nor `Q_i`. +In other words, compromise of the online key `P_i` allows an attacker to whitelist +"cancellation keys" for which the attacker alone does not know the discrete logarithm; +to whitelist an attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult +because by design, only the sum `S = W + Q_i` is used when signing; then by choosing +`S` freely, a participant can delegate without the secret key to `Q_i` ever being online. +(Later, when she wants to actually use `W`, she will need to compute its key as the +difference between `S` and `Q_i`; but this can be done offline and much later +and with more expensive security requirements.) + +The message to be signed contains all public keys to prevent a class of attacks +centered around choosing keys to match pre-computed signatures. In our proposed +use case, whitelisted keys already must be computed before they are signed, and +the remaining public keys are verified out-of-band when setting up the system, +so there is no direct benefit to this. We do it only to reduce fragility and +increase safety of unforeseen uses. + +Having to access the offline key `Q_i` to compute the secret to the sum `W + +Q_i` for every authorization is onerous. Instead, if the whitelisted keys are +created using +[BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) +unhardened derivation, the sum can be computed on an online machine. In order +to achieve that, the offline key `Q_j` is set to the negated last hardened +BIP32 derived parent key (typically, the public key corresponding to the xpub). +As a result `W + Q_i = I_L*G` where `I_L` is the public tweak used +to derive `W` and can be easily computed online using the extended public key +and the derivation path. diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h new file mode 100644 index 00000000..edb90b4f --- /dev/null +++ b/src/modules/whitelist/whitelist_impl.h @@ -0,0 +1,126 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_WHITELIST_IMPL_H +#define SECP256K1_WHITELIST_IMPL_H + +static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_gej* pubkey) { + unsigned char h[32]; + unsigned char c[33]; + secp256k1_sha256 sha; + int overflow = 0; + size_t size = 33; + secp256k1_ge ge; + + secp256k1_ge_set_gej(&ge, pubkey); + + secp256k1_sha256_initialize(&sha); + if (secp256k1_ge_is_infinity(&ge)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(&ge, c); + secp256k1_sha256_write(&sha, c, size); + secp256k1_sha256_finalize(&sha, h); + secp256k1_sha256_clear(&sha); + + secp256k1_scalar_set_b32(output, h, &overflow); + if (overflow || secp256k1_scalar_is_zero(output)) { + /* This return path is mathematically impossible to hit */ + secp256k1_scalar_clear(output); + return 0; + } + return 1; +} + +static int secp256k1_whitelist_tweak_pubkey(secp256k1_gej* pub_tweaked) { + secp256k1_scalar tweak; + secp256k1_scalar zero; + int ret; + + secp256k1_scalar_set_int(&zero, 0); + + ret = secp256k1_whitelist_hash_pubkey(&tweak, pub_tweaked); + if (ret) { + secp256k1_ecmult(pub_tweaked, pub_tweaked, &tweak, &zero); + } + return ret; +} + +static int secp256k1_whitelist_compute_tweaked_privkey(const secp256k1_context* ctx, secp256k1_scalar* skey, const unsigned char *online_key, const unsigned char *summed_key) { + secp256k1_scalar tweak; + int ret = 1; + int overflow = 0; + + secp256k1_scalar_set_b32(skey, summed_key, &overflow); + if (overflow || secp256k1_scalar_is_zero(skey)) { + ret = 0; + } + if (ret) { + secp256k1_gej pkeyj; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkeyj, skey); + ret = secp256k1_whitelist_hash_pubkey(&tweak, &pkeyj); + } + if (ret) { + secp256k1_scalar sonline; + secp256k1_scalar_mul(skey, skey, &tweak); + + secp256k1_scalar_set_b32(&sonline, online_key, &overflow); + if (overflow || secp256k1_scalar_is_zero(&sonline)) { + ret = 0; + } + secp256k1_scalar_add(skey, skey, &sonline); + secp256k1_scalar_clear(&sonline); + secp256k1_scalar_clear(&tweak); + } + + if (!ret) { + secp256k1_scalar_clear(skey); + } + return ret; +} + +/* Takes a list of pubkeys and combines them to form the public keys needed + * for the ring signature; also produce a commitment to every one that will + * be our "message". */ +static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* ctx, unsigned char *msg32, secp256k1_gej *keys, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const int n_keys, const secp256k1_pubkey *sub_pubkey) { + unsigned char c[33]; + size_t size = 33; + secp256k1_sha256 sha; + int i; + secp256k1_ge subkey_ge; + + secp256k1_sha256_initialize(&sha); + secp256k1_pubkey_load(ctx, &subkey_ge, sub_pubkey); + + /* commit to sub-key */ + secp256k1_eckey_pubkey_serialize33(&subkey_ge, c); + secp256k1_sha256_write(&sha, c, size); + for (i = 0; i < n_keys; i++) { + secp256k1_ge offline_ge; + secp256k1_ge online_ge; + secp256k1_gej tweaked_gej; + + /* commit to fixed keys */ + secp256k1_pubkey_load(ctx, &offline_ge, &offline_pubkeys[i]); + secp256k1_eckey_pubkey_serialize33(&offline_ge, c); + secp256k1_sha256_write(&sha, c, size); + secp256k1_pubkey_load(ctx, &online_ge, &online_pubkeys[i]); + secp256k1_eckey_pubkey_serialize33(&online_ge, c); + secp256k1_sha256_write(&sha, c, size); + + /* compute tweaked keys */ + secp256k1_gej_set_ge(&tweaked_gej, &offline_ge); + secp256k1_gej_add_ge_var(&tweaked_gej, &tweaked_gej, &subkey_ge, NULL); + secp256k1_whitelist_tweak_pubkey(&tweaked_gej); + secp256k1_gej_add_ge_var(&keys[i], &tweaked_gej, &online_ge, NULL); + } + secp256k1_sha256_finalize(&sha, msg32); + secp256k1_sha256_clear(&sha); + return 1; +} + + +#endif diff --git a/src/scalar.h b/src/scalar.h index 40d67191..4dd20a69 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -42,6 +42,9 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c /** Set a scalar to an unsigned integer. */ static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); +/** Set a scalar to an unsigned 64-bit integer */ +static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v); + /** Convert a scalar to a byte array. */ static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); @@ -54,6 +57,9 @@ static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int /** Multiply two scalars (modulo the group order). */ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + /** Compute the inverse of a scalar (modulo the group order). */ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index a5bf18fe..0f62ee7a 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include #include "checkmem.h" #include "int128.h" #include "modinv64_impl.h" @@ -38,6 +39,15 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig SECP256K1_SCALAR_VERIFY(r); } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + + SECP256K1_SCALAR_VERIFY(r); +} + SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { SECP256K1_SCALAR_VERIFY(a); VERIFY_CHECK(count > 0 && count <= 32); @@ -313,6 +323,29 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { VERIFY_CHECK(c1 >= th); \ } +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint64_t tl, th, th2, tl2; \ + { \ + secp256k1_uint128 t; \ + secp256k1_u128_mul(&t, a, b); \ + th = secp256k1_u128_hi_u64(&t); /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = secp256k1_u128_to_u64(&t); \ + } \ + th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ + c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ + th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2); /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ @@ -847,10 +880,151 @@ static void secp256k1_scalar_mul_512(uint64_t *l8, const secp256k1_scalar *a, co #endif } +static void secp256k1_scalar_sqr_512(uint64_t *l8, const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l8[0] */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l8[1] */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l8[2] */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l8[3] */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l8[4] */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l8[5] */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l8[6] */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l8[7] */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l8), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); + + SECP256K1_CHECKMEM_MSAN_DEFINE(l8, sizeof(*l8) * 8); + +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l8[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l8[0]); + muladd2(a->d[0], a->d[1]); + extract(l8[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l8[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l8[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l8[4]); + muladd2(a->d[2], a->d[3]); + extract(l8[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l8[6]); + VERIFY_CHECK(c1 == 0); + l8[7] = c0; +#endif +} + #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast +#undef muladd2 #undef extract #undef extract_fast @@ -865,6 +1039,12 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, SECP256K1_SCALAR_VERIFY(r); } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { SECP256K1_SCALAR_VERIFY(k); diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index aa87b1d3..11ae4b45 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include + #include "checkmem.h" #include "modinv32_impl.h" #include "util.h" @@ -51,6 +53,19 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig SECP256K1_SCALAR_VERIFY(r); } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + r->d[0] = v; + r->d[1] = v >> 32; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; + + SECP256K1_SCALAR_VERIFY(r); +} + SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { SECP256K1_SCALAR_VERIFY(a); VERIFY_CHECK(count > 0 && count <= 32); @@ -370,6 +385,28 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { VERIFY_CHECK(c1 >= th); \ } +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint32_t tl, th, th2, tl2; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ + c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ + th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2); /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ @@ -633,10 +670,71 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, con l[15] = c0; } +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast +#undef muladd2 #undef extract #undef extract_fast @@ -651,6 +749,12 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, SECP256K1_SCALAR_VERIFY(r); } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { SECP256K1_SCALAR_VERIFY(k); diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 628bfd33..6b401d37 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -25,6 +25,12 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig SECP256K1_SCALAR_VERIFY(r); } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + *r = v % EXHAUSTIVE_TEST_ORDER; + + SECP256K1_SCALAR_VERIFY(r); +} + SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { SECP256K1_SCALAR_VERIFY(a); @@ -139,6 +145,10 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, SECP256K1_SCALAR_VERIFY(r); } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { SECP256K1_SCALAR_VERIFY(a); diff --git a/src/secp256k1.c b/src/secp256k1.c index e4b80fff..532c9fdf 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -27,6 +27,7 @@ #include "field_impl.h" #include "scalar_impl.h" #include "group_impl.h" +#include "eccommit_impl.h" #include "ecmult_impl.h" #include "ecmult_const_impl.h" #include "ecmult_gen_impl.h" @@ -42,6 +43,26 @@ # error "secp256k1.h processed without SECP256K1_BUILD defined while building secp256k1.c" #endif +#ifdef ENABLE_MODULE_GENERATOR +# include "../include/secp256k1_generator.h" +#endif + +#ifdef ENABLE_MODULE_RANGEPROOF +# include "../include/secp256k1_rangeproof.h" +#endif + +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "../include/secp256k1_ecdsa_s2c.h" +static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge); +#else +typedef void secp256k1_ecdsa_s2c_opening; +static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) { + (void) opening; + (void) ge; + VERIFY_CHECK(0); +} +#endif + #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ @@ -538,7 +559,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; -static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { +static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, secp256k1_sha256* s2c_sha, secp256k1_ecdsa_s2c_opening *s2c_opening, const unsigned char* s2c_data32, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { secp256k1_scalar sec, non, msg; int ret = 0; int is_sec_valid; @@ -550,7 +571,12 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc if (recid) { *recid = 0; } - + /* sign-to-contract commitments only work with the default nonce function, + * because we need to ensure that s2c_data is actually hashed into the nonce and + * not just ignored. Otherwise an attacker can exfiltrate the secret key by + * signing the same message thrice with different commitments. */ + VERIFY_CHECK(s2c_data32 == NULL || noncefp == secp256k1_nonce_function_default); + /* Fail if the secret key is invalid. */ is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey); secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !is_sec_valid); @@ -572,6 +598,30 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc /* The nonce is still secret here, but it being invalid is less likely than 1:2^255. */ secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); if (is_nonce_valid) { + if (s2c_data32 != NULL) { + secp256k1_gej nonce_pj; + secp256k1_ge nonce_p; + + /* Compute original nonce commitment/pubkey */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_pj, &non); + secp256k1_ge_set_gej(&nonce_p, &nonce_pj); + if (s2c_opening != NULL) { + secp256k1_ecdsa_s2c_opening_save(s2c_opening, &nonce_p); + } + + /* Because the nonce is valid, the nonce point isn't the point + * at infinity and we can declassify that information to be able to + * serialize the point. */ + secp256k1_declassify(ctx, &nonce_p.infinity, sizeof(nonce_p.infinity)); + + /* Tweak nonce with s2c commitment. */ + ret = secp256k1_ec_commit_seckey(&non, &nonce_p, s2c_sha, s2c_data32, 32); + secp256k1_declassify(ctx, &ret, sizeof(ret)); /* may be secret that the tweak falied, but happens with negligible probability */ + if (!ret) { + break; + } + } + ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid); /* The final signature is no longer a secret, nor is the fact that we were successful or not. */ secp256k1_declassify(ctx, &ret, sizeof(ret)); @@ -607,7 +657,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature ARG_CHECK(signature != NULL); ARG_CHECK(seckey != NULL); - ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msghash32, seckey, noncefp, noncedata); + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, NULL, NULL, NULL, msghash32, seckey, noncefp, noncedata); secp256k1_ecdsa_signature_save(signature, &r, &s); return ret; } @@ -829,6 +879,32 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, return 1; } +/* Outputs 33 zero bytes if the given group element is the point at infinity and + * otherwise outputs the compressed serialization */ +static void secp256k1_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { + if (secp256k1_ge_is_infinity(ge)) { + memset(out33, 0, 33); + } else { + secp256k1_eckey_pubkey_serialize33(ge, out33); + } +} + +/* Outputs the point at infinity if the given byte array is all zero, otherwise + * attempts to parse compressed point serialization. */ +static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { + unsigned char zeros[33] = { 0 }; + + if (secp256k1_memcmp_var(in33, zeros, sizeof(zeros)) == 0) { + secp256k1_ge_set_infinity(ge); + return 1; + } + return secp256k1_eckey_pubkey_parse(ge, in33, 33); +} + +#ifdef ENABLE_MODULE_BPPP +# include "modules/bppp/main_impl.h" +#endif + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif @@ -849,6 +925,34 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, # include "modules/musig/main_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG +# include "modules/schnorrsig_halfagg/main_impl.h" +#endif + #ifdef ENABLE_MODULE_ELLSWIFT # include "modules/ellswift/main_impl.h" #endif + +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "modules/ecdsa_s2c/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +# include "modules/ecdsa_adaptor/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_GENERATOR +# include "modules/generator/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_RANGEPROOF +# include "modules/rangeproof/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_WHITELIST +# include "modules/whitelist/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SURJECTIONPROOF +# include "modules/surjection/main_impl.h" +#endif diff --git a/src/testrand.h b/src/testrand.h index 215b6fc7..3e31d50f 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -36,6 +36,9 @@ static void testrand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void testrand_bytes_test(unsigned char *bytes, size_t len); +/** Generate a pseudorandom 64-bit integer in the range min..max, inclusive. */ +static int64_t testrandi64(uint64_t min, uint64_t max); + /** Flip a single random bit in a byte array */ static void testrand_flip(unsigned char *b, size_t len); diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 58e71e10..95b76160 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -1,5 +1,5 @@ /*********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ @@ -10,6 +10,7 @@ #include #include #include +#include #include "testrand.h" #include "hash.h" @@ -118,6 +119,23 @@ static void testrand256_test(unsigned char *b32) { testrand_bytes_test(b32, 32); } +SECP256K1_INLINE static int64_t testrandi64(uint64_t min, uint64_t max) { + uint64_t range; + uint64_t r; + uint64_t clz; + VERIFY_CHECK(max >= min); + if (max == min) { + return min; + } + range = max - min; + clz = secp256k1_clz64_var(range); + do { + r = ((uint64_t)testrand32() << 32) | testrand32(); + r >>= clz; + } while (r > range); + return min + (int64_t)r; +} + static void testrand_flip(unsigned char *b, size_t len) { b[testrand_int(len)] ^= (1 << testrand_bits(3)); } diff --git a/src/tests.c b/src/tests.c index 862bef61..bb0bb190 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,5 +1,5 @@ /*********************************************************************** - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ @@ -88,6 +88,52 @@ static void counting_callback_fn(const char* str, void* data) { (*p)++; } +static void run_util_tests(void) { + int i; + uint64_t r; + uint64_t r2; + uint64_t r3; + int64_t s; + CHECK(secp256k1_clz64_var(0) == 64); + CHECK(secp256k1_clz64_var(1) == 63); + CHECK(secp256k1_clz64_var(2) == 62); + CHECK(secp256k1_clz64_var(3) == 62); + CHECK(secp256k1_clz64_var(~0ULL) == 0); + CHECK(secp256k1_clz64_var((~0ULL) - 1) == 0); + CHECK(secp256k1_clz64_var((~0ULL) >> 1) == 1); + CHECK(secp256k1_clz64_var((~0ULL) >> 2) == 2); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MAX) == 0); + CHECK(r == INT64_MAX); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MAX - 1) == 0); + CHECK(r == INT64_MAX - 1); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MIN) == 1); + CHECK(r == (uint64_t)INT64_MAX + 1); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MIN + 1) == 1); + CHECK(r == (uint64_t)INT64_MAX); + CHECK(secp256k1_sign_and_abs64(&r, 0) == 0); + CHECK(r == 0); + CHECK(secp256k1_sign_and_abs64(&r, 1) == 0); + CHECK(r == 1); + CHECK(secp256k1_sign_and_abs64(&r, -1) == 1); + CHECK(r == 1); + CHECK(secp256k1_sign_and_abs64(&r, 2) == 0); + CHECK(r == 2); + CHECK(secp256k1_sign_and_abs64(&r, -2) == 1); + CHECK(r == 2); + for (i = 0; i < 10; i++) { + CHECK(secp256k1_clz64_var((~0ULL) - testrand32()) == 0); + r = ((uint64_t)testrand32() << 32) | testrand32(); + r2 = testrandi64(0, r); + CHECK(r2 <= r); + r3 = testrandi64(r2, r); + CHECK((r3 >= r2) && (r3 <= r)); + r = testrandi64(0, INT64_MAX); + s = (int64_t)r * (testrand32()&1?-1:1); + CHECK(secp256k1_sign_and_abs64(&r2, s) == (s < 0)); + CHECK(r2 == r); + } +} + static void run_xoshiro256pp_tests(void) { { size_t i; @@ -2305,6 +2351,14 @@ static void scalar_test(void) { CHECK(secp256k1_scalar_eq(&r1, &r2)); } + { + /* Test square. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_sqr(&r1, &s1); + secp256k1_scalar_mul(&r2, &s1, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + { /* Test multiplicative identity. */ secp256k1_scalar r1; @@ -3034,6 +3088,8 @@ static void run_scalar_tests(void) { CHECK(secp256k1_scalar_eq(&secp256k1_scalar_one, &zz)); } secp256k1_scalar_mul(&z, &x, &x); + secp256k1_scalar_sqr(&zz, &x); + CHECK(secp256k1_scalar_eq(&zz, &z)); CHECK(secp256k1_scalar_eq(&r2, &z)); } } @@ -4330,38 +4386,144 @@ static void run_ec_combine(void) { } } +static void test_ec_commit(void) { + secp256k1_scalar seckey_s; + secp256k1_ge pubkey; + secp256k1_gej pubkeyj; + secp256k1_ge commitment; + unsigned char data[32]; + secp256k1_sha256 sha; + + /* Create random keypair and data */ + testutil_random_scalar_order_test(&seckey_s); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubkeyj, &seckey_s); + secp256k1_ge_set_gej(&pubkey, &pubkeyj); + testrand256_test(data); + + /* Commit to data and verify */ + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit(&commitment, &pubkey, &sha, data, 32) == 1); + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_verify(&commitment, &pubkey, &sha, data, 32) == 1); + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_seckey(&seckey_s, &pubkey, &sha, data, 32) == 1); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubkeyj, &seckey_s); + secp256k1_gej_eq_ge_var(&pubkeyj, &commitment); + + /* Check that verification fails with different data */ + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_verify(&commitment, &pubkey, &sha, data, 31) == 0); + + /* Check that commmitting fails when the inner pubkey is the point at + * infinity */ + secp256k1_sha256_initialize(&sha); + secp256k1_ge_set_infinity(&pubkey); + CHECK(secp256k1_ec_commit(&commitment, &pubkey, &sha, data, 32) == 0); + secp256k1_scalar_set_int(&seckey_s, 0); + CHECK(secp256k1_ec_commit_seckey(&seckey_s, &pubkey, &sha, data, 32) == 0); + CHECK(secp256k1_ec_commit_verify(&commitment, &pubkey, &sha, data, 32) == 0); +} + +static void test_ec_commit_api(void) { + unsigned char seckey[32]; + secp256k1_scalar seckey_s; + secp256k1_ge pubkey; + secp256k1_gej pubkeyj; + secp256k1_ge commitment; + unsigned char data[32]; + secp256k1_sha256 sha; + + memset(data, 23, sizeof(data)); + + /* Create random keypair */ + testutil_random_scalar_order_test(&seckey_s); + secp256k1_scalar_get_b32(seckey, &seckey_s); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubkeyj, &seckey_s); + secp256k1_ge_set_gej(&pubkey, &pubkeyj); + + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit(&commitment, &pubkey, &sha, data, 1) == 1); + /* The same pubkey can be both input and output of the function */ + { + secp256k1_ge pubkey_tmp = pubkey; + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit(&pubkey_tmp, &pubkey_tmp, &sha, data, 1) == 1); + secp256k1_ge_eq_var(&commitment, &pubkey_tmp); + } + + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_verify(&commitment, &pubkey, &sha, data, 1) == 1); +} + +static void run_ec_commit(void) { + int i; + for (i = 0; i < COUNT * 8; i++) { + test_ec_commit(); + } + test_ec_commit_api(); +} + static void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; - /* Results of set_xo_var(..., 0), set_xo_var(..., 1). */ - secp256k1_ge ge_even, ge_odd; + secp256k1_fe fez; + /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ + secp256k1_ge ge_quad, ge_even, ge_odd; + secp256k1_gej gej_quad; /* Return values of the above calls. */ - int res_even, res_odd; + int res_quad, res_even, res_odd; secp256k1_fe_normalize_var(&fex); + res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); - CHECK(res_even == res_odd); + CHECK(res_quad == res_even); + CHECK(res_quad == res_odd); - if (res_even) { + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); secp256k1_fe_normalize_var(&ge_odd.x); secp256k1_fe_normalize_var(&ge_even.x); + secp256k1_fe_normalize_var(&ge_quad.y); secp256k1_fe_normalize_var(&ge_odd.y); secp256k1_fe_normalize_var(&ge_even.y); /* No infinity allowed. */ + CHECK(!secp256k1_ge_is_infinity(&ge_quad)); CHECK(!secp256k1_ge_is_infinity(&ge_even)); CHECK(!secp256k1_ge_is_infinity(&ge_odd)); /* Check that the x coordinates check out. */ + CHECK(secp256k1_fe_equal(&ge_quad.x, x)); CHECK(secp256k1_fe_equal(&ge_even.x, x)); CHECK(secp256k1_fe_equal(&ge_odd.x, x)); + /* Check that the Y coordinate result in ge_quad is a square. */ + CHECK(secp256k1_fe_is_square_var(&ge_quad.y)); + /* Check odd/even Y in ge_odd, ge_even. */ CHECK(secp256k1_fe_is_odd(&ge_odd.y)); CHECK(!secp256k1_fe_is_odd(&ge_even.y)); + + /* Check secp256k1_gej_has_quad_y_var. */ + secp256k1_gej_set_ge(&gej_quad, &ge_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + testutil_random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + testutil_random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); } } @@ -7671,6 +7833,14 @@ static void run_ecdsa_wycheproof(void) { test_ecdsa_wycheproof(); } +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG +# include "modules/schnorrsig_halfagg/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_BPPP +# include "modules/bppp/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/tests_impl.h" #endif @@ -7679,6 +7849,22 @@ static void run_ecdsa_wycheproof(void) { # include "modules/recovery/tests_impl.h" #endif +#ifdef ENABLE_MODULE_GENERATOR +# include "modules/generator/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_RANGEPROOF +# include "modules/rangeproof/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_WHITELIST +# include "modules/whitelist/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_SURJECTIONPROOF +# include "modules/surjection/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_EXTRAKEYS # include "modules/extrakeys/tests_impl.h" #endif @@ -7695,6 +7881,14 @@ static void run_ecdsa_wycheproof(void) { # include "modules/ellswift/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "modules/ecdsa_s2c/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +# include "modules/ecdsa_adaptor/tests_impl.h" +#endif + static void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -8032,6 +8226,31 @@ static const struct tf_test_module registry_modules[] = { #endif #ifdef ENABLE_MODULE_ELLSWIFT MAKE_TEST_MODULE(ellswift), +#endif + /* --- ZKP-SPECIFIC MODULES --- */ +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG + MAKE_TEST_MODULE(schnorrsig_halfagg), +#endif +#ifdef ENABLE_MODULE_BPPP + MAKE_TEST_MODULE(bppp), +#endif +#ifdef ENABLE_MODULE_GENERATOR + MAKE_TEST_MODULE(generator), +#endif +#ifdef ENABLE_MODULE_RANGEPROOF + MAKE_TEST_MODULE(rangeproof), +#endif +#ifdef ENABLE_MODULE_WHITELIST + MAKE_TEST_MODULE(whitelist), +#endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF + MAKE_TEST_MODULE(surjection), +#endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + MAKE_TEST_MODULE(ecdsa_adaptor), +#endif +#ifdef ENABLE_MODULE_ECDSA_S2C + MAKE_TEST_MODULE(ecdsa_s2c), #endif MAKE_TEST_MODULE(utils), }; diff --git a/src/unit_test.h b/src/unit_test.h index 5259efbf..6f67204d 100644 --- a/src/unit_test.h +++ b/src/unit_test.h @@ -15,7 +15,7 @@ /* Maximum number of command-line arguments. * Must be at least as large as the total number of tests * to allow specifying all tests individually. */ -#define MAX_ARGS 150 +#define MAX_ARGS 200 /* Maximum number of parallel jobs */ #define MAX_SUBPROCESSES 16 diff --git a/src/util.h b/src/util.h index 5d03e4c7..712e0fe9 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ /*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ @@ -181,6 +181,32 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ #define ROUND_TO_ALIGN(size) (CEIL_DIV(size, ALIGNMENT) * ALIGNMENT) +/* Extract the sign of an int64, take the abs and return a uint64, constant time. */ +SECP256K1_INLINE static int secp256k1_sign_and_abs64(uint64_t *out, int64_t in) { + uint64_t mask0, mask1; + int ret; + ret = in < 0; + mask0 = ret + ~((uint64_t)0); + mask1 = ~mask0; + *out = (uint64_t)in; + *out = (*out & mask0) | ((~*out + 1) & mask1); + return ret; +} + +SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { + int ret; + if (!x) { + return 64; + } +# if defined(HAVE_BUILTIN_CLZLL) + ret = __builtin_clzll(x); +# else + /*FIXME: debruijn fallback. */ + for (ret = 0; ((x & (1ULL << 63)) == 0); x <<= 1, ret++); +# endif + return ret; +} + #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) /* Macro for restrict, when available and not in a VERIFY build. */