diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59d2251497..09cb297228 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ env: SCHNORRSIG: 'no' MUSIG: 'no' ELLSWIFT: 'no' + DLEQ: 'no' ### test options SECP256K1_TEST_ITERS: 64 BENCH: 'yes' @@ -83,18 +84,18 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: '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' } + - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', DLEQ: 'yes' } - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } - - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' } - - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', DLEQ: 'yes' } + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', DLEQ: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } - - env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } - env_vars: { ECMULTGENKB: 2, ECMULTWINDOW: 2 } - env_vars: { ECMULTGENKB: 86, ECMULTWINDOW: 4 } cc: @@ -146,6 +147,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CC: ${{ matrix.cc }} steps: @@ -174,6 +176,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' steps: @@ -204,6 +207,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' steps: @@ -225,6 +229,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' CC: ${{ matrix.cc }} @@ -265,6 +270,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' steps: @@ -310,6 +316,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' SECP256K1_TEST_ITERS: 2 @@ -339,6 +346,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' CFLAGS: '-fsanitize=undefined,address -g' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' @@ -385,6 +393,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CC: ${{ matrix.cc }} SECP256K1_TEST_ITERS: 32 ASM: 'no' @@ -410,6 +419,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' CTIMETESTS: 'no' strategy: @@ -442,15 +452,15 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: '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', DLEQ: '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', DLEQ: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: @@ -499,13 +509,13 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', DLEQ: 'yes', CPPFLAGS: '-DVERIFY' } - BUILD: 'distcheck' steps: @@ -618,6 +628,7 @@ jobs: SCHNORRSIG: 'yes' MUSIG: 'yes' ELLSWIFT: 'yes' + DLEQ: 'yes' steps: - *CHECKOUT diff --git a/CMakeLists.txt b/CMakeLists.txt index 11dc3f6e53..f7d2658562 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ 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_ELLSWIFT "Enable ElligatorSwift module." ON) +option(SECP256K1_ENABLE_MODULE_DLEQ "Enable DLEQ module." OFF) option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) @@ -285,6 +286,7 @@ message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRA message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") +message(" DLEQ ................................ ${SECP256K1_ENABLE_MODULE_DLEQ}") message("Parameters:") message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") message(" ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB") diff --git a/Makefile.am b/Makefile.am index dc798575e3..9bfda0e602 100644 --- a/Makefile.am +++ b/Makefile.am @@ -314,3 +314,7 @@ endif if ENABLE_MODULE_ELLSWIFT include src/modules/ellswift/Makefile.am.include endif + +if ENABLE_MODULE_DLEQ +include src/modules/dleq/Makefile.am.include +endif diff --git a/ci/ci.sh b/ci/ci.sh index 515c14cd04..6d64750598 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 \ + EXPERIMENTAL ECDH RECOVERY EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT DLEQ \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ @@ -64,6 +64,7 @@ fi --enable-module-extrakeys="$EXTRAKEYS" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-module-musig="$MUSIG" \ + --enable-module-dleq="$DLEQ" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ diff --git a/configure.ac b/configure.ac index 6028ee288d..927e84cbba 100644 --- a/configure.ac +++ b/configure.ac @@ -191,6 +191,10 @@ AC_ARG_ENABLE(module_ellswift, AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) +AC_ARG_ENABLE(module_dleq, + AS_HELP_STRING([--enable-module-dleq],[enable DLEQ module [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_dleq], [no], [no])]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -397,6 +401,10 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" # Processing must be done in a reverse topological sorting of the dependency graph # (dependent module first). +if test x"$enable_module_dleq" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_DLEQ=1" +fi + if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi @@ -470,6 +478,7 @@ AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x" AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_DLEQ], [test x"$enable_module_dleq" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) @@ -494,6 +503,7 @@ echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module musig = $enable_module_musig" echo " module ellswift = $enable_module_ellswift" +echo " module dleq = $enable_module_dleq" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/include/secp256k1_dleq.h b/include/secp256k1_dleq.h new file mode 100644 index 0000000000..a43c2118ab --- /dev/null +++ b/include/secp256k1_dleq.h @@ -0,0 +1,76 @@ +#ifndef SECP256K1_DLEQ_H +#define SECP256K1_DLEQ_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module provides an implementation of Discrete Log Equality (DLEQ) proofs, + * as specified in BIP-374. A DLEQ proof allows proving knowledge of a discrete + * logarithm relationship between two pairs of elliptic curve points without + * revealing the secret scalar. + * + * Specifically, given points A, B, C, and the generator G, a DLEQ proof + * demonstrates that A = a*G and C = a*B for the same scalar a, without + * revealing a. + * + * The proof consists of two 32-byte scalars (e, s) totaling 64 bytes. + */ + +/** Generate a DLEQ proof. + * + * Proves knowledge of scalar a such that A = a*G and C = a*B without + * revealing a. + * + * Returns: 1 if proof generation succeeded + * 0 if nonce generation failed (negligible probability) or + * if any input is invalid + * + * Args: ctx: pointer to a context object + * Out: proof64: pointer to 64-byte proof = bytes(32, e) || bytes(32, s) + * In: seckey32: pointer to 32-byte secret key (scalar a) + * pubkey_B: pointer to public key B (base point) + * aux_rand32: pointer to 32-byte auxiliary randomness (can be NULL) + * msg: pointer to 32-byte message (can be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_dleq_prove( + const secp256k1_context *ctx, + unsigned char *proof64, + const unsigned char *seckey32, + const secp256k1_pubkey *pubkey_B, + const unsigned char *aux_rand32, + const unsigned char *msg +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) + SECP256K1_ARG_NONNULL(4); + +/** Verify a DLEQ proof. + * + * Verifies that A and C were generated from the same scalar. + * + * Returns: 1 if proof is valid + * 0 if proof is invalid or any input is invalid + * + * Args: ctx: pointer to a context object + * In: proof64: pointer to 64-byte proof = bytes(32, e) || bytes(32, s) + * pubkey_A: pointer to public key A + * pubkey_B: pointer to public key B (base point) + * pubkey_C: pointer to public key C + * msg: pointer to optional 32-byte message (can be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_dleq_verify( + const secp256k1_context *ctx, + const unsigned char *proof64, + const secp256k1_pubkey *pubkey_A, + const secp256k1_pubkey *pubkey_B, + const secp256k1_pubkey *pubkey_C, + const unsigned char *msg +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) + SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_DLEQ_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ecbbbbe8e9..578221aa48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,11 @@ if(SECP256K1_ENABLE_MODULE_ELLSWIFT) set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h) endif() +if(SECP256K1_ENABLE_MODULE_DLEQ) + add_compile_definitions(ENABLE_MODULE_DLEQ=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_dleq.h) +endif() + if(SECP256K1_ENABLE_MODULE_MUSIG) if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG) message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.") diff --git a/src/modules/dleq/Makefile.am.include b/src/modules/dleq/Makefile.am.include new file mode 100644 index 0000000000..f4740a2a99 --- /dev/null +++ b/src/modules/dleq/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_dleq.h +noinst_HEADERS += src/modules/dleq/main_impl.h +noinst_HEADERS += src/modules/dleq/tests_impl.h +noinst_HEADERS += src/modules/dleq/dleq_vectors.h diff --git a/src/modules/dleq/dleq_vectors.h b/src/modules/dleq/dleq_vectors.h new file mode 100644 index 0000000000..b26c933915 --- /dev/null +++ b/src/modules/dleq/dleq_vectors.h @@ -0,0 +1,105 @@ +/** + * Automatically generated by ./tools/test_vectors_dleq_generate.py. + * + * Test vectors according to BIP-374 ("Discrete Log Equality Proofs") are included in this file. + * Tests are included in src/modules/dleq/tests_impl.h. */ + +static const unsigned char a_bytes[6][32] = { + { 0xC0, 0x8C, 0xA8, 0xE0, 0xBB, 0x59, 0x76, 0x9F, 0xC6, 0xA4, 0xE0, 0x78, 0x45, 0x62, 0x84, 0xE0, 0x0E, 0xA3, 0x4F, 0x65, 0xAD, 0xD9, 0x88, 0xC2, 0x46, 0xE1, 0xBB, 0xA8, 0x58, 0x24, 0xCC, 0xDC }, + { 0x8E, 0x64, 0x1B, 0xA6, 0xBF, 0x7F, 0x64, 0xEE, 0xC7, 0x60, 0x05, 0xA2, 0x95, 0x85, 0xA5, 0x03, 0x53, 0x76, 0x37, 0x5F, 0x33, 0xE3, 0x31, 0x21, 0x5A, 0xED, 0xFE, 0x03, 0xB8, 0xE8, 0x0E, 0x7A }, + { 0xCF, 0xB9, 0xA7, 0xEC, 0xC4, 0x9B, 0xEA, 0x4F, 0x2E, 0x2E, 0xE3, 0x4C, 0x38, 0xA6, 0xF4, 0x8B, 0x5C, 0xD5, 0xBD, 0x06, 0xF4, 0xE4, 0xD4, 0xFF, 0xB4, 0x59, 0x05, 0xB3, 0xD2, 0x6D, 0xB8, 0x42 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }, + { 0xCF, 0xB9, 0xA7, 0xEC, 0xC4, 0x9B, 0xEA, 0x4F, 0x2E, 0x2E, 0xE3, 0x4C, 0x38, 0xA6, 0xF4, 0x8B, 0x5C, 0xD5, 0xBD, 0x06, 0xF4, 0xE4, 0xD4, 0xFF, 0xB4, 0x59, 0x05, 0xB3, 0xD2, 0x6D, 0xB8, 0x42 }, +}; + +static const unsigned char A_bytes[13][33] = { + { 0x02, 0x63, 0x7B, 0x2C, 0x3E, 0xA8, 0xCA, 0x80, 0xB9, 0xCA, 0xEC, 0xC5, 0x0F, 0x41, 0x34, 0xC8, 0x6A, 0xE9, 0xCF, 0x7A, 0x26, 0x91, 0x33, 0xE7, 0xAF, 0xC7, 0x1F, 0x30, 0xE3, 0xA3, 0xCD, 0xA6, 0x0C }, + { 0x02, 0x98, 0x3A, 0x72, 0xB4, 0xCB, 0x44, 0xD4, 0x32, 0x26, 0x41, 0xA7, 0xB2, 0x00, 0x19, 0x00, 0xCD, 0x6A, 0xE0, 0x90, 0x8A, 0x61, 0x05, 0x46, 0xC7, 0x3E, 0xD1, 0x26, 0xAC, 0xCD, 0xBA, 0x05, 0x14 }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x00 }, + { 0x00 }, + { 0x00 }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, +}; + +static const unsigned char B_bytes[13][33] = { + { 0x03, 0x4B, 0xCC, 0xB1, 0xC5, 0x70, 0xAC, 0x1F, 0x3B, 0xC4, 0x2D, 0x61, 0xFE, 0x35, 0xDE, 0x60, 0x5B, 0x99, 0x62, 0x65, 0x01, 0xCC, 0xB2, 0x02, 0x97, 0xE1, 0xAC, 0xBB, 0xF2, 0xD7, 0x15, 0x2A, 0xA1 }, + { 0x02, 0x31, 0xC6, 0x4E, 0x3E, 0xFA, 0x50, 0x6F, 0xDA, 0xD6, 0xAA, 0xD0, 0xF6, 0x08, 0x4D, 0x5F, 0x67, 0x39, 0xDE, 0x7F, 0x44, 0x8D, 0x7E, 0x66, 0xF9, 0xD2, 0x2F, 0x84, 0x26, 0x38, 0xF4, 0x1D, 0x60 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x00 }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, +}; + +static const unsigned char C_bytes[13][33] = { + { 0x02, 0x85, 0xB8, 0x26, 0xC8, 0xDD, 0x17, 0x58, 0x05, 0x90, 0x19, 0x06, 0xB6, 0xC9, 0xB4, 0x14, 0x0A, 0x30, 0xCB, 0xCC, 0x94, 0xC6, 0xE7, 0xDC, 0xF3, 0x64, 0x76, 0x03, 0x8B, 0xF9, 0x0D, 0x47, 0x18 }, + { 0x03, 0xAF, 0x1B, 0xC1, 0x4B, 0x38, 0x4E, 0xDA, 0x28, 0x39, 0x8D, 0xF6, 0xA7, 0x90, 0x0E, 0x56, 0x7C, 0x5B, 0x6F, 0x66, 0x13, 0xCA, 0xFC, 0xE5, 0x02, 0x7B, 0x98, 0xBE, 0x01, 0x52, 0x86, 0xF7, 0x1B }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x00 }, + { 0x00 }, + { 0x00 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x02, 0x1C, 0xB8, 0x11, 0x21, 0xA0, 0x0F, 0x89, 0x76, 0x99, 0x03, 0x30, 0x5A, 0x36, 0x7A, 0xD3, 0xCC, 0x02, 0xD5, 0xB4, 0x02, 0xB1, 0x2C, 0x02, 0x6E, 0x06, 0xAC, 0x94, 0xBD, 0xE2, 0x8C, 0xD6, 0x08 }, + { 0x03, 0x61, 0x14, 0x10, 0x56, 0x1C, 0x35, 0xDA, 0xE1, 0x31, 0x35, 0xE4, 0xAD, 0x80, 0x94, 0xBA, 0xAC, 0x9B, 0xBC, 0xF2, 0xF4, 0xE1, 0x84, 0x98, 0x18, 0x1A, 0x8F, 0xF8, 0xA6, 0xD4, 0x3B, 0xE9, 0xD9 }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, + { 0x03, 0xD9, 0xA9, 0x86, 0x24, 0xC0, 0xC7, 0x4F, 0xC7, 0xEE, 0xBD, 0x39, 0xED, 0x84, 0x17, 0x5F, 0x80, 0xD0, 0x3C, 0x77, 0x49, 0x08, 0xE7, 0x5C, 0xA7, 0x37, 0xA0, 0x74, 0x5D, 0x1C, 0x64, 0xE2, 0x0A }, +}; + +static const unsigned char auxrand_bytes[6][32] = { + { 0xC8, 0xD7, 0x05, 0x6A, 0xBD, 0x47, 0x26, 0xEB, 0x5A, 0x0F, 0x19, 0x87, 0x40, 0xAF, 0x14, 0xD6, 0xC1, 0xF0, 0xC1, 0x6E, 0x5D, 0x7A, 0x37, 0xEA, 0xEC, 0x62, 0x1B, 0x66, 0x1E, 0x66, 0x9A, 0xC4 }, + { 0x02, 0xA7, 0xB2, 0xE2, 0xF5, 0xA5, 0xE9, 0xB1, 0x07, 0x8D, 0xBB, 0x16, 0x05, 0x02, 0xA3, 0x24, 0x91, 0xFE, 0x80, 0xA0, 0x91, 0xE9, 0x1D, 0xD9, 0x2C, 0xF7, 0x7B, 0x0B, 0x7D, 0x90, 0x97, 0x0F }, + { 0xD3, 0x84, 0x66, 0xB7, 0x74, 0x84, 0x15, 0x4A, 0x3F, 0xCB, 0x31, 0x51, 0x09, 0x4C, 0x1C, 0x8A, 0x84, 0x5C, 0x73, 0xA3, 0xC0, 0x36, 0xB3, 0xA8, 0xEB, 0xFF, 0xD8, 0xEF, 0x62, 0xC9, 0x04, 0x7F }, + { 0xD3, 0x84, 0x66, 0xB7, 0x74, 0x84, 0x15, 0x4A, 0x3F, 0xCB, 0x31, 0x51, 0x09, 0x4C, 0x1C, 0x8A, 0x84, 0x5C, 0x73, 0xA3, 0xC0, 0x36, 0xB3, 0xA8, 0xEB, 0xFF, 0xD8, 0xEF, 0x62, 0xC9, 0x04, 0x7F }, + { 0xD3, 0x84, 0x66, 0xB7, 0x74, 0x84, 0x15, 0x4A, 0x3F, 0xCB, 0x31, 0x51, 0x09, 0x4C, 0x1C, 0x8A, 0x84, 0x5C, 0x73, 0xA3, 0xC0, 0x36, 0xB3, 0xA8, 0xEB, 0xFF, 0xD8, 0xEF, 0x62, 0xC9, 0x04, 0x7F }, + { 0xD3, 0x84, 0x66, 0xB7, 0x74, 0x84, 0x15, 0x4A, 0x3F, 0xCB, 0x31, 0x51, 0x09, 0x4C, 0x1C, 0x8A, 0x84, 0x5C, 0x73, 0xA3, 0xC0, 0x36, 0xB3, 0xA8, 0xEB, 0xFF, 0xD8, 0xEF, 0x62, 0xC9, 0x04, 0x7F }, +}; + +static const unsigned char msg_bytes[13][32] = { + { 0x00 }, + { 0x35, 0x84, 0x1C, 0xA5, 0x32, 0x84, 0x6E, 0x1C, 0xDD, 0x23, 0xA3, 0xD1, 0x07, 0x82, 0x43, 0x43, 0x58, 0x4F, 0x88, 0xEF, 0xF5, 0x80, 0x92, 0x94, 0x69, 0x86, 0x5E, 0xAE, 0x83, 0x55, 0xEE, 0x3C }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x2D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, + { 0x22, 0x61, 0x6B, 0xB5, 0xFB, 0x6D, 0x7C, 0x68, 0x27, 0x0F, 0x30, 0x51, 0x22, 0xF2, 0xA0, 0x9E, 0x83, 0x32, 0x39, 0xC4, 0xB1, 0xC9, 0xA0, 0x4E, 0x28, 0x51, 0x19, 0xFB, 0x60, 0x6A, 0xC7, 0x94 }, +}; + +static const unsigned char proof_bytes[13][64] = { + { 0x50, 0x35, 0x62, 0xD3, 0x69, 0x10, 0xCD, 0x2D, 0x61, 0xA4, 0xD0, 0x7C, 0x8F, 0xF6, 0x80, 0x26, 0x5C, 0x71, 0x3E, 0x63, 0xDD, 0xE0, 0xDC, 0xB8, 0x8E, 0x6E, 0xA3, 0xC5, 0x85, 0x97, 0xBD, 0xC0, 0x5B, 0x86, 0xDB, 0x9A, 0xF9, 0x5E, 0xCC, 0xC4, 0x75, 0xCE, 0x21, 0x77, 0xF9, 0x41, 0xC1, 0x18, 0xFE, 0xFE, 0xD2, 0x02, 0x27, 0xD4, 0xCE, 0x8C, 0xE9, 0x55, 0x7C, 0xB0, 0x08, 0x75, 0x8D, 0xE6 }, + { 0x5C, 0x7B, 0x27, 0xA3, 0x32, 0x10, 0x75, 0x0E, 0x9D, 0xE8, 0x67, 0x9D, 0x9F, 0x43, 0x49, 0x7C, 0xF9, 0xF1, 0x2A, 0xC6, 0x42, 0xCD, 0xE0, 0xA1, 0xFC, 0x26, 0x44, 0x3A, 0xA2, 0xFC, 0x89, 0xBF, 0x71, 0xAA, 0xBF, 0x7B, 0xAC, 0x89, 0xF5, 0xD8, 0xA9, 0x6C, 0xBE, 0x86, 0xDA, 0xBA, 0x15, 0x5F, 0xA7, 0x4D, 0x6F, 0x3E, 0x11, 0x11, 0x36, 0x17, 0x9E, 0x53, 0xB0, 0x4E, 0xB6, 0xD7, 0x80, 0x7F }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x00 }, + { 0x00 }, + { 0x00 }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1F, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, + { 0x78, 0xA5, 0x54, 0x4A, 0xFA, 0x75, 0xBF, 0x15, 0x26, 0x53, 0xFE, 0x55, 0xFB, 0x76, 0x92, 0x6F, 0x2F, 0x65, 0x13, 0x1B, 0xF0, 0x90, 0x97, 0x2A, 0x0B, 0x0B, 0x37, 0xD3, 0x10, 0xC2, 0x8A, 0x6B, 0xDE, 0x0E, 0x7B, 0xFA, 0xCC, 0x10, 0xAC, 0x12, 0xD3, 0x6F, 0x55, 0x31, 0x6B, 0xA1, 0x34, 0xB6, 0xBA, 0x0B, 0x84, 0x4A, 0x65, 0xAE, 0x05, 0xCA, 0xD5, 0x3C, 0x0B, 0x29, 0x6C, 0x66, 0x39, 0xBB }, +}; + +static const unsigned char success[13] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; diff --git a/src/modules/dleq/main_impl.h b/src/modules/dleq/main_impl.h new file mode 100644 index 0000000000..e2f6f51eb8 --- /dev/null +++ b/src/modules/dleq/main_impl.h @@ -0,0 +1,334 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_DLEQ_MAIN_H +#define SECP256K1_MODULE_DLEQ_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_dleq.h" +#include "../../hash.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0374/aux")||SHA256("BIP0374/aux"). */ +static void secp256k1_nonce_function_bip374_sha256_tagged_aux(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x48479343ul; + sha->s[1] = 0xa9eb648cul; + sha->s[2] = 0x58952fe4ul; + sha->s[3] = 0x4772d3b2ul; + sha->s[4] = 0x977ab0a0ul; + sha->s[5] = 0xcb8e2740ul; + sha->s[6] = 0x60bb4b81ul; + sha->s[7] = 0x68a41b66ul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0374/nonce")||SHA256("BIP0374/nonce"). */ +static void secp256k1_nonce_function_bip374_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xa810fc87ul; + sha->s[1] = 0x3b4a4d2aul; + sha->s[2] = 0xe302cfb4ul; + sha->s[3] = 0x322df1a0ul; + sha->s[4] = 0xd2e7fb82ul; + sha->s[5] = 0x7808570dul; + sha->s[6] = 0x9c33e0cdul; + sha->s[7] = 0x2dfbf7f6ul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0374/challenge")||SHA256("BIP0374/challenge"). */ +static void secp256k1_dleq_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x24f1c9c7ul; + sha->s[1] = 0xd1538c75ul; + sha->s[2] = 0xc9874ae8ul; + sha->s[3] = 0x6566de76ul; + sha->s[4] = 0x487843c9ul; + sha->s[5] = 0xc13d8026ul; + sha->s[6] = 0x39a2f3eful; + sha->s[7] = 0x2ad0fcb3ul; + + sha->bytes = 64; +} + +static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { + unsigned char buf[33]; + size_t size = 33; + secp256k1_eckey_pubkey_serialize33(p, buf); + secp256k1_sha256_write(sha, buf, size); + return 1; +} + +static void secp256k1_nonce_function_dleq(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *aux_rand32, const unsigned char *m) { + secp256k1_sha256 sha; + unsigned char masked_key[32]; + int i; + + if (aux_rand32 != NULL) { + secp256k1_nonce_function_bip374_sha256_tagged_aux(&sha); + secp256k1_sha256_write(&sha, aux_rand32, 32); + secp256k1_sha256_finalize(&sha, masked_key); + for (i = 0; i < 32; i++) { + masked_key[i] ^= key32[i]; + } + } else { + /* Precomputed TaggedHash("BIP0374/aux", 0x0000...00); */ + static const unsigned char ZERO_MASK[32] = { + 38, 255, 199, 133, 21, 94, 75, 99, + 18, 166, 0, 53, 197, 146, 253, 84, + 197, 228, 235, 145, 124, 59, 203, 21, + 66, 88, 250, 253, 207, 123, 43, 55 + }; + for (i = 0; i < 32; i++) { + masked_key[i] = key32[i] ^ ZERO_MASK[i]; + } + } + + secp256k1_nonce_function_bip374_sha256_tagged(&sha); + /* Hash masked-key||msg||m using the tagged hash as defined in BIP0374 + * Note: msg contains the serialized points A||C (66 bytes) */ + secp256k1_sha256_write(&sha, masked_key, 32); + secp256k1_sha256_write(&sha, msg, msglen); + if (m != NULL) { + secp256k1_sha256_write(&sha, m, 32); + } + secp256k1_sha256_finalize(&sha, nonce32); + secp256k1_sha256_clear(&sha); + secp256k1_memclear_explicit(masked_key, sizeof(masked_key)); +} + +/* Generates a nonce as defined in BIP0374 */ +static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *a32, const unsigned char *A_33, const unsigned char *C_33, const unsigned char *aux_rand32, const unsigned char *m) { + unsigned char buf[66]; + unsigned char nonce[32]; + + memcpy(buf, A_33, 33); + memcpy(buf + 33, C_33, 33); + secp256k1_nonce_function_dleq(nonce, buf, 66, a32, aux_rand32, m); + + secp256k1_scalar_set_b32(k, nonce, NULL); + if (secp256k1_scalar_is_zero(k)) { + return 0; + } + + return 1; +} + +/* Generates a challenge as defined in BIP0374 */ +static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *B, secp256k1_ge *R1, secp256k1_ge *R2, secp256k1_ge *A, secp256k1_ge *C, const unsigned char *m) { + unsigned char buf[32]; + secp256k1_sha256 sha; + secp256k1_ge generator_point = secp256k1_ge_const_g; + + secp256k1_dleq_sha256_tagged(&sha); + secp256k1_dleq_hash_point(&sha, A); + secp256k1_dleq_hash_point(&sha, B); + secp256k1_dleq_hash_point(&sha, C); + secp256k1_dleq_hash_point(&sha, &generator_point); + secp256k1_dleq_hash_point(&sha, R1); + secp256k1_dleq_hash_point(&sha, R2); + if (m) secp256k1_sha256_write(&sha, m, 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(e, buf, NULL); +} + +/* Generate points from scalar a such that A = a*G and C = a*B */ +static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *A, secp256k1_ge *C, const secp256k1_scalar *a, const secp256k1_ge *B) { + secp256k1_gej Aj, Cj; + + secp256k1_ecmult_gen(ecmult_gen_ctx, &Aj, a); + secp256k1_ge_set_gej(A, &Aj); + secp256k1_ecmult_const(&Cj, B, a); + secp256k1_ge_set_gej(C, &Cj); +} + +/* DLEQ Proof Generation (internal) + * + * For given elliptic curve points A, B, C, and G, the prover generates a proof to prove knowledge of a scalar a such + * that A = a⋅G and C = a⋅B without revealing anything about a. + * + * Returns: 1 if proof creation was successful. 0 if an error occurred. + * Out: scalar e: part of proof = bytes(32, e) || bytes(32, s). + * scalar s: other part of proof = bytes(32, e) || bytes(32, s). + * In: a : scalar a to be proven that both A and C were generated from + * B : point on the curve + * A : point on the curve(a⋅G) generated from a + * C : point on the curve(a⋅B) generated from a + * aux_rand32 : pointer to 32-byte auxiliary randomness used to generate the nonce in secp256k1_nonce_function_dleq. + * m : an optional message + * */ +static int secp256k1_dleq_prove_internal(const secp256k1_context *ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *a, secp256k1_ge *B, secp256k1_ge *A, secp256k1_ge *C, const unsigned char *aux_rand32, const unsigned char *m) { + secp256k1_ge R1, R2; + secp256k1_scalar k = { 0 }; + unsigned char a32[32]; + unsigned char A_33[33]; + unsigned char B_33[33]; + unsigned char C_33[33]; + int ret = 1; + + secp256k1_scalar_get_b32(a32, a); + /* Reject infinity points */ + if (secp256k1_ge_is_infinity(B) || secp256k1_ge_is_infinity(A) || secp256k1_ge_is_infinity(C)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(B, B_33); + secp256k1_eckey_pubkey_serialize33(A, A_33); + secp256k1_eckey_pubkey_serialize33(C, C_33); + ret &= secp256k1_dleq_nonce(&k, a32, A_33, C_33, aux_rand32, m); + + /* R1 = k*G, R2 = k*B */ + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &R1, &R2, &k, B); + /* We declassify the non-secret values R1 and R2 to allow using them as + * branch points. */ + secp256k1_declassify(ctx, &R1, sizeof(R1)); + secp256k1_declassify(ctx, &R2, sizeof(R2)); + + /* e = tagged hash(A, B, C, R1, R2) */ + /* s = k + e * a */ + secp256k1_dleq_challenge(e, B, &R1, &R2, A, C, m); + secp256k1_scalar_mul(s, e, a); + secp256k1_scalar_add(s, s, &k); + + secp256k1_scalar_clear(&k); + secp256k1_memclear_explicit(a32, sizeof(a32)); + return ret; +} + +/* DLEQ Proof Verification (internal) + * + * Verifies the proof. If the following algorithm succeeds, the points A and C were both generated from the same scalar. + * The former from multiplying by G, and the latter from multiplying by B. + * + * Returns: 1 if proof verification was successful. 0 if an error occurred. + * In: proof : proof bytes(32, e) || bytes(32, s) consists of scalar e and scalar s + * A : point on the curve(a⋅G) computed from a + * B : point on the curve + * C : point on the curve(a⋅B) computed from a + * m : optional message + * */ +static int secp256k1_dleq_verify_internal(secp256k1_scalar *s, secp256k1_scalar *e, secp256k1_ge *A, secp256k1_ge *B, secp256k1_ge *C, const unsigned char *m) { + secp256k1_scalar e_neg; + secp256k1_scalar e_expected; + secp256k1_gej Bj; + secp256k1_gej Aj, Cj; + secp256k1_gej R1j, R2j; + secp256k1_ge R1, R2; + secp256k1_gej tmpj; + + secp256k1_gej_set_ge(&Aj, A); + secp256k1_gej_set_ge(&Cj, C); + + secp256k1_scalar_negate(&e_neg, e); + /* R1 = s*G - e*A */ + secp256k1_ecmult(&R1j, &Aj, &e_neg, s); + /* R2 = s*B - e*C */ + secp256k1_ecmult(&tmpj, &Cj, &e_neg, &secp256k1_scalar_zero); + secp256k1_gej_set_ge(&Bj, B); + secp256k1_ecmult(&R2j, &Bj, s, &secp256k1_scalar_zero); + secp256k1_gej_add_var(&R2j, &R2j, &tmpj, NULL); + + /* Fail verification if R1j or R2j are infinity */ + if (secp256k1_gej_is_infinity(&R1j) || secp256k1_gej_is_infinity(&R2j)) { + return 0; + } + secp256k1_ge_set_gej(&R1, &R1j); + secp256k1_ge_set_gej(&R2, &R2j); + secp256k1_dleq_challenge(&e_expected, B, &R1, &R2, A, C, m); + + secp256k1_scalar_add(&e_expected, &e_expected, &e_neg); + return secp256k1_scalar_is_zero(&e_expected); +} + +int secp256k1_dleq_prove( + const secp256k1_context *ctx, + unsigned char *proof64, + const unsigned char *seckey32, + const secp256k1_pubkey *pubkey_B, + const unsigned char *aux_rand32, + const unsigned char *msg +) { + secp256k1_scalar a, s, e; + secp256k1_ge A, B, C; + int overflow; + int ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(proof64 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(pubkey_B != NULL); + + secp256k1_scalar_set_b32(&a, seckey32, &overflow); + if (overflow || secp256k1_scalar_is_zero(&a)) { + return 0; + } + + if (!secp256k1_pubkey_load(ctx, &B, pubkey_B)) { + secp256k1_scalar_clear(&a); + return 0; + } + + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &A, &C, &a, &B); + + ret = secp256k1_dleq_prove_internal(ctx, &s, &e, &a, &B, &A, &C, aux_rand32, msg); + secp256k1_scalar_clear(&a); + if (!ret) { + return 0; + } + + secp256k1_scalar_get_b32(&proof64[0], &e); + secp256k1_scalar_get_b32(&proof64[32], &s); + + return 1; +} + +int secp256k1_dleq_verify( + const secp256k1_context *ctx, + const unsigned char *proof64, + const secp256k1_pubkey *pubkey_A, + const secp256k1_pubkey *pubkey_B, + const secp256k1_pubkey *pubkey_C, + const unsigned char *msg +) { + secp256k1_scalar s, e; + secp256k1_ge A, B, C; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof64 != NULL); + ARG_CHECK(pubkey_A != NULL); + ARG_CHECK(pubkey_B != NULL); + ARG_CHECK(pubkey_C != NULL); + + secp256k1_scalar_set_b32(&e, &proof64[0], &overflow); + if (overflow) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &proof64[32], &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_pubkey_load(ctx, &A, pubkey_A)) { + return 0; + } + if (!secp256k1_pubkey_load(ctx, &B, pubkey_B)) { + return 0; + } + if (!secp256k1_pubkey_load(ctx, &C, pubkey_C)) { + return 0; + } + + return secp256k1_dleq_verify_internal(&s, &e, &A, &B, &C, msg); +} + +#endif /* SECP256K1_MODULE_DLEQ_MAIN_H */ diff --git a/src/modules/dleq/tests_impl.h b/src/modules/dleq/tests_impl.h new file mode 100644 index 0000000000..ece3f5c548 --- /dev/null +++ b/src/modules/dleq/tests_impl.h @@ -0,0 +1,252 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_DLEQ_TESTS_H +#define SECP256K1_MODULE_DLEQ_TESTS_H + +#include "dleq_vectors.h" +#include "../../unit_test.h" + +static void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { + secp256k1_scalar k1, k2; + CHECK(secp256k1_dleq_nonce(&k1, args[0], args[1], args[2], args[3], args[4]) == 1); + testrand_flip(args[n_flip], n_bytes); + CHECK(secp256k1_dleq_nonce(&k2, args[0], args[1], args[2], args[3], args[4]) == 1); + CHECK(secp256k1_scalar_eq(&k1, &k2) == 0); +} + +static void run_test_dleq_prove_verify(void) { + secp256k1_scalar s, e, a, k; + secp256k1_ge A, B, C; + unsigned char *args[5]; + unsigned char a32[32]; + unsigned char A_33[33]; + unsigned char C_33[33]; + unsigned char aux_rand[32]; + unsigned char msg[32]; + unsigned char proof_64[64] = {0}; + int i; + int overflow; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char aux_tag[] = {'B', 'I', 'P', '0', '3', '7', '4', '/', 'a', 'u', 'x'}; + unsigned char tag[] = {'B', 'I', 'P', '0', '3', '7', '4', '/', 'n', 'o', 'n', 'c', 'e'}; + unsigned char challenge_tag[] = {'B', 'I', 'P', '0', '3', '7', '4', '/', 'c', 'h', 'a', 'l', 'l', 'e', 'n', 'g', 'e'}; + + /* Check that hash initialized by secp256k1_nonce_function_bip374_sha256_tagged_aux has the expected state. */ + secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); + secp256k1_nonce_function_bip374_sha256_tagged_aux(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by secp256k1_nonce_function_bip374_sha256_tagged has the expected state. */ + secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); + secp256k1_nonce_function_bip374_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by secp256k1_dleq_sha256_tagged has the expected state. */ + secp256k1_sha256_initialize_tagged(&sha, challenge_tag, sizeof(challenge_tag)); + secp256k1_dleq_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + for (i = 0; i < COUNT; i++) { + testutil_random_ge_test(&B); + testutil_random_scalar_order(&a); + testrand256(aux_rand); + testrand_bytes_test(msg, sizeof(msg)); + secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, &A, &C, &a, &B); + CHECK(secp256k1_dleq_prove_internal(CTX, &s, &e, &a, &B, &A, &C, aux_rand, (i & 1) ? msg : NULL) == 1); + CHECK(secp256k1_dleq_verify_internal(&s, &e, &A, &B, &C, (i & 1) ? msg : NULL) == 1); + secp256k1_scalar_set_b32(&s, proof_64, &overflow); + VERIFY_CHECK(overflow == 0); + secp256k1_scalar_set_b32(&e, proof_64 + 32, &overflow); + VERIFY_CHECK(overflow == 0); + } + + { + secp256k1_scalar tmp; + secp256k1_scalar_set_int(&tmp, 1); + CHECK(secp256k1_dleq_verify_internal(&tmp, &e, &A, &B, &C, msg) == 0); + CHECK(secp256k1_dleq_verify_internal(&s, &tmp, &A, &B, &C, msg) == 0); + } + { + secp256k1_ge p_tmp; + testutil_random_ge_test(&p_tmp); + CHECK(secp256k1_dleq_verify_internal(&s, &e, &p_tmp, &B, &C, msg) == 0); + CHECK(secp256k1_dleq_verify_internal(&s, &e, &A, &p_tmp, &C, msg) == 0); + CHECK(secp256k1_dleq_verify_internal(&s, &e, &A, &B, &p_tmp, msg) == 0); + } + { + secp256k1_ge p_inf; + secp256k1_ge_set_infinity(&p_inf); + CHECK(secp256k1_dleq_prove_internal(CTX, &s, &e, &a, &p_inf, &A, &C, aux_rand, msg) == 0); + CHECK(secp256k1_dleq_prove_internal(CTX, &s, &e, &a, &B, &p_inf, &C, aux_rand, msg) == 0); + CHECK(secp256k1_dleq_prove_internal(CTX, &s, &e, &a, &B, &A, &p_inf, aux_rand, msg) == 0); + } + + /* Nonce tests */ + secp256k1_scalar_get_b32(a32, &a); + secp256k1_eckey_pubkey_serialize33(&A, A_33); + secp256k1_eckey_pubkey_serialize33(&C, C_33); + CHECK(secp256k1_dleq_nonce(&k, a32, A_33, C_33, aux_rand, msg) == 1); + + testrand_bytes_test(a32, sizeof(a32)); + testrand_bytes_test(A_33, sizeof(A_33)); + testrand_bytes_test(C_33, sizeof(C_33)); + testrand_bytes_test(aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = a32; + args[1] = A_33; + args[2] = C_33; + args[3] = aux_rand; + args[4] = msg; + for (i = 0; i < COUNT; i++) { + dleq_nonce_bitflip(args, 0, sizeof(a32)); + dleq_nonce_bitflip(args, 1, sizeof(A_33)); + /* Flip C */ + dleq_nonce_bitflip(args, 2, sizeof(C_33)); + /* Flip C again */ + dleq_nonce_bitflip(args, 2, sizeof(C_33)); + dleq_nonce_bitflip(args, 3, sizeof(aux_rand)); + dleq_nonce_bitflip(args, 4, sizeof(msg)); + } + + /* NULL aux_rand and msg arguments are allowed.*/ + CHECK(secp256k1_dleq_nonce(&k, a32, A_33, C_33, NULL, NULL) == 1); + CHECK(secp256k1_dleq_nonce(&k, a32, A_33, C_33, aux_rand, NULL) == 1); + CHECK(secp256k1_dleq_nonce(&k, a32, A_33, C_33, NULL, msg) == 1); +} + +/* Test BIP-374 test vectors ("Discrete Log Equality Proofs"). + * See tools/test_vectors_dleq_generate.py + * */ + +static unsigned char zero_array[32] = {0x00}; + +/* Helper function to check if given array is NOT equivalent to all zero array. + * Used to detect test vectors where zero array can represent: + * B_bytes at infinity + * Empty optional msg_bytes + * */ +static int is_not_empty(const unsigned char *arr){ + return (memcmp(arr, zero_array, 32) != 0); +} + +static void run_test_dleq_bip374_vectors(void) { + secp256k1_scalar a, s, e; + secp256k1_ge A; + secp256k1_ge B; + secp256k1_ge C; + int i; + + /* bip-0374/test_vectors_generate_proof.csv */ + for (i = 0; i < 6; ++i) { + int ret = 1; + const unsigned char *m = NULL; + secp256k1_ge_set_infinity(&B); + /* expect the last 3 generate proof vectors to fail */ + if (i > 2) ret = 0; + + secp256k1_scalar_set_b32(&a, a_bytes[i], NULL); + if (is_not_empty(B_bytes[i])) { + CHECK(secp256k1_eckey_pubkey_parse(&B, B_bytes[i], 33) == 1); + } + + secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, &A, &C, &a, &B); + + if (is_not_empty(msg_bytes[i])) { + m = msg_bytes[i]; + } + CHECK(secp256k1_dleq_prove_internal(CTX, &s, &e, &a, &B, &A, &C, (unsigned char*)(auxrand_bytes[i]), m) == ret); + + if (ret) { + unsigned char proof[64]; + secp256k1_scalar_get_b32(proof, &e); + secp256k1_scalar_get_b32(proof + 32, &s); + CHECK(memcmp(proof, proof_bytes[i], 64) == 0); + CHECK(secp256k1_dleq_verify_internal(&s, &e, &A, &B, &C, m) == 1); + } + } + + /* bip-0374/test_vectors_verify_proof.csv */ + for (i = 0; i < 13; ++i) { + const unsigned char *m = NULL; + + if (i > 2 && i < 6) { + /* Skip tests indices 3-5: proof generation failure cases (a=0, a=N, B=infinity). + * These contain placeholder data from test_vectors_generate_proof.csv that would + * fail to parse. Only indices 0-2 and 6-12 have valid test data. + * */ + continue; + } + + CHECK(secp256k1_eckey_pubkey_parse(&A, A_bytes[i], 33) == 1); + CHECK(secp256k1_eckey_pubkey_parse(&B, B_bytes[i], 33) == 1); + CHECK(secp256k1_eckey_pubkey_parse(&C, C_bytes[i], 33) == 1); + + secp256k1_scalar_set_b32(&e, proof_bytes[i], NULL); + secp256k1_scalar_set_b32(&s, proof_bytes[i] + 32, NULL); + + if (is_not_empty(msg_bytes[i])) { + m = msg_bytes[i]; + } + + CHECK(secp256k1_dleq_verify_internal(&s, &e, &A, &B, &C, m) == success[i]); + } +} + +static void run_test_dleq_api(void) { + secp256k1_pubkey B, A, C; + unsigned char seckey[32]; + unsigned char proof[64]; + unsigned char aux_rand[32]; + unsigned char msg[32]; + secp256k1_scalar a; + secp256k1_ge A_ge, B_ge, C_ge; + + /* Generate prove material */ + testrand256(seckey); + testrand256(aux_rand); + testrand256(msg); + testutil_random_ge_test(&B_ge); + secp256k1_pubkey_save(&B, &B_ge); + + /* Check dleq prove input validation */ + CHECK_ILLEGAL(STATIC_CTX, secp256k1_dleq_prove(STATIC_CTX, proof, seckey, &B, aux_rand, msg)); + CHECK_ILLEGAL(CTX, secp256k1_dleq_prove(CTX, NULL, seckey, &B, aux_rand, msg)); + CHECK_ILLEGAL(CTX, secp256k1_dleq_prove(CTX, proof, NULL, &B, aux_rand, msg)); + CHECK_ILLEGAL(CTX, secp256k1_dleq_prove(CTX, proof, seckey, NULL, aux_rand, msg)); + CHECK(secp256k1_dleq_prove(CTX, proof, seckey, &B, NULL, msg) == 1); + CHECK(secp256k1_dleq_prove(CTX, proof, seckey, &B, aux_rand, NULL) == 1); + + /* Generate verify material */ + secp256k1_scalar_set_b32(&a, seckey, NULL); + secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, &A_ge, &C_ge, &a, &B_ge); + secp256k1_pubkey_save(&A, &A_ge); + secp256k1_pubkey_save(&C, &C_ge); + + /* Check dleq verify input validation */ + CHECK_ILLEGAL(CTX, secp256k1_dleq_verify(CTX, NULL, &A, &B, &C, msg)); + CHECK_ILLEGAL(CTX, secp256k1_dleq_verify(CTX, proof, NULL, &B, &C, msg)); + CHECK_ILLEGAL(CTX, secp256k1_dleq_verify(CTX, proof, &A, NULL, &C, msg)); + CHECK_ILLEGAL(CTX, secp256k1_dleq_verify(CTX, proof, &A, &B, NULL, msg)); + /* Verify rejects an invalid (all-zero) proof */ + memset(proof, 0, sizeof(proof)); + CHECK(secp256k1_dleq_verify(CTX, proof, &A, &B, &C, msg) == 0); + + /* Verify public API prove and verify functions */ + CHECK(secp256k1_dleq_prove(CTX, proof, seckey, &B, aux_rand, msg) == 1); + CHECK(secp256k1_dleq_verify(CTX, proof, &A, &B, &C, msg) == 1); + CHECK(secp256k1_dleq_prove(CTX, proof, seckey, &B, NULL, NULL) == 1); + CHECK(secp256k1_dleq_verify(CTX, proof, &A, &B, &C, NULL) == 1); +} + +static const struct tf_test_entry tests_dleq[] = { + CASE(test_dleq_prove_verify), + CASE(test_dleq_bip374_vectors), + CASE(test_dleq_api), +}; + +#endif /* SECP256K1_MODULE_DLEQ_TESTS_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index ddd9849546..4299063522 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -825,3 +825,7 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_ELLSWIFT # include "modules/ellswift/main_impl.h" #endif + +#ifdef ENABLE_MODULE_DLEQ +# include "modules/dleq/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index e09f5c7d23..b42e47e3a3 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7467,6 +7467,10 @@ static void run_ecdsa_wycheproof(void) { # include "modules/ellswift/tests_impl.h" #endif +#ifdef ENABLE_MODULE_DLEQ +#include "modules/dleq/tests_impl.h" +#endif + static void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7800,6 +7804,9 @@ static const struct tf_test_module registry_modules[] = { #endif #ifdef ENABLE_MODULE_ELLSWIFT MAKE_TEST_MODULE(ellswift), +#endif +#ifdef ENABLE_MODULE_DLEQ + MAKE_TEST_MODULE(dleq), #endif MAKE_TEST_MODULE(utils), }; diff --git a/tools/test_vectors_dleq_generate.py b/tools/test_vectors_dleq_generate.py new file mode 100644 index 0000000000..f6ad8080ef --- /dev/null +++ b/tools/test_vectors_dleq_generate.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 + +import sys +import csv +import textwrap + +if len(sys.argv) < 2: + print( + "This script converts BIP 374 DLEQ 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) + +s = """/** + * Automatically generated by %s. + * + * Test vectors according to BIP-374 ("Discrete Log Equality Proofs") are included in this file. + * Tests are included in src/modules/dleq/tests_impl.h. */ +""" % sys.argv[0] + + +def hexstr_to_intarray(str): + try: + return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)]) + except ValueError: + return "0x00" + + +def create_init(name, rows, cols): + return """ +static const unsigned char %s[%d][%d] = { +""" % ( + name, + rows, + cols, + ) + + +def init_array(key): + return textwrap.indent("{ %s };" % ", ".join(test_case[key]), "") + + +def init_arrays(key): + s = textwrap.indent( + ",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in test_case[key]]), 4 * " " + ) + s += textwrap.indent(",\n};\n", "") + return s + + +# Define lists to store each column from the test_vectors_(generate|verify)_proof.csv files +test_case = { + "index": [], + "point_G": [], + "scalar_a": [], + "point_A": [], + "point_B": [], + "point_C": [], + "auxrand_r": [], + "message": [], + "comment": [], + "result_proof": [], + "result_success": [], +} + + +with open(sys.argv[1] + "/test_vectors_generate_proof.csv", newline="") as csvfile: + reader = csv.DictReader(csvfile) + # Skip the first 5 rows since those test vectors don't use secp's generator point + for _ in range(5): + next(reader, None) + + for row in reader: + for key in test_case: + if key in row: + # these keys are present in test_vectors_generate_proof.csv + # if special cases are encountered, "0" is filled in place of the value (INFINITY/INVALID/"") + special_cases = { + "point_B": "INFINITY", + "result_proof": "INVALID", + "message": "", + } + test_case[key].append( + "0" if row[key] in special_cases.get(key, []) else row[key] + ) + else: + # these keys are not present in current csv file but are present in test_vectors_verify_proof.csv + if key in {"point_A", "point_C"}: + # "0" is filled as value for these missing keys + test_case[key].append("0") + elif key == "result_success": + # success/failure value is obtained from row["comment"] for the missing key "result_success" + test_case[key].append( + "1" if "Success" in row.get("comment", "") else "0" + ) + else: + sys.exit( + "Unexpected missing_key encountered when parsing test_vectors_generate_proof.csv" + ) + + +with open(sys.argv[1] + "/test_vectors_verify_proof.csv", newline="") as csvfile: + reader = csv.DictReader(csvfile) + for _ in range( + 5 + ): # Skip the first 5 rows since those test vectors don't use secp's generator point + next(reader, None) + + for i in range(3): + # Fill point_A and point_C for the 3 success cases (array indices 0-2) from verify CSV + # These correspond to generate and verify CSV rows 5-7 + row = next(reader) + test_case["point_A"][i] = row["point_A"] + test_case["point_C"][i] = row["point_C"] + + for row in reader: + for key in test_case: + if key in row: + # these keys are present in test_vectors_verify_proof.csv + # not handling row[key] == "TRUE" since it doesn't appear in the BIP test vectors + test_case[key].append( + "0" if key == "result_success" and row[key] == "FALSE" else row[key] + ) + else: + # these keys are not present in current csv file but are present in test_vectors_generate_proof.csv + if key == "result_proof": + # interpret "result_proof" key in the test_vectors_generate_proof.csv test vectors + # same as "proof" key in the test_vectors_verify_proof.csv test vectors + test_case["result_proof"].append(row["proof"]) + elif key not in {"scalar_a", "auxrand_r"}: # skip expected missing keys + sys.exit( + "Unexpected missing key encountered when test_vectors_verify_proof.csv" + ) + + +s += create_init("a_bytes", len(test_case["scalar_a"]), 32) +s += init_arrays("scalar_a") + +s += create_init("A_bytes", len(test_case["point_A"]), 33) +s += init_arrays("point_A") + +s += create_init("B_bytes", len(test_case["point_B"]), 33) +s += init_arrays("point_B") + +s += create_init("C_bytes", len(test_case["point_C"]), 33) +s += init_arrays("point_C") + +s += create_init("auxrand_bytes", len(test_case["auxrand_r"]), 32) +s += init_arrays("auxrand_r") + +s += create_init("msg_bytes", len(test_case["message"]), 32) +s += init_arrays("message") + +s += create_init("proof_bytes", len(test_case["result_proof"]), 64) +s += init_arrays("result_proof") + +s += "\nstatic const unsigned char success[%d] = " % len(test_case["result_success"]) +s += init_array("result_success") + +print(s)