diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3371fe31f5..6134437fc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,7 @@ env: EXTRAKEYS: 'no' SCHNORRSIG: 'no' MUSIG: 'no' + FULLAGG: 'no' ELLSWIFT: 'no' SILENTPAYMENTS: 'no' ### test options @@ -97,14 +98,14 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } - env_vars: { WIDEMUL: 'int128' } - 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', SILENTPAYMENTS: 'yes' } + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: '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', SILENTPAYMENTS: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', FULLAGG: 'yes' } + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } @@ -159,6 +160,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -188,6 +191,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -219,6 +224,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -241,6 +248,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -282,6 +291,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -328,6 +339,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -358,6 +371,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -405,6 +420,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -431,6 +448,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' @@ -466,14 +485,14 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: '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', SILENTPAYMENTS: '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', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: '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', SILENTPAYMENTS: 'yes', FULLAGG: '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' } - BUILD: 'distcheck' @@ -523,13 +542,13 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', SILENTPAYMENTS: 'yes', CPPFLAGS: '-DVERIFY' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', SILENTPAYMENTS: 'yes', FULLAGG: 'yes', CPPFLAGS: '-DVERIFY' } - BUILD: 'distcheck' steps: @@ -639,6 +658,8 @@ jobs: RECOVERY: 'yes' EXTRAKEYS: 'yes' SCHNORRSIG: 'yes' + EXPERIMENTAL: 'yes' + FULLAGG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' SILENTPAYMENTS: 'yes' diff --git a/.gitignore b/.gitignore index 60d9464e01..1b23b9244d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ schnorr_example ellswift_example musig_example silentpayments_example +fullagg_example *.exe *.so *.a diff --git a/CMakeLists.txt b/CMakeLists.txt index 7268270025..e8a5d73c3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." O option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON) +option(SECP256K1_ENABLE_MODULE_FULLAGG "Enable fullagg module." OFF) option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) option(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS "Enable Silent Payments module." ON) @@ -118,6 +119,9 @@ if(NOT SECP256K1_EXPERIMENTAL) if(SECP256K1_ASM STREQUAL "arm32") message(FATAL_ERROR "ARM32 assembly is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") endif() + if(SECP256K1_ENABLE_MODULE_FULLAGG) + message(FATAL_ERROR "Full aggregation is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") + endif() endif() set(SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]") @@ -289,6 +293,7 @@ message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOV message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}") +message(" fullagg ............................. ${SECP256K1_ENABLE_MODULE_FULLAGG}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") message(" Silent Payments ..................... ${SECP256K1_ENABLE_MODULE_SILENTPAYMENTS}") message("Parameters:") diff --git a/Makefile.am b/Makefile.am index 02588edc1a..1280b39513 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,6 +70,8 @@ noinst_HEADERS += src/bench.h noinst_HEADERS += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h noinst_HEADERS += src/hsort.h noinst_HEADERS += src/hsort_impl.h +noinst_HEADERS += src/modules/nonce_common.h +noinst_HEADERS += src/modules/nonce_common_impl.h noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h @@ -221,6 +223,18 @@ silentpayments_example_LDFLAGS += -lbcrypt endif TESTS += silentpayments_example endif + +if ENABLE_MODULE_FULLAGG +noinst_PROGRAMS += fullagg_example +fullagg_example_SOURCES = examples/fullagg.c +fullagg_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC +fullagg_example_LDADD = libsecp256k1.la +fullagg_example_LDFLAGS = -static +if BUILD_WINDOWS +fullagg_example_LDFLAGS += -lbcrypt +endif +TESTS += fullagg_example +endif endif ### Precomputed tables @@ -296,7 +310,7 @@ maintainer-clean-local: clean-testvectors ### Additional files to distribute EXTRA_DIST = autogen.sh CHANGELOG.md SECURITY.md EXTRA_DIST += doc/release-process.md doc/safegcd_implementation.md -EXTRA_DIST += doc/ellswift.md doc/musig.md +EXTRA_DIST += doc/ellswift.md doc/musig.md doc/fullagg.md EXTRA_DIST += examples/EXAMPLES_COPYING EXTRA_DIST += sage/gen_exhaustive_groups.sage EXTRA_DIST += sage/gen_split_lambda_constants.sage @@ -332,6 +346,10 @@ if ENABLE_MODULE_MUSIG include src/modules/musig/Makefile.am.include endif +if ENABLE_MODULE_FULLAGG +include src/modules/fullagg/Makefile.am.include +endif + if ENABLE_MODULE_ELLSWIFT include src/modules/ellswift/Makefile.am.include endif diff --git a/README.md b/README.md index fc64d40e7e..7998d87bdb 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Features: * Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). * Optional module for MuSig2 Schnorr multi-signatures according to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). * Optional module for Silent Payments sending and receiving according to [BIP-352](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki). +* Optional module for full aggregation of signatures according to [BIP-459](https://github.com/bitcoin/bips/blob/master/bip-0459.mediawiki). Implementation details ---------------------- @@ -153,6 +154,7 @@ Usage examples can be found in the [examples](examples) directory. To compile th * [ElligatorSwift key exchange example](examples/ellswift.c) * [MuSig2 Schnorr multi-signatures example](examples/musig.c) * [Silent Payments send and receive example](examples/silentpayments.c) + * [Full aggregation of signatures example](examples/fullagg.c) To compile the examples, make sure the corresponding modules are enabled. diff --git a/ci/ci.sh b/ci/ci.sh index d948c638d8..10f8b437be 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -13,7 +13,7 @@ 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 SILENTPAYMENTS \ + EXPERIMENTAL ECDH RECOVERY EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT SILENTPAYMENTS FULLAGG \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ @@ -65,6 +65,7 @@ fi --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-module-musig="$MUSIG" \ --enable-module-silentpayments="$SILENTPAYMENTS" \ + --enable-module-fullagg="$FULLAGG" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ diff --git a/configure.ac b/configure.ac index 1c6f103655..5e5d345f0f 100644 --- a/configure.ac +++ b/configure.ac @@ -189,6 +189,10 @@ 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_fullagg, + AS_HELP_STRING([--enable-module-fullagg],[enable fullagg module (experimental) [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_fullagg], [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])]) @@ -411,6 +415,14 @@ if test x"$enable_module_silentpayments" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SILENTPAYMENTS=1" fi +if test x"$enable_module_fullagg" = x"yes"; then + if test x"$enable_module_extrakeys" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the fullagg module.]) + fi + enable_module_extrakeys=yes + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_FULLAGG=1" +fi + if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi @@ -455,6 +467,10 @@ if test x"$enable_experimental" = x"no"; then if test x"$set_asm" = x"arm32"; then AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.]) fi + + if test x"$enable_module_fullagg" = x"yes"; then + AC_MSG_ERROR([Full aggregation is experimental. Use --enable-experimental to allow.]) + fi fi # Check for concurrency support (tests only) @@ -482,6 +498,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_FULLAGG], [test x"$enable_module_fullagg" = 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_SILENTPAYMENTS], [test x"$enable_module_silentpayments" = x"yes"]) @@ -496,35 +513,36 @@ AC_OUTPUT echo echo "Build Options:" -echo " with external callbacks = $enable_external_default_callbacks" -echo " with benchmarks = $enable_benchmark" -echo " with tests = $enable_tests" -echo " with exhaustive tests = $enable_exhaustive_tests" -echo " with ctime tests = $enable_ctime_tests" -echo " with coverage = $enable_coverage" -echo " with examples = $enable_examples" -echo " module ecdh = $enable_module_ecdh" -echo " module recovery = $enable_module_recovery" -echo " module extrakeys = $enable_module_extrakeys" -echo " module schnorrsig = $enable_module_schnorrsig" -echo " module musig = $enable_module_musig" -echo " module ellswift = $enable_module_ellswift" -echo " module silentpayments = $enable_module_silentpayments" +echo " with external callbacks = $enable_external_default_callbacks" +echo " with benchmarks = $enable_benchmark" +echo " with tests = $enable_tests" +echo " with exhaustive tests = $enable_exhaustive_tests" +echo " with ctime tests = $enable_ctime_tests" +echo " with coverage = $enable_coverage" +echo " with examples = $enable_examples" +echo " module ecdh = $enable_module_ecdh" +echo " module recovery = $enable_module_recovery" +echo " module extrakeys = $enable_module_extrakeys" +echo " module schnorrsig = $enable_module_schnorrsig" +echo " module fullagg = $enable_module_fullagg" +echo " module musig = $enable_module_musig" +echo " module ellswift = $enable_module_ellswift" +echo " module silentpayments = $enable_module_silentpayments" echo -echo " asm = $set_asm" -echo " ecmult window size = $set_ecmult_window" -echo " ecmult gen table size = $set_ecmult_gen_kb KiB" +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" -echo " CC = $CC" -echo " CPPFLAGS = $CPPFLAGS" -echo " SECP_CFLAGS = $SECP_CFLAGS" -echo " CFLAGS = $CFLAGS" -echo " LDFLAGS = $LDFLAGS" +echo " valgrind = $enable_valgrind" +echo " CC = $CC" +echo " CPPFLAGS = $CPPFLAGS" +echo " SECP_CFLAGS = $SECP_CFLAGS" +echo " CFLAGS = $CFLAGS" +echo " LDFLAGS = $LDFLAGS" if test x"$print_msan_notice" = x"yes"; then echo diff --git a/doc/fullagg.md b/doc/fullagg.md new file mode 100644 index 0000000000..e2e248a379 --- /dev/null +++ b/doc/fullagg.md @@ -0,0 +1,54 @@ +Notes on the fullagg module API +=============================== + +The following sections contain additional notes on the API of the fullagg module (`include/secp256k1_fullagg.h`). +A usage example can be found in `examples/fullagg.c`. + +## API misuse + +The fullagg API is designed with a focus on misuse resistance. +However, due to the interactive nature of the signing protocol, there are additional failure modes that are not present in regular (single-party) signature creation. +While the results can be catastrophic (e.g. leaking of the secret key), it is unfortunately not possible for the fullagg implementation to prevent all such failure modes. + +Therefore, users of the fullagg module must take great care to make sure of the following: + +1. A unique nonce per signing session is generated in `secp256k1_fullagg_nonce_gen`. + See the corresponding comment in `include/secp256k1_fullagg.h` for how to ensure that. +2. The `secp256k1_fullagg_secnonce` structure is never copied or serialized. + See also the comment on `secp256k1_fullagg_secnonce` in `include/secp256k1_fullagg.h`. +3. Opaque data structures are never written to or read from directly. + Instead, only the provided accessor functions are used. +4. The session is initialized with `secp256k1_fullagg_session_init` by the signer itself, using the lists of public keys, messages, and public nonces it received. + A session obtained from an untrusted third party such as the coordinator must not be used for signing. + +## Signing + +Each signer signs their own message under their own public key; there is no aggregate public key. +Essentially, the protocol proceeds in the following steps: + +1. Generate a keypair with `secp256k1_keypair_create` and obtain the x-only public key with `secp256k1_keypair_xonly_pub`. +2. Generate a pair of secret and public nonce with `secp256k1_fullagg_nonce_gen` and send the public nonce to the coordinator. +3. The coordinator aggregates the public nonces of all signers with `secp256k1_fullagg_nonce_agg` and sends the aggregate nonce back to the signers, along with the ordered lists of all public keys, messages, and public nonces. +4. Initialize the session with `secp256k1_fullagg_session_init`. +5. Create a partial signature with `secp256k1_fullagg_partial_sign`. +6. Verify the partial signatures (optional in some scenarios) with `secp256k1_fullagg_partial_sig_verify`. +7. The coordinator obtains all partial signatures and aggregates them into the final signature using `secp256k1_fullagg_partial_sig_agg`. + +The aggregate signature can be verified with `secp256k1_fullagg_verify`. +The signature depends on the order of the lists of public keys, messages, and public nonces, so all signers, the coordinator, and the verifier must use the same order. + +Steps 1 and 2 above can occur as a preprocessing step before the messages or the set of signers are known. +However, applications that use this method presumably store the nonces for a longer time and must be even more careful not to reuse them. + +## Tweaking + +The scheme supports additive tweaking of individual key pairs without changes to the verification algorithm. +Since the keys are individual keys rather than an aggregate, the existing key APIs are used and no fullagg-specific functions are needed. +A plain tweak as used for BIP 32 unhardened derivation can be applied to the secret key with `secp256k1_ec_seckey_tweak_add`. +A Taproot tweak as defined in BIP 341 can be applied to the keypair with `secp256k1_keypair_xonly_tweak_add`. +The tweaked x-only public key appears in the public key list and the signer uses the tweaked keypair in `secp256k1_fullagg_partial_sign`. + +## 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 participant skips steps 1, 2, 4, 5 and 7. +A valid partial signature does not prove that its signer knows the corresponding secret key, so partial signature verification only serves to identify disruptive signers, and only if the public nonces were received over authenticated channels. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 83547ee2cf..64af6331bd 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -35,3 +35,7 @@ endif() if(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS) add_example(silentpayments) endif() + +if(SECP256K1_ENABLE_MODULE_FULLAGG) + add_example(fullagg) +endif() diff --git a/examples/fullagg.c b/examples/fullagg.c new file mode 100644 index 0000000000..1b1d3c0d95 --- /dev/null +++ b/examples/fullagg.c @@ -0,0 +1,208 @@ +/************************************************************************* + * Written in 2025 by Fabian Jahr * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +/** This file demonstrates how to use the fullagg module to create an + * aggregate signature for three signers who each sign their own message. + * Additionally, see the documentation in + * include/secp256k1_fullagg.h and doc/fullagg.md. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "examples_util.h" + +#define N_SIGNERS 3 + +struct signer_secrets { + secp256k1_keypair keypair; + secp256k1_fullagg_secnonce secnonce; +}; + +struct signer { + secp256k1_xonly_pubkey pubkey; + secp256k1_fullagg_pubnonce pubnonce; + secp256k1_fullagg_partial_sig partial_sig; + unsigned char message[32]; +}; + +/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */ +static int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) { + unsigned char seckey[32]; + + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 0; + } + /* Try to create a keypair with a valid context. This only fails if the + * secret key is zero or out of range (greater than secp256k1's order). Note + * that the probability of this occurring is negligible with a properly + * functioning random number generator. */ + if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { + return 0; + } + if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) { + return 0; + } + + secure_erase(seckey, sizeof(seckey)); + return 1; +} + +/* Every signer signs their own message */ +static void setup_messages(struct signer *signers) { + memset(signers[0].message, 0, 32); + memset(signers[1].message, 0, 32); + memset(signers[2].message, 0, 32); + memcpy(signers[0].message, "jonas", 5); + memcpy(signers[1].message, "tim", 3); + memcpy(signers[2].message, "yannick", 7); +} + +/* Sign the messages of all signers and store the aggregate signature in sig64 */ +static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signers, const secp256k1_xonly_pubkey **pubkeys, const unsigned char **messages, unsigned char *sig64) { + int i; + const secp256k1_fullagg_pubnonce *pubnonces[N_SIGNERS]; + const secp256k1_fullagg_partial_sig *partial_sigs[N_SIGNERS]; + /* The same for all signers */ + secp256k1_fullagg_session session; + secp256k1_fullagg_aggnonce agg_pubnonce; + + for (i = 0; i < N_SIGNERS; i++) { + unsigned char seckey[32]; + unsigned char session_secrand[32]; + /* Create random session ID. It is absolutely necessary that the session ID + * is unique for every call of secp256k1_fullagg_nonce_gen. Otherwise + * it's trivial for an attacker to extract the secret key! */ + if (!fill_random(session_secrand, sizeof(session_secrand))) { + return 0; + } + if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) { + return 0; + } + /* Initialize session and create secret nonce for signing and public + * nonce to send to the coordinator. */ + if (!secp256k1_fullagg_nonce_gen(ctx, &signer_secrets[i].secnonce, &signers[i].pubnonce, session_secrand, seckey, &signers[i].pubkey, NULL)) { + return 0; + } + pubnonces[i] = &signers[i].pubnonce; + + secure_erase(seckey, sizeof(seckey)); + } + + /* Communication round 1: Every signer sends their pubnonce to the + * coordinator. The coordinator runs secp256k1_fullagg_nonce_agg and sends + * agg_pubnonce back to each signer, along with the ordered lists of all + * public keys, messages and pubnonces. */ + if (!secp256k1_fullagg_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) { + return 0; + } + + /* Every signer creates a partial signature for their own message */ + for (i = 0; i < N_SIGNERS; i++) { + /* Initialize the signing session by processing the aggregate nonce. + * Every signer computes the session itself and must not use session + * data from an untrusted third party. */ + if (!secp256k1_fullagg_session_init(ctx, &session, &agg_pubnonce, pubkeys, messages, pubnonces, N_SIGNERS)) { + return 0; + } + /* partial_sign will clear the secnonce by setting it to 0. That's because + * you must _never_ reuse the secnonce (or use the same session_secrand to + * create a secnonce). If you do, you effectively reuse the nonce and + * leak the secret key. */ + if (!secp256k1_fullagg_partial_sign(ctx, &signers[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, signers[i].message, &session, pubkeys, messages, pubnonces, N_SIGNERS, i)) { + return 0; + } + partial_sigs[i] = &signers[i].partial_sig; + } + /* Communication round 2: Every signer sends their partial signature to the + * coordinator, who verifies the partial signatures and aggregates them. */ + for (i = 0; i < N_SIGNERS; i++) { + /* To check whether signing was successful, it suffices to either verify + * the aggregate signature with secp256k1_fullagg_verify, or verify all + * partial signatures of all signers individually. Verifying the + * aggregate signature is cheaper but verifying the individual partial + * signatures has the advantage that it can be used to determine which + * of the partial signatures are invalid (if any), i.e., which of the + * partial signatures cause the aggregate signature to be invalid and + * thus the protocol run to fail. It's also fine to first verify the + * aggregate sig, and only verify the individual sigs if it does not + * work. + */ + if (!secp256k1_fullagg_partial_sig_verify(ctx, &signers[i].partial_sig, pubnonces, pubkeys, messages, N_SIGNERS, i)) { + return 0; + } + } + return secp256k1_fullagg_partial_sig_agg(ctx, sig64, &session, partial_sigs, N_SIGNERS); +} + +int main(void) { + secp256k1_context* ctx; + int i; + struct signer_secrets signer_secrets[N_SIGNERS]; + struct signer signers[N_SIGNERS]; + const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; + const unsigned char *messages_ptr[N_SIGNERS]; + unsigned char sig[64]; + + /* Create a secp256k1 context */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + printf("Creating key pairs......"); + fflush(stdout); + for (i = 0; i < N_SIGNERS; i++) { + if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) { + printf("FAILED\n"); + return EXIT_FAILURE; + } + pubkeys_ptr[i] = &signers[i].pubkey; + } + printf("ok\n"); + + /* The aggregate signature and the challenges depend on the order of the + * lists of public keys, messages and pubnonces. All signers, the + * coordinator and the verifier must use the same order. */ + setup_messages(signers); + for (i = 0; i < N_SIGNERS; i++) { + messages_ptr[i] = signers[i].message; + } + + printf("Signing messages........"); + fflush(stdout); + if (!sign(ctx, signer_secrets, signers, pubkeys_ptr, messages_ptr, sig)) { + printf("FAILED\n"); + return EXIT_FAILURE; + } + printf("ok\n"); + printf("Verifying signature....."); + fflush(stdout); + if (!secp256k1_fullagg_verify(ctx, sig, pubkeys_ptr, messages_ptr, N_SIGNERS)) { + printf("FAILED\n"); + return EXIT_FAILURE; + } + printf("ok\n"); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), or the OS + * swapping them to disk. Hence, we overwrite secret key material with zeros. + * + * Here we are preventing these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + for (i = 0; i < N_SIGNERS; i++) { + secure_erase(&signer_secrets[i], sizeof(signer_secrets[i])); + } + secp256k1_context_destroy(ctx); + return EXIT_SUCCESS; +} diff --git a/include/secp256k1_fullagg.h b/include/secp256k1_fullagg.h new file mode 100644 index 0000000000..aae750b162 --- /dev/null +++ b/include/secp256k1_fullagg.h @@ -0,0 +1,448 @@ +#ifndef SECP256K1_FULLAGG_H +#define SECP256K1_FULLAGG_H + +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** This module implements BIP 459 "DahLIAS fully aggregated signatures for + * secp256k1" (https://github.com/bitcoin/bips/blob/master/bip-0459.mediawiki), + * allowing multiple signers to each sign different messages and aggregate + * their signatures into a single signature. You can find an example + * demonstrating the fullagg module in examples/fullagg.c. + */ + +/** Opaque data structures + * + * The exact representation of data inside the opaque data structures is + * implementation defined and not guaranteed to be portable between different + * platforms or versions. With the exception of `secp256k1_fullagg_secnonce`, + * the data structures can be safely copied/moved. If you need to convert to a + * format suitable for storage, transmission, or comparison, use the + * corresponding serialization and parsing functions. + */ + +/** Opaque data structure that holds a signer's _secret_ nonce (r1_i and r2_i). + * + * Guaranteed to be 132 bytes in size. + * + * WARNING: This structure MUST NOT be copied or read or written to directly. + * A signer who is online throughout the whole process and can keep this + * structure in memory can use the provided API functions for a safe standard + * workflow. + * + * Copying this data structure can result in nonce reuse which will leak the + * secret signing key. + */ +typedef struct secp256k1_fullagg_secnonce { + unsigned char data[132]; +} secp256k1_fullagg_secnonce; + +/** Opaque data structure that holds a signer's public nonce (R1_i and R2_i). + * + * Guaranteed to be 132 bytes in size. Serialized and parsed with + * `fullagg_pubnonce_serialize` and `fullagg_pubnonce_parse`. + */ +typedef struct secp256k1_fullagg_pubnonce { + unsigned char data[132]; +} secp256k1_fullagg_pubnonce; + +/** Opaque data structure that holds an aggregate public nonce (aggregated R1 and R2). + * + * Guaranteed to be 132 bytes in size. Serialized and parsed with + * `fullagg_aggnonce_serialize` and `fullagg_aggnonce_parse`. + */ +typedef struct secp256k1_fullagg_aggnonce { + unsigned char data[132]; +} secp256k1_fullagg_aggnonce; + +/** Opaque data structure that holds a FullAgg session. + * + * This structure contains the computed values needed for signing: + * the final nonce R, the nonce coefficient b, the aggregate nonce, and the + * number of signers. + * + * Guaranteed to be 143 bytes in size. + */ +typedef struct secp256k1_fullagg_session { + unsigned char data[143]; +} secp256k1_fullagg_session; + +/** Opaque data structure that holds a partial FullAgg signature (s_i). + * + * Guaranteed to be 36 bytes in size. Serialized and parsed with + * `fullagg_partial_sig_serialize` and `fullagg_partial_sig_parse`. + */ +typedef struct secp256k1_fullagg_partial_sig { + unsigned char data[36]; +} secp256k1_fullagg_partial_sig; + +/** Parse a signer's public nonce. + * + * Returns: 1 when the nonce could be parsed, 0 otherwise. + * Args: ctx: pointer to a context object + * Out: nonce: pointer to a nonce object + * In: in66: pointer to the 66-byte nonce to be parsed + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_pubnonce_parse( + const secp256k1_context *ctx, + secp256k1_fullagg_pubnonce *nonce, + const unsigned char *in66 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a signer's public nonce + * + * Returns: 1 always + * Args: ctx: pointer to a context object + * Out: out66: pointer to a 66-byte array to store the serialized nonce + * In: nonce: pointer to the nonce + */ +SECP256K1_API int secp256k1_fullagg_pubnonce_serialize( + const secp256k1_context *ctx, + unsigned char *out66, + const secp256k1_fullagg_pubnonce *nonce +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse an aggregate public nonce. + * + * Returns: 1 when the nonce could be parsed, 0 otherwise. + * Args: ctx: pointer to a context object + * Out: nonce: pointer to a nonce object + * In: in66: pointer to the 66-byte nonce to be parsed + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_aggnonce_parse( + const secp256k1_context *ctx, + secp256k1_fullagg_aggnonce *nonce, + const unsigned char *in66 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an aggregate public nonce + * + * Returns: 1 always + * Args: ctx: pointer to a context object + * Out: out66: pointer to a 66-byte array to store the serialized nonce + * In: nonce: pointer to the nonce + */ +SECP256K1_API int secp256k1_fullagg_aggnonce_serialize( + const secp256k1_context *ctx, + unsigned char *out66, + const secp256k1_fullagg_aggnonce *nonce +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a FullAgg partial 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: in32: pointer to the 32-byte signature to be parsed + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_partial_sig_parse( + const secp256k1_context *ctx, + secp256k1_fullagg_partial_sig *sig, + const unsigned char *in32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a FullAgg partial signature + * + * Returns: 1 always + * Args: ctx: pointer to a context object + * Out: out32: pointer to a 32-byte array to store the serialized signature + * In: sig: pointer to the signature + */ +SECP256K1_API int secp256k1_fullagg_partial_sig_serialize( + const secp256k1_context *ctx, + unsigned char *out32, + const secp256k1_fullagg_partial_sig *sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Starts a signing session by generating a nonce + * + * This function outputs a secret nonce that will be required for signing and a + * corresponding public nonce that is intended to be sent to the coordinator. + * + * FullAgg differs from regular signing in that implementers _must_ take + * special care to not reuse a nonce. This can be ensured by following these rules: + * + * 1. Each call to this function must have a UNIQUE session_secrand32 that must + * NOT BE REUSED in subsequent calls to this function and must be KEPT + * SECRET (even from other signers). + * 2. If you already know the seckey, it can be optionally provided to derive + * the nonce and increase misuse-resistance. The extra_input32 argument can + * be used to provide additional data that does not repeat in normal + * scenarios, such as the current time. + * 3. Avoid copying (or serializing) the secnonce. This reduces the possibility + * that it is used more than once for signing. + * + * If you don't have access to good randomness for session_secrand32, but you + * have access to a non-repeating counter, then see + * secp256k1_fullagg_nonce_gen_counter. + * + * Remember that nonce reuse will leak the secret key! + * Note that using the same seckey for multiple FullAgg sessions is fine. + * + * Returns: 0 if the arguments are invalid and 1 otherwise + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: secnonce: pointer to a structure to store the secret nonce (r1_i, r2_i) + * pubnonce: pointer to a structure to store the public nonce (R1_i, R2_i) + * In/Out: + * session_secrand32: a 32-byte session_secrand32 as explained above. Must be unique to + * this call to secp256k1_fullagg_nonce_gen and must be + * uniformly random. If the function call is successful, the + * session_secrand32 buffer is invalidated to prevent reuse. + * In: + * seckey: the 32-byte secret key that will later be used for signing, if + * already known (can be NULL) + * pubkey: x-only public key of the signer creating the nonce. The + * secnonce output of this function cannot be used to sign + * for any other public key. While the public key should + * correspond to the provided seckey, a mismatch will not + * cause the function to return 0. + * extra_input32: an optional 32-byte array that is input to the nonce + * derivation function (can be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_nonce_gen( + const secp256k1_context *ctx, + secp256k1_fullagg_secnonce *secnonce, + secp256k1_fullagg_pubnonce *pubnonce, + unsigned char *session_secrand32, + const unsigned char *seckey, + const secp256k1_xonly_pubkey *pubkey, + const unsigned char *extra_input32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6); + +/** Alternative way to generate a nonce and start a signing session + * + * This function outputs a secret nonce that will be required for signing and a + * corresponding public nonce that is intended to be sent to the coordinator. + * + * This function differs from `secp256k1_fullagg_nonce_gen` by accepting a + * non-repeating counter value instead of a secret random value. This requires + * that a secret key is provided to `secp256k1_fullagg_nonce_gen_counter` + * (through the keypair argument). + * + * Returns: 0 if the arguments are invalid and 1 otherwise + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: secnonce: pointer to a structure to store the secret nonce + * pubnonce: pointer to a structure to store the public nonce + * In: + * nonrepeating_cnt: the value of a counter as explained above. Must be + * unique to this call to secp256k1_fullagg_nonce_gen_counter. + * keypair: keypair of the signer creating the nonce. The secnonce + * output of this function cannot be used to sign for any + * other keypair. + * extra_input32: an optional 32-byte array that is input to the nonce + * derivation function (can be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_nonce_gen_counter( + const secp256k1_context *ctx, + secp256k1_fullagg_secnonce *secnonce, + secp256k1_fullagg_pubnonce *pubnonce, + uint64_t nonrepeating_cnt, + const secp256k1_keypair *keypair, + const unsigned char *extra_input32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Aggregates the nonces of all signers into a single nonce + * + * This is done by the coordinator to compute the aggregate nonces R1 and R2 + * from all signers' individual nonces R1_i and R2_i. + * + * If the aggregator does not compute the aggregate nonce correctly, the final + * signature will be invalid. + * + * Returns: 0 if the arguments are invalid or if an aggregate nonce is the + * point at infinity, 1 otherwise. In the (cryptographically + * unreachable for honest signers) failure case of an aggregate nonce + * at infinity, the signers should restart the session with fresh + * nonces. + * Args: ctx: pointer to a context object + * Out: aggnonce: pointer to an aggregate public nonce object for + * fullagg_session_init (contains R1 and R2) + * In: pubnonces: array of pointers to public nonces sent by the + * signers (each containing R1_i and R2_i) + * n_pubnonces: number of elements in the pubnonces array. Must be + * greater than 0. + */ +SECP256K1_API int secp256k1_fullagg_nonce_agg( + const secp256k1_context *ctx, + secp256k1_fullagg_aggnonce *aggnonce, + const secp256k1_fullagg_pubnonce * const *pubnonces, + size_t n_pubnonces +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Initialize a FullAgg signing session + * + * Creates a session context that contains the computed values needed for + * signing and verification of partial signatures. This includes the final + * nonce R and the nonce coefficient b. The arrays of public keys, messages, + * and nonces must be provided again when signing or verifying. + * + * Returns: 0 if the arguments are invalid or if the final nonce is the point + * at infinity, 1 otherwise. In the (cryptographically unreachable + * for honest signers) failure case of a final nonce at infinity, the + * signers should restart the session with fresh nonces. + * Args: ctx: pointer to a context object + * Out: session: pointer to a struct to store the session + * In: aggnonce: pointer to an aggregate public nonce object that is the + * output of fullagg_nonce_agg + * pubkeys: array of pointers to the x-only public keys of all + * signers. The order must be consistent across all + * signers and the coordinator. + * messages: array of pointers to 32-byte messages, where messages[i] + * is the message to be signed by the signer with pubkeys[i] + * pubnonces: array of pointers to public nonces, where pubnonces[i] + * contains the R2_i value from the signer with pubkeys[i]. + * n_signers: number of signers (length of pubkeys, messages, and + * pubnonces arrays). Must be greater than 0. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_session_init( + const secp256k1_context *ctx, + secp256k1_fullagg_session *session, + const secp256k1_fullagg_aggnonce *aggnonce, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + const secp256k1_fullagg_pubnonce * const *pubnonces, + size_t n_signers +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +/** Produces a partial signature + * + * This function overwrites the given secnonce with zeros and will abort if given a + * secnonce that is all zeros. This is a best effort attempt to protect against nonce + * reuse. However, this is of course easily defeated if the secnonce has been + * copied (or serialized). Remember that nonce reuse will leak the secret key! + * + * For signing to succeed, the secnonce provided to this function must have + * been generated for the provided keypair. + * + * This function checks that the signer's public key and intended message + * appear at signer_index in the arrays and that the signer's R2_i nonce + * appears exactly once in the pubnonces array, at signer_index. It also + * verifies that the session was initialized with the same lists of public + * keys, messages, and public nonces by recomputing the nonce coefficient. + * + * Returns: 0 if the arguments are invalid or the provided secnonce has already + * been used for signing, 1 otherwise + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: partial_sig: pointer to struct to store the partial signature + * In/Out: secnonce: pointer to the secnonce struct created in + * fullagg_nonce_gen that has never been used in a + * partial_sign call before + * In: keypair: pointer to keypair to sign the message with + * msg32: the 32-byte message that the signer intends to sign. It + * must match the message at signer_index in the messages + * array. + * session: pointer to the session that was created with + * fullagg_session_init + * pubkeys: array of pointers to x-only public keys (same as in + * session_init) + * messages: array of pointers to messages (same as in session_init) + * pubnonces: array of pointers to public nonces (same as in session_init) + * n_signers: number of signers (length of the pubkeys, messages, and + * pubnonces arrays). Must be greater than 0 and equal to + * the number of signers in the session. + * signer_index: the index of this signer in the arrays (0-indexed) + */ +SECP256K1_API int secp256k1_fullagg_partial_sign( + const secp256k1_context *ctx, + secp256k1_fullagg_partial_sig *partial_sig, + secp256k1_fullagg_secnonce *secnonce, + const secp256k1_keypair *keypair, + const unsigned char *msg32, + const secp256k1_fullagg_session *session, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + const secp256k1_fullagg_pubnonce * const *pubnonces, + size_t n_signers, + size_t signer_index +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9); + +/** Verifies an individual signer's partial signature + * + * The aggregate nonce and the session values are recomputed from the + * pubnonces, so the result cannot be influenced by the coordinator. If the + * public nonces are received over authenticated channels and this function + * succeeds for the partial signatures of all signers, then partial_sig_agg + * produces a valid aggregate signature. + * + * It is not required to call this function in regular FullAgg sessions, + * because if any partial signature does not verify, the final signature + * will not verify either. However, this function provides the ability to + * identify which specific partial signature fails verification. + * + * Returns: 0 if the arguments are invalid or the partial signature does not + * verify, 1 otherwise + * Args ctx: pointer to a context object + * In: partial_sig: pointer to partial signature to verify + * pubnonces: array of pointers to public nonces (same as in + * session_init) + * pubkeys: array of pointers to x-only public keys (same as in + * session_init) + * messages: array of pointers to messages (same as in session_init) + * n_signers: number of signers (length of pubnonces, pubkeys, and + * messages arrays). Must be greater than 0. + * signer_index: the index of the signer being verified in the arrays + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_partial_sig_verify( + const secp256k1_context *ctx, + const secp256k1_fullagg_partial_sig *partial_sig, + const secp256k1_fullagg_pubnonce * const *pubnonces, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + size_t n_signers, + size_t signer_index +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Aggregates partial signatures + * + * Produces the final aggregated signature from all partial signatures. + * + * Returns: 0 if the arguments are invalid, 1 otherwise (which does NOT mean + * the resulting signature verifies). + * Args: ctx: pointer to a context object + * Out: sig64: complete (but possibly invalid) aggregate signature + * In: session: pointer to the session that was created with + * fullagg_session_init + * partial_sigs: array of pointers to partial signatures to aggregate + * n_sigs: number of elements in the partial_sigs array. Must be + * greater than 0 and equal to the number of signers. + */ +SECP256K1_API int secp256k1_fullagg_partial_sig_agg( + const secp256k1_context *ctx, + unsigned char *sig64, + const secp256k1_fullagg_session *session, + const secp256k1_fullagg_partial_sig * const *partial_sigs, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify a FullAgg aggregate signature + * + * Verifies that the signature is valid for the given list of public keys + * and their corresponding messages. + * + * Returns: 1 if the signature is valid, 0 otherwise + * Args: ctx: pointer to a context object + * In: sig64: pointer to the 64-byte signature to verify + * pubkeys: array of pointers to x-only public keys + * messages: array of pointers to 32-byte messages, where messages[i] + * corresponds to pubkeys[i] + * n_signers: number of signers (length of pubkeys and messages arrays) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_fullagg_verify( + const secp256k1_context *ctx, + const unsigned char *sig64, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + size_t n_signers +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 911804f8dc..29d7ac72c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,15 @@ if(SECP256K1_ENABLE_MODULE_SILENTPAYMENTS) set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_silentpayments.h) endif() +if(SECP256K1_ENABLE_MODULE_FULLAGG) + if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS) + message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the fullagg module.") + endif() + set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON) + add_compile_definitions(ENABLE_MODULE_FULLAGG=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_fullagg.h) +endif() + if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS) message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.") diff --git a/src/bench.c b/src/bench.c index ac2054a52e..56755b0d54 100644 --- a/src/bench.c +++ b/src/bench.c @@ -36,6 +36,10 @@ static void help(const char *executable_path, int default_iters) { printf(" - Silent payments (optional module)\n"); #endif +#ifdef ENABLE_MODULE_FULLAGG + printf(" - Full aggregation (optional module)\n"); +#endif + printf("\n"); printf("The default number of iterations for each benchmark is %d. This can be\n", default_iters); printf("customized using the SECP256K1_BENCH_ITERS environment variable.\n"); @@ -78,6 +82,12 @@ static void help(const char *executable_path, int default_iters) { printf(" silentpayments_scan_worstcase : Silent payments scanning worst case (block-sized tx, all match)\n"); #endif +#ifdef ENABLE_MODULE_FULLAGG + printf(" fullagg : all full aggregation algorithms (sign, verify)\n"); + printf(" fullagg_sign : full aggregation partial signing algorithm\n"); + printf(" fullagg_verify : full aggregation verification algorithm\n"); +#endif + printf("\n"); } @@ -184,6 +194,10 @@ static void bench_keygen_run(void *arg, int iters) { # include "modules/silentpayments/bench_impl.h" #endif +#ifdef ENABLE_MODULE_FULLAGG +# include "modules/fullagg/bench_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_pubkey pubkey; @@ -197,7 +211,8 @@ int main(int argc, char** argv) { "ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign", "ec", "keygen", "ec_keygen", "ellswift", "encode", "ellswift_encode", "decode", "ellswift_decode", "ellswift_keygen", "ellswift_ecdh", "silentpayments", - "silentpayments_scan_nomatch", "silentpayments_scan_worstcase"}; + "silentpayments_scan_nomatch", "silentpayments_scan_worstcase", "fullagg", + "fullagg_sign", "fullagg_verify"}; int invalid_args = have_invalid_args(argc, argv, valid_args, ARRAY_SIZE(valid_args)); int default_iters = 20000; @@ -264,6 +279,14 @@ int main(int argc, char** argv) { } #endif +#ifndef ENABLE_MODULE_FULLAGG + if (have_flag(argc, argv, "fullagg") || have_flag(argc, argv, "fullagg_sign") || have_flag(argc, argv, "fullagg_verify")) { + fprintf(stderr, "./bench: fullagg module not enabled.\n"); + fprintf(stderr, "See README.md for configuration instructions.\n\n"); + return EXIT_FAILURE; + } +#endif + /* ECDSA benchmark */ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -313,6 +336,10 @@ int main(int argc, char** argv) { run_silentpayments_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_FULLAGG + /* Full aggregation benchmarks */ + run_fullagg_bench(iters, argc, argv); +#endif return EXIT_SUCCESS; } diff --git a/src/ctime_tests.c b/src/ctime_tests.c index 0d201d20ad..c72f2b1a9e 100644 --- a/src/ctime_tests.c +++ b/src/ctime_tests.c @@ -36,6 +36,10 @@ #include "../include/secp256k1_musig.h" #endif +#ifdef ENABLE_MODULE_FULLAGG +#include "../include/secp256k1_fullagg.h" +#endif + #ifdef ENABLE_MODULE_ELLSWIFT #include "../include/secp256k1_ellswift.h" #endif @@ -268,6 +272,57 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { } #endif +#ifdef ENABLE_MODULE_FULLAGG + { + secp256k1_xonly_pubkey pk; + const secp256k1_xonly_pubkey *pk_ptr[1]; + const unsigned char *msg_ptr[1]; + unsigned char session_secrand[32]; + uint64_t nonrepeating_cnt = 0; + secp256k1_fullagg_secnonce secnonce; + secp256k1_fullagg_pubnonce pubnonce; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[1]; + secp256k1_fullagg_aggnonce aggnonce; + secp256k1_fullagg_session session; + secp256k1_fullagg_partial_sig partial_sig; + unsigned char extra_input[32]; + + pk_ptr[0] = &pk; + msg_ptr[0] = msg; + pubnonce_ptr[0] = &pubnonce; + SECP256K1_CHECKMEM_DEFINE(key, 32); + memcpy(session_secrand, key, sizeof(session_secrand)); + session_secrand[0] = session_secrand[0] + 1; + memcpy(extra_input, key, sizeof(extra_input)); + extra_input[0] = extra_input[0] + 2; + + CHECK(secp256k1_keypair_create(ctx, &keypair, key)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(session_secrand, sizeof(session_secrand)); + SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input)); + ret = secp256k1_fullagg_nonce_gen(ctx, &secnonce, &pubnonce, session_secrand, key, &pk, extra_input); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_fullagg_nonce_gen_counter(ctx, &secnonce, &pubnonce, nonrepeating_cnt, &keypair, extra_input); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + CHECK(secp256k1_fullagg_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_fullagg_session_init(ctx, &session, &aggnonce, pk_ptr, msg_ptr, pubnonce_ptr, 1) == 1); + + ret = secp256k1_keypair_create(ctx, &keypair, key); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_fullagg_partial_sign(ctx, &partial_sig, &secnonce, &keypair, msg, &session, pk_ptr, msg_ptr, pubnonce_ptr, 1, 0); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + } +#endif + #ifdef ENABLE_MODULE_ELLSWIFT SECP256K1_CHECKMEM_UNDEFINE(key, 32); ret = secp256k1_ellswift_create(ctx, ellswift, key, NULL); diff --git a/src/eckey.h b/src/eckey.h index c2bbc4703e..5b328a80c0 100644 --- a/src/eckey.h +++ b/src/eckey.h @@ -20,6 +20,9 @@ static void secp256k1_eckey_pubkey_serialize33(secp256k1_ge *elem, unsigned char /** Serialize a group element (that is not allowed to be infinity) to an uncompressed public key (65 bytes). */ static void secp256k1_eckey_pubkey_serialize65(secp256k1_ge *elem, unsigned char *pub65); +static void secp256k1_eckey_serialize_ext(unsigned char *out33, secp256k1_ge* ge); +static int secp256k1_eckey_parse_ext(secp256k1_ge* ge, const unsigned char *in33); + static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak); static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak); static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); diff --git a/src/eckey_impl.h b/src/eckey_impl.h index 57024e409d..30cf03914d 100644 --- a/src/eckey_impl.h +++ b/src/eckey_impl.h @@ -54,6 +54,32 @@ static void secp256k1_eckey_pubkey_serialize65(secp256k1_ge *elem, unsigned char secp256k1_fe_get_b32(&pub65[33], &elem->y); } +/* Outputs 33 zero bytes if the given group element is the point at infinity, + * otherwise outputs the compressed serialization */ +static void secp256k1_eckey_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { + if (secp256k1_ge_is_infinity(ge)) { + memset(out33, 0, 33); + } else { + /* Serialize must succeed because the point is not at infinity */ + 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_eckey_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; + } + if (!secp256k1_eckey_pubkey_parse(ge, in33, 33)) { + return 0; + } + return secp256k1_ge_is_in_correct_subgroup(ge); +} + static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) { secp256k1_scalar_add(key, key, tweak); return !secp256k1_scalar_is_zero(key); diff --git a/src/modules/fullagg/Makefile.am.include b/src/modules/fullagg/Makefile.am.include new file mode 100644 index 0000000000..29fe41c6b4 --- /dev/null +++ b/src/modules/fullagg/Makefile.am.include @@ -0,0 +1,5 @@ +include_HEADERS += include/secp256k1_fullagg.h +noinst_HEADERS += src/modules/fullagg/main_impl.h +noinst_HEADERS += src/modules/fullagg/tests_impl.h +noinst_HEADERS += src/modules/fullagg/vectors.h +noinst_HEADERS += src/modules/fullagg/bench_impl.h diff --git a/src/modules/fullagg/bench_impl.h b/src/modules/fullagg/bench_impl.h new file mode 100644 index 0000000000..d1af7f5b60 --- /dev/null +++ b/src/modules/fullagg/bench_impl.h @@ -0,0 +1,106 @@ +/*********************************************************************** + * Copyright (c) 2026 The libsecp256k1 developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FULLAGG_BENCH_H +#define SECP256K1_MODULE_FULLAGG_BENCH_H + +#include "../../../include/secp256k1_fullagg.h" + +/* Number of signers in the benchmarked signing session */ +#define FULLAGG_BENCH_N_SIGNERS 3 + +typedef struct { + secp256k1_context *ctx; + + secp256k1_keypair keypairs[FULLAGG_BENCH_N_SIGNERS]; + secp256k1_xonly_pubkey pks[FULLAGG_BENCH_N_SIGNERS]; + const secp256k1_xonly_pubkey *pk_ptrs[FULLAGG_BENCH_N_SIGNERS]; + unsigned char msgs[FULLAGG_BENCH_N_SIGNERS][32]; + const unsigned char *msg_ptrs[FULLAGG_BENCH_N_SIGNERS]; + secp256k1_fullagg_pubnonce pubnonces[FULLAGG_BENCH_N_SIGNERS]; + const secp256k1_fullagg_pubnonce *pubnonce_ptrs[FULLAGG_BENCH_N_SIGNERS]; + secp256k1_fullagg_secnonce secnonce; + secp256k1_fullagg_session session; + unsigned char sig[64]; +} bench_fullagg_data; + +static void bench_fullagg_sign(void* arg, int iters) { + bench_fullagg_data *data = (bench_fullagg_data *)arg; + int i; + secp256k1_fullagg_partial_sig partial_sig; + + for (i = 0; i < iters; i++) { + secp256k1_fullagg_secnonce secnonce; + /* The secnonce is copied because signing invalidates it and this + * benchmark signs repeatedly with the same session. Application code + * must never copy a secnonce (see secp256k1_fullagg_secnonce). */ + memcpy(&secnonce, &data->secnonce, sizeof(secnonce)); + CHECK(secp256k1_fullagg_partial_sign(data->ctx, &partial_sig, &secnonce, &data->keypairs[0], + data->msgs[0], &data->session, data->pk_ptrs, + data->msg_ptrs, data->pubnonce_ptrs, + FULLAGG_BENCH_N_SIGNERS, 0)); + } +} + +static void bench_fullagg_verify(void* arg, int iters) { + bench_fullagg_data *data = (bench_fullagg_data *)arg; + int i; + + for (i = 0; i < iters; i++) { + CHECK(secp256k1_fullagg_verify(data->ctx, data->sig, data->pk_ptrs, data->msg_ptrs, + FULLAGG_BENCH_N_SIGNERS)); + } +} + +static void run_fullagg_bench(int iters, int argc, char** argv) { + int i; + bench_fullagg_data data; + secp256k1_fullagg_secnonce secnonces[FULLAGG_BENCH_N_SIGNERS]; + secp256k1_fullagg_aggnonce aggnonce; + secp256k1_fullagg_partial_sig psigs[FULLAGG_BENCH_N_SIGNERS]; + const secp256k1_fullagg_partial_sig *psig_ptrs[FULLAGG_BENCH_N_SIGNERS]; + int d = argc == 1; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + for (i = 0; i < FULLAGG_BENCH_N_SIGNERS; i++) { + unsigned char sk[32]; + unsigned char secrand[32]; + + memset(sk, (unsigned char)(i + 1), sizeof(sk)); + memset(secrand, (unsigned char)(i + 0x40), sizeof(secrand)); + memset(data.msgs[i], (unsigned char)(i + 0x80), sizeof(data.msgs[i])); + data.pk_ptrs[i] = &data.pks[i]; + data.msg_ptrs[i] = data.msgs[i]; + data.pubnonce_ptrs[i] = &data.pubnonces[i]; + psig_ptrs[i] = &psigs[i]; + + CHECK(secp256k1_keypair_create(data.ctx, &data.keypairs[i], sk)); + CHECK(secp256k1_keypair_xonly_pub(data.ctx, &data.pks[i], NULL, &data.keypairs[i])); + CHECK(secp256k1_fullagg_nonce_gen(data.ctx, &secnonces[i], &data.pubnonces[i], secrand, + sk, &data.pks[i], NULL)); + } + /* Save signer 0's secnonce for bench_fullagg_sign before it is consumed */ + memcpy(&data.secnonce, &secnonces[0], sizeof(data.secnonce)); + + CHECK(secp256k1_fullagg_nonce_agg(data.ctx, &aggnonce, data.pubnonce_ptrs, FULLAGG_BENCH_N_SIGNERS)); + CHECK(secp256k1_fullagg_session_init(data.ctx, &data.session, &aggnonce, data.pk_ptrs, + data.msg_ptrs, data.pubnonce_ptrs, FULLAGG_BENCH_N_SIGNERS)); + for (i = 0; i < FULLAGG_BENCH_N_SIGNERS; i++) { + CHECK(secp256k1_fullagg_partial_sign(data.ctx, &psigs[i], &secnonces[i], &data.keypairs[i], + data.msgs[i], &data.session, data.pk_ptrs, + data.msg_ptrs, data.pubnonce_ptrs, + FULLAGG_BENCH_N_SIGNERS, i)); + } + CHECK(secp256k1_fullagg_partial_sig_agg(data.ctx, data.sig, &data.session, psig_ptrs, FULLAGG_BENCH_N_SIGNERS)); + + if (d || have_flag(argc, argv, "fullagg") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "fullagg_sign")) run_benchmark("fullagg_sign", bench_fullagg_sign, NULL, NULL, (void *) &data, 10, iters); + if (d || have_flag(argc, argv, "fullagg") || have_flag(argc, argv, "verify") || have_flag(argc, argv, "fullagg_verify")) run_benchmark("fullagg_verify", bench_fullagg_verify, NULL, NULL, (void *) &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif diff --git a/src/modules/fullagg/main_impl.h b/src/modules/fullagg/main_impl.h new file mode 100644 index 0000000000..595310cb98 --- /dev/null +++ b/src/modules/fullagg/main_impl.h @@ -0,0 +1,1043 @@ +/*********************************************************************** + * Copyright (c) 2025 Fabian Jahr * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FULLAGG_MAIN_H +#define SECP256K1_MODULE_FULLAGG_MAIN_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_fullagg.h" + +#include "../nonce_common.h" +#include "../../eckey.h" +#include "../../ecmult.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../scalar.h" +#include "../../util.h" + +static const unsigned char secp256k1_fullagg_secnonce_magic[4] = { 0xf1, 0x1a, 0x99, 0x01 }; + +static void secp256k1_fullagg_secnonce_save(secp256k1_fullagg_secnonce *secnonce, const secp256k1_scalar *k, const secp256k1_ge *pk) { + secp256k1_secnonce_save(secnonce->data, secp256k1_fullagg_secnonce_magic, k, pk); +} + +static int secp256k1_fullagg_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_ge *pk, const secp256k1_fullagg_secnonce *secnonce) { + return secp256k1_secnonce_load(ctx, k, pk, secnonce->data, secp256k1_fullagg_secnonce_magic); +} + +static void secp256k1_fullagg_secnonce_invalidate(const secp256k1_context* ctx, secp256k1_fullagg_secnonce *secnonce, int flag) { + secp256k1_secnonce_invalidate(ctx, secnonce->data, flag); +} + +static const unsigned char secp256k1_fullagg_pubnonce_magic[4] = { 0xf1, 0x1a, 0x99, 0x02 }; + +static void secp256k1_fullagg_pubnonce_save(secp256k1_fullagg_pubnonce* nonce, const secp256k1_ge* ges) { + secp256k1_nonce_points_save(nonce->data, secp256k1_fullagg_pubnonce_magic, ges); +} + +static int secp256k1_fullagg_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge* ges, const secp256k1_fullagg_pubnonce* nonce) { + return secp256k1_nonce_points_load(ctx, ges, nonce->data, secp256k1_fullagg_pubnonce_magic); +} + +static const unsigned char secp256k1_fullagg_aggnonce_magic[4] = { 0xf1, 0x1a, 0x99, 0x03 }; + +/* Save aggregate nonce. The points are guaranteed not to be the point at + * infinity because nonce aggregation fails in that case. */ +static void secp256k1_fullagg_aggnonce_save(secp256k1_fullagg_aggnonce* nonce, const secp256k1_ge* ges) { + secp256k1_nonce_points_save(nonce->data, secp256k1_fullagg_aggnonce_magic, ges); +} + +/* Load aggregate nonce */ +static int secp256k1_fullagg_aggnonce_load(const secp256k1_context* ctx, secp256k1_ge* ges, const secp256k1_fullagg_aggnonce* nonce) { + return secp256k1_nonce_points_load(ctx, ges, nonce->data, secp256k1_fullagg_aggnonce_magic); +} + +static const unsigned char secp256k1_fullagg_partial_sig_magic[4] = { 0xf1, 0x1a, 0x99, 0x05 }; + +static void secp256k1_fullagg_partial_sig_save(secp256k1_fullagg_partial_sig* sig, secp256k1_scalar *s) { + secp256k1_partial_sig_save(sig->data, secp256k1_fullagg_partial_sig_magic, s); +} + +static int secp256k1_fullagg_partial_sig_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_fullagg_partial_sig* sig) { + return secp256k1_partial_sig_load(ctx, s, sig->data, secp256k1_fullagg_partial_sig_magic); +} + +/* Parse/serialize functions for public interface */ +int secp256k1_fullagg_pubnonce_parse(const secp256k1_context* ctx, secp256k1_fullagg_pubnonce* nonce, const unsigned char *in66) { + secp256k1_ge ges[2]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(in66 != NULL); + + if (!secp256k1_nonce_points_parse(ges, in66)) { + return 0; + } + secp256k1_fullagg_pubnonce_save(nonce, ges); + return 1; +} + +int secp256k1_fullagg_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_fullagg_pubnonce* nonce) { + secp256k1_ge ges[2]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out66 != NULL); + ARG_CHECK(nonce != NULL); + memset(out66, 0, 66); + + if (!secp256k1_fullagg_pubnonce_load(ctx, ges, nonce)) { + return 0; + } + secp256k1_nonce_points_serialize(out66, ges); + return 1; +} + +int secp256k1_fullagg_aggnonce_parse(const secp256k1_context* ctx, secp256k1_fullagg_aggnonce* nonce, const unsigned char *in66) { + secp256k1_ge ges[2]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(in66 != NULL); + + /* The aggregate nonce consists of two compressed points. There is no + * encoding for the point at infinity because nonce aggregation fails in + * that case. */ + if (!secp256k1_nonce_points_parse(ges, in66)) { + return 0; + } + secp256k1_fullagg_aggnonce_save(nonce, ges); + return 1; +} + +int secp256k1_fullagg_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_fullagg_aggnonce* nonce) { + secp256k1_ge ges[2]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out66 != NULL); + ARG_CHECK(nonce != NULL); + memset(out66, 0, 66); + + if (!secp256k1_fullagg_aggnonce_load(ctx, ges, nonce)) { + return 0; + } + secp256k1_nonce_points_serialize(out66, ges); + return 1; +} + +int secp256k1_fullagg_partial_sig_parse(const secp256k1_context* ctx, secp256k1_fullagg_partial_sig* sig, const unsigned char *in32) { + secp256k1_scalar tmp; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in32 != NULL); + + memset(sig, 0, sizeof(*sig)); + + secp256k1_scalar_set_b32(&tmp, in32, &overflow); + if (overflow) { + return 0; + } + secp256k1_fullagg_partial_sig_save(sig, &tmp); + return 1; +} + +int secp256k1_fullagg_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_fullagg_partial_sig* sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_fullagg_partial_sig_magic, 4) == 0); + + memcpy(out32, &sig->data[4], 32); + return 1; +} + +/* Initializes SHA256 with fixed midstate for "FullAgg/aux" */ +static void secp256k1_fullagg_sha256_tagged_aux(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x4cb4139bul; + sha->s[1] = 0xac6dd715ul; + sha->s[2] = 0x46eb898bul; + sha->s[3] = 0xc13797e2ul; + sha->s[4] = 0xa7c1aea6ul; + sha->s[5] = 0x21aab077ul; + sha->s[6] = 0x6f1746b2ul; + sha->s[7] = 0x5c2bedd8ul; + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate for "FullAgg/nonce" */ +static void secp256k1_fullagg_sha256_tagged_nonce(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x742a20a9ul; + sha->s[1] = 0x2c939aaeul; + sha->s[2] = 0xf8f0f6c0ul; + sha->s[3] = 0x9b975422ul; + sha->s[4] = 0xbf5a4f08ul; + sha->s[5] = 0xe5fa99eeul; + sha->s[6] = 0xa64c241ful; + sha->s[7] = 0x5b12ebccul; + sha->bytes = 64; +} + +/* FullAgg nonce generation function: + * r_i = int(hash_{FullAgg/nonce}(rand || extra_in || i)) mod n */ +static void secp256k1_fullagg_nonce_function(const secp256k1_hash_ctx *hash_ctx, secp256k1_scalar *k, + const unsigned char *session_secrand, const unsigned char *seckey32, + const unsigned char *extra_input32) { + secp256k1_sha256 sha; + unsigned char rand[32]; + unsigned char i; + + /* Bind nonce to secret key if it was provided, otherwise use secrand directly. */ + if (seckey32 != NULL) { + secp256k1_fullagg_sha256_tagged_aux(&sha); + secp256k1_sha256_write(hash_ctx, &sha, session_secrand, 32); + secp256k1_sha256_finalize(hash_ctx, &sha, rand); + for (i = 0; i < 32; i++) { + rand[i] ^= seckey32[i]; + } + } else { + memcpy(rand, session_secrand, sizeof(rand)); + } + + /* Write all relevant data into hash for nonce. */ + secp256k1_fullagg_sha256_tagged_nonce(&sha); + secp256k1_sha256_write(hash_ctx, &sha, rand, sizeof(rand)); + if (extra_input32 != NULL) { + secp256k1_sha256_write(hash_ctx, &sha, extra_input32, 32); + } + + /* Generate the two nonces. */ + for (i = 0; i < 2; i++) { + unsigned char buf[32]; + secp256k1_sha256 sha_tmp = sha; + secp256k1_sha256_write(hash_ctx, &sha_tmp, &i, 1); + secp256k1_sha256_finalize(hash_ctx, &sha_tmp, buf); + secp256k1_scalar_set_b32(&k[i], buf, NULL); + + secp256k1_memclear_explicit(buf, sizeof(buf)); + secp256k1_sha256_clear(&sha_tmp); + } + secp256k1_memclear_explicit(rand, sizeof(rand)); + secp256k1_sha256_clear(&sha); +} + +/* Internal nonce generation */ +static int secp256k1_fullagg_nonce_gen_internal(const secp256k1_context* ctx, secp256k1_fullagg_secnonce *secnonce, + secp256k1_fullagg_pubnonce *pubnonce, const unsigned char *input_nonce, + const unsigned char *seckey, const secp256k1_xonly_pubkey *pubkey, + const unsigned char *extra_input32) { + secp256k1_scalar k[2]; + secp256k1_ge nonce_pts[2]; + secp256k1_gej nonce_ptj[2]; + int i; + secp256k1_ge pk; + int ret = 1; + + ARG_CHECK(pubnonce != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + memset(pubnonce, 0, sizeof(*pubnonce)); + + /* Check that the seckey is valid to be able to sign for it later. */ + if (seckey != NULL) { + secp256k1_scalar sk; + ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey); + secp256k1_scalar_clear(&sk); + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + + /* Get secret nonce */ + secp256k1_fullagg_nonce_function(secp256k1_get_hash_context(ctx), k, input_nonce, seckey, extra_input32); + VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); + VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1])); + secp256k1_fullagg_secnonce_save(secnonce, k, &pk); + secp256k1_fullagg_secnonce_invalidate(ctx, secnonce, !ret); + + /* Compute pubnonce as R1_i = k[0]*G, R2_i = k[1]*G */ + for (i = 0; i < 2; i++) { + secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &nonce_ptj[i], &k[i]); + secp256k1_scalar_clear(&k[i]); + } + + /* Convert pubnonce from jacobian to affine and mark as non-secret */ + secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2); + for (i = 0; i < 2; i++) { + secp256k1_gej_clear(&nonce_ptj[i]); + secp256k1_declassify(ctx, &nonce_pts[i], sizeof(nonce_pts[i])); + } + + secp256k1_fullagg_pubnonce_save(pubnonce, nonce_pts); + return ret; +} + +int secp256k1_fullagg_nonce_gen(const secp256k1_context* ctx, secp256k1_fullagg_secnonce *secnonce, + secp256k1_fullagg_pubnonce *pubnonce, unsigned char *session_secrand32, + const unsigned char *seckey, const secp256k1_xonly_pubkey *pubkey, + const unsigned char *extra_input32) { + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secnonce != NULL); + ARG_CHECK(session_secrand32 != NULL); + memset(secnonce, 0, sizeof(*secnonce)); + + ret &= !secp256k1_is_zero_array(session_secrand32, 32); + secp256k1_declassify(ctx, &ret, sizeof(ret)); + if (ret == 0) { + secp256k1_fullagg_secnonce_invalidate(ctx, secnonce, 1); + return 0; + } + + ret &= secp256k1_fullagg_nonce_gen_internal(ctx, secnonce, pubnonce, session_secrand32, + seckey, pubkey, extra_input32); + secp256k1_memczero(session_secrand32, 32, ret); + return ret; +} + +int secp256k1_fullagg_nonce_gen_counter(const secp256k1_context* ctx, secp256k1_fullagg_secnonce *secnonce, + secp256k1_fullagg_pubnonce *pubnonce, uint64_t nonrepeating_cnt, + const secp256k1_keypair *keypair, + const unsigned char *extra_input32) { + unsigned char buf[32] = { 0 }; + unsigned char seckey[32]; + secp256k1_xonly_pubkey pubkey; + int ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secnonce != NULL); + ARG_CHECK(keypair != NULL); + memset(secnonce, 0, sizeof(*secnonce)); + + secp256k1_write_be64(buf, nonrepeating_cnt); + /* keypair_sec and keypair_xonly_pub do not fail if the arguments are not NULL */ + ret = secp256k1_keypair_sec(ctx, seckey, keypair); + VERIFY_CHECK(ret); + ret = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, keypair); + VERIFY_CHECK(ret); +#ifndef VERIFY + (void) ret; +#endif + + ret = secp256k1_fullagg_nonce_gen_internal(ctx, secnonce, pubnonce, buf, seckey, + &pubkey, extra_input32); + secp256k1_memclear_explicit(seckey, sizeof(seckey)); + return ret; +} + +static int secp256k1_fullagg_sum_pubnonces(const secp256k1_context* ctx, secp256k1_gej *summed_pubnonces, const secp256k1_fullagg_pubnonce * const* pubnonces, size_t n_pubnonces) { + size_t i; + int j; + + secp256k1_gej_set_infinity(&summed_pubnonces[0]); + secp256k1_gej_set_infinity(&summed_pubnonces[1]); + + for (i = 0; i < n_pubnonces; i++) { + secp256k1_ge nonce_pts[2]; + if (!secp256k1_fullagg_pubnonce_load(ctx, nonce_pts, pubnonces[i])) { + return 0; + } + for (j = 0; j < 2; j++) { + secp256k1_gej_add_ge_var(&summed_pubnonces[j], &summed_pubnonces[j], &nonce_pts[j], NULL); + } + } + return 1; +} + +/* Aggregate nonces from all signers */ +int secp256k1_fullagg_nonce_agg(const secp256k1_context* ctx, secp256k1_fullagg_aggnonce *aggnonce, + const secp256k1_fullagg_pubnonce * const* pubnonces, size_t n_pubnonces) { + secp256k1_gej aggnonce_ptsj[2]; + secp256k1_ge aggnonce_pts[2]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(aggnonce != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(n_pubnonces > 0); + for (i = 0; i < n_pubnonces; i++) { + ARG_CHECK(pubnonces[i] != NULL); + } + + if (!secp256k1_fullagg_sum_pubnonces(ctx, aggnonce_ptsj, pubnonces, n_pubnonces)) { + return 0; + } + if (secp256k1_gej_is_infinity(&aggnonce_ptsj[0]) || secp256k1_gej_is_infinity(&aggnonce_ptsj[1])) { + return 0; + } + + secp256k1_ge_set_all_gej_var(aggnonce_pts, aggnonce_ptsj, 2); + secp256k1_fullagg_aggnonce_save(aggnonce, aggnonce_pts); + return 1; +} + +/* Initializes SHA256 with fixed midstate for "FullAgg/noncecoef" */ +static void secp256k1_fullagg_sha256_tagged_noncecoef(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xe7a1238aul; + sha->s[1] = 0x8d0cb445ul; + sha->s[2] = 0x82ea69faul; + sha->s[3] = 0x6cc8a517ul; + sha->s[4] = 0xfce019a0ul; + sha->s[5] = 0xdc36828ful; + sha->s[6] = 0x727f042bul; + sha->s[7] = 0xf325ff8eul; + sha->bytes = 64; +} + +/* Compute hash_noncecoef: b = H_noncecoef(cbytes(R1) || cbytes(R2) || pk_i || m_i || cbytes(R2_i) for all signers). + * aggnonce_ser66 is the serialized aggregate nonce cbytes(R1) || cbytes(R2). */ +static int secp256k1_fullagg_compute_noncehash(const secp256k1_context* ctx, + unsigned char *noncehash, + const unsigned char *aggnonce_ser66, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + const secp256k1_fullagg_pubnonce * const *pubnonces, + size_t n_signers) { + const secp256k1_hash_ctx *hash_ctx = secp256k1_get_hash_context(ctx); + unsigned char buf[33]; + size_t i; + secp256k1_sha256 sha; + secp256k1_fullagg_sha256_tagged_noncecoef(&sha); + + secp256k1_sha256_write(hash_ctx, &sha, aggnonce_ser66, 66); + + /* Write pk_i || m_i || cbytes(R2_i) for all signers */ + for (i = 0; i < n_signers; i++) { + secp256k1_ge pk_ge, nonce_pts[2]; + + /* Load and write pk_i */ + if (!secp256k1_xonly_pubkey_load(ctx, &pk_ge, pubkeys[i])) { + return 0; + } + secp256k1_fe_get_b32(buf, &pk_ge.x); + secp256k1_sha256_write(hash_ctx, &sha, buf, 32); + + /* Write m_i */ + secp256k1_sha256_write(hash_ctx, &sha, messages[i], 32); + + /* Load and write R2_i. The pubnonce points are never the point at + * infinity because parsing and generation do not allow it. */ + if (!secp256k1_fullagg_pubnonce_load(ctx, nonce_pts, pubnonces[i])) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(&nonce_pts[1], buf); + secp256k1_sha256_write(hash_ctx, &sha, buf, 33); + } + + secp256k1_sha256_finalize(hash_ctx, &sha, noncehash); + return 1; +} + +/* Initializes SHA256 with fixed midstate for "FullAgg/sig" */ +static void secp256k1_fullagg_sha256_tagged_sig(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xd25dfffbul; + sha->s[1] = 0xd0479fb3ul; + sha->s[2] = 0x32e0d40eul; + sha->s[3] = 0x9c4f065aul; + sha->s[4] = 0xf9bf9e14ul; + sha->s[5] = 0x8f22cce6ul; + sha->s[6] = 0x24f00eaeul; + sha->s[7] = 0xed749b73ul; + sha->bytes = 64; +} + +/* Compute hash_sig: c_i = H_sig(L || bytes(R) || pk_i || m_i) where L is the + * list of all pk_i || m_i pairs */ +static int secp256k1_fullagg_compute_sighash(const secp256k1_context* ctx, + secp256k1_scalar *c_i, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + size_t n_signers, + const unsigned char *r32, + size_t signer_index) { + const secp256k1_hash_ctx *hash_ctx = secp256k1_get_hash_context(ctx); + unsigned char buf[32]; + unsigned char hash[32]; + size_t i; + secp256k1_ge pk_ge; + secp256k1_sha256 sha; + secp256k1_fullagg_sha256_tagged_sig(&sha); + + /* Write L (list of all pk_i || m_i) */ + for (i = 0; i < n_signers; i++) { + if (!secp256k1_xonly_pubkey_load(ctx, &pk_ge, pubkeys[i])) { + return 0; + } + secp256k1_fe_get_b32(buf, &pk_ge.x); + secp256k1_sha256_write(hash_ctx, &sha, buf, 32); + secp256k1_sha256_write(hash_ctx, &sha, messages[i], 32); + } + + /* Write bytes(R) */ + secp256k1_sha256_write(hash_ctx, &sha, r32, 32); + + /* Write pk_i for this signer */ + if (!secp256k1_xonly_pubkey_load(ctx, &pk_ge, pubkeys[signer_index])) { + return 0; + } + secp256k1_fe_get_b32(buf, &pk_ge.x); + secp256k1_sha256_write(hash_ctx, &sha, buf, 32); + + /* Write m_i for this signer */ + secp256k1_sha256_write(hash_ctx, &sha, messages[signer_index], 32); + + secp256k1_sha256_finalize(hash_ctx, &sha, hash); + secp256k1_scalar_set_b32(c_i, hash, NULL); + + return 1; +} + +static const unsigned char secp256k1_fullagg_session_magic[4] = { 0xf1, 0x1a, 0x99, 0x04 }; + +static void secp256k1_fullagg_session_save(secp256k1_fullagg_session *session, + const unsigned char *fin_nonce, + int fin_nonce_parity, + const secp256k1_scalar *noncecoef, + const unsigned char *aggnonce_ser66, + size_t n_signers) { + unsigned char *ptr = session->data; + + memcpy(ptr, secp256k1_fullagg_session_magic, 4); + ptr += 4; + memcpy(ptr, fin_nonce, 32); + ptr += 32; + *ptr = (unsigned char)fin_nonce_parity; + ptr += 1; + secp256k1_scalar_get_b32(ptr, noncecoef); + ptr += 32; + secp256k1_write_be64(ptr, (uint64_t)n_signers); + ptr += 8; + memcpy(ptr, aggnonce_ser66, 66); +} + +static int secp256k1_fullagg_session_load(const secp256k1_context* ctx, + unsigned char *fin_nonce, + int *fin_nonce_parity, + secp256k1_scalar *noncecoef, + unsigned char *aggnonce_ser66, + size_t *n_signers, + const secp256k1_fullagg_session *session) { + const unsigned char *ptr = session->data; + + ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_fullagg_session_magic, 4) == 0); + ptr += 4; + memcpy(fin_nonce, ptr, 32); + ptr += 32; + *fin_nonce_parity = (int)*ptr; + ptr += 1; + secp256k1_scalar_set_b32(noncecoef, ptr, NULL); + ptr += 32; + *n_signers = (size_t)secp256k1_read_be64(ptr); + ptr += 8; + memcpy(aggnonce_ser66, ptr, 66); + + return 1; +} + + +/* Initialize a FullAgg session. This implements GetSessionValues. */ +int secp256k1_fullagg_session_init(const secp256k1_context* ctx, secp256k1_fullagg_session *session, + const secp256k1_fullagg_aggnonce *aggnonce, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + const secp256k1_fullagg_pubnonce * const *pubnonces, + size_t n_signers) { + secp256k1_ge aggnonce_pts[2]; + secp256k1_ge r; + secp256k1_gej rj; + unsigned char noncehash[32]; + unsigned char fin_nonce[32]; + unsigned char aggnonce_ser[66]; + int fin_nonce_parity; + secp256k1_scalar noncecoef; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(aggnonce != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(messages != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(n_signers > 0); + for (i = 0; i < n_signers; i++) { + ARG_CHECK(pubkeys[i] != NULL); + ARG_CHECK(messages[i] != NULL); + ARG_CHECK(pubnonces[i] != NULL); + } + + if (!secp256k1_fullagg_aggnonce_load(ctx, aggnonce_pts, aggnonce)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(&aggnonce_pts[0], &aggnonce_ser[0]); + secp256k1_eckey_pubkey_serialize33(&aggnonce_pts[1], &aggnonce_ser[33]); + + /* Compute nonce coefficient b */ + if (!secp256k1_fullagg_compute_noncehash(ctx, noncehash, aggnonce_ser, + pubkeys, messages, pubnonces, n_signers)) { + return 0; + } + secp256k1_scalar_set_b32(&noncecoef, noncehash, NULL); + + /* Compute final nonce R = R1 + b*R2 and fail if it is the point at + * infinity */ + secp256k1_effective_nonce(&rj, aggnonce_pts, &noncecoef); + secp256k1_ge_set_gej(&r, &rj); + if (secp256k1_ge_is_infinity(&r)) { + return 0; + } + + secp256k1_fe_normalize_var(&r.x); + secp256k1_fe_normalize_var(&r.y); + secp256k1_fe_get_b32(fin_nonce, &r.x); + fin_nonce_parity = secp256k1_fe_is_odd(&r.y); + + secp256k1_fullagg_session_save(session, fin_nonce, fin_nonce_parity, &noncecoef, aggnonce_ser, n_signers); + return 1; +} + +/* Create a partial signature */ +int secp256k1_fullagg_partial_sign(const secp256k1_context* ctx, secp256k1_fullagg_partial_sig *partial_sig, + secp256k1_fullagg_secnonce *secnonce, const secp256k1_keypair *keypair, + const unsigned char *msg32, + const secp256k1_fullagg_session *session, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + const secp256k1_fullagg_pubnonce * const *pubnonces, + size_t n_signers, + size_t signer_index) { + secp256k1_scalar sk; + secp256k1_ge pk, keypair_pk; + secp256k1_scalar k[2]; + secp256k1_scalar s, c_i; + unsigned char fin_nonce[32]; + int fin_nonce_parity; + secp256k1_scalar noncecoef; + unsigned char aggnonce_ser[66]; + size_t n_session_signers; + size_t i; + int ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secnonce != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(keypair != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(messages != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(n_signers > 0); + for (i = 0; i < n_signers; i++) { + ARG_CHECK(pubkeys[i] != NULL); + ARG_CHECK(messages[i] != NULL); + ARG_CHECK(pubnonces[i] != NULL); + } + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + + /* Load and invalidate secnonce */ + ret = secp256k1_fullagg_secnonce_load(ctx, k, &pk, secnonce); + /* Always clear the secnonce to avoid nonce reuse */ + secp256k1_memzero_explicit(secnonce, sizeof(*secnonce)); + if (!ret) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + if (!secp256k1_keypair_load(ctx, &sk, &keypair_pk, keypair)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + /* Verify the keypair matches the secnonce. The secnonce stores the point + * with even Y, so only the X coordinates are compared. */ + if (!secp256k1_fe_equal(&pk.x, &keypair_pk.x)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + /* d' = d if has_even_y(P), otherwise d' = n - d */ + secp256k1_fe_normalize_var(&keypair_pk.y); + if (secp256k1_fe_is_odd(&keypair_pk.y)) { + secp256k1_scalar_negate(&sk, &sk); + } + + if (!secp256k1_fullagg_session_load(ctx, fin_nonce, &fin_nonce_parity, &noncecoef, aggnonce_ser, &n_session_signers, session)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + ARG_CHECK(n_signers == n_session_signers); + + if (signer_index >= n_signers) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + /* Verify that the public key at the signer's position matches the + * signer's key and that the message at the signer's position is the + * message the signer intends to sign. Omitting the message check would + * let a malicious coordinator substitute a different message at the + * signer's position. */ + { + secp256k1_ge signer_pk; + if (!secp256k1_xonly_pubkey_load(ctx, &signer_pk, pubkeys[signer_index])) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + if (!secp256k1_fe_equal(&keypair_pk.x, &signer_pk.x)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + } + if (secp256k1_memcmp_var(messages[signer_index], msg32, 32) != 0) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + /* Verify this signer's R2 appears exactly once at the correct index. The + * uniqueness check is security-critical: if the signer's R2 appears at a + * second index, signing enables a key extraction attack (see Section 4.2 + * of the DahLIAS paper). */ + { + secp256k1_ge nonce_pts[2]; + secp256k1_gej r2j; + secp256k1_ge r2_self; + int found_count = 0; + int found_index = -1; + size_t j; + + /* Compute our R2 from k[1]. The point is public because it has been + * broadcast as part of the pubnonce, so it can be declassified. */ + secp256k1_ecmult_gen_gej(&ctx->ecmult_gen_ctx, &r2j, &k[1]); + secp256k1_ge_set_gej(&r2_self, &r2j); + secp256k1_declassify(ctx, &r2_self, sizeof(r2_self)); + secp256k1_gej_clear(&r2j); + + /* Check all pubnonces for our R2. The pubnonce points are never the + * point at infinity, so a match requires r2_self not to be infinity + * either. */ + for (j = 0; j < n_signers; j++) { + if (!secp256k1_fullagg_pubnonce_load(ctx, nonce_pts, pubnonces[j])) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + if (!secp256k1_ge_is_infinity(&r2_self) + && secp256k1_fe_equal(&r2_self.x, &nonce_pts[1].x) + && secp256k1_fe_equal(&r2_self.y, &nonce_pts[1].y)) { + found_count++; + found_index = (int)j; + } + } + + /* R2 must appear exactly once and at the signer's index */ + if (found_count != 1 || found_index != (int)signer_index) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + } + + /* Verify that the session was initialized with the same aggregate nonce + * and the same lists of public keys, messages and pubnonces by + * recomputing the nonce coefficient */ + { + unsigned char noncehash[32]; + secp256k1_scalar noncecoef_check; + if (!secp256k1_fullagg_compute_noncehash(ctx, noncehash, aggnonce_ser, pubkeys, + messages, pubnonces, n_signers)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + secp256k1_scalar_set_b32(&noncecoef_check, noncehash, NULL); + if (!secp256k1_scalar_eq(&noncecoef_check, &noncecoef)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + } + + /* e = 1 if has_even_y(R), otherwise e = n - 1. Multiply the secret + * nonces by e. */ + if (fin_nonce_parity) { + secp256k1_scalar_negate(&k[0], &k[0]); + secp256k1_scalar_negate(&k[1], &k[1]); + } + + /* Compute signer's challenge c_i */ + if (!secp256k1_fullagg_compute_sighash(ctx, &c_i, pubkeys, messages, n_signers, + fin_nonce, signer_index)) { + secp256k1_partial_sign_clear(&sk, k); + return 0; + } + + /* Compute s_i = e*(k1_i + b*k2_i) + c_i*d' */ + secp256k1_scalar_mul(&s, &noncecoef, &k[1]); /* b*k2_i */ + secp256k1_scalar_add(&s, &s, &k[0]); /* + k1_i */ + secp256k1_scalar_mul(&k[0], &c_i, &sk); /* c_i*d' (reuse k[0]) */ + secp256k1_scalar_add(&s, &s, &k[0]); /* + c_i*d' */ + + secp256k1_fullagg_partial_sig_save(partial_sig, &s); + + /* Clear sensitive data */ + secp256k1_partial_sign_clear(&sk, k); + secp256k1_scalar_clear(&s); + + return 1; +} + +/* Verify a partial signature. The aggregate nonce and the session values are + * recomputed from the pubnonces so that the result cannot be influenced by + * the coordinator. */ +int secp256k1_fullagg_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_fullagg_partial_sig *partial_sig, + const secp256k1_fullagg_pubnonce * const *pubnonces, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + size_t n_signers, + size_t signer_index) { + secp256k1_gej aggnonce_ptsj[2]; + secp256k1_ge aggnonce_pts[2]; + unsigned char noncehash[32]; + unsigned char fin_nonce[32]; + unsigned char aggnonce_ser[66]; + int fin_nonce_parity; + secp256k1_scalar noncecoef; + secp256k1_scalar s, c_i; + secp256k1_ge pk, r; + secp256k1_ge nonce_pts[2]; + secp256k1_gej rj, pkj, tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(messages != NULL); + ARG_CHECK(n_signers > 0); + for (i = 0; i < n_signers; i++) { + ARG_CHECK(pubnonces[i] != NULL); + ARG_CHECK(pubkeys[i] != NULL); + ARG_CHECK(messages[i] != NULL); + } + + if (signer_index >= n_signers) { + return 0; + } + + if (!secp256k1_fullagg_partial_sig_load(ctx, &s, partial_sig)) { + return 0; + } + + /* aggnonce = NonceAgg(pubnonce_0..u-1) */ + if (!secp256k1_fullagg_sum_pubnonces(ctx, aggnonce_ptsj, pubnonces, n_signers)) { + return 0; + } + if (secp256k1_gej_is_infinity(&aggnonce_ptsj[0]) || secp256k1_gej_is_infinity(&aggnonce_ptsj[1])) { + return 0; + } + secp256k1_ge_set_all_gej_var(aggnonce_pts, aggnonce_ptsj, 2); + secp256k1_eckey_pubkey_serialize33(&aggnonce_pts[0], &aggnonce_ser[0]); + secp256k1_eckey_pubkey_serialize33(&aggnonce_pts[1], &aggnonce_ser[33]); + + /* (R, b) = GetSessionValues(aggnonce, pk_0..u-1, m_0..u-1, pubnonce_0..u-1) */ + if (!secp256k1_fullagg_compute_noncehash(ctx, noncehash, aggnonce_ser, + pubkeys, messages, pubnonces, n_signers)) { + return 0; + } + secp256k1_scalar_set_b32(&noncecoef, noncehash, NULL); + secp256k1_effective_nonce(&rj, aggnonce_pts, &noncecoef); + secp256k1_ge_set_gej(&r, &rj); + if (secp256k1_ge_is_infinity(&r)) { + return 0; + } + secp256k1_fe_normalize_var(&r.x); + secp256k1_fe_normalize_var(&r.y); + secp256k1_fe_get_b32(fin_nonce, &r.x); + fin_nonce_parity = secp256k1_fe_is_odd(&r.y); + + /* Compute effective nonce of this signer: R'_i = R1_i + b*R2_i */ + if (!secp256k1_fullagg_pubnonce_load(ctx, nonce_pts, pubnonces[signer_index])) { + return 0; + } + secp256k1_effective_nonce(&rj, nonce_pts, &noncecoef); + + /* Multiply by e: negate if R has odd Y */ + if (fin_nonce_parity) { + secp256k1_gej_neg(&rj, &rj); + } + + /* P_i = lift_x(int(pk_i)) */ + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkeys[signer_index])) { + return 0; + } + + /* Compute signer's challenge c_i */ + if (!secp256k1_fullagg_compute_sighash(ctx, &c_i, pubkeys, messages, n_signers, + fin_nonce, signer_index)) { + return 0; + } + + /* Verify: s_i*G = e*R'_i + c_i*P_i */ + secp256k1_scalar_negate(&s, &s); + secp256k1_gej_set_ge(&pkj, &pk); + secp256k1_ecmult(&tmp, &pkj, &c_i, &s); + secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL); + + return secp256k1_gej_is_infinity(&tmp); +} + +/* Aggregate partial signatures */ +int secp256k1_fullagg_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, + const secp256k1_fullagg_session *session, + const secp256k1_fullagg_partial_sig * const *partial_sigs, + size_t n_sigs) { + unsigned char fin_nonce[32]; + unsigned char aggnonce_ser[66]; + int fin_nonce_parity; + secp256k1_scalar noncecoef; + size_t n_signers; + secp256k1_scalar s_agg; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(partial_sigs != NULL); + ARG_CHECK(n_sigs > 0); + for (i = 0; i < n_sigs; i++) { + ARG_CHECK(partial_sigs[i] != NULL); + } + + if (!secp256k1_fullagg_session_load(ctx, fin_nonce, &fin_nonce_parity, &noncecoef, aggnonce_ser, &n_signers, session)) { + return 0; + } + + ARG_CHECK(n_sigs == n_signers); + + /* Aggregate all the s values */ + secp256k1_scalar_set_int(&s_agg, 0); + for (i = 0; i < n_sigs; i++) { + secp256k1_scalar s_i; + if (!secp256k1_fullagg_partial_sig_load(ctx, &s_i, partial_sigs[i])) { + return 0; + } + secp256k1_scalar_add(&s_agg, &s_agg, &s_i); + } + + /* Output aggregate signature */ + memcpy(sig64, fin_nonce, 32); + secp256k1_scalar_get_b32(&sig64[32], &s_agg); + + return 1; +} + +typedef struct { + const secp256k1_context *ctx; + /* Hash state after writing the tagged hash midstate and L, the list of + * all pk_i || m_i */ + secp256k1_sha256 sha_l; + const unsigned char *sig64; + const secp256k1_xonly_pubkey * const *pubkeys; + const unsigned char * const *messages; +} secp256k1_fullagg_verify_ecmult_data; + +/* Provides the c_i*P_i terms for the verification multi-exponentiation */ +static int secp256k1_fullagg_verify_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_fullagg_verify_ecmult_data *ecmult_data = (secp256k1_fullagg_verify_ecmult_data *)data; + const secp256k1_hash_ctx *hash_ctx = secp256k1_get_hash_context(ecmult_data->ctx); + secp256k1_sha256 sha = ecmult_data->sha_l; + unsigned char buf[32]; + + if (!secp256k1_xonly_pubkey_load(ecmult_data->ctx, pt, ecmult_data->pubkeys[idx])) { + return 0; + } + /* c_i = hash_sig(L || bytes(R) || pk_i || m_i) */ + secp256k1_sha256_write(hash_ctx, &sha, ecmult_data->sig64, 32); + secp256k1_fe_get_b32(buf, &pt->x); + secp256k1_sha256_write(hash_ctx, &sha, buf, 32); + secp256k1_sha256_write(hash_ctx, &sha, ecmult_data->messages[idx], 32); + secp256k1_sha256_finalize(hash_ctx, &sha, buf); + secp256k1_scalar_set_b32(sc, buf, NULL); + return 1; +} + +/* Verify a FullAgg aggregate signature */ +int secp256k1_fullagg_verify(const secp256k1_context* ctx, const unsigned char *sig64, + const secp256k1_xonly_pubkey * const *pubkeys, + const unsigned char * const *messages, + size_t n_signers) { + const secp256k1_hash_ctx *hash_ctx; + secp256k1_fullagg_verify_ecmult_data ecmult_data; + secp256k1_scalar s; + secp256k1_ge r; + secp256k1_gej resj; + unsigned char buf[32]; + size_t i; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(messages != NULL); + ARG_CHECK(n_signers > 0); + for (i = 0; i < n_signers; i++) { + ARG_CHECK(pubkeys[i] != NULL); + ARG_CHECK(messages[i] != NULL); + } + + /* Parse signature */ + if (!secp256k1_fe_set_b32_limit(&r.x, sig64)) { + return 0; + } + secp256k1_scalar_set_b32(&s, &sig64[32], &overflow); + if (overflow) { + return 0; + } + + /* R = lift_x(int(r)) */ + if (!secp256k1_ge_set_xo_var(&r, &r.x, 0)) { + return 0; + } + + /* Precompute the hash state after writing L, the list of all pk_i || m_i. + * Computing it once here instead of once per challenge avoids hashing + * quadratic in the number of signers. */ + hash_ctx = secp256k1_get_hash_context(ctx); + secp256k1_fullagg_sha256_tagged_sig(&ecmult_data.sha_l); + for (i = 0; i < n_signers; i++) { + secp256k1_ge pk_ge; + if (!secp256k1_xonly_pubkey_load(ctx, &pk_ge, pubkeys[i])) { + return 0; + } + secp256k1_fe_get_b32(buf, &pk_ge.x); + secp256k1_sha256_write(hash_ctx, &ecmult_data.sha_l, buf, 32); + secp256k1_sha256_write(hash_ctx, &ecmult_data.sha_l, messages[i], 32); + } + ecmult_data.ctx = ctx; + ecmult_data.sig64 = sig64; + ecmult_data.pubkeys = pubkeys; + ecmult_data.messages = messages; + + /* Compute resj = -s*G + sum(c_i*P_i). Note that the public keys are never + * the point at infinity because they are the result of lift_x. */ + /* TODO: actually use optimized ecmult_multi algorithms by providing a + * scratch space */ + secp256k1_scalar_negate(&s, &s); + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &resj, &s, secp256k1_fullagg_verify_ecmult_callback, (void *) &ecmult_data, n_signers)) { + return 0; + } + + /* Verify: s*G = R + sum(c_i*P_i) */ + secp256k1_gej_add_ge_var(&resj, &resj, &r, NULL); + return secp256k1_gej_is_infinity(&resj); +} + +#endif /* SECP256K1_MODULE_FULLAGG_MAIN_H */ diff --git a/src/modules/fullagg/tests_impl.h b/src/modules/fullagg/tests_impl.h new file mode 100644 index 0000000000..b3dedea25f --- /dev/null +++ b/src/modules/fullagg/tests_impl.h @@ -0,0 +1,580 @@ +/*********************************************************************** + * Copyright (c) 2025 Fabian Jahr * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_FULLAGG_TESTS_IMPL_H +#define SECP256K1_MODULE_FULLAGG_TESTS_IMPL_H + +#include +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_fullagg.h" + +#include "../../scalar.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../util.h" + +#include "vectors.h" + +static int create_keypair_and_pk_fullagg(secp256k1_keypair *keypair, secp256k1_xonly_pubkey *pk, const unsigned char *sk) { + int ret; + secp256k1_keypair keypair_tmp; + ret = secp256k1_keypair_create(CTX, &keypair_tmp, sk); + ret &= secp256k1_keypair_xonly_pub(CTX, pk, NULL, &keypair_tmp); + if (keypair != NULL) { + *keypair = keypair_tmp; + } + return ret; +} + +/* Simple test with three signers */ +static void fullagg_simple_test_internal(void) { + unsigned char sk[3][32]; + secp256k1_keypair keypair[3]; + secp256k1_fullagg_pubnonce pubnonce[3]; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[3]; + secp256k1_fullagg_aggnonce aggnonce; + unsigned char msg[3][32]; + unsigned char session_secrand[3][32]; + secp256k1_fullagg_secnonce secnonce[3]; + secp256k1_xonly_pubkey pk[3]; + const secp256k1_xonly_pubkey *pk_ptr[3]; + const unsigned char *msg_ptr[3]; + secp256k1_fullagg_partial_sig partial_sig[3]; + const secp256k1_fullagg_partial_sig *partial_sig_ptr[3]; + unsigned char final_sig[64]; + secp256k1_fullagg_session session; + int i; + + testrand256(msg[0]); + testrand256(msg[1]); + testrand256(msg[2]); + + for (i = 0; i < 3; i++) { + testrand256(sk[i]); + pk_ptr[i] = &pk[i]; + msg_ptr[i] = msg[i]; + pubnonce_ptr[i] = &pubnonce[i]; + partial_sig_ptr[i] = &partial_sig[i]; + + CHECK(create_keypair_and_pk_fullagg(&keypair[i], &pk[i], sk[i])); + } + + /* Generate nonces */ + for (i = 0; i < 3; i++) { + testrand256(session_secrand[i]); + CHECK(secp256k1_fullagg_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_secrand[i], + sk[i], &pk[i], NULL) == 1); + } + + /* Aggregate nonces */ + CHECK(secp256k1_fullagg_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 3) == 1); + + /* Initialize session */ + CHECK(secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, 3) == 1); + + /* The signers create and verify their partial signatures */ + for (i = 0; i < 3; i++) { + CHECK(secp256k1_fullagg_partial_sign(CTX, &partial_sig[i], &secnonce[i], + &keypair[i], msg[i], &session, pk_ptr, msg_ptr, + pubnonce_ptr, 3, i) == 1); + CHECK(secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[i], pubnonce_ptr, + pk_ptr, msg_ptr, 3, i) == 1); + } + + /* Aggregate partial signatures */ + CHECK(secp256k1_fullagg_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 3) == 1); + + /* Verify the aggregate signature */ + CHECK(secp256k1_fullagg_verify(CTX, final_sig, pk_ptr, msg_ptr, 3) == 1); +} + +/* Test API parameter validation */ +static void fullagg_api_tests(void) { + secp256k1_fullagg_partial_sig partial_sig[2]; + const secp256k1_fullagg_partial_sig *partial_sig_ptr[2]; + unsigned char sig[64]; + unsigned char sk[2][32]; + secp256k1_keypair keypair[2]; + unsigned char zeros132[132] = { 0 }; + unsigned char session_secrand[2][32]; + secp256k1_fullagg_secnonce secnonce[2]; + secp256k1_fullagg_pubnonce pubnonce[2]; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[2]; + unsigned char pubnonce_ser[66]; + secp256k1_fullagg_aggnonce aggnonce; + unsigned char aggnonce_ser[66]; + unsigned char msg[2][32]; + const unsigned char *msg_ptr[2]; + secp256k1_fullagg_session session; + secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; + int i; + + for (i = 0; i < 2; i++) { + pk_ptr[i] = &pk[i]; + msg_ptr[i] = msg[i]; + pubnonce_ptr[i] = &pubnonce[i]; + partial_sig_ptr[i] = &partial_sig[i]; + testrand256(session_secrand[i]); + testrand256(sk[i]); + testrand256(msg[i]); + CHECK(create_keypair_and_pk_fullagg(&keypair[i], &pk[i], sk[i])); + } + + /* Nonce generation */ + CHECK(secp256k1_fullagg_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_secrand[0], + sk[0], &pk[0], NULL) == 1); + /* Check that session_secrand is zeroed */ + CHECK(secp256k1_memcmp_var(session_secrand[0], zeros132, sizeof(session_secrand[0])) == 0); + + /* session_secrand = 0 is disallowed */ + memset(session_secrand[0], 0, sizeof(session_secrand[0])); + CHECK(secp256k1_fullagg_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_secrand[0], + sk[0], &pk[0], NULL) == 0); + + /* Test NULL parameters */ + testrand256(session_secrand[0]); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_gen(CTX, NULL, &pubnonce[0], session_secrand[0], + sk[0], &pk[0], NULL)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_gen(CTX, &secnonce[0], NULL, session_secrand[0], + sk[0], &pk[0], NULL)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_gen(CTX, &secnonce[0], &pubnonce[0], NULL, + sk[0], &pk[0], NULL)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_secrand[0], + sk[0], NULL, NULL)); + + /* Generate valid nonces for both signers */ + for (i = 0; i < 2; i++) { + testrand256(session_secrand[i]); + CHECK(secp256k1_fullagg_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_secrand[i], + sk[i], &pk[i], NULL) == 1); + } + + /* Serialize and parse public nonces */ + CHECK(secp256k1_fullagg_pubnonce_serialize(CTX, pubnonce_ser, &pubnonce[0]) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_pubnonce_serialize(CTX, NULL, &pubnonce[0])); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_pubnonce_serialize(CTX, pubnonce_ser, NULL)); + + CHECK(secp256k1_fullagg_pubnonce_parse(CTX, &pubnonce[0], pubnonce_ser) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_pubnonce_parse(CTX, NULL, pubnonce_ser)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_pubnonce_parse(CTX, &pubnonce[0], NULL)); + + /* Nonce aggregation */ + CHECK(secp256k1_fullagg_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_agg(CTX, NULL, pubnonce_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_agg(CTX, &aggnonce, NULL, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 0)); + + /* Serialize and parse aggregate nonces */ + CHECK(secp256k1_fullagg_aggnonce_serialize(CTX, aggnonce_ser, &aggnonce) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_aggnonce_serialize(CTX, NULL, &aggnonce)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_aggnonce_serialize(CTX, aggnonce_ser, NULL)); + + CHECK(secp256k1_fullagg_aggnonce_parse(CTX, &aggnonce, aggnonce_ser) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_aggnonce_parse(CTX, NULL, aggnonce_ser)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_aggnonce_parse(CTX, &aggnonce, NULL)); + + /* Session initialization */ + CHECK(secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, 2) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_session_init(CTX, NULL, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_session_init(CTX, &session, NULL, pk_ptr, msg_ptr, + pubnonce_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_session_init(CTX, &session, &aggnonce, NULL, msg_ptr, + pubnonce_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, NULL, + pubnonce_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + NULL, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, 0)); + + /* Partial signing requires a context with the generator context built */ + CHECK_ILLEGAL(STATIC_CTX, secp256k1_fullagg_partial_sign(STATIC_CTX, &partial_sig[0], &secnonce[0], + &keypair[0], msg[0], &session, pk_ptr, msg_ptr, + pubnonce_ptr, 2, 0)); + + /* n_signers must match the number of signers in the session */ + { + secp256k1_fullagg_secnonce secnonce_tmp; + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], + msg[0], &session, pk_ptr, msg_ptr, + pubnonce_ptr, 1, 0)); + } + + /* Signing fails if the lists differ from those the session was + * initialized with */ + { + secp256k1_fullagg_secnonce secnonce_tmp; + unsigned char other_msg[32]; + const unsigned char *wrong_msg_ptr[2]; + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + memcpy(other_msg, msg[1], 32); + other_msg[0] ^= 1; + wrong_msg_ptr[0] = msg[0]; + wrong_msg_ptr[1] = other_msg; + CHECK(secp256k1_fullagg_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], + msg[0], &session, pk_ptr, wrong_msg_ptr, + pubnonce_ptr, 2, 0) == 0); + } + + /* Partial signing */ + for (i = 0; i < 2; i++) { + secp256k1_fullagg_secnonce secnonce_tmp; + memcpy(&secnonce_tmp, &secnonce[i], sizeof(secnonce_tmp)); + CHECK(secp256k1_fullagg_partial_sign(CTX, &partial_sig[i], &secnonce_tmp, &keypair[i], + msg[i], &session, pk_ptr, msg_ptr, pubnonce_ptr, 2, i) == 1); + /* The secnonce is set to 0 and following signing attempts fail */ + CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros132, sizeof(secnonce_tmp)) == 0); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sign(CTX, &partial_sig[i], &secnonce_tmp, + &keypair[i], msg[i], &session, pk_ptr, msg_ptr, + pubnonce_ptr, 2, i)); + } + + /** Partial signature verification **/ + CHECK(secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[0], pubnonce_ptr, pk_ptr, + msg_ptr, 2, 0) == 1); + CHECK(secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[1], pubnonce_ptr, pk_ptr, + msg_ptr, 2, 1) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_verify(CTX, NULL, pubnonce_ptr, pk_ptr, + msg_ptr, 2, 0)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[0], NULL, pk_ptr, + msg_ptr, 2, 0)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[0], pubnonce_ptr, NULL, + msg_ptr, 2, 0)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[0], pubnonce_ptr, pk_ptr, + NULL, 2, 0)); + + /** Signature aggregation **/ + CHECK(secp256k1_fullagg_partial_sig_agg(CTX, sig, &session, partial_sig_ptr, 2) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_agg(CTX, NULL, &session, partial_sig_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_agg(CTX, sig, NULL, partial_sig_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_agg(CTX, sig, &session, NULL, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_partial_sig_agg(CTX, sig, &session, partial_sig_ptr, 0)); + + /** Verification **/ + CHECK(secp256k1_fullagg_verify(CTX, sig, pk_ptr, msg_ptr, 2) == 1); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_verify(CTX, NULL, pk_ptr, msg_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_verify(CTX, sig, NULL, msg_ptr, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_verify(CTX, sig, pk_ptr, NULL, 2)); + CHECK_ILLEGAL(CTX, secp256k1_fullagg_verify(CTX, sig, pk_ptr, msg_ptr, 0)); +} + +/* Test with counter-based nonce generation */ +static void fullagg_nonce_gen_counter_test(void) { + unsigned char sk[2][32]; + secp256k1_keypair keypair[2]; + secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; + secp256k1_fullagg_pubnonce pubnonce[2]; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[2]; + secp256k1_fullagg_aggnonce aggnonce; + secp256k1_fullagg_secnonce secnonce[2]; + secp256k1_fullagg_session session; + secp256k1_fullagg_partial_sig partial_sig[2]; + const secp256k1_fullagg_partial_sig *partial_sig_ptr[2]; + unsigned char msg[2][32]; + const unsigned char *msg_ptr[2]; + unsigned char final_sig[64]; + uint64_t nonrepeating_cnt = 0; + int i; + + for (i = 0; i < 2; i++) { + testrand256(sk[i]); + testrand256(msg[i]); + CHECK(create_keypair_and_pk_fullagg(&keypair[i], &pk[i], sk[i])); + pk_ptr[i] = &pk[i]; + msg_ptr[i] = msg[i]; + pubnonce_ptr[i] = &pubnonce[i]; + partial_sig_ptr[i] = &partial_sig[i]; + } + + /* Generate nonces using counter */ + for (i = 0; i < 2; i++) { + CHECK(secp256k1_fullagg_nonce_gen_counter(CTX, &secnonce[i], &pubnonce[i], + nonrepeating_cnt + i, &keypair[i], + NULL) == 1); + } + + /* Complete the protocol */ + CHECK(secp256k1_fullagg_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); + CHECK(secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, 2) == 1); + + for (i = 0; i < 2; i++) { + CHECK(secp256k1_fullagg_partial_sign(CTX, &partial_sig[i], &secnonce[i], &keypair[i], + msg[i], &session, pk_ptr, msg_ptr, pubnonce_ptr, 2, i) == 1); + } + + CHECK(secp256k1_fullagg_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 2) == 1); + CHECK(secp256k1_fullagg_verify(CTX, final_sig, pk_ptr, msg_ptr, 2) == 1); +} + +/* Test serialization round-trip */ +static void fullagg_serialization_test(void) { + secp256k1_fullagg_pubnonce pubnonce, pubnonce2; + secp256k1_fullagg_aggnonce aggnonce, aggnonce2; + secp256k1_fullagg_partial_sig partial_sig, partial_sig2; + unsigned char pubnonce_ser[66]; + unsigned char aggnonce_ser[66]; + unsigned char partial_sig_ser[32]; + unsigned char session_secrand[32]; + unsigned char sk[32]; + secp256k1_xonly_pubkey pk; + secp256k1_fullagg_secnonce secnonce; + secp256k1_scalar s; + const secp256k1_fullagg_pubnonce *pubnonce_ptr; + + testrand256(sk); + testrand256(session_secrand); + CHECK(create_keypair_and_pk_fullagg(NULL, &pk, sk)); + + /* Test pubnonce serialization */ + CHECK(secp256k1_fullagg_nonce_gen(CTX, &secnonce, &pubnonce, session_secrand, + sk, &pk, NULL) == 1); + CHECK(secp256k1_fullagg_pubnonce_serialize(CTX, pubnonce_ser, &pubnonce) == 1); + CHECK(secp256k1_fullagg_pubnonce_parse(CTX, &pubnonce2, pubnonce_ser) == 1); + CHECK(secp256k1_memcmp_var(&pubnonce, &pubnonce2, sizeof(pubnonce)) == 0); + + /* Test aggnonce serialization */ + pubnonce_ptr = &pubnonce; + CHECK(secp256k1_fullagg_nonce_agg(CTX, &aggnonce, &pubnonce_ptr, 1) == 1); + CHECK(secp256k1_fullagg_aggnonce_serialize(CTX, aggnonce_ser, &aggnonce) == 1); + CHECK(secp256k1_fullagg_aggnonce_parse(CTX, &aggnonce2, aggnonce_ser) == 1); + CHECK(secp256k1_memcmp_var(&aggnonce, &aggnonce2, sizeof(aggnonce)) == 0); + + /* Test partial_sig serialization */ + testutil_random_scalar_order_test(&s); + secp256k1_fullagg_partial_sig_save(&partial_sig, &s); + CHECK(secp256k1_fullagg_partial_sig_serialize(CTX, partial_sig_ser, &partial_sig) == 1); + CHECK(secp256k1_fullagg_partial_sig_parse(CTX, &partial_sig2, partial_sig_ser) == 1); + CHECK(secp256k1_memcmp_var(&partial_sig, &partial_sig2, sizeof(partial_sig)) == 0); +} + +/* Writes a test vector secnonce (two secret nonce scalars) into a secnonce + * object */ +static void fullagg_test_set_secnonce(secp256k1_fullagg_secnonce *secnonce, const unsigned char *secnonce64, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_ge pk; + secp256k1_scalar k[2]; + + secp256k1_scalar_set_b32(&k[0], &secnonce64[0], NULL); + secp256k1_scalar_set_b32(&k[1], &secnonce64[32], NULL); + CHECK(secp256k1_xonly_pubkey_load(CTX, &pk, pubkey)); + secp256k1_fullagg_secnonce_save(secnonce, k, &pk); +} + +/* Computes the pubnonce (R1_i, R2_i) = (k1_i*G, k2_i*G) corresponding to a + * test vector secnonce */ +static void fullagg_test_pubnonce_from_secnonce(secp256k1_fullagg_pubnonce *pubnonce, const unsigned char *secnonce64) { + unsigned char pubnonce_ser[66]; + secp256k1_pubkey tmp; + int i; + + for (i = 0; i < 2; i++) { + size_t len = 33; + CHECK(secp256k1_ec_pubkey_create(CTX, &tmp, &secnonce64[32*i])); + CHECK(secp256k1_ec_pubkey_serialize(CTX, &pubnonce_ser[33*i], &len, &tmp, SECP256K1_EC_COMPRESSED)); + } + CHECK(secp256k1_fullagg_pubnonce_parse(CTX, pubnonce, pubnonce_ser)); +} + +static void fullagg_test_vectors_sign(void) { + size_t i, j; + + for (i = 0; i < ARRAY_SIZE(fullagg_sign_cases); i++) { + const struct fullagg_sign_case *c = &fullagg_sign_cases[i]; + secp256k1_keypair keypair[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_xonly_pubkey pk[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_xonly_pubkey *pk_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + const unsigned char *msg_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_pubnonce pubnonce[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_partial_sig partial_sig[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_fullagg_partial_sig *partial_sig_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_aggnonce aggnonce; + unsigned char aggnonce_ser[66]; + secp256k1_fullagg_session session; + unsigned char sig[64]; + + for (j = 0; j < c->n_signers; j++) { + CHECK(create_keypair_and_pk_fullagg(&keypair[j], &pk[j], c->sks[j])); + fullagg_test_pubnonce_from_secnonce(&pubnonce[j], c->secnonces[j]); + pk_ptr[j] = &pk[j]; + msg_ptr[j] = c->msgs[j]; + pubnonce_ptr[j] = &pubnonce[j]; + partial_sig_ptr[j] = &partial_sig[j]; + } + + CHECK(secp256k1_fullagg_nonce_agg(CTX, &aggnonce, pubnonce_ptr, c->n_signers)); + CHECK(secp256k1_fullagg_aggnonce_serialize(CTX, aggnonce_ser, &aggnonce)); + CHECK(secp256k1_memcmp_var(aggnonce_ser, c->expected_aggnonce, sizeof(aggnonce_ser)) == 0); + + CHECK(secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, c->n_signers)); + + for (j = 0; j < c->n_signers; j++) { + unsigned char psig_ser[32]; + secp256k1_fullagg_secnonce secnonce; + + fullagg_test_set_secnonce(&secnonce, c->secnonces[j], &pk[j]); + CHECK(secp256k1_fullagg_partial_sign(CTX, &partial_sig[j], &secnonce, &keypair[j], + c->msgs[j], &session, pk_ptr, msg_ptr, pubnonce_ptr, + c->n_signers, j)); + CHECK(secp256k1_fullagg_partial_sig_serialize(CTX, psig_ser, &partial_sig[j])); + CHECK(secp256k1_memcmp_var(psig_ser, c->expected_psigs[j], sizeof(psig_ser)) == 0); + CHECK(secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig[j], pubnonce_ptr, pk_ptr, + msg_ptr, c->n_signers, j)); + } + + CHECK(secp256k1_fullagg_partial_sig_agg(CTX, sig, &session, partial_sig_ptr, c->n_signers)); + CHECK(secp256k1_memcmp_var(sig, c->expected_sig, sizeof(sig)) == 0); + CHECK(secp256k1_fullagg_verify(CTX, sig, pk_ptr, msg_ptr, c->n_signers)); + } +} + +static void fullagg_test_vectors_sign_error(void) { + size_t i, j; + + for (i = 0; i < ARRAY_SIZE(fullagg_sign_error_cases); i++) { + const struct fullagg_sign_error_case *c = &fullagg_sign_error_cases[i]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey signer_pk; + secp256k1_xonly_pubkey pk[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_xonly_pubkey *pk_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + const unsigned char *msg_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_pubnonce pubnonce[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_aggnonce aggnonce; + secp256k1_fullagg_session session; + secp256k1_fullagg_secnonce secnonce; + secp256k1_fullagg_partial_sig partial_sig; + + CHECK(create_keypair_and_pk_fullagg(&keypair, &signer_pk, c->sk)); + for (j = 0; j < c->n_signers; j++) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk[j], c->pubkeys[j])); + CHECK(secp256k1_fullagg_pubnonce_parse(CTX, &pubnonce[j], c->pubnonces[j])); + pk_ptr[j] = &pk[j]; + msg_ptr[j] = c->msgs[j]; + pubnonce_ptr[j] = &pubnonce[j]; + } + + CHECK(secp256k1_fullagg_nonce_agg(CTX, &aggnonce, pubnonce_ptr, c->n_signers)); + CHECK(secp256k1_fullagg_session_init(CTX, &session, &aggnonce, pk_ptr, msg_ptr, + pubnonce_ptr, c->n_signers)); + /* The signer's slot is index 0 in all sign error vectors. Signing + * must fail. */ + fullagg_test_set_secnonce(&secnonce, c->secnonce, &signer_pk); + CHECK(secp256k1_fullagg_partial_sign(CTX, &partial_sig, &secnonce, &keypair, + c->msg, &session, pk_ptr, msg_ptr, pubnonce_ptr, + c->n_signers, 0) == 0); + } +} + +static void fullagg_test_vectors_verify(void) { + size_t i, j; + + for (i = 0; i < ARRAY_SIZE(fullagg_verify_cases); i++) { + const struct fullagg_verify_case *c = &fullagg_verify_cases[i]; + secp256k1_xonly_pubkey pk[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_xonly_pubkey *pk_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + const unsigned char *msg_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + int parse_ok = 1; + + for (j = 0; j < c->n_signers; j++) { + parse_ok &= secp256k1_xonly_pubkey_parse(CTX, &pk[j], c->pubkeys[j]); + pk_ptr[j] = &pk[j]; + msg_ptr[j] = c->msgs[j]; + } + /* A public key that is not on the curve fails to parse */ + if (!parse_ok) { + CHECK(!c->expected); + continue; + } + CHECK(secp256k1_fullagg_verify(CTX, c->sig, pk_ptr, msg_ptr, c->n_signers) == c->expected); + } +} + +static void fullagg_test_vectors_partial_sig_verify(void) { + size_t i, j; + + for (i = 0; i < ARRAY_SIZE(fullagg_partial_sig_verify_cases); i++) { + const struct fullagg_partial_sig_verify_case *c = &fullagg_partial_sig_verify_cases[i]; + secp256k1_xonly_pubkey pk[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_xonly_pubkey *pk_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + const unsigned char *msg_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_pubnonce pubnonce[FULLAGG_VECTORS_MAX_SIGNERS]; + const secp256k1_fullagg_pubnonce *pubnonce_ptr[FULLAGG_VECTORS_MAX_SIGNERS]; + secp256k1_fullagg_partial_sig partial_sig; + + for (j = 0; j < c->n_signers; j++) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk[j], c->pubkeys[j])); + CHECK(secp256k1_fullagg_pubnonce_parse(CTX, &pubnonce[j], c->pubnonces[j])); + pk_ptr[j] = &pk[j]; + msg_ptr[j] = c->msgs[j]; + pubnonce_ptr[j] = &pubnonce[j]; + } + + /* A partial signature that is not less than the group order fails to + * parse */ + if (!secp256k1_fullagg_partial_sig_parse(CTX, &partial_sig, c->psig)) { + CHECK(!c->expected); + continue; + } + CHECK(secp256k1_fullagg_partial_sig_verify(CTX, &partial_sig, pubnonce_ptr, pk_ptr, + msg_ptr, c->n_signers, c->signer_index) == c->expected); + } +} + +static void fullagg_test_vectors_tweak(void) { + size_t i; + + for (i = 0; i < ARRAY_SIZE(fullagg_tweak_cases); i++) { + const struct fullagg_tweak_case *c = &fullagg_tweak_cases[i]; + unsigned char sk[32]; + unsigned char pk_ser[33]; + size_t len = 33; + secp256k1_pubkey pk; + + if (c->is_xonly) { + secp256k1_keypair keypair; + CHECK(secp256k1_keypair_create(CTX, &keypair, c->sk)); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, c->tweak)); + CHECK(secp256k1_keypair_sec(CTX, sk, &keypair)); + CHECK(secp256k1_keypair_pub(CTX, &pk, &keypair)); + } else { + memcpy(sk, c->sk, sizeof(sk)); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, sk, c->tweak)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk)); + } + CHECK(secp256k1_memcmp_var(sk, c->expected_sk, sizeof(sk)) == 0); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pk_ser, &len, &pk, SECP256K1_EC_COMPRESSED)); + CHECK(secp256k1_memcmp_var(pk_ser, c->expected_pk, sizeof(pk_ser)) == 0); + } +} + +/* --- Test registry --- */ +REPEAT_TEST(fullagg_simple_test) + +static const struct tf_test_entry tests_fullagg[] = { + CASE1(fullagg_simple_test), + CASE1(fullagg_api_tests), + CASE1(fullagg_nonce_gen_counter_test), + CASE1(fullagg_serialization_test), + CASE1(fullagg_test_vectors_sign), + CASE1(fullagg_test_vectors_sign_error), + CASE1(fullagg_test_vectors_verify), + CASE1(fullagg_test_vectors_partial_sig_verify), + CASE1(fullagg_test_vectors_tweak), +}; + +#endif /* SECP256K1_MODULE_FULLAGG_TESTS_IMPL_H */ diff --git a/src/modules/fullagg/vectors.h b/src/modules/fullagg/vectors.h new file mode 100644 index 0000000000..c86632d175 --- /dev/null +++ b/src/modules/fullagg/vectors.h @@ -0,0 +1,679 @@ +/** + * Automatically generated by ./tools/test_vectors_fullagg_generate.py. */ + +struct fullagg_sign_case { + size_t n_signers; + unsigned char sks[5][32]; + unsigned char msgs[5][32]; + unsigned char secnonces[5][64]; + unsigned char expected_aggnonce[66]; + unsigned char expected_psigs[5][32]; + unsigned char expected_sig[64]; +}; + +static const struct fullagg_sign_case fullagg_sign_cases[7] = { + /* Single signer */ + { + 1, + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 } + }, + { + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 } + }, + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { + { 0x6C, 0x10, 0x7E, 0x8B, 0x71, 0x45, 0x55, 0x05, 0x1F, 0x1D, 0x56, 0x63, 0x7B, 0x92, 0x86, 0x75, 0xAE, 0x21, 0x53, 0x14, 0xBB, 0xE5, 0x11, 0x56, 0x29, 0xA6, 0xE3, 0x09, 0x84, 0x31, 0x3C, 0x95 } + }, + { 0x55, 0x74, 0xD2, 0xBA, 0x5A, 0xB8, 0x91, 0xF9, 0xE5, 0xBA, 0x99, 0xF3, 0x60, 0xFE, 0x45, 0x98, 0xAE, 0x8B, 0x71, 0x8C, 0xC7, 0xE4, 0xC6, 0x07, 0x73, 0xFB, 0x7D, 0xE1, 0xC6, 0x15, 0xC0, 0xC9, 0x6C, 0x10, 0x7E, 0x8B, 0x71, 0x45, 0x55, 0x05, 0x1F, 0x1D, 0x56, 0x63, 0x7B, 0x92, 0x86, 0x75, 0xAE, 0x21, 0x53, 0x14, 0xBB, 0xE5, 0x11, 0x56, 0x29, 0xA6, 0xE3, 0x09, 0x84, 0x31, 0x3C, 0x95 } + }, + /* Two signers */ + { + 2, + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFA, 0xB6, 0xAA, 0xD8, 0xE2, 0xAB, 0x44, 0x9C, 0x37, 0xBB, 0xCE, 0x5A, 0x88, 0xCC, 0x32, 0x3D, 0x3D } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 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, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 } + }, + { 0x02, 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F, 0x02, 0x56, 0xB3, 0x28, 0xB3, 0x0C, 0x8B, 0xF5, 0x83, 0x9E, 0x24, 0x05, 0x87, 0x47, 0x87, 0x94, 0x08, 0xBD, 0xB3, 0x62, 0x41, 0xDC, 0x9C, 0x2E, 0x7C, 0x61, 0x9F, 0xAA, 0x12, 0xB2, 0x92, 0x09, 0x67 }, + { + { 0x22, 0xB4, 0xD1, 0x8F, 0xB6, 0x1A, 0xAA, 0xDD, 0xC4, 0x53, 0x04, 0x60, 0xBA, 0xF0, 0xC5, 0x0D, 0xD3, 0xF6, 0x18, 0x5A, 0x2A, 0x8C, 0x17, 0x31, 0x84, 0x68, 0x18, 0xE8, 0xB1, 0x91, 0x05, 0x7E }, + { 0x6D, 0xBD, 0x27, 0xBD, 0x37, 0xC2, 0x57, 0x36, 0xEA, 0x15, 0xAC, 0x8D, 0x87, 0x7B, 0x00, 0x4A, 0xC9, 0x55, 0xB1, 0x5D, 0x28, 0x4D, 0x61, 0xE1, 0xFC, 0x81, 0xC7, 0x2D, 0x56, 0xE7, 0xEA, 0x77 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 } + }, + /* Three signers */ + { + 3, + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFA, 0xB6, 0xAA, 0xD8, 0xE2, 0xAB, 0x44, 0x9C, 0x37, 0xBB, 0xCE, 0x5A, 0x88, 0xCC, 0x32, 0x3D, 0x3D }, + { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 } + }, + { + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 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, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }, + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 } + }, + { 0x02, 0x1A, 0x7A, 0x56, 0x9E, 0x91, 0xDB, 0xF6, 0x05, 0x81, 0x50, 0x9C, 0x7F, 0xC9, 0x46, 0xD1, 0x00, 0x3B, 0x60, 0xC7, 0xDE, 0xE8, 0x52, 0x99, 0x53, 0x8D, 0xB6, 0x35, 0x35, 0x38, 0xD5, 0x95, 0x74, 0x03, 0x63, 0x60, 0xE8, 0x56, 0x31, 0x0C, 0xE5, 0xD2, 0x94, 0xE8, 0xBE, 0x33, 0xFC, 0x80, 0x70, 0x77, 0xDC, 0x56, 0xAC, 0x80, 0xD9, 0x5D, 0x9C, 0xD4, 0xDD, 0xBD, 0x21, 0x32, 0x5E, 0xFF, 0x73, 0xF7 }, + { + { 0x42, 0x1A, 0xD0, 0x11, 0x36, 0x2C, 0x0B, 0x2E, 0x71, 0xD6, 0x56, 0x8E, 0xBB, 0x9B, 0xCD, 0xF3, 0x5E, 0x30, 0x91, 0x90, 0x57, 0xD3, 0x21, 0x5C, 0x80, 0x8D, 0xA4, 0x2D, 0xAB, 0x81, 0xA2, 0xF2 }, + { 0x17, 0xA3, 0x03, 0x15, 0x3A, 0x3D, 0x20, 0xCB, 0xDB, 0x70, 0x12, 0xC9, 0xE3, 0xD1, 0x68, 0x7A, 0xF2, 0x4B, 0x35, 0x0D, 0x94, 0x19, 0xB1, 0x51, 0x43, 0xED, 0xE5, 0xBE, 0xC3, 0x09, 0x54, 0x10 }, + { 0x65, 0x4E, 0x63, 0xBB, 0x8B, 0x84, 0x01, 0x9B, 0x39, 0xB7, 0x2D, 0xAA, 0x5D, 0x9B, 0x4C, 0x15, 0xD1, 0xF1, 0xA8, 0x50, 0x7C, 0xB8, 0xB7, 0x1E, 0x6B, 0xA8, 0x4C, 0x98, 0x7F, 0x99, 0x61, 0x78 } + }, + { 0xAF, 0xFD, 0xF8, 0x03, 0xD4, 0x8F, 0xC2, 0x16, 0x0A, 0x7D, 0x14, 0xF2, 0x3F, 0x3F, 0x95, 0x74, 0xFE, 0x3A, 0xF9, 0x94, 0xE9, 0x32, 0xDC, 0x0D, 0x0B, 0x20, 0xDB, 0xF2, 0x08, 0xBD, 0xD0, 0xEC, 0xBF, 0x0C, 0x36, 0xE1, 0xFB, 0xED, 0x2D, 0x95, 0x86, 0xFD, 0x97, 0x02, 0xFD, 0x08, 0x82, 0x84, 0x22, 0x6D, 0x6E, 0xEE, 0x68, 0xA5, 0x89, 0xCC, 0x30, 0x23, 0xD6, 0x84, 0xEE, 0x24, 0x58, 0x7A } + }, + /* Five signers */ + { + 5, + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFA, 0xB6, 0xAA, 0xD8, 0xE2, 0xAB, 0x44, 0x9C, 0x37, 0xBB, 0xCE, 0x5A, 0x88, 0xCC, 0x32, 0x3D, 0x3D }, + { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 }, + { 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF4, 0xB0, 0xA4, 0xD2, 0xDC, 0xA5, 0x3E, 0x96, 0x31, 0xB5, 0xC8, 0x54, 0x82, 0xC6, 0x2C, 0x37, 0x37 }, + { 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 }, + { 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83 }, + { 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84 } + }, + { + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 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, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }, + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 }, + { 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F } + }, + { 0x02, 0xBA, 0x72, 0xA6, 0xE8, 0xBA, 0x53, 0xE8, 0xB9, 0x71, 0xAD, 0x0C, 0x98, 0x23, 0x96, 0x8A, 0xEF, 0x4D, 0x78, 0xCE, 0x8A, 0xF2, 0x55, 0xAB, 0x43, 0xDF, 0xF8, 0x30, 0x03, 0xC9, 0x02, 0xFB, 0x8D, 0x03, 0x94, 0x8B, 0x53, 0xDA, 0x97, 0xFD, 0xF6, 0x74, 0xC0, 0x87, 0x73, 0x15, 0xAC, 0xBC, 0xC8, 0x76, 0x1A, 0xA3, 0xB9, 0xA5, 0x82, 0xB4, 0x39, 0x98, 0x2F, 0xBA, 0xFA, 0xC9, 0x9F, 0x97, 0x21, 0x0F }, + { + { 0x64, 0xDD, 0xCF, 0x21, 0x90, 0x21, 0x68, 0x65, 0x17, 0xB6, 0x42, 0x2A, 0x99, 0x07, 0xFE, 0x4D, 0xEA, 0xF6, 0x9F, 0xE8, 0x52, 0x97, 0xBA, 0x59, 0x8B, 0x52, 0xD6, 0x76, 0xD9, 0xE2, 0xF3, 0x88 }, + { 0x71, 0x6A, 0xCB, 0x88, 0x5C, 0xE4, 0x8B, 0x44, 0x6E, 0x27, 0x47, 0xEC, 0x0D, 0x27, 0x49, 0xDA, 0x8F, 0x7F, 0x08, 0x3B, 0x5A, 0x6C, 0x62, 0x3E, 0xE2, 0x58, 0xD5, 0x31, 0x9A, 0x15, 0x62, 0xB8 }, + { 0xFB, 0xB2, 0xF7, 0x63, 0xA3, 0x3A, 0xC9, 0x73, 0x7D, 0x65, 0x33, 0xBD, 0xDD, 0xF1, 0xE7, 0x49, 0xA3, 0xE2, 0x90, 0x1F, 0x13, 0xEF, 0xFD, 0x00, 0xAA, 0x46, 0xA0, 0x9E, 0xDA, 0x52, 0xC1, 0x5C }, + { 0x7B, 0x98, 0xD3, 0x64, 0x57, 0x54, 0x0E, 0x38, 0x50, 0x05, 0x22, 0x44, 0x13, 0xEA, 0xBC, 0x9D, 0x84, 0x65, 0xA4, 0x83, 0x65, 0x79, 0x16, 0xBE, 0xE3, 0x7C, 0x17, 0xF6, 0xED, 0xB5, 0xE2, 0xA7 }, + { 0xA5, 0x6E, 0xE5, 0x40, 0x4D, 0x3E, 0x8C, 0x45, 0xBC, 0xC5, 0xEB, 0x07, 0x27, 0x4F, 0xD2, 0x0E, 0xDB, 0x62, 0xF5, 0xDE, 0x09, 0x18, 0x52, 0x06, 0xE9, 0x95, 0x8C, 0x76, 0x00, 0x12, 0x11, 0x2D } + }, + { 0x72, 0x5E, 0xD0, 0xEA, 0x19, 0x8C, 0x1B, 0x61, 0x44, 0x77, 0x49, 0x32, 0xFF, 0x1D, 0xBC, 0x77, 0xB1, 0x90, 0x29, 0x90, 0x23, 0xBC, 0x6A, 0x3F, 0x3C, 0x66, 0x21, 0x86, 0xF1, 0x2D, 0xBC, 0x7F, 0xF3, 0x03, 0x4A, 0xB2, 0x34, 0xD3, 0x57, 0x9B, 0x10, 0x0D, 0xCB, 0x1F, 0xBF, 0x5B, 0xBE, 0x21, 0x08, 0xC3, 0x18, 0xD6, 0xD0, 0xF4, 0x41, 0xE7, 0x65, 0x5F, 0x33, 0x9A, 0x9B, 0xA6, 0x88, 0xEE } + }, + /* Same pubkey signs two different messages */ + { + 2, + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 } + }, + { + { 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 }, + { 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1 } + }, + { + { 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }, + { 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E } + }, + { 0x03, 0x29, 0xCE, 0x75, 0x8B, 0xB2, 0xD2, 0x1D, 0xEF, 0x21, 0x4C, 0xEA, 0x06, 0x9A, 0xDD, 0xBD, 0x16, 0x89, 0x6E, 0xC7, 0x79, 0x24, 0xBF, 0x0E, 0xC9, 0x21, 0x9E, 0x00, 0xDC, 0xF0, 0x77, 0x60, 0x5E, 0x02, 0xDC, 0xEC, 0x80, 0x6D, 0xEB, 0x8D, 0xA0, 0x77, 0x4E, 0xC5, 0xA5, 0xD0, 0xF5, 0x09, 0x7F, 0xFD, 0xE1, 0xF7, 0x0A, 0x9F, 0x40, 0xD4, 0x43, 0x63, 0xBD, 0x3D, 0xC2, 0xDC, 0x25, 0x90, 0xCA, 0x5D }, + { + { 0x85, 0x6B, 0x27, 0x43, 0xB7, 0x83, 0x3E, 0x2E, 0xB0, 0xB6, 0x71, 0x09, 0xCC, 0xD1, 0x15, 0x43, 0x26, 0xB1, 0x7D, 0xAF, 0x86, 0xBF, 0xB4, 0xE7, 0xEB, 0xF2, 0x02, 0x97, 0xEA, 0x1F, 0xA6, 0x42 }, + { 0x9E, 0xF6, 0x0A, 0xB2, 0x7A, 0xA0, 0x5A, 0xE6, 0xC1, 0x2E, 0x15, 0x06, 0xB1, 0x52, 0xA4, 0x1C, 0x7B, 0x84, 0xB1, 0x31, 0x9B, 0x94, 0x64, 0x51, 0xA5, 0x3D, 0x16, 0x02, 0xB3, 0x2A, 0xA0, 0x30 } + }, + { 0x49, 0x37, 0xFA, 0x0F, 0xBE, 0xD6, 0x24, 0x08, 0xA9, 0x01, 0x5A, 0x24, 0x47, 0xB4, 0xC6, 0x10, 0x24, 0x96, 0x4D, 0x55, 0x7D, 0x09, 0x40, 0x98, 0x8F, 0x04, 0x60, 0xE9, 0xBE, 0xEA, 0x9D, 0x82, 0x24, 0x61, 0x31, 0xF6, 0x32, 0x23, 0x99, 0x15, 0x71, 0xE4, 0x86, 0x10, 0x7E, 0x23, 0xB9, 0x60, 0xE7, 0x87, 0x51, 0xFA, 0x73, 0x0B, 0x78, 0xFD, 0xD1, 0x5C, 0xBA, 0x0D, 0xCD, 0x14, 0x05, 0x31 } + }, + /* Signers with tweaked keys */ + { + 2, + { + { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, + { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 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, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 } + }, + { 0x02, 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F, 0x02, 0x56, 0xB3, 0x28, 0xB3, 0x0C, 0x8B, 0xF5, 0x83, 0x9E, 0x24, 0x05, 0x87, 0x47, 0x87, 0x94, 0x08, 0xBD, 0xB3, 0x62, 0x41, 0xDC, 0x9C, 0x2E, 0x7C, 0x61, 0x9F, 0xAA, 0x12, 0xB2, 0x92, 0x09, 0x67 }, + { + { 0xBD, 0xCF, 0xA7, 0x76, 0x6B, 0x41, 0x7E, 0x05, 0xEB, 0x21, 0x76, 0x43, 0xF0, 0x0F, 0x60, 0xAA, 0x4C, 0xF0, 0xFA, 0x5A, 0xE4, 0x28, 0x7F, 0xB0, 0xCA, 0xD4, 0x39, 0xD8, 0xFA, 0x35, 0x1D, 0xA6 }, + { 0x28, 0x30, 0x1B, 0x09, 0x00, 0x0D, 0x4C, 0xF0, 0x33, 0xBD, 0x7A, 0xA5, 0x1F, 0x2D, 0xF6, 0xFD, 0x0A, 0x72, 0xBA, 0x13, 0x8E, 0xD2, 0x23, 0x9A, 0x17, 0x50, 0x83, 0x44, 0x4D, 0xAE, 0x5B, 0xC6 } + }, + { 0x70, 0x86, 0xC4, 0xFE, 0xE3, 0x01, 0x4A, 0x26, 0xE2, 0x95, 0x38, 0x91, 0x3B, 0x79, 0x1B, 0x72, 0xD4, 0xDC, 0xE7, 0x42, 0x56, 0x1B, 0x92, 0xD6, 0x21, 0xE6, 0xB2, 0xE4, 0xB3, 0xDE, 0xE2, 0xA4, 0xE5, 0xFF, 0xC2, 0x7F, 0x6B, 0x4E, 0xCA, 0xF6, 0x1E, 0xDE, 0xF0, 0xE9, 0x0F, 0x3D, 0x57, 0xA7, 0x57, 0x63, 0xB4, 0x6E, 0x72, 0xFA, 0xA3, 0x4A, 0xE2, 0x24, 0xBD, 0x1D, 0x47, 0xE3, 0x79, 0x6C } + }, + /* Exact duplicate (pk, m) entries */ + { + 2, + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 } + }, + { + { 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0 }, + { 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0 } + }, + { + { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15 }, + { 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17 } + }, + { 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, 0x02, 0x8F, 0x53, 0x04, 0xE2, 0x37, 0x3E, 0x56, 0xEE, 0x7D, 0x77, 0x4C, 0xB8, 0x9E, 0x9F, 0x1A, 0xFE, 0xCF, 0x0E, 0xE7, 0xE3, 0xE3, 0x75, 0x7F, 0x18, 0x99, 0x08, 0xF0, 0x69, 0xDA, 0xA3, 0x6C, 0x60 }, + { + { 0x69, 0xBA, 0xC5, 0x75, 0x7B, 0x4C, 0x51, 0xE7, 0xB3, 0x00, 0x8C, 0xBD, 0x28, 0xF3, 0xFE, 0x38, 0xE0, 0x3D, 0x90, 0xEF, 0xDC, 0x00, 0x2B, 0x4C, 0x41, 0x03, 0x7A, 0x0C, 0xA3, 0x81, 0xB6, 0xEB }, + { 0x0D, 0x32, 0xBD, 0x47, 0xC6, 0xAD, 0x27, 0x19, 0x27, 0x90, 0x3F, 0x64, 0x48, 0xB0, 0x2B, 0xBE, 0x53, 0x3B, 0xBF, 0xB1, 0xC8, 0x27, 0x3E, 0x7F, 0xB0, 0x23, 0x58, 0x86, 0x23, 0x9B, 0x04, 0xA9 } + }, + { 0x45, 0xD4, 0xDB, 0x5A, 0xE5, 0xDE, 0x74, 0xA9, 0xF0, 0x4C, 0xF4, 0x04, 0xD4, 0x34, 0x03, 0x1A, 0x42, 0x9C, 0x69, 0x4B, 0xBA, 0xE2, 0x19, 0x61, 0xF2, 0xB7, 0x9A, 0xE0, 0x35, 0x30, 0x93, 0xBF, 0x76, 0xED, 0x82, 0xBD, 0x41, 0xF9, 0x79, 0x00, 0xDA, 0x90, 0xCC, 0x21, 0x71, 0xA4, 0x29, 0xF7, 0x33, 0x79, 0x50, 0xA1, 0xA4, 0x27, 0x69, 0xCB, 0xF1, 0x26, 0xD2, 0x92, 0xC7, 0x1C, 0xBB, 0x94 } + }, +}; + +struct fullagg_sign_error_case { + unsigned char sk[32]; + unsigned char msg[32]; + unsigned char secnonce[64]; + size_t n_signers; + unsigned char pubkeys[3][32]; + unsigned char msgs[3][32]; + unsigned char pubnonces[3][66]; +}; + +static const struct fullagg_sign_error_case fullagg_sign_error_cases[4] = { + /* Signer's R2 appears at two indices */ + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + 3, + { + { 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 }, + { 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A }, + { 0x03, 0xF9, 0x91, 0xF9, 0x44, 0xD1, 0xE1, 0x95, 0x4A, 0x7F, 0xC8, 0xB9, 0xBF, 0x62, 0xE0, 0xD7, 0x8F, 0x01, 0x5F, 0x4C, 0x07, 0x76, 0x2D, 0x50, 0x5E, 0x20, 0xE6, 0xC4, 0x52, 0x60, 0xA3, 0x66, 0x1B, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 } + } + }, + /* Signer's R2 appears at no index */ + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + 3, + { + { 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 }, + { 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x03, 0x11, 0x95, 0xA8, 0x04, 0x6D, 0xCB, 0xB8, 0xE1, 0x70, 0x34, 0xBC, 0xA6, 0x30, 0x06, 0x5E, 0x7A, 0x09, 0x82, 0xE4, 0xE3, 0x6F, 0x6F, 0x7E, 0x5A, 0x8D, 0x45, 0x54, 0xE4, 0x84, 0x6F, 0xCD, 0x99 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A }, + { 0x03, 0xF9, 0x91, 0xF9, 0x44, 0xD1, 0xE1, 0x95, 0x4A, 0x7F, 0xC8, 0xB9, 0xBF, 0x62, 0xE0, 0xD7, 0x8F, 0x01, 0x5F, 0x4C, 0x07, 0x76, 0x2D, 0x50, 0x5E, 0x20, 0xE6, 0xC4, 0x52, 0x60, 0xA3, 0x66, 0x1B, 0x02, 0x56, 0xB3, 0x28, 0xB3, 0x0C, 0x8B, 0xF5, 0x83, 0x9E, 0x24, 0x05, 0x87, 0x47, 0x87, 0x94, 0x08, 0xBD, 0xB3, 0x62, 0x41, 0xDC, 0x9C, 0x2E, 0x7C, 0x61, 0x9F, 0xAA, 0x12, 0xB2, 0x92, 0x09, 0x67 } + } + }, + /* Message at signer's index was substituted */ + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + 3, + { + { 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 }, + { 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F } + }, + { + { 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A }, + { 0x03, 0xF9, 0x91, 0xF9, 0x44, 0xD1, 0xE1, 0x95, 0x4A, 0x7F, 0xC8, 0xB9, 0xBF, 0x62, 0xE0, 0xD7, 0x8F, 0x01, 0x5F, 0x4C, 0x07, 0x76, 0x2D, 0x50, 0x5E, 0x20, 0xE6, 0xC4, 0x52, 0x60, 0xA3, 0x66, 0x1B, 0x02, 0x56, 0xB3, 0x28, 0xB3, 0x0C, 0x8B, 0xF5, 0x83, 0x9E, 0x24, 0x05, 0x87, 0x47, 0x87, 0x94, 0x08, 0xBD, 0xB3, 0x62, 0x41, 0xDC, 0x9C, 0x2E, 0x7C, 0x61, 0x9F, 0xAA, 0x12, 0xB2, 0x92, 0x09, 0x67 } + } + }, + /* Public key at signer's index was substituted */ + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 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, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + 3, + { + { 0x9A, 0xC2, 0x03, 0x35, 0xEB, 0x38, 0x76, 0x8D, 0x20, 0x52, 0xBE, 0x1D, 0xBB, 0xC3, 0xC8, 0xF6, 0x17, 0x84, 0x07, 0x45, 0x8E, 0x51, 0xE6, 0xB4, 0xAD, 0x22, 0xF1, 0xD9, 0x17, 0x58, 0x89, 0x5B }, + { 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 }, + { 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A }, + { 0x03, 0xF9, 0x91, 0xF9, 0x44, 0xD1, 0xE1, 0x95, 0x4A, 0x7F, 0xC8, 0xB9, 0xBF, 0x62, 0xE0, 0xD7, 0x8F, 0x01, 0x5F, 0x4C, 0x07, 0x76, 0x2D, 0x50, 0x5E, 0x20, 0xE6, 0xC4, 0x52, 0x60, 0xA3, 0x66, 0x1B, 0x02, 0x56, 0xB3, 0x28, 0xB3, 0x0C, 0x8B, 0xF5, 0x83, 0x9E, 0x24, 0x05, 0x87, 0x47, 0x87, 0x94, 0x08, 0xBD, 0xB3, 0x62, 0x41, 0xDC, 0x9C, 0x2E, 0x7C, 0x61, 0x9F, 0xAA, 0x12, 0xB2, 0x92, 0x09, 0x67 } + } + }, +}; + +/* The verify vectors whose inputs cannot be expressed through the C API are + * omitted (signatures whose length is not 64 bytes, lists of different + * lengths and empty signer lists). */ +struct fullagg_verify_case { + size_t n_signers; + unsigned char pubkeys[3][32]; + unsigned char msgs[3][32]; + unsigned char sig[64]; + int expected; +}; + +static const struct fullagg_verify_case fullagg_verify_cases[11] = { + /* Two-signer valid aggregate */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 }, + 1 + }, + /* Three-signer valid aggregate */ + { + 3, + { + { 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 }, + { 0x98, 0x9C, 0x0B, 0x76, 0xCB, 0x56, 0x39, 0x71, 0xFD, 0xC9, 0xBE, 0xF3, 0x1E, 0xC0, 0x6C, 0x35, 0x60, 0xF3, 0x24, 0x9D, 0x6E, 0xE9, 0xE5, 0xD8, 0x3C, 0x57, 0x62, 0x55, 0x96, 0xE0, 0x5F, 0x6F } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 }, + { 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 } + }, + { 0xAF, 0xFD, 0xF8, 0x03, 0xD4, 0x8F, 0xC2, 0x16, 0x0A, 0x7D, 0x14, 0xF2, 0x3F, 0x3F, 0x95, 0x74, 0xFE, 0x3A, 0xF9, 0x94, 0xE9, 0x32, 0xDC, 0x0D, 0x0B, 0x20, 0xDB, 0xF2, 0x08, 0xBD, 0xD0, 0xEC, 0xBF, 0x0C, 0x36, 0xE1, 0xFB, 0xED, 0x2D, 0x95, 0x86, 0xFD, 0x97, 0x02, 0xFD, 0x08, 0x82, 0x84, 0x22, 0x6D, 0x6E, 0xEE, 0x68, 0xA5, 0x89, 0xCC, 0x30, 0x23, 0xD6, 0x84, 0xEE, 0x24, 0x58, 0x7A }, + 1 + }, + /* Wrong message for second signer */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 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 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 }, + 0 + }, + /* Pubkey order swapped */ + { + 2, + { + { 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 }, + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 }, + 0 + }, + /* Dropped second signer */ + { + 1, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 }, + 0 + }, + /* Tampered s (bit flip) */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF4 }, + 0 + }, + /* s = 0 (in range, wrong) */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 0 + }, + /* s = n (out of range) */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 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 }, + 0 + }, + /* s = 2^256 - 1 */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 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 }, + 0 + }, + /* R x-coord not on curve */ + { + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 }, + 0 + }, + /* Public key not on curve */ + { + 2, + { + { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { 0x8F, 0xB6, 0x0A, 0xB3, 0x9F, 0x70, 0x8A, 0xFE, 0xE7, 0x9A, 0xD2, 0x5E, 0x6F, 0x55, 0x28, 0xBF, 0x4F, 0xCC, 0x5E, 0x56, 0x08, 0x3E, 0x82, 0x41, 0x73, 0x57, 0x82, 0x6E, 0x19, 0xE2, 0x50, 0xBC, 0x90, 0x71, 0xF9, 0x4C, 0xED, 0xDD, 0x02, 0x14, 0xAE, 0x68, 0xB0, 0xEE, 0x42, 0x6B, 0xC5, 0x58, 0x9D, 0x4B, 0xC9, 0xB7, 0x52, 0xD9, 0x79, 0x13, 0x80, 0xE9, 0xE0, 0x16, 0x08, 0x78, 0xEF, 0xF5 }, + 0 + }, +}; + +struct fullagg_partial_sig_verify_case { + size_t signer_index; + unsigned char psig[32]; + size_t n_signers; + unsigned char pubkeys[2][32]; + unsigned char msgs[2][32]; + unsigned char pubnonces[2][66]; + int expected; +}; + +static const struct fullagg_partial_sig_verify_case fullagg_partial_sig_verify_cases[9] = { + /* Honest partial sig (signer 0) */ + { + 0, + { 0x22, 0xB4, 0xD1, 0x8F, 0xB6, 0x1A, 0xAA, 0xDD, 0xC4, 0x53, 0x04, 0x60, 0xBA, 0xF0, 0xC5, 0x0D, 0xD3, 0xF6, 0x18, 0x5A, 0x2A, 0x8C, 0x17, 0x31, 0x84, 0x68, 0x18, 0xE8, 0xB1, 0x91, 0x05, 0x7E }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 1 + }, + /* Honest partial sig (signer 1) */ + { + 1, + { 0x6D, 0xBD, 0x27, 0xBD, 0x37, 0xC2, 0x57, 0x36, 0xEA, 0x15, 0xAC, 0x8D, 0x87, 0x7B, 0x00, 0x4A, 0xC9, 0x55, 0xB1, 0x5D, 0x28, 0x4D, 0x61, 0xE1, 0xFC, 0x81, 0xC7, 0x2D, 0x56, 0xE7, 0xEA, 0x77 }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 1 + }, + /* Honest psig verified at the wrong signer_index */ + { + 1, + { 0x22, 0xB4, 0xD1, 0x8F, 0xB6, 0x1A, 0xAA, 0xDD, 0xC4, 0x53, 0x04, 0x60, 0xBA, 0xF0, 0xC5, 0x0D, 0xD3, 0xF6, 0x18, 0x5A, 0x2A, 0x8C, 0x17, 0x31, 0x84, 0x68, 0x18, 0xE8, 0xB1, 0x91, 0x05, 0x7E }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, + /* Tampered partial sig (bit flip) */ + { + 0, + { 0x22, 0xB4, 0xD1, 0x8F, 0xB6, 0x1A, 0xAA, 0xDD, 0xC4, 0x53, 0x04, 0x60, 0xBA, 0xF0, 0xC5, 0x0D, 0xD3, 0xF6, 0x18, 0x5A, 0x2A, 0x8C, 0x17, 0x31, 0x84, 0x68, 0x18, 0xE8, 0xB1, 0x91, 0x05, 0x7F }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, + /* psig = 0 (in range, wrong) */ + { + 0, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, + /* psig = n (out of range) */ + { + 0, + { 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 }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, + /* signer_index out of range */ + { + 2, + { 0x22, 0xB4, 0xD1, 0x8F, 0xB6, 0x1A, 0xAA, 0xDD, 0xC4, 0x53, 0x04, 0x60, 0xBA, 0xF0, 0xC5, 0x0D, 0xD3, 0xF6, 0x18, 0x5A, 0x2A, 0x8C, 0x17, 0x31, 0x84, 0x68, 0x18, 0xE8, 0xB1, 0x91, 0x05, 0x7E }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, + /* psig for a different aggregate nonce */ + { + 0, + { 0x80, 0xBA, 0xC2, 0xC1, 0xFA, 0xA8, 0xF2, 0x95, 0xF5, 0x13, 0x59, 0x8F, 0x0E, 0x7B, 0x01, 0xEA, 0xFD, 0x05, 0xA9, 0x6B, 0x20, 0xAC, 0x7D, 0x9B, 0x8A, 0x2F, 0x24, 0x85, 0xBD, 0x22, 0x0B, 0xEA }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x62, 0xC0, 0xA0, 0x46, 0xDA, 0xCC, 0xE8, 0x6D, 0xDD, 0x03, 0x43, 0xC6, 0xD3, 0xC7, 0xC7, 0x9C, 0x22, 0x08, 0xBA, 0x0D, 0x9C, 0x9C, 0xF2, 0x4A, 0x6D, 0x04, 0x6D, 0x21, 0xD2, 0x1F, 0x90, 0xF7, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, + /* Aggregate nonce R1 is infinity */ + { + 0, + { 0x22, 0xB4, 0xD1, 0x8F, 0xB6, 0x1A, 0xAA, 0xDD, 0xC4, 0x53, 0x04, 0x60, 0xBA, 0xF0, 0xC5, 0x0D, 0xD3, 0xF6, 0x18, 0x5A, 0x2A, 0x8C, 0x17, 0x31, 0x84, 0x68, 0x18, 0xE8, 0xB1, 0x91, 0x05, 0x7E }, + 2, + { + { 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 } + }, + { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 } + }, + { + { 0x02, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 }, + { 0x03, 0x4D, 0x4B, 0x6C, 0xD1, 0x36, 0x10, 0x32, 0xCA, 0x9B, 0xD2, 0xAE, 0xB9, 0xD9, 0x00, 0xAA, 0x4D, 0x45, 0xD9, 0xEA, 0xD8, 0x0A, 0xC9, 0x42, 0x33, 0x74, 0xC4, 0x51, 0xA7, 0x25, 0x4D, 0x07, 0x66, 0x03, 0xF0, 0x06, 0xA1, 0x8D, 0x56, 0x53, 0xC4, 0xED, 0xF5, 0x39, 0x1F, 0xF2, 0x3A, 0x61, 0xF0, 0x3F, 0xF8, 0x3D, 0x23, 0x7E, 0x88, 0x0E, 0xE6, 0x11, 0x87, 0xFA, 0x9F, 0x37, 0x9A, 0x02, 0x8E, 0x0A } + }, + 0 + }, +}; + +struct fullagg_tweak_case { + unsigned char sk[32]; + unsigned char tweak[32]; + int is_xonly; + unsigned char expected_sk[32]; + unsigned char expected_pk[33]; +}; + +static const struct fullagg_tweak_case fullagg_tweak_cases[4] = { + /* Plain tweak of an even-y key */ + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 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 }, + 0, + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 0x03, 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 } + }, + /* Plain tweak of an odd-y key */ + { + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 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 }, + 0, + { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, + { 0x02, 0x53, 0x1F, 0xE6, 0x06, 0x81, 0x34, 0x50, 0x3D, 0x27, 0x23, 0x13, 0x32, 0x27, 0xC8, 0x67, 0xAC, 0x8F, 0xA6, 0xC8, 0x3C, 0x53, 0x7E, 0x9A, 0x44, 0xC3, 0xC5, 0xBD, 0xBD, 0xCB, 0x1F, 0xE3, 0x37 } + }, + /* X-only tweak of an even-y key */ + { + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xB9, 0xAD, 0xDB, 0xE5, 0xAE, 0x47, 0x9F, 0x3A, 0xBE, 0xD1, 0x5D, 0x8B, 0xCF, 0x35, 0x40, 0x40 }, + { 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 }, + 1, + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 0x03, 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 } + }, + /* X-only tweak of an odd-y key */ + { + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 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 }, + 1, + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 0x03, 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 } + }, +}; + +enum { FULLAGG_VECTORS_MAX_SIGNERS = 5 }; diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index c05801eecf..5326a971e5 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -14,95 +14,34 @@ #include "keyagg.h" #include "session.h" +#include "../nonce_common.h" #include "../../eckey.h" #include "../../hash.h" #include "../../scalar.h" #include "../../util.h" -/* Outputs 33 zero bytes if the given group element is the point at infinity and - * otherwise outputs the compressed serialization */ -static void secp256k1_musig_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { - if (secp256k1_ge_is_infinity(ge)) { - memset(out33, 0, 33); - } else { - /* Serialize must succeed because the point is not at infinity */ - 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_musig_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; - } - if (!secp256k1_eckey_pubkey_parse(ge, in33, 33)) { - return 0; - } - return secp256k1_ge_is_in_correct_subgroup(ge); -} - static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 }; static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, const secp256k1_scalar *k, const secp256k1_ge *pk) { - memcpy(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4); - secp256k1_scalar_get_b32(&secnonce->data[4], &k[0]); - secp256k1_scalar_get_b32(&secnonce->data[36], &k[1]); - secp256k1_ge_to_bytes(&secnonce->data[68], pk); + secp256k1_secnonce_save(secnonce->data, secp256k1_musig_secnonce_magic, k, pk); } static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_ge *pk, const secp256k1_musig_secnonce *secnonce) { - int is_zero; - ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0); - /* We make very sure that the nonce isn't invalidated by checking the values - * in addition to the magic. */ - is_zero = secp256k1_is_zero_array(&secnonce->data[4], 2 * 32); - secp256k1_declassify(ctx, &is_zero, sizeof(is_zero)); - ARG_CHECK(!is_zero); - - secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL); - secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL); - secp256k1_ge_from_bytes(pk, &secnonce->data[68]); - return 1; + return secp256k1_secnonce_load(ctx, k, pk, secnonce->data, secp256k1_musig_secnonce_magic); } -/* If flag is 1, invalidate the secnonce; if flag is 0, leave it. - * Constant-time. Flag must be 0 or 1. */ static void secp256k1_musig_secnonce_invalidate(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, int flag) { - secp256k1_memczero(secnonce->data, sizeof(secnonce->data), flag); - /* The flag argument is usually classified. So, the line above makes the - * magic and public key classified. However, we need both to be - * declassified. Note that we don't declassify the entire object, because if - * flag is 0, then k[0] and k[1] have not been zeroed. */ - secp256k1_declassify(ctx, secnonce->data, sizeof(secp256k1_musig_secnonce_magic)); - secp256k1_declassify(ctx, &secnonce->data[68], 64); + secp256k1_secnonce_invalidate(ctx, secnonce->data, flag); } static const unsigned char secp256k1_musig_pubnonce_magic[4] = { 0xf5, 0x7a, 0x3d, 0xa0 }; -/* Saves two group elements into a pubnonce. Requires that none of the provided - * group elements is infinity. */ static void secp256k1_musig_pubnonce_save(secp256k1_musig_pubnonce* nonce, const secp256k1_ge* ges) { - int i; - memcpy(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4); - for (i = 0; i < 2; i++) { - secp256k1_ge_to_bytes(nonce->data + 4+64*i, &ges[i]); - } + secp256k1_nonce_points_save(nonce->data, secp256k1_musig_pubnonce_magic, ges); } -/* Loads two group elements from a pubnonce. Returns 1 unless the nonce wasn't - * properly initialized */ static int secp256k1_musig_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge* ges, const secp256k1_musig_pubnonce* nonce) { - int i; - - ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4) == 0); - for (i = 0; i < 2; i++) { - secp256k1_ge_from_bytes(&ges[i], nonce->data + 4 + 64*i); - } - return 1; + return secp256k1_nonce_points_load(ctx, ges, nonce->data, secp256k1_musig_pubnonce_magic); } static const unsigned char secp256k1_musig_aggnonce_magic[4] = { 0xa8, 0xb7, 0xe4, 0x67 }; @@ -171,35 +110,22 @@ static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_ static const unsigned char secp256k1_musig_partial_sig_magic[4] = { 0xeb, 0xfb, 0x1a, 0x32 }; static void secp256k1_musig_partial_sig_save(secp256k1_musig_partial_sig* sig, secp256k1_scalar *s) { - memcpy(&sig->data[0], secp256k1_musig_partial_sig_magic, 4); - secp256k1_scalar_get_b32(&sig->data[4], s); + secp256k1_partial_sig_save(sig->data, secp256k1_musig_partial_sig_magic, s); } static int secp256k1_musig_partial_sig_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_musig_partial_sig* sig) { - int overflow; - - ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_musig_partial_sig_magic, 4) == 0); - secp256k1_scalar_set_b32(s, &sig->data[4], &overflow); - /* Parsed signatures can not overflow */ - VERIFY_CHECK(!overflow); - return 1; + return secp256k1_partial_sig_load(ctx, s, sig->data, secp256k1_musig_partial_sig_magic); } int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig_pubnonce* nonce, const unsigned char *in66) { secp256k1_ge ges[2]; - int i; VERIFY_CHECK(ctx != NULL); ARG_CHECK(nonce != NULL); ARG_CHECK(in66 != NULL); - for (i = 0; i < 2; i++) { - if (!secp256k1_eckey_pubkey_parse(&ges[i], &in66[33*i], 33)) { - return 0; - } - if (!secp256k1_ge_is_in_correct_subgroup(&ges[i])) { - return 0; - } + if (!secp256k1_nonce_points_parse(ges, in66)) { + return 0; } secp256k1_musig_pubnonce_save(nonce, ges); return 1; @@ -207,7 +133,6 @@ int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_pubnonce* nonce) { secp256k1_ge ges[2]; - int i; VERIFY_CHECK(ctx != NULL); ARG_CHECK(out66 != NULL); @@ -217,10 +142,8 @@ int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned ch if (!secp256k1_musig_pubnonce_load(ctx, ges, nonce)) { return 0; } - for (i = 0; i < 2; i++) { - /* serialize must succeed because the point was just loaded */ - secp256k1_eckey_pubkey_serialize33(&ges[i], &out66[33*i]); - } + /* serialize must succeed because the points were just loaded */ + secp256k1_nonce_points_serialize(out66, ges); return 1; } @@ -233,7 +156,7 @@ int secp256k1_musig_aggnonce_parse(const secp256k1_context* ctx, secp256k1_musig ARG_CHECK(in66 != NULL); for (i = 0; i < 2; i++) { - if (!secp256k1_musig_ge_parse_ext(&ges[i], &in66[33*i])) { + if (!secp256k1_eckey_parse_ext(&ges[i], &in66[33*i])) { return 0; } } @@ -254,7 +177,7 @@ int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned ch return 0; } for (i = 0; i < 2; i++) { - secp256k1_musig_ge_serialize_ext(&out66[33*i], &ges[i]); + secp256k1_eckey_serialize_ext(&out66[33*i], &ges[i]); } return 1; } @@ -546,7 +469,7 @@ static void secp256k1_musig_compute_noncehash(const secp256k1_hash_ctx *hash_ctx secp256k1_musig_compute_noncehash_sha256_tagged(&sha); for (i = 0; i < 2; i++) { - secp256k1_musig_ge_serialize_ext(buf, &aggnonce[i]); + secp256k1_eckey_serialize_ext(buf, &aggnonce[i]); secp256k1_sha256_write(hash_ctx, &sha, buf, sizeof(buf)); } secp256k1_sha256_write(hash_ctx, &sha, agg_pk32, 32); @@ -554,15 +477,6 @@ static void secp256k1_musig_compute_noncehash(const secp256k1_hash_ctx *hash_ctx secp256k1_sha256_finalize(hash_ctx, &sha, noncehash); } -/* out_nonce = nonce_pts[0] + b*nonce_pts[1] */ -static void secp256k1_effective_nonce(secp256k1_gej *out_nonce, const secp256k1_ge *nonce_pts, const secp256k1_scalar *b) { - secp256k1_gej tmp; - - secp256k1_gej_set_ge(&tmp, &nonce_pts[1]); - secp256k1_ecmult(out_nonce, &tmp, b, NULL); - secp256k1_gej_add_ge_var(out_nonce, out_nonce, &nonce_pts[0], NULL); -} - static void secp256k1_musig_nonce_process_internal(const secp256k1_context *ctx, int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_ge *aggnonce_pts, const unsigned char *agg_pk32, const unsigned char *msg) { unsigned char noncehash[32]; secp256k1_ge fin_nonce_pt; @@ -623,12 +537,6 @@ int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_ return 1; } -static void secp256k1_musig_partial_sign_clear(secp256k1_scalar *sk, secp256k1_scalar *k) { - secp256k1_scalar_clear(sk); - secp256k1_scalar_clear(&k[0]); - secp256k1_scalar_clear(&k[1]); -} - int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_partial_sig *partial_sig, secp256k1_musig_secnonce *secnonce, const secp256k1_keypair *keypair, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { secp256k1_scalar sk; secp256k1_ge pk, keypair_pk; @@ -647,7 +555,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p * of this function to fail */ secp256k1_memzero_explicit(secnonce, sizeof(*secnonce)); if (!ret) { - secp256k1_musig_partial_sign_clear(&sk, k); + secp256k1_partial_sign_clear(&sk, k); return 0; } @@ -657,13 +565,13 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p ARG_CHECK(session != NULL); if (!secp256k1_keypair_load(ctx, &sk, &keypair_pk, keypair)) { - secp256k1_musig_partial_sign_clear(&sk, k); + secp256k1_partial_sign_clear(&sk, k); return 0; } ARG_CHECK(secp256k1_fe_equal(&pk.x, &keypair_pk.x) && secp256k1_fe_equal(&pk.y, &keypair_pk.y)); if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - secp256k1_musig_partial_sign_clear(&sk, k); + secp256k1_partial_sign_clear(&sk, k); return 0; } @@ -680,7 +588,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p secp256k1_scalar_mul(&sk, &sk, &mu); if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - secp256k1_musig_partial_sign_clear(&sk, k); + secp256k1_partial_sign_clear(&sk, k); return 0; } @@ -695,7 +603,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p secp256k1_scalar_add(&k[0], &k[0], &k[1]); secp256k1_scalar_add(&s, &s, &k[0]); secp256k1_musig_partial_sig_save(partial_sig, &s); - secp256k1_musig_partial_sign_clear(&sk, k); + secp256k1_partial_sign_clear(&sk, k); return 1; } diff --git a/src/modules/nonce_common.h b/src/modules/nonce_common.h new file mode 100644 index 0000000000..de5b636769 --- /dev/null +++ b/src/modules/nonce_common.h @@ -0,0 +1,52 @@ +/*********************************************************************** + * Copyright (c) The libsecp256k1 developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_NONCE_COMMON_H +#define SECP256K1_MODULE_NONCE_COMMON_H + +#include "../../include/secp256k1.h" + +#include "../group.h" +#include "../scalar.h" + +/* Helpers shared between the modules implementing interactive signing + * protocols with two-point nonces (musig and fullagg). The magic + * argument distinguishes the modules' opaque objects from each other so that + * passing an object to the wrong module's API fails. */ + +/* Saves a secret nonce into a 132 byte buffer: magic (4 bytes) || k[0] (32 + * bytes) || k[1] (32 bytes) || pk (64 bytes). */ +static void secp256k1_secnonce_save(unsigned char *data, const unsigned char *magic, const secp256k1_scalar *k, const secp256k1_ge *pk); +static int secp256k1_secnonce_load(const secp256k1_context *ctx, secp256k1_scalar *k, secp256k1_ge *pk, const unsigned char *data, const unsigned char *magic); +/* If flag is 1, invalidate the secnonce; if flag is 0, leave it. + * Constant-time. Flag must be 0 or 1. */ +static void secp256k1_secnonce_invalidate(const secp256k1_context *ctx, unsigned char *data, int flag); + +/* Saves two group elements into a 132 byte buffer: magic (4 bytes) || ges[0] + * (64 bytes) || ges[1] (64 bytes). Requires that none of the provided group + * elements is infinity. */ +static void secp256k1_nonce_points_save(unsigned char *data, const unsigned char *magic, const secp256k1_ge *ges); +/* Loads two group elements. Returns 1 unless the buffer wasn't properly + * initialized. */ +static int secp256k1_nonce_points_load(const secp256k1_context *ctx, secp256k1_ge *ges, const unsigned char *data, const unsigned char *magic); +/* Parses two compressed points and verifies that they are in the correct + * subgroup. */ +static int secp256k1_nonce_points_parse(secp256k1_ge *ges, const unsigned char *in66); +/* Serializes two group elements as compressed points. Requires that none of + * the provided group elements is infinity. */ +static void secp256k1_nonce_points_serialize(unsigned char *out66, secp256k1_ge *ges); + +/* Saves a partial signature into a 36 byte buffer: magic (4 bytes) || s (32 + * bytes). */ +static void secp256k1_partial_sig_save(unsigned char *data, const unsigned char *magic, const secp256k1_scalar *s); +static int secp256k1_partial_sig_load(const secp256k1_context *ctx, secp256k1_scalar *s, const unsigned char *data, const unsigned char *magic); + +/* out_nonce = nonce_pts[0] + b*nonce_pts[1] */ +static void secp256k1_effective_nonce(secp256k1_gej *out_nonce, const secp256k1_ge *nonce_pts, const secp256k1_scalar *b); + +static void secp256k1_partial_sign_clear(secp256k1_scalar *sk, secp256k1_scalar *k); + +#endif diff --git a/src/modules/nonce_common_impl.h b/src/modules/nonce_common_impl.h new file mode 100644 index 0000000000..73751dfe91 --- /dev/null +++ b/src/modules/nonce_common_impl.h @@ -0,0 +1,122 @@ +/*********************************************************************** + * Copyright (c) The libsecp256k1 developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_NONCE_COMMON_IMPL_H +#define SECP256K1_MODULE_NONCE_COMMON_IMPL_H + +#include + +#include "../../include/secp256k1.h" + +#include "nonce_common.h" +#include "../eckey.h" +#include "../ecmult.h" +#include "../group.h" +#include "../scalar.h" +#include "../util.h" + +static void secp256k1_secnonce_save(unsigned char *data, const unsigned char *magic, const secp256k1_scalar *k, const secp256k1_ge *pk) { + memcpy(&data[0], magic, 4); + secp256k1_scalar_get_b32(&data[4], &k[0]); + secp256k1_scalar_get_b32(&data[36], &k[1]); + secp256k1_ge_to_bytes(&data[68], pk); +} + +static int secp256k1_secnonce_load(const secp256k1_context *ctx, secp256k1_scalar *k, secp256k1_ge *pk, const unsigned char *data, const unsigned char *magic) { + int is_zero; + ARG_CHECK(secp256k1_memcmp_var(&data[0], magic, 4) == 0); + /* We make very sure that the nonce isn't invalidated by checking the values + * in addition to the magic. */ + is_zero = secp256k1_is_zero_array(&data[4], 2 * 32); + secp256k1_declassify(ctx, &is_zero, sizeof(is_zero)); + ARG_CHECK(!is_zero); + + secp256k1_scalar_set_b32(&k[0], &data[4], NULL); + secp256k1_scalar_set_b32(&k[1], &data[36], NULL); + secp256k1_ge_from_bytes(pk, &data[68]); + return 1; +} + +static void secp256k1_secnonce_invalidate(const secp256k1_context *ctx, unsigned char *data, int flag) { + secp256k1_memczero(data, 132, flag); + /* The flag argument is usually classified. So, the line above makes the + * magic and public key classified. However, we need both to be + * declassified. Note that we don't declassify the entire object, because if + * flag is 0, then k[0] and k[1] have not been zeroed. */ + secp256k1_declassify(ctx, data, 4); + secp256k1_declassify(ctx, &data[68], 64); +} + +static void secp256k1_nonce_points_save(unsigned char *data, const unsigned char *magic, const secp256k1_ge *ges) { + int i; + memcpy(&data[0], magic, 4); + for (i = 0; i < 2; i++) { + secp256k1_ge_to_bytes(&data[4 + 64*i], &ges[i]); + } +} + +static int secp256k1_nonce_points_load(const secp256k1_context *ctx, secp256k1_ge *ges, const unsigned char *data, const unsigned char *magic) { + int i; + + ARG_CHECK(secp256k1_memcmp_var(&data[0], magic, 4) == 0); + for (i = 0; i < 2; i++) { + secp256k1_ge_from_bytes(&ges[i], &data[4 + 64*i]); + } + return 1; +} + +static int secp256k1_nonce_points_parse(secp256k1_ge *ges, const unsigned char *in66) { + int i; + + for (i = 0; i < 2; i++) { + if (!secp256k1_eckey_pubkey_parse(&ges[i], &in66[33*i], 33)) { + return 0; + } + if (!secp256k1_ge_is_in_correct_subgroup(&ges[i])) { + return 0; + } + } + return 1; +} + +static void secp256k1_nonce_points_serialize(unsigned char *out66, secp256k1_ge *ges) { + int i; + + for (i = 0; i < 2; i++) { + secp256k1_eckey_pubkey_serialize33(&ges[i], &out66[33*i]); + } +} + +static void secp256k1_partial_sig_save(unsigned char *data, const unsigned char *magic, const secp256k1_scalar *s) { + memcpy(&data[0], magic, 4); + secp256k1_scalar_get_b32(&data[4], s); +} + +static int secp256k1_partial_sig_load(const secp256k1_context *ctx, secp256k1_scalar *s, const unsigned char *data, const unsigned char *magic) { + int overflow; + + ARG_CHECK(secp256k1_memcmp_var(&data[0], magic, 4) == 0); + secp256k1_scalar_set_b32(s, &data[4], &overflow); + /* Parsed signatures can not overflow */ + VERIFY_CHECK(!overflow); + return 1; +} + +static void secp256k1_effective_nonce(secp256k1_gej *out_nonce, const secp256k1_ge *nonce_pts, const secp256k1_scalar *b) { + secp256k1_gej tmp; + + secp256k1_gej_set_ge(&tmp, &nonce_pts[1]); + secp256k1_ecmult(out_nonce, &tmp, b, NULL); + secp256k1_gej_add_ge_var(out_nonce, out_nonce, &nonce_pts[0], NULL); +} + +static void secp256k1_partial_sign_clear(secp256k1_scalar *sk, secp256k1_scalar *k) { + secp256k1_scalar_clear(sk); + secp256k1_scalar_clear(&k[0]); + secp256k1_scalar_clear(&k[1]); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 4b28bc51c3..74a40241bc 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -842,6 +842,14 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, # include "modules/schnorrsig/main_impl.h" #endif +#if defined(ENABLE_MODULE_FULLAGG) || defined(ENABLE_MODULE_MUSIG) +# include "modules/nonce_common_impl.h" +#endif + +#ifdef ENABLE_MODULE_FULLAGG +# include "modules/fullagg/main_impl.h" +#endif + #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index 1a4987bd22..b663fa3ced 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7790,6 +7790,10 @@ static void run_ecdsa_wycheproof(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_FULLAGG +# include "modules/fullagg/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/tests_impl.h" #endif @@ -8138,6 +8142,9 @@ static const struct tf_test_module registry_modules[] = { #ifdef ENABLE_MODULE_SCHNORRSIG MAKE_TEST_MODULE(schnorrsig), #endif +#ifdef ENABLE_MODULE_FULLAGG + MAKE_TEST_MODULE(fullagg), +#endif #ifdef ENABLE_MODULE_MUSIG MAKE_TEST_MODULE(musig), #endif diff --git a/tools/test_vectors_fullagg_generate.py b/tools/test_vectors_fullagg_generate.py new file mode 100755 index 0000000000..4b14703211 --- /dev/null +++ b/tools/test_vectors_fullagg_generate.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 + +import csv +import os +import sys + +if len(sys.argv) < 2: + print( + "This script converts BIP FullAgg test vectors in a given directory to a C file that can be used in the test framework." + ) + print("Usage: %s " % sys.argv[0]) + sys.exit(1) + + +def read_csv(filename): + with open(os.path.join(sys.argv[1], filename), newline="") as f: + rows = list(csv.reader(f)) + return rows[1:] + + +def hexstr_to_intarray(s): + return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(s)]) + + +def hexlist(s): + return s.split(";") if s else [] + + +def init_int(value, indent): + return indent * " " + "%d" % value + + +def init_array(s, indent): + return indent * " " + "{ %s }" % hexstr_to_intarray(s) + + +def init_arrays(strs, indent): + out = indent * " " + "{\n" + out += ",\n".join([init_array(x, indent + 4) for x in strs]) + out += "\n" + indent * " " + "}" + return out + + +def init_case(comment, fields): + out = " /* %s */\n" % comment + out += " {\n" + out += ",\n".join(fields) + out += "\n },\n" + return out + + +max_signers = 0 + +s = ( + """/** + * Automatically generated by %s. */ +""" + % sys.argv[0] +) + +# sign vectors +rows = read_csv("test_vectors_sign.csv") +n = max(len(hexlist(row[1])) for row in rows) +max_signers = max(max_signers, n) +s += """ +struct fullagg_sign_case { + size_t n_signers; + unsigned char sks[%d][32]; + unsigned char msgs[%d][32]; + unsigned char secnonces[%d][64]; + unsigned char expected_aggnonce[66]; + unsigned char expected_psigs[%d][32]; + unsigned char expected_sig[64]; +}; + +static const struct fullagg_sign_case fullagg_sign_cases[%d] = { +""" % (n, n, n, n, len(rows)) +for row in rows: + _, sks, msgs, secnonces, aggnonce, psigs, sig, comment = row + s += init_case(comment, [ + init_int(len(hexlist(sks)), 8), + init_arrays(hexlist(sks), 8), + init_arrays(hexlist(msgs), 8), + init_arrays(hexlist(secnonces), 8), + init_array(aggnonce, 8), + init_arrays(hexlist(psigs), 8), + init_array(sig, 8), + ]) +s += "};\n" + +# sign error vectors +rows = read_csv("test_vectors_sign_error.csv") +n = max(len(hexlist(row[4])) for row in rows) +max_signers = max(max_signers, n) +s += """ +struct fullagg_sign_error_case { + unsigned char sk[32]; + unsigned char msg[32]; + unsigned char secnonce[64]; + size_t n_signers; + unsigned char pubkeys[%d][32]; + unsigned char msgs[%d][32]; + unsigned char pubnonces[%d][66]; +}; + +static const struct fullagg_sign_error_case fullagg_sign_error_cases[%d] = { +""" % (n, n, n, len(rows)) +for row in rows: + _, sk, msg, secnonce, pubkeys, msgs, pubnonces, comment = row + s += init_case(comment, [ + init_array(sk, 8), + init_array(msg, 8), + init_array(secnonce, 8), + init_int(len(hexlist(pubkeys)), 8), + init_arrays(hexlist(pubkeys), 8), + init_arrays(hexlist(msgs), 8), + init_arrays(hexlist(pubnonces), 8), + ]) +s += "};\n" + +# verify vectors. The cases with signatures whose length is not 64 bytes, +# with lists of different lengths and with an empty signer list are omitted +# because they cannot be expressed through the C API. +rows = [ + row for row in read_csv("test_vectors_verify.csv") + if len(row[3]) == 128 + and len(hexlist(row[1])) == len(hexlist(row[2])) + and len(hexlist(row[1])) > 0 +] +n = max(len(hexlist(row[1])) for row in rows) +max_signers = max(max_signers, n) +s += """ +/* The verify vectors whose inputs cannot be expressed through the C API are + * omitted (signatures whose length is not 64 bytes, lists of different + * lengths and empty signer lists). */ +struct fullagg_verify_case { + size_t n_signers; + unsigned char pubkeys[%d][32]; + unsigned char msgs[%d][32]; + unsigned char sig[64]; + int expected; +}; + +static const struct fullagg_verify_case fullagg_verify_cases[%d] = { +""" % (n, n, len(rows)) +for row in rows: + _, pubkeys, msgs, sig, expected, comment = row + s += init_case(comment, [ + init_int(len(hexlist(pubkeys)), 8), + init_arrays(hexlist(pubkeys), 8), + init_arrays(hexlist(msgs), 8), + init_array(sig, 8), + init_int(int(expected == "TRUE"), 8), + ]) +s += "};\n" + +# partial signature verify vectors +rows = read_csv("test_vectors_partial_sig_verify.csv") +n = max(len(hexlist(row[3])) for row in rows) +max_signers = max(max_signers, n) +s += """ +struct fullagg_partial_sig_verify_case { + size_t signer_index; + unsigned char psig[32]; + size_t n_signers; + unsigned char pubkeys[%d][32]; + unsigned char msgs[%d][32]; + unsigned char pubnonces[%d][66]; + int expected; +}; + +static const struct fullagg_partial_sig_verify_case fullagg_partial_sig_verify_cases[%d] = { +""" % (n, n, n, len(rows)) +for row in rows: + _, signer_index, psig, pubkeys, msgs, pubnonces, expected, comment = row + s += init_case(comment, [ + init_int(int(signer_index), 8), + init_array(psig, 8), + init_int(len(hexlist(pubkeys)), 8), + init_arrays(hexlist(pubkeys), 8), + init_arrays(hexlist(msgs), 8), + init_arrays(hexlist(pubnonces), 8), + init_int(int(expected == "TRUE"), 8), + ]) +s += "};\n" + +# tweak vectors +rows = read_csv("test_vectors_tweak.csv") +s += """ +struct fullagg_tweak_case { + unsigned char sk[32]; + unsigned char tweak[32]; + int is_xonly; + unsigned char expected_sk[32]; + unsigned char expected_pk[33]; +}; + +static const struct fullagg_tweak_case fullagg_tweak_cases[%d] = { +""" % len(rows) +for row in rows: + _, sk, tweak, is_xonly, expected_sk, expected_pk, comment = row + s += init_case(comment, [ + init_array(sk, 8), + init_array(tweak, 8), + init_int(int(is_xonly == "TRUE"), 8), + init_array(expected_sk, 8), + init_array(expected_pk, 8), + ]) +s += "};\n" + +s += "\nenum { FULLAGG_VECTORS_MAX_SIGNERS = %d };" % max_signers +print(s)