From 3cf8f70ba15b9f323689fa3448540fe2980311fe Mon Sep 17 00:00:00 2001 From: Greg Maxwell Date: Wed, 5 Aug 2015 16:17:50 +0200 Subject: [PATCH 001/381] Add 64-bit integer utilities --- configure.ac | 6 ++++++ src/testrand.h | 3 +++ src/testrand_impl.h | 19 +++++++++++++++++- src/tests.c | 49 ++++++++++++++++++++++++++++++++++++++++++++- src/util.h | 28 +++++++++++++++++++++++++- 5 files changed, 102 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 6021b760..e61b3a70 100644 --- a/configure.ac +++ b/configure.ac @@ -238,6 +238,12 @@ else set_precomp=no fi +AC_MSG_CHECKING([for __builtin_clzll]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() { __builtin_clzll(1);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_CLZLL,1,[Define this symbol if __builtin_clzll is available]) ], + [ AC_MSG_RESULT([no]) + ]) + if test x"$req_asm" = x"auto"; then SECP_64BIT_ASM_CHECK if test x"$has_64bit_asm" = x"yes"; then diff --git a/src/testrand.h b/src/testrand.h index f1f9be07..82599593 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -35,4 +35,7 @@ static void secp256k1_rand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); +/** Generate a pseudorandom 64-bit integer in the range min..max, inclusive. */ +static int64_t secp256k1_rands64(uint64_t min, uint64_t max); + #endif /* SECP256K1_TESTRAND_H */ diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 30a91e52..0db523d2 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -107,4 +107,21 @@ static void secp256k1_rand256_test(unsigned char *b32) { secp256k1_rand_bytes_test(b32, 32); } +SECP256K1_INLINE static int64_t secp256k1_rands64(uint64_t min, uint64_t max) { + uint64_t range; + uint64_t r; + uint64_t clz; + VERIFY_CHECK(max >= min); + if (max == min) { + return min; + } + range = max - min; + clz = secp256k1_clz64_var(range); + do { + r = ((uint64_t)secp256k1_rand32() << 32) | secp256k1_rand32(); + r >>= clz; + } while (r > range); + return min + (int64_t)r; +} + #endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/tests.c b/src/tests.c index b07a8e6d..27d30ccc 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -135,6 +135,52 @@ void random_scalar_order_b32(unsigned char *b32) { secp256k1_scalar_get_b32(b32, &num); } +void run_util_tests(void) { + int i; + uint64_t r; + uint64_t r2; + uint64_t r3; + int64_t s; + CHECK(secp256k1_clz64_var(0) == 64); + CHECK(secp256k1_clz64_var(1) == 63); + CHECK(secp256k1_clz64_var(2) == 62); + CHECK(secp256k1_clz64_var(3) == 62); + CHECK(secp256k1_clz64_var(~0ULL) == 0); + CHECK(secp256k1_clz64_var((~0ULL) - 1) == 0); + CHECK(secp256k1_clz64_var((~0ULL) >> 1) == 1); + CHECK(secp256k1_clz64_var((~0ULL) >> 2) == 2); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MAX) == 0); + CHECK(r == INT64_MAX); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MAX - 1) == 0); + CHECK(r == INT64_MAX - 1); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MIN) == 1); + CHECK(r == (uint64_t)INT64_MAX + 1); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MIN + 1) == 1); + CHECK(r == (uint64_t)INT64_MAX); + CHECK(secp256k1_sign_and_abs64(&r, 0) == 0); + CHECK(r == 0); + CHECK(secp256k1_sign_and_abs64(&r, 1) == 0); + CHECK(r == 1); + CHECK(secp256k1_sign_and_abs64(&r, -1) == 1); + CHECK(r == 1); + CHECK(secp256k1_sign_and_abs64(&r, 2) == 0); + CHECK(r == 2); + CHECK(secp256k1_sign_and_abs64(&r, -2) == 1); + CHECK(r == 2); + for (i = 0; i < 10; i++) { + CHECK(secp256k1_clz64_var((~0ULL) - secp256k1_rand32()) == 0); + r = ((uint64_t)secp256k1_rand32() << 32) | secp256k1_rand32(); + r2 = secp256k1_rands64(0, r); + CHECK(r2 <= r); + r3 = secp256k1_rands64(r2, r); + CHECK((r3 >= r2) && (r3 <= r)); + r = secp256k1_rands64(0, INT64_MAX); + s = (int64_t)r * (secp256k1_rand32()&1?-1:1); + CHECK(secp256k1_sign_and_abs64(&r2, s) == (s < 0)); + CHECK(r2 == r); + } +} + void run_context_tests(int use_prealloc) { secp256k1_pubkey pubkey; secp256k1_pubkey zero_pubkey; @@ -5515,6 +5561,7 @@ int main(int argc, char **argv) { run_rand_bits(); run_rand_int(); + run_util_tests(); run_sha256_tests(); run_hmac_sha256_tests(); diff --git a/src/util.h b/src/util.h index 8289e23e..065f47da 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -145,6 +145,32 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz return ret; } +/* Extract the sign of an int64, take the abs and return a uint64, constant time. */ +SECP256K1_INLINE static int secp256k1_sign_and_abs64(uint64_t *out, int64_t in) { + uint64_t mask0, mask1; + int ret; + ret = in < 0; + mask0 = ret + ~((uint64_t)0); + mask1 = ~mask0; + *out = (uint64_t)in; + *out = (*out & mask0) | ((~*out + 1) & mask1); + return ret; +} + +SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { + int ret; + if (!x) { + return 64; + } +# if defined(HAVE_BUILTIN_CLZLL) + ret = __builtin_clzll(x); +# else + /*FIXME: debruijn fallback. */ + for (ret = 0; ((x & (1ULL << 63)) == 0); x <<= 1, ret++); +# endif + return ret; +} + /* Macro for restrict, when available and not in a VERIFY build. */ #if defined(SECP256K1_BUILD) && defined(VERIFY) # define SECP256K1_RESTRICT From 16618fcd8db778cabfaa6faf378ea4cf94aeb952 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Wed, 5 Aug 2015 19:04:14 +0200 Subject: [PATCH 002/381] Pedersen commitments, borromean ring signatures, and ZK range proofs. This commit adds three new cryptosystems to libsecp256k1: Pedersen commitments are a system for making blinded commitments to a value. Functionally they work like: commit_b,v = H(blind_b || value_v), except they are additively homorphic, e.g. C(b1, v1) - C(b2, v2) = C(b1 - b2, v1 - v2) and C(b1, v1) - C(b1, v1) = 0, etc. The commitments themselves are EC points, serialized as 33 bytes. In addition to the commit function this implementation includes utility functions for verifying that a set of commitments sums to zero, and for picking blinding factors that sum to zero. If the blinding factors are uniformly random, pedersen commitments have information theoretic privacy. Borromean ring signatures are a novel efficient ring signature construction for AND/OR admissions policies (the code here implements an AND of ORs, each of any size). This construction requires 32 bytes of signature per pubkey used plus 32 bytes of constant overhead. With these you can construct signatures like "Given pubkeys A B C D E F G, the signer knows the discrete logs satisifying (A || B) & (C || D || E) & (F || G)". ZK range proofs allow someone to prove a pedersen commitment is in a particular range (e.g. [0..2^64)) without revealing the specific value. The construction here is based on the above borromean ring signature and uses a radix-4 encoding and other optimizations to maximize efficiency. It also supports encoding proofs with a non-private base-10 exponent and minimum-value to allow trading off secrecy for size and speed (or just avoiding wasting space keeping data private that was already public due to external constraints). A proof for a 32-bit mantissa takes 2564 bytes, but 2048 bytes of this can be used to communicate a private message to a receiver who shares a secret random seed with the prover. Also: get rid of precomputed H tables (Pieter Wuille) --- Makefile.am | 4 + configure.ac | 15 + include/secp256k1_rangeproof.h | 186 ++++++ src/bench_rangeproof.c | 65 +++ src/modules/rangeproof/Makefile.am.include | 15 + src/modules/rangeproof/borromean.h | 24 + src/modules/rangeproof/borromean_impl.h | 201 +++++++ src/modules/rangeproof/main_impl.h | 160 +++++ src/modules/rangeproof/pedersen.h | 21 + src/modules/rangeproof/pedersen_impl.h | 54 ++ src/modules/rangeproof/rangeproof.h | 18 + src/modules/rangeproof/rangeproof_impl.h | 649 +++++++++++++++++++++ src/modules/rangeproof/tests_impl.h | 279 +++++++++ src/secp256k1.c | 9 + src/tests.c | 8 + 15 files changed, 1708 insertions(+) create mode 100644 include/secp256k1_rangeproof.h create mode 100644 src/bench_rangeproof.c create mode 100644 src/modules/rangeproof/Makefile.am.include create mode 100644 src/modules/rangeproof/borromean.h create mode 100644 src/modules/rangeproof/borromean_impl.h create mode 100644 src/modules/rangeproof/main_impl.h create mode 100644 src/modules/rangeproof/pedersen.h create mode 100644 src/modules/rangeproof/pedersen_impl.h create mode 100644 src/modules/rangeproof/rangeproof.h create mode 100644 src/modules/rangeproof/rangeproof_impl.h create mode 100644 src/modules/rangeproof/tests_impl.h diff --git a/Makefile.am b/Makefile.am index d8c1c79e..fbf219a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -152,3 +152,7 @@ endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif + +if ENABLE_MODULE_RANGEPROOF +include src/modules/rangeproof/Makefile.am.include +endif diff --git a/configure.ac b/configure.ac index e61b3a70..fb16263a 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,12 @@ AC_ARG_ENABLE(module_recovery, [enable_module_recovery=$enableval], [enable_module_recovery=no]) + +AC_ARG_ENABLE(module_rangeproof, + AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module (default is no)]), + [enable_module_rangeproof=$enableval], + [enable_module_rangeproof=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -499,6 +505,10 @@ if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi +if test x"$enable_module_rangeproof" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) +fi + AC_C_BIGENDIAN() if test x"$use_external_asm" = x"yes"; then @@ -514,6 +524,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([******]) else if test x"$enable_module_ecdh" = x"yes"; then @@ -522,6 +533,9 @@ else if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_rangeproof" = x"yes"; then + AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.]) + fi fi AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) @@ -537,6 +551,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) 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_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h new file mode 100644 index 00000000..54b454ef --- /dev/null +++ b/include/secp256k1_rangeproof.h @@ -0,0 +1,186 @@ +#ifndef _SECP256K1_RANGEPROOF_ +# define _SECP256K1_RANGEPROOF_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** Initialize a context for usage with Pedersen commitments. */ +void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); + +/** Generate a pedersen commitment. + * Returns 1: commitment successfully created. + * 0: error + * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) + * blind: pointer to a 32-byte blinding factor (cannot be NULL) + * value: unsigned 64-bit integer value to commit to. + * Out: commit: pointer to a 33-byte array for the commitment (cannot be NULL) + * + * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( + const secp256k1_context* ctx, + unsigned char *commit, + unsigned char *blind, + uint64_t value +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Computes the sum of multiple positive and negative blinding factors. + * Returns 1: sum successfully computed. + * 0: error + * In: ctx: pointer to a context object (cannot be NULL) + * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) + * n: number of factors pointed to by blinds. + * nneg: how many of the initial factors should be treated with a positive sign. + * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( + const secp256k1_context* ctx, + unsigned char *blind_out, + const unsigned char * const *blinds, + int n, + int npositive +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a tally of pedersen commitments + * Returns 1: commitments successfully sum to zero. + * 0: Commitments do not sum to zero or other error. + * In: ctx: pointer to a context object, initialized for Pedersen commitment (cannot be NULL) + * commits: pointer to pointers to 33-byte character arrays for the commitments. (cannot be NULL if pcnt is non-zero) + * pcnt: number of commitments pointed to by commits. + * ncommits: pointer to pointers to 33-byte character arrays for negative commitments. (cannot be NULL if ncnt is non-zero) + * ncnt: number of commitments pointed to by ncommits. + * excess: signed 64bit amount to add to the total to bring it to zero, can be negative. + * + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) - excess*H == 0. + * + * A pedersen commitment is xG + vH where G and H are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero both their blinding factors and + * values must sum to zero. + * + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( + const secp256k1_context* ctx, + const unsigned char * const *commits, + int pcnt, + const unsigned char * const *ncommits, + int ncnt, + int64_t excess +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Initialize a context for usage with Pedersen commitments. */ +void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); + +/** Verify a proof that a committed value is within a range. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. + * 0: Proof failed or other error. + * In: ctx: pointer to a context object, initialized for range-proof and commitment (cannot be NULL) + * commit: the 33-byte commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( + const secp256k1_context* ctx, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *commit, + const unsigned char *proof, + int plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a range proof proof and rewind the proof to recover information sent by its author. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. + * 0: Proof failed, rewind failed, or other error. + * In: ctx: pointer to a context object, initialized for range-proof and Pedersen commitment (cannot be NULL) + * commit: the 33-byte commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * nonce: 32-byte secret nonce used by the prover (cannot be NULL) + * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment + * value_out: pointer to an unsigned int64 which has the exact value of the commitment. + * message_out: pointer to a 4096 byte character array to receive message data from the proof author. + * outlen: length of message data written to message_out. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( + const secp256k1_context* ctx, + unsigned char *blind_out, + uint64_t *value_out, + unsigned char *message_out, + int *outlen, + const unsigned char *nonce, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *commit, + const unsigned char *proof, + int plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10); + +/** Author a proof that a committed value is within a range. + * Returns 1: Proof successfully created. + * 0: Error + * In: ctx: pointer to a context object, initialized for range-proof, signing, and Pedersen commitment (cannot be NULL) + * proof: pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL) + * min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount. + * commit: 33-byte array with the commitment being proved. + * blind: 32-byte blinding factor used by commit. + * nonce: 32-byte secret nonce used to initialize the proof (value can be reverse-engineered out of the proof if this secret is known.) + * exp: Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18. + * (-1 is a special case that makes the value public. 0 is the most private.) + * min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64). + * value: Actual value of the commitment. + * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. + * + * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. + * + * If exp is -1 the value is revealed by the proof (e.g. it proves that the proof is a blinding of a specific value, without revealing the blinding key.) + * + * This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding. + * + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( + const secp256k1_context* ctx, + unsigned char *proof, + int *plen, + uint64_t min_value, + const unsigned char *commit, + const unsigned char *blind, + const unsigned char *nonce, + int exp, + int min_bits, + uint64_t value +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Extract some basic information from a range-proof. + * Returns 1: Information successfully extracted. + * 0: Decode failed. + * In: ctx: pointer to a context object + * proof: pointer to character array with the proof. + * plen: length of proof in bytes. + * Out: exp: Exponent used in the proof (-1 means the value isn't private). + * mantissa: Number of bits covered by the proof. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( + const secp256k1_context* ctx, + int *exp, + int *mantissa, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *proof, + int plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c new file mode 100644 index 00000000..36aa795e --- /dev/null +++ b/src/bench_rangeproof.c @@ -0,0 +1,65 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1_rangeproof.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + unsigned char commit[33]; + unsigned char proof[5134]; + unsigned char blind[32]; + int len; + int min_bits; + uint64_t v; +} bench_rangeproof_t; + +static void bench_rangeproof_setup(void* arg) { + int i; + uint64_t minv; + uint64_t maxv; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + data->v = 0; + for (i = 0; i < 32; i++) data->blind[i] = i + 1; + CHECK(secp256k1_pedersen_commit(data->ctx, data->commit, data->blind, data->v)); + data->len = 5134; + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, data->commit, data->blind, data->commit, 0, data->min_bits, data->v)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, data->commit, data->proof, data->len)); +} + +static void bench_rangeproof(void* arg, int iters) { + int i; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + for (i = 0; i < iters/data->min_bits; i++) { + int j; + uint64_t minv; + uint64_t maxv; + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, data->commit, data->proof, data->len); + for (j = 0; j < 4; j++) { + data->proof[j + 2 + 32 *((data->min_bits + 1) >> 1) - 4] = (i >> 8)&255; + } + } +} + +int main(void) { + bench_rangeproof_t data; + int iters; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + data.min_bits = 32; + iters = data.min_bits*get_iters(32); + + run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include new file mode 100644 index 00000000..ff8b8d38 --- /dev/null +++ b/src/modules/rangeproof/Makefile.am.include @@ -0,0 +1,15 @@ +include_HEADERS += include/secp256k1_rangeproof.h +noinst_HEADERS += src/modules/rangeproof/main_impl.h +noinst_HEADERS += src/modules/rangeproof/pedersen.h +noinst_HEADERS += src/modules/rangeproof/pedersen_impl.h +noinst_HEADERS += src/modules/rangeproof/borromean.h +noinst_HEADERS += src/modules/rangeproof/borromean_impl.h +noinst_HEADERS += src/modules/rangeproof/rangeproof.h +noinst_HEADERS += src/modules/rangeproof/rangeproof_impl.h +noinst_HEADERS += src/modules/rangeproof/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_rangeproof +bench_rangeproof_SOURCES = src/bench_rangeproof.c +bench_rangeproof_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_rangeproof_LDFLAGS = -static +endif diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h new file mode 100644 index 00000000..11fd6c5b --- /dev/null +++ b/src/modules/rangeproof/borromean.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_BORROMEAN_H_ +#define _SECP256K1_BORROMEAN_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +int secp256k1_borromean_verify(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_scalar *evalues, const unsigned char *e0, const secp256k1_scalar *s, + const secp256k1_gej *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen); + +int secp256k1_borromean_sign(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context *ecmult_gen_ctx, + unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, + const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen); + +#endif diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h new file mode 100644 index 00000000..83145160 --- /dev/null +++ b/src/modules/rangeproof/borromean_impl.h @@ -0,0 +1,201 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_BORROMEAN_IMPL_H_ +#define _SECP256K1_BORROMEAN_IMPL_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#include "borromean.h" + +#include + +#ifdef WORDS_BIGENDIAN +#define BE32(x) (x) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#endif + +SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, int mlen, const unsigned char *e, int elen, + int ridx, int eidx) { + uint32_t ring; + uint32_t epos; + secp256k1_sha256 sha256_en; + secp256k1_sha256_initialize(&sha256_en); + ring = BE32((uint32_t)ridx); + epos = BE32((uint32_t)eidx); + secp256k1_sha256_write(&sha256_en, e, elen); + secp256k1_sha256_write(&sha256_en, m, mlen); + secp256k1_sha256_write(&sha256_en, (unsigned char*)&ring, 4); + secp256k1_sha256_write(&sha256_en, (unsigned char*)&epos, 4); + secp256k1_sha256_finalize(&sha256_en, hash); +} + +/** "Borromean" ring signature. + * Verifies nrings concurrent ring signatures all sharing a challenge value. + * Signature is one s value per pubkey and a hash. + * Verification equation: + * | m = H(P_{0..}||message) (Message must contain pubkeys or a pubkey commitment) + * | For each ring i: + * | | en = to_scalar(H(e0||m||i||0)) + * | | For each pubkey j: + * | | | r = s_i_j G + en * P_i_j + * | | | e = H(r||m||i||j) + * | | | en = to_scalar(e) + * | | r_i = r + * | return e_0 ==== H(r_{0..i}||m) + */ +int secp256k1_borromean_verify(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_scalar *evalues, const unsigned char *e0, + const secp256k1_scalar *s, const secp256k1_gej *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen) { + secp256k1_gej rgej; + secp256k1_ge rge; + secp256k1_scalar ens; + secp256k1_sha256 sha256_e0; + unsigned char tmp[33]; + int i; + int j; + int count; + size_t size; + int overflow; + VERIFY_CHECK(ecmult_ctx != NULL); + VERIFY_CHECK(e0 != NULL); + VERIFY_CHECK(s != NULL); + VERIFY_CHECK(pubs != NULL); + VERIFY_CHECK(rsizes != NULL); + VERIFY_CHECK(nrings > 0); + VERIFY_CHECK(m != NULL); + count = 0; + secp256k1_sha256_initialize(&sha256_e0); + for (i = 0; i < nrings; i++) { + VERIFY_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + for (j = 0; j < rsizes[i]; j++) { + if (overflow || secp256k1_scalar_is_zero(&s[count]) || secp256k1_scalar_is_zero(&ens) || secp256k1_gej_is_infinity(&pubs[count])) { + return 0; + } + if (evalues) { + /*If requested, save the challenges for proof rewind.*/ + evalues[count] = ens; + } + secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count], &ens, &s[count]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + /* OPT: loop can be hoisted and split to use batch inversion across all the rings; this would make it much faster. */ + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + if (j != rsizes[i] - 1) { + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + } else { + secp256k1_sha256_write(&sha256_e0, tmp, size); + } + count++; + } + } + secp256k1_sha256_write(&sha256_e0, m, mlen); + secp256k1_sha256_finalize(&sha256_e0, tmp); + return memcmp(e0, tmp, 32) == 0; +} + +int secp256k1_borromean_sign(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context *ecmult_gen_ctx, + unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, + const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen) { + secp256k1_gej rgej; + secp256k1_ge rge; + secp256k1_scalar ens; + secp256k1_sha256 sha256_e0; + unsigned char tmp[33]; + int i; + int j; + int count; + size_t size; + int overflow; + VERIFY_CHECK(ecmult_ctx != NULL); + VERIFY_CHECK(ecmult_gen_ctx != NULL); + VERIFY_CHECK(e0 != NULL); + VERIFY_CHECK(s != NULL); + VERIFY_CHECK(pubs != NULL); + VERIFY_CHECK(k != NULL); + VERIFY_CHECK(sec != NULL); + VERIFY_CHECK(rsizes != NULL); + VERIFY_CHECK(secidx != NULL); + VERIFY_CHECK(nrings > 0); + VERIFY_CHECK(m != NULL); + secp256k1_sha256_initialize(&sha256_e0); + count = 0; + for (i = 0; i < nrings; i++) { + VERIFY_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &k[i]); + secp256k1_ge_set_gej(&rge, &rgej); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + for (j = secidx[i] + 1; j < rsizes[i]; j++) { + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + /** The signing algorithm as a whole is not memory uniform so there is likely a cache sidechannel that + * leaks which members are non-forgeries. That the forgeries themselves are variable time may leave + * an additional privacy impacting timing side-channel, but not a key loss one. + */ + secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count + j], &ens, &s[count + j]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + } + secp256k1_sha256_write(&sha256_e0, tmp, size); + count += rsizes[i]; + } + secp256k1_sha256_write(&sha256_e0, m, mlen); + secp256k1_sha256_finalize(&sha256_e0, e0); + count = 0; + for (i = 0; i < nrings; i++) { + VERIFY_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + for (j = 0; j < secidx[i]; j++) { + secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count + j], &ens, &s[count + j]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + } + secp256k1_scalar_mul(&s[count + j], &ens, &sec[i]); + secp256k1_scalar_negate(&s[count + j], &s[count + j]); + secp256k1_scalar_add(&s[count + j], &s[count + j], &k[i]); + if (secp256k1_scalar_is_zero(&s[count + j])) { + return 0; + } + count += rsizes[i]; + } + secp256k1_scalar_clear(&ens); + secp256k1_ge_clear(&rge); + secp256k1_gej_clear(&rgej); + memset(tmp, 0, 33); + return 1; +} + +#endif diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h new file mode 100644 index 00000000..20f05a05 --- /dev/null +++ b/src/modules/rangeproof/main_impl.h @@ -0,0 +1,160 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RANGEPROOF_MAIN +#define SECP256K1_MODULE_RANGEPROOF_MAIN + +#include "modules/rangeproof/pedersen_impl.h" +#include "modules/rangeproof/borromean_impl.h" +#include "modules/rangeproof/rangeproof_impl.h" + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The commitment is 33 bytes, the blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context* ctx, unsigned char *commit, unsigned char *blind, uint64_t value) { + secp256k1_gej rj; + secp256k1_ge r; + secp256k1_scalar sec; + size_t sz; + int overflow; + int ret = 0; + ARG_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + secp256k1_scalar_set_b32(&sec, blind, &overflow); + if (!overflow) { + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value); + if (!secp256k1_gej_is_infinity(&rj)) { + secp256k1_ge_set_gej(&r, &rj); + sz = 33; + ret = secp256k1_eckey_pubkey_serialize(&r, commit, &sz, 1); + } + secp256k1_gej_clear(&rj); + secp256k1_ge_clear(&r); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest + * negative, then calculates an additional blinding value that adds to zero. + */ +int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, int n, int npositive) { + secp256k1_scalar acc; + secp256k1_scalar x; + int i; + int overflow; + ARG_CHECK(ctx != NULL); + ARG_CHECK(blind_out != NULL); + ARG_CHECK(blinds != NULL); + secp256k1_scalar_set_int(&acc, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&x, blinds[i], &overflow); + if (overflow) { + return 0; + } + if (i >= npositive) { + secp256k1_scalar_negate(&x, &x); + } + secp256k1_scalar_add(&acc, &acc, &x); + } + secp256k1_scalar_get_b32(blind_out, &acc); + secp256k1_scalar_clear(&acc); + secp256k1_scalar_clear(&x); + return 1; +} + +/* Takes two list of 33-byte commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const unsigned char * const *commits, int pcnt, + const unsigned char * const *ncommits, int ncnt, int64_t excess) { + secp256k1_gej accj; + secp256k1_ge add; + int i; + ARG_CHECK(ctx != NULL); + ARG_CHECK(!pcnt || (commits != NULL)); + ARG_CHECK(!ncnt || (ncommits != NULL)); + secp256k1_gej_set_infinity(&accj); + if (excess) { + uint64_t ex; + int neg; + /* Take the absolute value, and negate the result if the input was negative. */ + neg = secp256k1_sign_and_abs64(&ex, excess); + secp256k1_pedersen_ecmult_small(&accj, ex); + if (neg) { + secp256k1_gej_neg(&accj, &accj); + } + } + for (i = 0; i < ncnt; i++) { + if (!secp256k1_eckey_pubkey_parse(&add, ncommits[i], 33)) { + return 0; + } + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + secp256k1_gej_neg(&accj, &accj); + for (i = 0; i < pcnt; i++) { + if (!secp256k1_eckey_pubkey_parse(&add, commits[i], 33)) { + return 0; + } + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + return secp256k1_gej_is_infinity(&accj); +} + +int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, int plen) { + int offset; + uint64_t scale; + ARG_CHECK(exp != NULL); + ARG_CHECK(mantissa != NULL); + ARG_CHECK(min_value != NULL); + ARG_CHECK(max_value != NULL); + offset = 0; + scale = 1; + (void)ctx; + return secp256k1_rangeproof_getheader_impl(&offset, exp, mantissa, &scale, min_value, max_value, proof, plen); +} + +int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, + unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, + const unsigned char *commit, const unsigned char *proof, int plen) { + ARG_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(min_value != NULL); + ARG_CHECK(max_value != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, commit, proof, plen); +} + +int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, + const unsigned char *commit, const unsigned char *proof, int plen) { + ARG_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(min_value != NULL); + ARG_CHECK(max_value != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, + NULL, NULL, NULL, NULL, NULL, min_value, max_value, commit, proof, plen); +} + +int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, int *plen, uint64_t min_value, + const unsigned char *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + ARG_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(plen != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, + proof, plen, min_value, commit, blind, nonce, exp, min_bits, value); +} + +#endif diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h new file mode 100644 index 00000000..cdfe2f8e --- /dev/null +++ b/src/modules/rangeproof/pedersen.h @@ -0,0 +1,21 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_PEDERSEN_H_ +#define _SECP256K1_PEDERSEN_H_ + +#include "group.h" +#include "scalar.h" + +#include + +/** Multiply a small number with the generator: r = gn*G2 */ +static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn); + +/* sec * G + value * G2. */ +static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value); + +#endif diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/rangeproof/pedersen_impl.h new file mode 100644 index 00000000..3ce2767c --- /dev/null +++ b/src/modules/rangeproof/pedersen_impl.h @@ -0,0 +1,54 @@ +/*********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php. * + ***********************************************************************/ + +#ifndef _SECP256K1_PEDERSEN_IMPL_H_ +#define _SECP256K1_PEDERSEN_IMPL_H_ + +/** Alternative generator for secp256k1. + * This is the sha256 of 'g' after DER encoding (without compression), + * which happens to be a point on the curve. + * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16)) + * sage: '%x %x'%G2.xy() + */ +static const secp256k1_ge secp256k1_ge_const_g2 = SECP256K1_GE_CONST( + 0x50929b74UL, 0xc1a04954UL, 0xb78b4b60UL, 0x35e97a5eUL, + 0x078a5a0fUL, 0x28ec96d5UL, 0x47bfee9aUL, 0xce803ac0UL, + 0x31d3c686UL, 0x3973926eUL, 0x049e637cUL, 0xb1b5f40aUL, + 0x36dac28aUL, 0xf1766968UL, 0xc30c2313UL, 0xf3a38904UL +); + +static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t value) { + unsigned char data[32]; + int i; + for (i = 0; i < 24; i++) { + data[i] = 0; + } + for (; i < 32; i++) { + data[i] = value >> 56; + value <<= 8; + } + secp256k1_scalar_set_b32(sec, data, NULL); + memset(data, 0, 32); +} + +static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn) { + secp256k1_scalar s; + secp256k1_pedersen_scalar_set_u64(&s, gn); + secp256k1_ecmult_const(r, &secp256k1_ge_const_g2, &s, 64); + secp256k1_scalar_clear(&s); +} + +/* sec * G + value * G2. */ +SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value) { + secp256k1_gej vj; + secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec); + secp256k1_pedersen_ecmult_small(&vj, value); + /* FIXME: constant time. */ + secp256k1_gej_add_var(rj, rj, &vj, NULL); + secp256k1_gej_clear(&vj); +} + +#endif diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h new file mode 100644 index 00000000..b0f53696 --- /dev/null +++ b/src/modules/rangeproof/rangeproof.h @@ -0,0 +1,18 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_RANGEPROOF_H_ +#define _SECP256K1_RANGEPROOF_H_ + +#include "scalar.h" +#include "group.h" + +static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, + const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const unsigned char *commit, const unsigned char *proof, int plen); + +#endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h new file mode 100644 index 00000000..f76e1b02 --- /dev/null +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -0,0 +1,649 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_RANGEPROOF_IMPL_H_ +#define _SECP256K1_RANGEPROOF_IMPL_H_ + +#include "scalar.h" +#include "group.h" +#include "rangeproof.h" +#include "hash_impl.h" + +#include "modules/rangeproof/pedersen.h" +#include "modules/rangeproof/borromean.h" + +SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, + int exp, int *rsizes, int rings) { + secp256k1_gej base; + int i; + int j; + int npub; + VERIFY_CHECK(exp < 19); + if (exp < 0) { + exp = 0; + } + secp256k1_gej_set_ge(&base, &secp256k1_ge_const_g2); + secp256k1_gej_neg(&base, &base); + while (exp--) { + /* Multiplication by 10 */ + secp256k1_gej tmp; + secp256k1_gej_double_var(&tmp, &base, NULL); + secp256k1_gej_double_var(&base, &tmp, NULL); + secp256k1_gej_double_var(&base, &base, NULL); + secp256k1_gej_add_var(&base, &base, &tmp, NULL); + } + npub = 0; + for (i = 0; i < rings; i++) { + for (j = 1; j < rsizes[i]; j++) { + secp256k1_gej_add_var(&pubs[npub + j], &pubs[npub + j - 1], &base, NULL); + } + if (i < rings - 1) { + secp256k1_gej_double_var(&base, &base, NULL); + secp256k1_gej_double_var(&base, &base, NULL); + } + npub += rsizes[i]; + } +} + +SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_scalar *s, unsigned char *message, + int *rsizes, int rings, const unsigned char *nonce, const unsigned char *commit, const unsigned char *proof, int len) { + unsigned char tmp[32]; + unsigned char rngseed[32 + 33 + 10]; + secp256k1_rfc6979_hmac_sha256 rng; + secp256k1_scalar acc; + int overflow; + int ret; + int i; + int j; + int b; + int npub; + VERIFY_CHECK(len <= 10); + memcpy(rngseed, nonce, 32); + memcpy(rngseed + 32, commit, 33); + memcpy(rngseed + 65, proof, len); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + len); + secp256k1_scalar_clear(&acc); + npub = 0; + ret = 1; + for (i = 0; i < rings; i++) { + if (i < rings - 1) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + secp256k1_scalar_set_b32(&sec[i], tmp, &overflow); + } while (overflow || secp256k1_scalar_is_zero(&sec[i])); + secp256k1_scalar_add(&acc, &acc, &sec[i]); + } else { + secp256k1_scalar_negate(&acc, &acc); + sec[i] = acc; + } + for (j = 0; j < rsizes[i]; j++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + if (message) { + for (b = 0; b < 32; b++) { + tmp[b] ^= message[(i * 4 + j) * 32 + b]; + message[(i * 4 + j) * 32 + b] = tmp[b]; + } + } + secp256k1_scalar_set_b32(&s[npub], tmp, &overflow); + ret &= !(overflow || secp256k1_scalar_is_zero(&s[npub])); + npub++; + } + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + secp256k1_scalar_clear(&acc); + memset(tmp, 0, 32); + return ret; +} + +SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, int *rings, int *rsizes, int *npub, int *secidx, uint64_t *min_value, + int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) { + int i; + *rings = 1; + rsizes[0] = 1; + secidx[0] = 0; + *scale = 1; + *mantissa = 0; + *npub = 0; + if (*min_value == UINT64_MAX) { + /* If the minimum value is the maximal representable value, then we cannot code a range. */ + *exp = -1; + } + if (*exp >= 0) { + int max_bits; + uint64_t v2; + if ((*min_value && value > INT64_MAX) || (value && *min_value >= INT64_MAX)) { + /* If either value or min_value is >= 2^63-1 then the other must by zero to avoid overflowing the proven range. */ + return 0; + } + max_bits = *min_value ? secp256k1_clz64_var(*min_value) : 64; + if (*min_bits > max_bits) { + *min_bits = max_bits; + } + if (*min_bits > 61 || value > INT64_MAX) { + /** Ten is not a power of two, so dividing by ten and then representing in base-2 times ten + * expands the representable range. The verifier requires the proven range is within 0..2**64. + * For very large numbers (all over 2**63) we must change our exponent to compensate. + * Rather than handling it precisely, this just disables use of the exponent for big values. + */ + *exp = 0; + } + /* Mask off the least significant digits, as requested. */ + *v = value - *min_value; + /* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */ + v2 = *min_bits ? (UINT64_MAX>>(64-*min_bits)) : 0; + for (i = 0; i < *exp && (v2 <= UINT64_MAX / 10); i++) { + *v /= 10; + v2 *= 10; + } + *exp = i; + v2 = *v; + for (i = 0; i < *exp; i++) { + v2 *= 10; + *scale *= 10; + } + /* If the masked number isn't precise, compute the public offset. */ + *min_value = value - v2; + /* How many bits do we need to represent our value? */ + *mantissa = *v ? 64 - secp256k1_clz64_var(*v) : 1; + if (*min_bits > *mantissa) { + /* If the user asked for more precision, give it to them. */ + *mantissa = *min_bits; + } + /* Digits in radix-4, except for the last digit if our mantissa length is odd. */ + *rings = (*mantissa + 1) >> 1; + for (i = 0; i < *rings; i++) { + rsizes[i] = ((i < *rings - 1) | (!(*mantissa&1))) ? 4 : 2; + *npub += rsizes[i]; + secidx[i] = (*v >> (i*2)) & 3; + } + VERIFY_CHECK(*mantissa>0); + VERIFY_CHECK((*v & ~(UINT64_MAX>>(64-*mantissa))) == 0); /* Did this get all the bits? */ + } else { + /* A proof for an exact value. */ + *exp = 0; + *min_value = value; + *v = 0; + *npub = 2; + } + VERIFY_CHECK(*v * *scale + *min_value == value); + VERIFY_CHECK(*rings > 0); + VERIFY_CHECK(*rings <= 32); + VERIFY_CHECK(*npub <= 128); + return 1; +} + +/* strawman interface, writes proof in proof, a buffer of plen, proves with respect to min_value the range for commit which has the provided blinding factor and value. */ +SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmult_context* ecmult_ctx, + const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + unsigned char *proof, int *plen, uint64_t min_value, + const unsigned char *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ + secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ + secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ + secp256k1_scalar k[32]; /* Nonces for our non-forged signatures. */ + secp256k1_scalar stmp; + secp256k1_sha256 sha256_m; + unsigned char prep[4096]; + unsigned char tmp[33]; + unsigned char *signs; /* Location of sign flags in the proof. */ + uint64_t v; + uint64_t scale; /* scale = 10^exp. */ + int mantissa; /* Number of bits proven in the blinded value. */ + int rings; /* How many digits will our proof cover. */ + int rsizes[32]; /* How many possible values there are for each place. */ + int secidx[32]; /* Which digit is the correct one. */ + int len; /* Number of bytes used so far. */ + int i; + int overflow; + int npub; + len = 0; + if (*plen < 65 || min_value > value || min_bits > 64 || min_bits < 0 || exp < -1 || exp > 18) { + return 0; + } + if (!secp256k1_range_proveparams(&v, &rings, rsizes, &npub, secidx, &min_value, &mantissa, &scale, &exp, &min_bits, value)) { + return 0; + } + proof[len] = (rsizes[0] > 1 ? (64 | exp) : 0) | (min_value ? 32 : 0); + len++; + if (rsizes[0] > 1) { + VERIFY_CHECK(mantissa > 0 && mantissa <= 64); + proof[len] = mantissa - 1; + len++; + } + if (min_value) { + for (i = 0; i < 8; i++) { + proof[len + i] = (min_value >> ((7-i) * 8)) & 255; + } + len += 8; + } + /* Do we have enough room for the proof? */ + if (*plen - len < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + return 0; + } + secp256k1_sha256_initialize(&sha256_m); + secp256k1_sha256_write(&sha256_m, commit, 33); + secp256k1_sha256_write(&sha256_m, proof, len); + + memset(prep, 0, 4096); + /* Note, the data corresponding to the blinding factors must be zero. */ + if (rsizes[rings - 1] > 1) { + int idx; + /* Value encoding sidechannel. */ + idx = rsizes[rings - 1] - 1; + idx -= secidx[rings - 1] == idx; + idx = ((rings - 1) * 4 + idx) * 32; + for (i = 0; i < 8; i++) { + prep[8 + i + idx] = prep[16 + i + idx] = prep[24 + i + idx] = (v >> (56 - i * 8)) & 255; + prep[i + idx] = 0; + } + prep[idx] = 128; + } + if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len)) { + return 0; + } + memset(prep, 0, 4096); + for (i = 0; i < rings; i++) { + /* Sign will overwrite the non-forged signature, move that random value into the nonce. */ + k[i] = s[i * 4 + secidx[i]]; + secp256k1_scalar_clear(&s[i * 4 + secidx[i]]); + } + /** Genrand returns the last blinding factor as -sum(rest), + * adding in the blinding factor for our commitment, results in the blinding factor for + * the commitment to the last digit that the verifier can compute for itself by subtracting + * all the digits in the proof from the commitment. This lets the prover skip sending the + * blinded value for one digit. + */ + secp256k1_scalar_set_b32(&stmp, blind, &overflow); + secp256k1_scalar_add(&sec[rings - 1], &sec[rings - 1], &stmp); + if (overflow || secp256k1_scalar_is_zero(&sec[rings - 1])) { + return 0; + } + signs = &proof[len]; + /* We need one sign bit for each blinded value we send. */ + for (i = 0; i < (rings + 6) >> 3; i++) { + signs[i] = 0; + len++; + } + npub = 0; + for (i = 0; i < rings; i++) { + /*OPT: Use the precomputed gen2 basis?*/ + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2)); + if (secp256k1_gej_is_infinity(&pubs[npub])) { + return 0; + } + if (i < rings - 1) { + size_t size = 33; + secp256k1_ge c; + /*OPT: split loop and batch invert.*/ + secp256k1_ge_set_gej_var(&c, &pubs[npub]); + if(!secp256k1_eckey_pubkey_serialize(&c, tmp, &size, 1)) { + return 0; + } + secp256k1_sha256_write(&sha256_m, tmp, 33); + signs[i>>3] |= (tmp[0] == 3) << (i&7); + memcpy(&proof[len], &tmp[1], 32); + len += 32; + } + npub += rsizes[i]; + } + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings); + secp256k1_sha256_finalize(&sha256_m, tmp); + if (!secp256k1_borromean_sign(ecmult_ctx, ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { + return 0; + } + len += 32; + for (i = 0; i < npub; i++) { + secp256k1_scalar_get_b32(&proof[len],&s[i]); + len += 32; + } + VERIFY_CHECK(len <= *plen); + *plen = len; + memset(prep, 0, 4096); + return 1; +} + +/* Computes blinding factor x given k, s, and the challenge e. */ +SECP256K1_INLINE static void secp256k1_rangeproof_recover_x(secp256k1_scalar *x, const secp256k1_scalar *k, const secp256k1_scalar *e, + const secp256k1_scalar *s) { + secp256k1_scalar stmp; + secp256k1_scalar_negate(x, s); + secp256k1_scalar_add(x, x, k); + secp256k1_scalar_inverse(&stmp, e); + secp256k1_scalar_mul(x, x, &stmp); +} + +/* Computes ring's nonce given the blinding factor x, the challenge e, and the signature s. */ +SECP256K1_INLINE static void secp256k1_rangeproof_recover_k(secp256k1_scalar *k, const secp256k1_scalar *x, const secp256k1_scalar *e, + const secp256k1_scalar *s) { + secp256k1_scalar stmp; + secp256k1_scalar_mul(&stmp, x, e); + secp256k1_scalar_add(k, s, &stmp); +} + +SECP256K1_INLINE static void secp256k1_rangeproof_ch32xor(unsigned char *x, const unsigned char *y) { + int i; + for (i = 0; i < 32; i++) { + x[i] ^= y[i]; + } +} + +SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar *blind, uint64_t *v, + unsigned char *m, int *mlen, secp256k1_scalar *ev, secp256k1_scalar *s, + int *rsizes, int rings, const unsigned char *nonce, const unsigned char *commit, const unsigned char *proof, int len) { + secp256k1_scalar s_orig[128]; + secp256k1_scalar sec[32]; + secp256k1_scalar stmp; + unsigned char prep[4096]; + unsigned char tmp[32]; + uint64_t value; + int offset; + int i; + int j; + int b; + int skip1; + int skip2; + int npub; + npub = ((rings - 1) << 2) + rsizes[rings-1]; + VERIFY_CHECK(npub <= 128); + VERIFY_CHECK(npub >= 1); + memset(prep, 0, 4096); + /* Reconstruct the provers random values. */ + secp256k1_rangeproof_genrand(sec, s_orig, prep, rsizes, rings, nonce, commit, proof, len); + *v = UINT64_MAX; + secp256k1_scalar_clear(blind); + if (rings == 1 && rsizes[0] == 1) { + /* With only a single proof, we can only recover the blinding factor. */ + secp256k1_rangeproof_recover_x(blind, &s_orig[0], &ev[0], &s[0]); + if (v) { + *v = 0; + } + if (mlen) { + *mlen = 0; + } + return 1; + } + npub = (rings - 1) << 2; + for (j = 0; j < 2; j++) { + int idx; + /* Look for a value encoding in the last ring. */ + idx = npub + rsizes[rings - 1] - 1 - j; + secp256k1_scalar_get_b32(tmp, &s[idx]); + secp256k1_rangeproof_ch32xor(tmp, &prep[idx * 32]); + if ((tmp[0] & 128) && (memcmp(&tmp[16], &tmp[24], 8) == 0) && (memcmp(&tmp[8], &tmp[16], 8) == 0)) { + value = 0; + for (i = 0; i < 8; i++) { + value = (value << 8) + tmp[24 + i]; + } + if (v) { + *v = value; + } + memcpy(&prep[idx * 32], tmp, 32); + break; + } + } + if (j > 1) { + /* Couldn't extract a value. */ + if (mlen) { + *mlen = 0; + } + return 0; + } + skip1 = rsizes[rings - 1] - 1 - j; + skip2 = ((value >> ((rings - 1) << 1)) & 3); + if (skip1 == skip2) { + /*Value is in wrong position.*/ + if (mlen) { + *mlen = 0; + } + return 0; + } + skip1 += (rings - 1) << 2; + skip2 += (rings - 1) << 2; + /* Like in the rsize[] == 1 case, Having figured out which s is the one which was not forged, we can recover the blinding factor. */ + secp256k1_rangeproof_recover_x(&stmp, &s_orig[skip2], &ev[skip2], &s[skip2]); + secp256k1_scalar_negate(&sec[rings - 1], &sec[rings - 1]); + secp256k1_scalar_add(blind, &stmp, &sec[rings - 1]); + if (!m || !mlen || *mlen == 0) { + if (mlen) { + *mlen = 0; + } + /* FIXME: cleanup in early out/failure cases. */ + return 1; + } + offset = 0; + npub = 0; + for (i = 0; i < rings; i++) { + int idx; + idx = (value >> (i << 1)) & 3; + for (j = 0; j < rsizes[i]; j++) { + if (npub == skip1 || npub == skip2) { + npub++; + continue; + } + if (idx == j) { + /** For the non-forged signatures the signature is calculated instead of random, instead we recover the prover's nonces. + * this could just as well recover the blinding factors and messages could be put there as is done for recovering the + * blinding factor in the last ring, but it takes an inversion to recover x so it's faster to put the message data in k. + */ + secp256k1_rangeproof_recover_k(&stmp, &sec[i], &ev[npub], &s[npub]); + } else { + stmp = s[npub]; + } + secp256k1_scalar_get_b32(tmp, &stmp); + secp256k1_rangeproof_ch32xor(tmp, &prep[npub * 32]); + for (b = 0; b < 32 && offset < *mlen; b++) { + m[offset] = tmp[b]; + offset++; + } + npub++; + } + } + *mlen = offset; + memset(prep, 0, 4096); + for (i = 0; i < 128; i++) { + secp256k1_scalar_clear(&s_orig[i]); + } + for (i = 0; i < 32; i++) { + secp256k1_scalar_clear(&sec[i]); + } + secp256k1_scalar_clear(&stmp); + return 1; +} + +SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(int *offset, int *exp, int *mantissa, uint64_t *scale, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, int plen) { + int i; + int has_nz_range; + int has_min; + if (plen < 65 || ((proof[*offset] & 128) != 0)) { + return 0; + } + has_nz_range = proof[*offset] & 64; + has_min = proof[*offset] & 32; + *exp = -1; + *mantissa = 0; + if (has_nz_range) { + *exp = proof[*offset] & 31; + *offset += 1; + if (*exp > 18) { + return 0; + } + *mantissa = proof[*offset] + 1; + if (*mantissa > 64) { + return 0; + } + *max_value = UINT64_MAX>>(64-*mantissa); + } else { + *max_value = 0; + } + *offset += 1; + *scale = 1; + for (i = 0; i < *exp; i++) { + if (*max_value > UINT64_MAX / 10) { + return 0; + } + *max_value *= 10; + *scale *= 10; + } + *min_value = 0; + if (has_min) { + if(plen - *offset < 8) { + return 0; + } + /*FIXME: Compact minvalue encoding?*/ + for (i = 0; i < 8; i++) { + *min_value = (*min_value << 8) | proof[*offset + i]; + } + *offset += 8; + } + if (*max_value > UINT64_MAX - *min_value) { + return 0; + } + *max_value += *min_value; + return 1; +} + +/* Verifies range proof (len plen) for 33-byte commit, the min/max values proven are put in the min/max arguments; returns 0 on failure 1 on success.*/ +SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, + const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const unsigned char *commit, const unsigned char *proof, int plen) { + secp256k1_gej accj; + secp256k1_gej pubs[128]; + secp256k1_ge c; + secp256k1_scalar s[128]; + secp256k1_scalar evalues[128]; /* Challenges, only used during proof rewind. */ + secp256k1_sha256 sha256_m; + int rsizes[32]; + int ret; + int i; + size_t size; + int exp; + int mantissa; + int offset; + int rings; + int overflow; + int npub; + int offset_post_header; + uint64_t scale; + unsigned char signs[31]; + unsigned char m[33]; + const unsigned char *e0; + offset = 0; + if (!secp256k1_rangeproof_getheader_impl(&offset, &exp, &mantissa, &scale, min_value, max_value, proof, plen)) { + return 0; + } + offset_post_header = offset; + rings = 1; + rsizes[0] = 1; + npub = 1; + if (mantissa != 0) { + rings = (mantissa >> 1); + for (i = 0; i < rings; i++) { + rsizes[i] = 4; + } + npub = (mantissa >> 1) << 2; + if (mantissa & 1) { + rsizes[rings] = 2; + npub += rsizes[rings]; + rings++; + } + } + VERIFY_CHECK(rings <= 32); + if (plen - offset < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + return 0; + } + secp256k1_sha256_initialize(&sha256_m); + secp256k1_sha256_write(&sha256_m, commit, 33); + secp256k1_sha256_write(&sha256_m, proof, offset); + for(i = 0; i < rings - 1; i++) { + signs[i] = (proof[offset + ( i>> 3)] & (1 << (i & 7))) != 0; + } + offset += (rings + 6) >> 3; + if ((rings - 1) & 7) { + /* Number of coded blinded points is not a multiple of 8, force extra sign bits to 0 to reject mutation. */ + if ((proof[offset - 1] >> ((rings - 1) & 7)) != 0) { + return 0; + } + } + npub = 0; + secp256k1_gej_set_infinity(&accj); + if (*min_value) { + secp256k1_pedersen_ecmult_small(&accj, *min_value); + } + for(i = 0; i < rings - 1; i++) { + memcpy(&m[1], &proof[offset], 32); + m[0] = 2 + signs[i]; + if (!secp256k1_eckey_pubkey_parse(&c, m, 33)) { + return 0; + } + secp256k1_sha256_write(&sha256_m, m, 33); + secp256k1_gej_set_ge(&pubs[npub], &c); + secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL); + offset += 32; + npub += rsizes[i]; + } + secp256k1_gej_neg(&accj, &accj); + if (!secp256k1_eckey_pubkey_parse(&c, commit, 33)) { + return 0; + } + secp256k1_gej_add_ge_var(&pubs[npub], &accj, &c, NULL); + if (secp256k1_gej_is_infinity(&pubs[npub])) { + return 0; + } + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings); + npub += rsizes[rings - 1]; + e0 = &proof[offset]; + offset += 32; + for (i = 0; i < npub; i++) { + secp256k1_scalar_set_b32(&s[i], &proof[offset], &overflow); + if (overflow) { + return 0; + } + offset += 32; + } + if (offset != plen) { + /*Extra data found, reject.*/ + return 0; + } + secp256k1_sha256_finalize(&sha256_m, m); + ret = secp256k1_borromean_verify(ecmult_ctx, nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); + if (ret && nonce) { + /* Given the nonce, try rewinding the witness to recover its initial state. */ + secp256k1_scalar blind; + unsigned char commitrec[33]; + uint64_t vv; + if (!ecmult_gen_ctx) { + return 0; + } + if (!secp256k1_rangeproof_rewind_inner(&blind, &vv, message_out, outlen, evalues, s, rsizes, rings, nonce, commit, proof, offset_post_header)) { + return 0; + } + /* Unwind apparently successful, see if the commitment can be reconstructed. */ + /* FIXME: should check vv is in the mantissa's range. */ + vv = (vv * scale) + *min_value; + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv); + if (secp256k1_gej_is_infinity(&accj)) { + return 0; + } + secp256k1_ge_set_gej(&c, &accj); + size = 33; + secp256k1_eckey_pubkey_serialize(&c, commitrec, &size, 1); + if (size != 33 || memcmp(commitrec, commit, 33) != 0) { + return 0; + } + if (blindout) { + secp256k1_scalar_get_b32(blindout, &blind); + } + if (value_out) { + *value_out = vv; + } + } + return ret; +} + +#endif diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h new file mode 100644 index 00000000..4e601697 --- /dev/null +++ b/src/modules/rangeproof/tests_impl.h @@ -0,0 +1,279 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_RANGEPROOF_TESTS +#define SECP256K1_MODULE_RANGEPROOF_TESTS + +#include "include/secp256k1_rangeproof.h" + +void test_pedersen(void) { + unsigned char commits[33*19]; + const unsigned char *cptr[19]; + unsigned char blinds[32*19]; + const unsigned char *bptr[19]; + secp256k1_scalar s; + uint64_t values[19]; + int64_t totalv; + int i; + int inputs; + int outputs; + int total; + inputs = (secp256k1_rand32() & 7) + 1; + outputs = (secp256k1_rand32() & 7) + 2; + total = inputs + outputs; + for (i = 0; i < 19; i++) { + cptr[i] = &commits[i * 33]; + bptr[i] = &blinds[i * 32]; + } + totalv = 0; + for (i = 0; i < inputs; i++) { + values[i] = secp256k1_rands64(0, INT64_MAX - totalv); + totalv += values[i]; + } + if (secp256k1_rand32() & 1) { + for (i = 0; i < outputs; i++) { + int64_t max = INT64_MAX; + if (totalv < 0) { + max += totalv; + } + values[i + inputs] = secp256k1_rands64(0, max); + totalv -= values[i + inputs]; + } + } else { + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_rands64(0, totalv); + totalv -= values[i + inputs]; + } + values[total - 1] = totalv >> (secp256k1_rand32() & 1); + totalv -= values[total - 1]; + } + for (i = 0; i < total - 1; i++) { + random_scalar_order(&s); + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); + for (i = 0; i < total; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i * 33], &blinds[i * 32], values[i])); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv)); + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1)); + random_scalar_order(&s); + for (i = 0; i < 4; i++) { + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + values[0] = INT64_MAX; + values[1] = 0; + values[2] = 1; + for (i = 0; i < 3; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i * 33], &blinds[i * 32], values[i])); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1, 0)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[1], 1, INT64_MAX)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1, 0)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[0], 1, -INT64_MAX)); +} + +void test_borromean(void) { + unsigned char e0[32]; + secp256k1_scalar s[64]; + secp256k1_gej pubs[64]; + secp256k1_scalar k[8]; + secp256k1_scalar sec[8]; + secp256k1_ge ge; + secp256k1_scalar one; + unsigned char m[32]; + int rsizes[8]; + int secidx[8]; + int nrings; + int i; + int j; + int c; + secp256k1_rand256_test(m); + nrings = 1 + (secp256k1_rand32()&7); + c = 0; + secp256k1_scalar_set_int(&one, 1); + if (secp256k1_rand32()&1) { + secp256k1_scalar_negate(&one, &one); + } + for (i = 0; i < nrings; i++) { + rsizes[i] = 1 + (secp256k1_rand32()&7); + secidx[i] = secp256k1_rand32() % rsizes[i]; + random_scalar_order(&sec[i]); + random_scalar_order(&k[i]); + if(secp256k1_rand32()&7) { + sec[i] = one; + } + if(secp256k1_rand32()&7) { + k[i] = one; + } + for (j = 0; j < rsizes[i]; j++) { + random_scalar_order(&s[c + j]); + if(secp256k1_rand32()&7) { + s[i] = one; + } + if (j == secidx[i]) { + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubs[c + j], &sec[i]); + } else { + random_group_element_test(&ge); + random_group_element_jacobian_test(&pubs[c + j],&ge); + } + } + c += rsizes[i]; + } + CHECK(secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, secidx, nrings, m, 32)); + CHECK(secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32)); + i = secp256k1_rand32() % c; + secp256k1_scalar_negate(&s[i],&s[i]); + CHECK(!secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32)); + secp256k1_scalar_negate(&s[i],&s[i]); + secp256k1_scalar_set_int(&one, 1); + for(j = 0; j < 4; j++) { + i = secp256k1_rand32() % c; + if (secp256k1_rand32() & 1) { + secp256k1_gej_double_var(&pubs[i],&pubs[i], NULL); + } else { + secp256k1_scalar_add(&s[i],&s[i],&one); + } + CHECK(!secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32)); + } +} + +void test_rangeproof(void) { + const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; + unsigned char commit[33]; + unsigned char commit2[33]; + unsigned char proof[5134]; + unsigned char blind[32]; + unsigned char blindout[32]; + unsigned char message[4096]; + int mlen; + uint64_t v; + uint64_t vout; + uint64_t vmin; + uint64_t minv; + uint64_t maxv; + int len; + int i; + int j; + int k; + secp256k1_rand256(blind); + for (i = 0; i < 11; i++) { + v = testvs[i]; + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, commit, blind, commit, 0, 0, v)); + CHECK(len <= 5134); + mlen = 4096; + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit, &minv, &maxv, commit, proof, len)); + for (j = 0; j < mlen; j++) { + CHECK(message[j] == 0); + } + CHECK(mlen <= 4096); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv <= v); + CHECK(maxv >= v); + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, commit, blind, commit, -1, 64, v)); + CHECK(len <= 73); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit, &minv, &maxv, commit, proof, len)); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv == v); + CHECK(maxv == v); + } + } + secp256k1_rand256(blind); + v = INT64_MAX - 1; + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + for (i = 0; i < 19; i++) { + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, commit, blind, commit, i, 0, v)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(len <= 5134); + CHECK(minv <= v); + CHECK(maxv >= v); + } + secp256k1_rand256(blind); + { + /*Malleability test.*/ + v = secp256k1_rands64(0, 255); + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, commit, blind, commit, 0, 3, v)); + CHECK(len <= 5134); + for (i = 0; i < len*8; i++) { + proof[i >> 3] ^= 1 << (i & 7); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + proof[i >> 3] ^= 1 << (i & 7); + } + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(minv <= v); + CHECK(maxv >= v); + } + memcpy(commit2, commit, 33); + for (i = 0; i < 10 * count; i++) { + int exp; + int min_bits; + v = secp256k1_rands64(0, UINT64_MAX >> (secp256k1_rand32()&63)); + vmin = 0; + if ((v < INT64_MAX) && (secp256k1_rand32()&1)) { + vmin = secp256k1_rands64(0, v); + } + secp256k1_rand256(blind); + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + len = 5134; + exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); + if (exp < 0) { + exp = -exp; + } + min_bits = (int)secp256k1_rands64(0,64)-(int)secp256k1_rands64(0,64); + if (min_bits < 0) { + min_bits = -min_bits; + } + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, commit, blind, commit, exp, min_bits, v)); + CHECK(len <= 5134); + mlen = 4096; + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit, &minv, &maxv, commit, proof, len)); + for (j = 0; j < mlen; j++) { + CHECK(message[j] == 0); + } + CHECK(mlen <= 4096); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv <= v); + CHECK(maxv >= v); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit, &minv, &maxv, commit, proof, len)); + memcpy(commit2, commit, 33); + } + for (j = 0; j < 10; j++) { + for (i = 0; i < 96; i++) { + secp256k1_rand256(&proof[i * 32]); + } + for (k = 0; k < 128; k++) { + len = k; + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit2, proof, len)); + } + len = secp256k1_rands64(0, 3072); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit2, proof, len)); + } +} + +void run_rangeproof_tests(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_pedersen(); + } + for (i = 0; i < 10*count; i++) { + test_borromean(); + } + test_rangeproof(); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index b03a6e63..b3312100 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -24,6 +24,11 @@ # include #endif +#ifdef ENABLE_MODULE_RANGEPROOF +# include "modules/rangeproof/pedersen.h" +# include "modules/rangeproof/rangeproof.h" +#endif + #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ @@ -741,3 +746,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif + +#ifdef ENABLE_MODULE_RANGEPROOF +# include "modules/rangeproof/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index 27d30ccc..ee30d8df 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5325,6 +5325,10 @@ void run_ecdsa_openssl(void) { # include "modules/recovery/tests_impl.h" #endif +#ifdef ENABLE_MODULE_RANGEPROOF +# include "modules/rangeproof/tests_impl.h" +#endif + void run_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5632,6 +5636,10 @@ int main(int argc, char **argv) { run_recovery_tests(); #endif +#ifdef ENABLE_MODULE_RANGEPROOF + run_rangeproof_tests(); +#endif + /* util tests */ run_memczero_test(); From a88db4a744e9806508d95b49031b79f41368379f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 4 Jul 2016 13:04:57 +0000 Subject: [PATCH 003/381] [RANGEPROOF BREAK] Use quadratic residue for tie break and modularity cleanup Switch to secp256k1_pedersen_commitment by Andrew Poelstra. Switch to quadratic residue based disambiguation by Pieter Wuille. --- include/secp256k1_rangeproof.h | 88 ++++++++++---- src/bench_rangeproof.c | 12 +- src/modules/rangeproof/borromean.h | 4 +- src/modules/rangeproof/borromean_impl.h | 23 ++-- src/modules/rangeproof/main_impl.h | 87 +++++++++----- src/modules/rangeproof/pedersen.h | 1 + src/modules/rangeproof/pedersen_impl.h | 10 ++ src/modules/rangeproof/rangeproof.h | 6 +- src/modules/rangeproof/rangeproof_impl.h | 146 ++++++++++++----------- src/modules/rangeproof/tests_impl.h | 81 +++++++------ src/secp256k1.c | 1 + 11 files changed, 285 insertions(+), 174 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 54b454ef..94cfaee8 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -9,6 +9,50 @@ extern "C" { #include +/** Opaque data structure that stores a Pedersen commitment + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 33 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_pedersen_commitment_serialize_* and + * secp256k1_pedersen_commitment_serialize_* functions. + * + * Furthermore, it is guaranteed to identical signatures will have identical + * representation, so they can be memcmp'ed. + */ +typedef struct { + unsigned char data[33]; +} secp256k1_pedersen_commitment; + +/** Parse a 33-byte commitment into a commitment object. + * + * Returns: 1 always + * Args: ctx: a secp256k1 context object. + * Out: commit: pointer to the output commitment object + * In: input: pointer to a 33-byte serialized commitment key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( + const secp256k1_context* ctx, + secp256k1_pedersen_commitment* commit, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a commitment object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 33-byte byte array + * In: commit: a pointer to a secp256k1_pedersen_commitment containing an + * initialized commitment + */ +SECP256K1_API int secp256k1_pedersen_commitment_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_pedersen_commitment* commit +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + + /** Initialize a context for usage with Pedersen commitments. */ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); @@ -18,14 +62,14 @@ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) * blind: pointer to a 32-byte blinding factor (cannot be NULL) * value: unsigned 64-bit integer value to commit to. - * Out: commit: pointer to a 33-byte array for the commitment (cannot be NULL) + * Out: commit: pointer to the commitment (cannot be NULL) * * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. */ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( const secp256k1_context* ctx, - unsigned char *commit, - unsigned char *blind, + secp256k1_pedersen_commitment *commit, + const unsigned char *blind, uint64_t value ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); @@ -42,17 +86,17 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, - int n, - int npositive + size_t n, + size_t npositive ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Verify a tally of pedersen commitments * Returns 1: commitments successfully sum to zero. * 0: Commitments do not sum to zero or other error. * In: ctx: pointer to a context object, initialized for Pedersen commitment (cannot be NULL) - * commits: pointer to pointers to 33-byte character arrays for the commitments. (cannot be NULL if pcnt is non-zero) + * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) * pcnt: number of commitments pointed to by commits. - * ncommits: pointer to pointers to 33-byte character arrays for negative commitments. (cannot be NULL if ncnt is non-zero) + * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) * ncnt: number of commitments pointed to by ncommits. * excess: signed 64bit amount to add to the total to bring it to zero, can be negative. * @@ -65,10 +109,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( */ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( const secp256k1_context* ctx, - const unsigned char * const *commits, - int pcnt, - const unsigned char * const *ncommits, - int ncnt, + const secp256k1_pedersen_commitment * const* commits, + size_t pcnt, + const secp256k1_pedersen_commitment * const* ncommits, + size_t ncnt, int64_t excess ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); @@ -79,7 +123,7 @@ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. * 0: Proof failed or other error. * In: ctx: pointer to a context object, initialized for range-proof and commitment (cannot be NULL) - * commit: the 33-byte commitment being proved. (cannot be NULL) + * commit: the commitment being proved. (cannot be NULL) * proof: pointer to character array with the proof. (cannot be NULL) * plen: length of proof in bytes. * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) @@ -89,16 +133,16 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, - const unsigned char *commit, + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, - int plen + size_t plen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Verify a range proof proof and rewind the proof to recover information sent by its author. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. * 0: Proof failed, rewind failed, or other error. * In: ctx: pointer to a context object, initialized for range-proof and Pedersen commitment (cannot be NULL) - * commit: the 33-byte commitment being proved. (cannot be NULL) + * commit: the commitment being proved. (cannot be NULL) * proof: pointer to character array with the proof. (cannot be NULL) * plen: length of proof in bytes. * nonce: 32-byte secret nonce used by the prover (cannot be NULL) @@ -114,13 +158,13 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, - int *outlen, + size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, - const unsigned char *commit, + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, - int plen + size_t plen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10); /** Author a proof that a committed value is within a range. @@ -129,7 +173,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * In: ctx: pointer to a context object, initialized for range-proof, signing, and Pedersen commitment (cannot be NULL) * proof: pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL) * min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount. - * commit: 33-byte array with the commitment being proved. + * commit: the commitment being proved. * blind: 32-byte blinding factor used by commit. * nonce: 32-byte secret nonce used to initialize the proof (value can be reverse-engineered out of the proof if this secret is known.) * exp: Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18. @@ -148,9 +192,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( const secp256k1_context* ctx, unsigned char *proof, - int *plen, + size_t *plen, uint64_t min_value, - const unsigned char *commit, + const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, @@ -176,7 +220,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, - int plen + size_t plen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); # ifdef __cplusplus diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 36aa795e..5e068361 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -12,10 +12,10 @@ typedef struct { secp256k1_context* ctx; - unsigned char commit[33]; + secp256k1_pedersen_commitment commit; unsigned char proof[5134]; unsigned char blind[32]; - int len; + size_t len; int min_bits; uint64_t v; } bench_rangeproof_t; @@ -28,10 +28,10 @@ static void bench_rangeproof_setup(void* arg) { data->v = 0; for (i = 0; i < 32; i++) data->blind[i] = i + 1; - CHECK(secp256k1_pedersen_commit(data->ctx, data->commit, data->blind, data->v)); + CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v)); data->len = 5134; - CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, data->commit, data->blind, data->commit, 0, data->min_bits, data->v)); - CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, data->commit, data->proof, data->len)); + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len)); } static void bench_rangeproof(void* arg, int iters) { @@ -42,7 +42,7 @@ static void bench_rangeproof(void* arg, int iters) { int j; uint64_t minv; uint64_t maxv; - j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, data->commit, data->proof, data->len); + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len); for (j = 0; j < 4; j++) { data->proof[j + 2 + 32 *((data->min_bits + 1) >> 1) - 4] = (i >> 8)&255; } diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h index 11fd6c5b..8f8cfedd 100644 --- a/src/modules/rangeproof/borromean.h +++ b/src/modules/rangeproof/borromean.h @@ -15,10 +15,10 @@ #include "ecmult_gen.h" int secp256k1_borromean_verify(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_scalar *evalues, const unsigned char *e0, const secp256k1_scalar *s, - const secp256k1_gej *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen); + const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen); int secp256k1_borromean_sign(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context *ecmult_gen_ctx, unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, - const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen); + const size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen); #endif diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 83145160..3a82f096 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -11,11 +11,14 @@ #include "scalar.h" #include "field.h" #include "group.h" +#include "hash.h" +#include "eckey.h" #include "ecmult.h" #include "ecmult_gen.h" #include "borromean.h" #include +#include #ifdef WORDS_BIGENDIAN #define BE32(x) (x) @@ -23,8 +26,8 @@ #define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #endif -SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, int mlen, const unsigned char *e, int elen, - int ridx, int eidx) { +SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, + size_t ridx, size_t eidx) { uint32_t ring; uint32_t epos; secp256k1_sha256 sha256_en; @@ -53,15 +56,15 @@ SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const * | return e_0 ==== H(r_{0..i}||m) */ int secp256k1_borromean_verify(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_scalar *evalues, const unsigned char *e0, - const secp256k1_scalar *s, const secp256k1_gej *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen) { + const secp256k1_scalar *s, const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen) { secp256k1_gej rgej; secp256k1_ge rge; secp256k1_scalar ens; secp256k1_sha256 sha256_e0; unsigned char tmp[33]; - int i; - int j; - int count; + size_t i; + size_t j; + size_t count; size_t size; int overflow; VERIFY_CHECK(ecmult_ctx != NULL); @@ -108,15 +111,15 @@ int secp256k1_borromean_verify(const secp256k1_ecmult_context* ecmult_ctx, secp2 int secp256k1_borromean_sign(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context *ecmult_gen_ctx, unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, - const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen) { + const size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen) { secp256k1_gej rgej; secp256k1_ge rge; secp256k1_scalar ens; secp256k1_sha256 sha256_e0; unsigned char tmp[33]; - int i; - int j; - int count; + size_t i; + size_t j; + size_t count; size_t size; int overflow; VERIFY_CHECK(ecmult_ctx != NULL); diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 20f05a05..a1ad6715 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -7,16 +7,48 @@ #ifndef SECP256K1_MODULE_RANGEPROOF_MAIN #define SECP256K1_MODULE_RANGEPROOF_MAIN +#include "group.h" + #include "modules/rangeproof/pedersen_impl.h" #include "modules/rangeproof/borromean_impl.h" #include "modules/rangeproof/rangeproof_impl.h" -/* Generates a pedersen commitment: *commit = blind * G + value * G2. The commitment is 33 bytes, the blinding factor is 32 bytes.*/ -int secp256k1_pedersen_commit(const secp256k1_context* ctx, unsigned char *commit, unsigned char *blind, uint64_t value) { +static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &commit->data[1]); + secp256k1_ge_set_xquad(ge, &fe); + if (commit->data[0] & 1) { + secp256k1_ge_neg(ge, ge); + } +} + +static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { + secp256k1_fe_normalize(&ge->x); + secp256k1_fe_get_b32(&commit->data[1], &ge->x); + commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y); +} + +int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(input != NULL); + memcpy(commit->data, input, sizeof(commit->data)); + return 1; +} + +int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(commit != NULL); + memcpy(output, commit->data, sizeof(commit->data)); + return 1; +} + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value) { secp256k1_gej rj; secp256k1_ge r; secp256k1_scalar sec; - size_t sz; int overflow; int ret = 0; ARG_CHECK(ctx != NULL); @@ -28,8 +60,8 @@ int secp256k1_pedersen_commit(const secp256k1_context* ctx, unsigned char *commi secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value); if (!secp256k1_gej_is_infinity(&rj)) { secp256k1_ge_set_gej(&r, &rj); - sz = 33; - ret = secp256k1_eckey_pubkey_serialize(&r, commit, &sz, 1); + secp256k1_pedersen_commitment_save(commit, &r); + ret = 1; } secp256k1_gej_clear(&rj); secp256k1_ge_clear(&r); @@ -41,10 +73,10 @@ int secp256k1_pedersen_commit(const secp256k1_context* ctx, unsigned char *commi /** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest * negative, then calculates an additional blinding value that adds to zero. */ -int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, int n, int npositive) { +int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { secp256k1_scalar acc; secp256k1_scalar x; - int i; + size_t i; int overflow; ARG_CHECK(ctx != NULL); ARG_CHECK(blind_out != NULL); @@ -66,12 +98,11 @@ int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *bl return 1; } -/* Takes two list of 33-byte commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ -int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const unsigned char * const *commits, int pcnt, - const unsigned char * const *ncommits, int ncnt, int64_t excess) { +/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt, int64_t excess) { secp256k1_gej accj; secp256k1_ge add; - int i; + size_t i; ARG_CHECK(ctx != NULL); ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); @@ -87,24 +118,20 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const unsigned } } for (i = 0; i < ncnt; i++) { - if (!secp256k1_eckey_pubkey_parse(&add, ncommits[i], 33)) { - return 0; - } + secp256k1_pedersen_commitment_load(&add, ncommits[i]); secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); } secp256k1_gej_neg(&accj, &accj); for (i = 0; i < pcnt; i++) { - if (!secp256k1_eckey_pubkey_parse(&add, commits[i], 33)) { - return 0; - } + secp256k1_pedersen_commitment_load(&add, commits[i]); secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); } return secp256k1_gej_is_infinity(&accj); } int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, - uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, int plen) { - int offset; + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { + size_t offset; uint64_t scale; ARG_CHECK(exp != NULL); ARG_CHECK(mantissa != NULL); @@ -117,9 +144,10 @@ int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *manti } int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, - unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, - const unsigned char *commit, const unsigned char *proof, int plen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen) { + secp256k1_ge commitp; ARG_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(proof != NULL); @@ -127,24 +155,28 @@ int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, ARG_CHECK(max_value != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_pedersen_commitment_load(&commitp, commit); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - blind_out, value_out, message_out, outlen, nonce, min_value, max_value, commit, proof, plen); + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen); } int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, - const unsigned char *commit, const unsigned char *proof, int plen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen) { + secp256k1_ge commitp; ARG_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(proof != NULL); ARG_CHECK(min_value != NULL); ARG_CHECK(max_value != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + secp256k1_pedersen_commitment_load(&commitp, commit); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, - NULL, NULL, NULL, NULL, NULL, min_value, max_value, commit, proof, plen); + NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen); } -int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, int *plen, uint64_t min_value, - const unsigned char *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ +int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value, + const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + secp256k1_ge commitp; ARG_CHECK(ctx != NULL); ARG_CHECK(proof != NULL); ARG_CHECK(plen != NULL); @@ -153,8 +185,9 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof ARG_CHECK(nonce != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_pedersen_commitment_load(&commitp, commit); return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - proof, plen, min_value, commit, blind, nonce, exp, min_bits, value); + proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value); } #endif diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h index cdfe2f8e..84dd20b4 100644 --- a/src/modules/rangeproof/pedersen.h +++ b/src/modules/rangeproof/pedersen.h @@ -7,6 +7,7 @@ #ifndef _SECP256K1_PEDERSEN_H_ #define _SECP256K1_PEDERSEN_H_ +#include "ecmult_gen.h" #include "group.h" #include "scalar.h" diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/rangeproof/pedersen_impl.h index 3ce2767c..991c60b3 100644 --- a/src/modules/rangeproof/pedersen_impl.h +++ b/src/modules/rangeproof/pedersen_impl.h @@ -7,6 +7,16 @@ #ifndef _SECP256K1_PEDERSEN_IMPL_H_ #define _SECP256K1_PEDERSEN_IMPL_H_ +#include + +#include "eckey.h" +#include "ecmult_const.h" +#include "ecmult_gen.h" +#include "group.h" +#include "field.h" +#include "scalar.h" +#include "util.h" + /** Alternative generator for secp256k1. * This is the sha256 of 'g' after DER encoding (without compression), * which happens to be a point on the curve. diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index b0f53696..85f94bc4 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -9,10 +9,12 @@ #include "scalar.h" #include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, - unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const unsigned char *commit, const unsigned char *proof, int plen); + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen); #endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index f76e1b02..3f2c3a46 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -7,20 +7,23 @@ #ifndef _SECP256K1_RANGEPROOF_IMPL_H_ #define _SECP256K1_RANGEPROOF_IMPL_H_ +#include "eckey.h" #include "scalar.h" #include "group.h" #include "rangeproof.h" #include "hash_impl.h" +#include "pedersen_impl.h" +#include "util.h" #include "modules/rangeproof/pedersen.h" #include "modules/rangeproof/borromean.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, - int exp, int *rsizes, int rings) { + int exp, size_t *rsizes, size_t rings) { secp256k1_gej base; - int i; - int j; - int npub; + size_t i; + size_t j; + size_t npub; VERIFY_CHECK(exp < 19); if (exp < 0) { exp = 0; @@ -48,22 +51,30 @@ SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs } } +SECP256K1_INLINE static void secp256k1_rangeproof_serialize_point(unsigned char* data, const secp256k1_ge *point) { + secp256k1_fe pointx; + pointx = point->x; + secp256k1_fe_normalize(&pointx); + data[0] = !secp256k1_fe_is_quad_var(&point->y); + secp256k1_fe_get_b32(data + 1, &pointx); +} + SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_scalar *s, unsigned char *message, - int *rsizes, int rings, const unsigned char *nonce, const unsigned char *commit, const unsigned char *proof, int len) { + size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len) { unsigned char tmp[32]; unsigned char rngseed[32 + 33 + 10]; secp256k1_rfc6979_hmac_sha256 rng; secp256k1_scalar acc; int overflow; int ret; - int i; - int j; + size_t i; + size_t j; int b; - int npub; + size_t npub; VERIFY_CHECK(len <= 10); memcpy(rngseed, nonce, 32); - memcpy(rngseed + 32, commit, 33); - memcpy(rngseed + 65, proof, len); + secp256k1_rangeproof_serialize_point(rngseed + 32, commit); + memcpy(rngseed + 33 + 32, proof, len); secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + len); secp256k1_scalar_clear(&acc); npub = 0; @@ -99,9 +110,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, return ret; } -SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, int *rings, int *rsizes, int *npub, int *secidx, uint64_t *min_value, +SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rings, size_t *rsizes, size_t *npub, size_t *secidx, uint64_t *min_value, int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) { - int i; + size_t i; *rings = 1; rsizes[0] = 1; secidx[0] = 0; @@ -135,13 +146,13 @@ SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, int *rings, *v = value - *min_value; /* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */ v2 = *min_bits ? (UINT64_MAX>>(64-*min_bits)) : 0; - for (i = 0; i < *exp && (v2 <= UINT64_MAX / 10); i++) { + for (i = 0; (int) i < *exp && (v2 <= UINT64_MAX / 10); i++) { *v /= 10; v2 *= 10; } *exp = i; v2 = *v; - for (i = 0; i < *exp; i++) { + for (i = 0; (int) i < *exp; i++) { v2 *= 10; *scale *= 10; } @@ -179,8 +190,8 @@ SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, int *rings, /* strawman interface, writes proof in proof, a buffer of plen, proves with respect to min_value the range for commit which has the provided blinding factor and value. */ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, - unsigned char *proof, int *plen, uint64_t min_value, - const unsigned char *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + unsigned char *proof, size_t *plen, uint64_t min_value, + const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ @@ -193,13 +204,13 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul uint64_t v; uint64_t scale; /* scale = 10^exp. */ int mantissa; /* Number of bits proven in the blinded value. */ - int rings; /* How many digits will our proof cover. */ - int rsizes[32]; /* How many possible values there are for each place. */ - int secidx[32]; /* Which digit is the correct one. */ - int len; /* Number of bytes used so far. */ - int i; + size_t rings; /* How many digits will our proof cover. */ + size_t rsizes[32]; /* How many possible values there are for each place. */ + size_t secidx[32]; /* Which digit is the correct one. */ + size_t len; /* Number of bytes used so far. */ + size_t i; int overflow; - int npub; + size_t npub; len = 0; if (*plen < 65 || min_value > value || min_bits > 64 || min_bits < 0 || exp < -1 || exp > 18) { return 0; @@ -225,13 +236,14 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul return 0; } secp256k1_sha256_initialize(&sha256_m); - secp256k1_sha256_write(&sha256_m, commit, 33); + secp256k1_rangeproof_serialize_point(tmp, commit); + secp256k1_sha256_write(&sha256_m, tmp, 33); secp256k1_sha256_write(&sha256_m, proof, len); memset(prep, 0, 4096); /* Note, the data corresponding to the blinding factors must be zero. */ if (rsizes[rings - 1] > 1) { - int idx; + size_t idx; /* Value encoding sidechannel. */ idx = rsizes[rings - 1] - 1; idx -= secidx[rings - 1] == idx; @@ -276,16 +288,17 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul return 0; } if (i < rings - 1) { - size_t size = 33; + unsigned char tmpc[33]; secp256k1_ge c; + unsigned char quadness; /*OPT: split loop and batch invert.*/ + /*OPT: do not compute full pubs[npub] in ge form; we only need x */ secp256k1_ge_set_gej_var(&c, &pubs[npub]); - if(!secp256k1_eckey_pubkey_serialize(&c, tmp, &size, 1)) { - return 0; - } - secp256k1_sha256_write(&sha256_m, tmp, 33); - signs[i>>3] |= (tmp[0] == 3) << (i&7); - memcpy(&proof[len], &tmp[1], 32); + secp256k1_rangeproof_serialize_point(tmpc, &c); + quadness = tmpc[0]; + secp256k1_sha256_write(&sha256_m, tmpc, 33); + signs[i>>3] |= quadness << (i&7); + memcpy(&proof[len], tmpc + 1, 32); len += 32; } npub += rsizes[i]; @@ -332,21 +345,21 @@ SECP256K1_INLINE static void secp256k1_rangeproof_ch32xor(unsigned char *x, cons } SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar *blind, uint64_t *v, - unsigned char *m, int *mlen, secp256k1_scalar *ev, secp256k1_scalar *s, - int *rsizes, int rings, const unsigned char *nonce, const unsigned char *commit, const unsigned char *proof, int len) { + unsigned char *m, size_t *mlen, secp256k1_scalar *ev, secp256k1_scalar *s, + size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len) { secp256k1_scalar s_orig[128]; secp256k1_scalar sec[32]; secp256k1_scalar stmp; unsigned char prep[4096]; unsigned char tmp[32]; uint64_t value; - int offset; - int i; - int j; + size_t offset; + size_t i; + size_t j; int b; - int skip1; - int skip2; - int npub; + size_t skip1; + size_t skip2; + size_t npub; npub = ((rings - 1) << 2) + rsizes[rings-1]; VERIFY_CHECK(npub <= 128); VERIFY_CHECK(npub >= 1); @@ -368,7 +381,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } npub = (rings - 1) << 2; for (j = 0; j < 2; j++) { - int idx; + size_t idx; /* Look for a value encoding in the last ring. */ idx = npub + rsizes[rings - 1] - 1 - j; secp256k1_scalar_get_b32(tmp, &s[idx]); @@ -417,7 +430,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * offset = 0; npub = 0; for (i = 0; i < rings; i++) { - int idx; + size_t idx; idx = (value >> (i << 1)) & 3; for (j = 0; j < rsizes[i]; j++) { if (npub == skip1 || npub == skip2) { @@ -454,8 +467,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * return 1; } -SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(int *offset, int *exp, int *mantissa, uint64_t *scale, - uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, int plen) { +SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(size_t *offset, int *exp, int *mantissa, uint64_t *scale, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { int i; int has_nz_range; int has_min; @@ -507,27 +520,26 @@ SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(int *offset, int return 1; } -/* Verifies range proof (len plen) for 33-byte commit, the min/max values proven are put in the min/max arguments; returns 0 on failure 1 on success.*/ +/* Verifies range proof (len plen) for commit, the min/max values proven are put in the min/max arguments; returns 0 on failure 1 on success.*/ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, - unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const unsigned char *commit, const unsigned char *proof, int plen) { + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen) { secp256k1_gej accj; secp256k1_gej pubs[128]; secp256k1_ge c; secp256k1_scalar s[128]; secp256k1_scalar evalues[128]; /* Challenges, only used during proof rewind. */ secp256k1_sha256 sha256_m; - int rsizes[32]; + size_t rsizes[32]; int ret; - int i; - size_t size; + size_t i; int exp; int mantissa; - int offset; - int rings; + size_t offset; + size_t rings; int overflow; - int npub; + size_t npub; int offset_post_header; uint64_t scale; unsigned char signs[31]; @@ -558,7 +570,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm return 0; } secp256k1_sha256_initialize(&sha256_m); - secp256k1_sha256_write(&sha256_m, commit, 33); + secp256k1_rangeproof_serialize_point(m, commit); + secp256k1_sha256_write(&sha256_m, m, 33); secp256k1_sha256_write(&sha256_m, proof, offset); for(i = 0; i < rings - 1; i++) { signs[i] = (proof[offset + ( i>> 3)] & (1 << (i & 7))) != 0; @@ -576,22 +589,23 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm secp256k1_pedersen_ecmult_small(&accj, *min_value); } for(i = 0; i < rings - 1; i++) { - memcpy(&m[1], &proof[offset], 32); - m[0] = 2 + signs[i]; - if (!secp256k1_eckey_pubkey_parse(&c, m, 33)) { - return 0; + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &proof[offset]); + secp256k1_ge_set_xquad(&c, &fe); + if (signs[i]) { + secp256k1_ge_neg(&c, &c); } - secp256k1_sha256_write(&sha256_m, m, 33); + /* Not using secp256k1_rangeproof_serialize_point as we almost have it + * serialized form already. */ + secp256k1_sha256_write(&sha256_m, &signs[i], 1); + secp256k1_sha256_write(&sha256_m, &proof[offset], 32); secp256k1_gej_set_ge(&pubs[npub], &c); secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL); offset += 32; npub += rsizes[i]; } secp256k1_gej_neg(&accj, &accj); - if (!secp256k1_eckey_pubkey_parse(&c, commit, 33)) { - return 0; - } - secp256k1_gej_add_ge_var(&pubs[npub], &accj, &c, NULL); + secp256k1_gej_add_ge_var(&pubs[npub], &accj, commit, NULL); if (secp256k1_gej_is_infinity(&pubs[npub])) { return 0; } @@ -615,7 +629,6 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm if (ret && nonce) { /* Given the nonce, try rewinding the witness to recover its initial state. */ secp256k1_scalar blind; - unsigned char commitrec[33]; uint64_t vv; if (!ecmult_gen_ctx) { return 0; @@ -630,10 +643,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm if (secp256k1_gej_is_infinity(&accj)) { return 0; } - secp256k1_ge_set_gej(&c, &accj); - size = 33; - secp256k1_eckey_pubkey_serialize(&c, commitrec, &size, 1); - if (size != 33 || memcmp(commitrec, commit, 33) != 0) { + secp256k1_gej_neg(&accj, &accj); + secp256k1_gej_add_ge_var(&accj, &accj, commit, NULL); + if (!secp256k1_gej_is_infinity(&accj)) { return 0; } if (blindout) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 4e601697..7cd27969 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -7,11 +7,16 @@ #ifndef SECP256K1_MODULE_RANGEPROOF_TESTS #define SECP256K1_MODULE_RANGEPROOF_TESTS +#include "group.h" +#include "scalar.h" +#include "testrand.h" +#include "util.h" + #include "include/secp256k1_rangeproof.h" void test_pedersen(void) { - unsigned char commits[33*19]; - const unsigned char *cptr[19]; + secp256k1_pedersen_commitment commits[19]; + const secp256k1_pedersen_commitment *cptr[19]; unsigned char blinds[32*19]; const unsigned char *bptr[19]; secp256k1_scalar s; @@ -25,7 +30,7 @@ void test_pedersen(void) { outputs = (secp256k1_rand32() & 7) + 2; total = inputs + outputs; for (i = 0; i < 19; i++) { - cptr[i] = &commits[i * 33]; + cptr[i] = &commits[i]; bptr[i] = &blinds[i * 32]; } totalv = 0; @@ -56,7 +61,7 @@ void test_pedersen(void) { } CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); for (i = 0; i < total; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i * 33], &blinds[i * 32], values[i])); + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i])); } CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv)); CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1)); @@ -68,7 +73,7 @@ void test_pedersen(void) { values[1] = 0; values[2] = 1; for (i = 0; i < 3; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i * 33], &blinds[i * 32], values[i])); + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i])); } CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1)); CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1)); @@ -87,11 +92,11 @@ void test_borromean(void) { secp256k1_ge ge; secp256k1_scalar one; unsigned char m[32]; - int rsizes[8]; - int secidx[8]; - int nrings; - int i; - int j; + size_t rsizes[8]; + size_t secidx[8]; + size_t nrings; + size_t i; + size_t j; int c; secp256k1_rand256_test(m); nrings = 1 + (secp256k1_rand32()&7); @@ -145,32 +150,32 @@ void test_borromean(void) { void test_rangeproof(void) { const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; - unsigned char commit[33]; - unsigned char commit2[33]; + secp256k1_pedersen_commitment commit; + secp256k1_pedersen_commitment commit2; unsigned char proof[5134]; unsigned char blind[32]; unsigned char blindout[32]; unsigned char message[4096]; - int mlen; + size_t mlen; uint64_t v; uint64_t vout; uint64_t vmin; uint64_t minv; uint64_t maxv; - int len; - int i; - int j; - int k; + size_t len; + size_t i; + size_t j; + size_t k; secp256k1_rand256(blind); for (i = 0; i < 11; i++) { v = testvs[i]; - CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, commit, blind, commit, 0, 0, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit, &minv, &maxv, commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len)); for (j = 0; j < mlen; j++) { CHECK(message[j] == 0); } @@ -180,9 +185,9 @@ void test_rangeproof(void) { CHECK(minv <= v); CHECK(maxv >= v); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, commit, blind, commit, -1, 64, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v)); CHECK(len <= 73); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit, &minv, &maxv, commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len)); CHECK(memcmp(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); @@ -191,11 +196,11 @@ void test_rangeproof(void) { } secp256k1_rand256(blind); v = INT64_MAX - 1; - CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); for (i = 0; i < 19; i++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, commit, blind, commit, i, 0, v)); - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); CHECK(len <= 5134); CHECK(minv <= v); CHECK(maxv >= v); @@ -204,21 +209,21 @@ void test_rangeproof(void) { { /*Malleability test.*/ v = secp256k1_rands64(0, 255); - CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, commit, blind, commit, 0, 3, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v)); CHECK(len <= 5134); for (i = 0; i < len*8; i++) { proof[i >> 3] ^= 1 << (i & 7); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); proof[i >> 3] ^= 1 << (i & 7); } - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); CHECK(minv <= v); CHECK(maxv >= v); } - memcpy(commit2, commit, 33); - for (i = 0; i < 10 * count; i++) { + memcpy(&commit2, &commit, sizeof(commit)); + for (i = 0; i < 10 * (size_t) count; i++) { int exp; int min_bits; v = secp256k1_rands64(0, UINT64_MAX >> (secp256k1_rand32()&63)); @@ -227,7 +232,7 @@ void test_rangeproof(void) { vmin = secp256k1_rands64(0, v); } secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); len = 5134; exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); if (exp < 0) { @@ -237,10 +242,10 @@ void test_rangeproof(void) { if (min_bits < 0) { min_bits = -min_bits; } - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, commit, blind, commit, exp, min_bits, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit, &minv, &maxv, commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len)); for (j = 0; j < mlen; j++) { CHECK(message[j] == 0); } @@ -249,8 +254,8 @@ void test_rangeproof(void) { CHECK(vout == v); CHECK(minv <= v); CHECK(maxv >= v); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit, &minv, &maxv, commit, proof, len)); - memcpy(commit2, commit, 33); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len)); + memcpy(&commit2, &commit, sizeof(commit)); } for (j = 0; j < 10; j++) { for (i = 0; i < 96; i++) { @@ -258,10 +263,10 @@ void test_rangeproof(void) { } for (k = 0; k < 128; k++) { len = k; - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit2, proof, len)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len)); } len = secp256k1_rands64(0, 3072); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit2, proof, len)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len)); } } diff --git a/src/secp256k1.c b/src/secp256k1.c index b3312100..b1042c35 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -25,6 +25,7 @@ #endif #ifdef ENABLE_MODULE_RANGEPROOF +# include "include/secp256k1_rangeproof.h" # include "modules/rangeproof/pedersen.h" # include "modules/rangeproof/rangeproof.h" #endif From e7a8a5f6383c705159f191b6f4c165efdcfb4334 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 5 Jul 2016 15:46:07 +0000 Subject: [PATCH 004/381] rangeproof: expose sidechannel message field in the signing API Including a fix by Jonas Nick. --- include/secp256k1_rangeproof.h | 4 ++- src/bench_rangeproof.c | 2 +- src/modules/rangeproof/main_impl.h | 5 +-- src/modules/rangeproof/rangeproof_impl.h | 13 +++++++- src/modules/rangeproof/tests_impl.h | 39 ++++++++++++++++++++---- 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 94cfaee8..7803e7a5 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -199,7 +199,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( const unsigned char *nonce, int exp, int min_bits, - uint64_t value + uint64_t value, + const unsigned char *message, + size_t msg_len ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); /** Extract some basic information from a range-proof. diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 5e068361..dc01835f 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -30,7 +30,7 @@ static void bench_rangeproof_setup(void* arg) { for (i = 0; i < 32; i++) data->blind[i] = i + 1; CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v)); data->len = 5134; - CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v)); + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0)); CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len)); } diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index a1ad6715..c743b6d7 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -175,7 +175,8 @@ int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_valu } int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, + const unsigned char *message, size_t msg_len){ secp256k1_ge commitp; ARG_CHECK(ctx != NULL); ARG_CHECK(proof != NULL); @@ -187,7 +188,7 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value); + proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len); } #endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 3f2c3a46..efd43e12 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -191,7 +191,8 @@ SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rin SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *proof, size_t *plen, uint64_t min_value, - const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, + const unsigned char *message, size_t msg_len){ secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ @@ -231,6 +232,13 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } len += 8; } + /* Do we have enough room in the proof for the message? Each ring gives us 128 bytes, but the + * final ring is used to encode the blinding factor and the value, so we can't use that. (Well, + * technically there are 64 bytes available if we avoided the other data, but this is difficult + * because it's not always in the same place. */ + if (msg_len > 0 && msg_len > 128 * (rings - 1)) { + return 0; + } /* Do we have enough room for the proof? */ if (*plen - len < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { return 0; @@ -241,6 +249,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul secp256k1_sha256_write(&sha256_m, proof, len); memset(prep, 0, 4096); + if (message != NULL) { + memcpy(prep, message, msg_len); + } /* Note, the data corresponding to the blinding factors must be zero. */ if (rsizes[rings - 1] > 1) { size_t idx; diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 7cd27969..c8815afb 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_MODULE_RANGEPROOF_TESTS #define SECP256K1_MODULE_RANGEPROOF_TESTS +#include + #include "group.h" #include "scalar.h" #include "testrand.h" @@ -166,17 +168,42 @@ void test_rangeproof(void) { size_t i; size_t j; size_t k; + /* Short message is a Simone de Beauvoir quote */ + const unsigned char message_short[120] = "When I see my own likeness in the depths of someone else's consciousness, I always experience a moment of panic."; + /* Long message is 0xA5 with a bunch of this quote in the middle */ + unsigned char message_long[3968]; + memset(message_long, 0xa5, sizeof(message_long)); + for (i = 1200; i < 3600; i += 120) { + memcpy(&message_long[i], message_short, sizeof(message_short)); + } + secp256k1_rand256(blind); for (i = 0; i < 11; i++) { v = testvs[i]; CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { + const unsigned char *input_message = NULL; + size_t input_message_len = 0; + /* vmin is always either 0 or 1; if it is 1, then we have no room for a message. + * If it's 0, we use "minimum encoding" and only have room for a small message when + * `testvs[i]` is >= 4; for a large message when it's >= 2^32. */ + if (vmin == 0 && i > 2) { + input_message = message_short; + input_message_len = sizeof(message_short); + } + if (vmin == 0 && i > 7) { + input_message = message_long; + input_message_len = sizeof(message_long); + } len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len)); CHECK(len <= 5134); mlen = 4096; CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len)); - for (j = 0; j < mlen; j++) { + if (input_message != NULL) { + CHECK(memcmp(message, input_message, input_message_len) == 0); + } + for (j = input_message_len; j < mlen; j++) { CHECK(message[j] == 0); } CHECK(mlen <= 4096); @@ -185,7 +212,7 @@ void test_rangeproof(void) { CHECK(minv <= v); CHECK(maxv >= v); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0)); CHECK(len <= 73); CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len)); CHECK(memcmp(blindout, blind, 32) == 0); @@ -199,7 +226,7 @@ void test_rangeproof(void) { CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); for (i = 0; i < 19; i++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0)); CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); CHECK(len <= 5134); CHECK(minv <= v); @@ -211,7 +238,7 @@ void test_rangeproof(void) { v = secp256k1_rands64(0, 255); CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0)); CHECK(len <= 5134); for (i = 0; i < len*8; i++) { proof[i >> 3] ^= 1 << (i & 7); @@ -242,7 +269,7 @@ void test_rangeproof(void) { if (min_bits < 0) { min_bits = -min_bits; } - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0)); CHECK(len <= 5134); mlen = 4096; CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len)); From 360e2180438f3a8cd09215cc5c84a9038c5b59c7 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 7 Jul 2016 00:47:41 +0200 Subject: [PATCH 005/381] Constant-time generator module --- Makefile.am | 4 + configure.ac | 18 ++ include/secp256k1_generator.h | 96 ++++++++++ include/secp256k1_rangeproof.h | 2 +- sage/shallue_van_de_woestijne.sage | 51 ++++++ src/bench_generator.c | 60 +++++++ src/modules/generator/Makefile.am.include | 8 + src/modules/generator/main_impl.h | 206 ++++++++++++++++++++++ src/modules/generator/tests_impl.h | 139 +++++++++++++++ src/secp256k1.c | 8 + src/tests.c | 8 + 11 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 include/secp256k1_generator.h create mode 100644 sage/shallue_van_de_woestijne.sage create mode 100644 src/bench_generator.c create mode 100644 src/modules/generator/Makefile.am.include create mode 100644 src/modules/generator/main_impl.h create mode 100644 src/modules/generator/tests_impl.h diff --git a/Makefile.am b/Makefile.am index fbf219a2..8b5911a6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,6 +153,10 @@ if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif +if ENABLE_MODULE_GENERATOR +include src/modules/generator/Makefile.am.include +endif + if ENABLE_MODULE_RANGEPROOF include src/modules/rangeproof/Makefile.am.include endif diff --git a/configure.ac b/configure.ac index fb16263a..d1fcff96 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,10 @@ AC_ARG_ENABLE(module_recovery, [enable_module_recovery=$enableval], [enable_module_recovery=no]) +AC_ARG_ENABLE(module_generator, + AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module (default is no)]), + [enable_module_generator=$enableval], + [enable_module_generator=no]) AC_ARG_ENABLE(module_rangeproof, AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module (default is no)]), @@ -505,6 +509,10 @@ if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi +if test x"$enable_module_generator" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_GENERATOR, 1, [Define this symbol to enable the NUMS generator module]) +fi + if test x"$enable_module_rangeproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) fi @@ -524,8 +532,14 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator]) AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([******]) + if test x"$enable_module_generator" != x"yes"; then + if test x"$enable_module_rangeproof" = x"yes"; then + AC_MSG_ERROR([Rangeproof module requires the generator module. Use --enable-module-generator to allow.]) + fi + fi else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) @@ -533,6 +547,9 @@ else if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_generator" = x"yes"; then + AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_rangeproof" = x"yes"; then AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.]) fi @@ -551,6 +568,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) 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_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h new file mode 100644 index 00000000..7743b06e --- /dev/null +++ b/include/secp256k1_generator.h @@ -0,0 +1,96 @@ +#ifndef _SECP256K1_GENERATOR_ +# define _SECP256K1_GENERATOR_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** Opaque data structure that stores a base point + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 33 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_generator_serialize_*. + * + * Furthermore, it is guaranteed to identical points will have identical + * representation, so they can be memcmp'ed. + */ +typedef struct { + unsigned char data[33]; +} secp256k1_generator; + +/** Parse a 33-byte generator byte sequence into a generator object. + * + * Returns: 1 if input contains a valid generator. + * Args: ctx: a secp256k1 context object. + * Out: commit: pointer to the output generator object + * In: input: pointer to a 33-byte serialized generator + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse( + const secp256k1_context* ctx, + secp256k1_generator* commit, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a 33-byte generator into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 33-byte byte array + * In: commit: a pointer to a generator + */ +SECP256K1_API int secp256k1_generator_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_generator* commit +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a generator for the curve. + * + * Returns: 0 in the highly unlikely case the seed is not acceptable, + * 1 otherwise. + * Args: ctx: a secp256k1 context object + * Out: gen: a generator object + * In: seed32: a 32-byte seed + * + * If succesful, a valid generator will be placed in gen. The produced + * generators are distributed uniformly over the curve, and will not have a + * known dicrete logarithm with respect to any other generator produced, + * or to the base generator G. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( + const secp256k1_context* ctx, + secp256k1_generator* gen, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a blinded generator for the curve. + * + * Returns: 0 in the highly unlikely case the seed is not acceptable or when + * blind is out of range. 1 otherwise. + * Args: ctx: a secp256k1 context object + * Out: gen: a generator object + * In: seed32: a 32-byte seed + * blind32: a 32-byte secret value to blind the generator with. + * + * The result is equivalent to first calling secp256k1_generator_generate, + * converting the result to a public key, calling secp256k1_ec_pubkey_tweak_add, + * and then converting back to generator form. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blinded( + const secp256k1_context* ctx, + secp256k1_generator* gen, + const unsigned char *key32, + const unsigned char *blind32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 7803e7a5..0afeed32 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -2,6 +2,7 @@ # define _SECP256K1_RANGEPROOF_ # include "secp256k1.h" +# include "secp256k1_generator.h" # ifdef __cplusplus extern "C" { @@ -52,7 +53,6 @@ SECP256K1_API int secp256k1_pedersen_commitment_serialize( const secp256k1_pedersen_commitment* commit ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - /** Initialize a context for usage with Pedersen commitments. */ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); diff --git a/sage/shallue_van_de_woestijne.sage b/sage/shallue_van_de_woestijne.sage new file mode 100644 index 00000000..1cc97b65 --- /dev/null +++ b/sage/shallue_van_de_woestijne.sage @@ -0,0 +1,51 @@ + +### http://www.di.ens.fr/~fouque/pub/latincrypt12.pdf + +# Parameters for secp256k1 +p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +a = 0 +b = 7 +F = FiniteField (p) +C = EllipticCurve ([F(a), F(b)]) + +def svdw(t): + sqrt_neg_3 = F(-3).nth_root(2) + + ## Compute candidate x values + w = sqrt_neg_3 * t / (1 + b + t^2) + x = [ F(0), F(0), F(0) ] + x[0] = (-1 + sqrt_neg_3) / 2 - t * w + x[1] = -1 - x[0] + x[2] = 1 + 1 / w^2 + + print + print "On %2d" % t + print " x1 %064x" % x[0] + print " x2 %064x" % x[1] + print " x3 %064x" % x[2] + + ## Select which to use + alph = jacobi_symbol(x[0]^3 + b, p) + beta = jacobi_symbol(x[1]^3 + b, p) + if alph == 1 and beta == 1: + i = 0 + elif alph == 1 and beta == -1: + i = 0 + elif alph == -1 and beta == 1: + i = 1 + elif alph == -1 and beta == -1: + i = 2 + else: + print "Help! I don't understand Python!" + + ## Expand to full point + sign = 1 - 2 * (int(F(t)) % 2) + ret_x = x[i] + ret_y = sign * F(x[i]^3 + b).nth_root(2) + return C.point((ret_x, ret_y)) + + +## main +for i in range(1, 11): + res = svdw(i) + print "Result: %064x %064x" % res.xy() diff --git a/src/bench_generator.c b/src/bench_generator.c new file mode 100644 index 00000000..d3b251e4 --- /dev/null +++ b/src/bench_generator.c @@ -0,0 +1,60 @@ +/********************************************************************** + * Copyright (c) 2016 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1_generator.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + unsigned char key[32]; + unsigned char blind[32]; +} bench_generator_t; + +static void bench_generator_setup(void* arg) { + bench_generator_t *data = (bench_generator_t*)arg; + memset(data->key, 0x31, 32); + memset(data->blind, 0x13, 32); +} + +static void bench_generator_generate(void* arg, int iters) { + int i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_generator gen; + CHECK(secp256k1_generator_generate(data->ctx, &gen, data->key)); + data->key[i & 31]++; + } +} + +static void bench_generator_generate_blinded(void* arg, int iters) { + int i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_generator gen; + CHECK(secp256k1_generator_generate_blinded(data->ctx, &gen, data->key, data->blind)); + data->key[1 + (i & 30)]++; + data->blind[1 + (i & 30)]++; + } +} + +int main(void) { + bench_generator_t data; + int iters = get_iters(20000); + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, iters); + run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include new file mode 100644 index 00000000..bc3c514f --- /dev/null +++ b/src/modules/generator/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_generator.h +noinst_HEADERS += src/modules/generator/main_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_generator +bench_generator_SOURCES = src/bench_generator.c +bench_generator_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_generator_LDFLAGS = -static +endif diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h new file mode 100644 index 00000000..07ad32e7 --- /dev/null +++ b/src/modules/generator/main_impl.h @@ -0,0 +1,206 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra & Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_GENERATOR_MAIN +#define SECP256K1_MODULE_GENERATOR_MAIN + +#include + +#include "field.h" +#include "group.h" +#include "hash.h" + +static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &gen->data[1]); + secp256k1_ge_set_xquad(ge, &fe); + if (gen->data[0] & 1) { + secp256k1_ge_neg(ge, ge); + } +} + +static void secp256k1_generator_save(secp256k1_generator* commit, secp256k1_ge* ge) { + secp256k1_fe_normalize(&ge->x); + secp256k1_fe_get_b32(&commit->data[1], &ge->x); + commit->data[0] = 11 ^ secp256k1_fe_is_quad_var(&ge->y); +} + +int secp256k1_generator_parse(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *input) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(input != NULL); + if ((input[0] & 0xFE) != 10) { + return 0; + } + memcpy(gen->data, input, sizeof(gen->data)); + return 1; +} + +int secp256k1_generator_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_generator* gen) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(gen != NULL); + memcpy(output, gen->data, sizeof(gen->data)); + return 1; +} + +static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { + /* Implements the algorithm from: + * Indifferentiable Hashing to Barreto-Naehrig Curves + * Pierre-Alain Fouque and Mehdi Tibouchi + * Latincrypt 2012 + */ + + /* Basic algorithm: + + c = sqrt(-3) + d = (c - 1)/2 + + w = c * t / (1 + b + t^2) [with b = 7] + x1 = d - t*w + x2 = -(x1 + 1) + x3 = 1 + 1/w^2 + + To avoid the 2 divisions, compute the above in numerator/denominator form: + wn = c * t + wd = 1 + 7 + t^2 + x1n = d*wd - t*wn + x1d = wd + x2n = -(x1n + wd) + x2d = wd + x3n = wd^2 + c^2 + t^2 + x3d = (c * t)^2 + + The joint denominator j = wd * c^2 * t^2, and + 1 / x1d = 1/j * c^2 * t^2 + 1 / x2d = x3d = 1/j * wd + */ + + static const secp256k1_fe c = SECP256K1_FE_CONST(0x0a2d2ba9, 0x3507f1df, 0x233770c2, 0xa797962c, 0xc61f6d15, 0xda14ecd4, 0x7d8d27ae, 0x1cd5f852); + static const secp256k1_fe d = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40); + static const secp256k1_fe b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7); + static const secp256k1_fe b_plus_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 8); + + secp256k1_fe wn, wd, x1n, x2n, x3n, x3d, jinv, tmp, x1, x2, x3, alphain, betain, gammain, y1, y2, y3; + int alphaquad, betaquad; + + secp256k1_fe_mul(&wn, &c, t); /* mag 1 */ + secp256k1_fe_sqr(&wd, t); /* mag 1 */ + secp256k1_fe_add(&wd, &b_plus_one); /* mag 2 */ + secp256k1_fe_mul(&tmp, t, &wn); /* mag 1 */ + secp256k1_fe_negate(&tmp, &tmp, 1); /* mag 2 */ + secp256k1_fe_mul(&x1n, &d, &wd); /* mag 1 */ + secp256k1_fe_add(&x1n, &tmp); /* mag 3 */ + x2n = x1n; /* mag 3 */ + secp256k1_fe_add(&x2n, &wd); /* mag 5 */ + secp256k1_fe_negate(&x2n, &x2n, 5); /* mag 6 */ + secp256k1_fe_mul(&x3d, &c, t); /* mag 1 */ + secp256k1_fe_sqr(&x3d, &x3d); /* mag 1 */ + secp256k1_fe_sqr(&x3n, &wd); /* mag 1 */ + secp256k1_fe_add(&x3n, &x3d); /* mag 2 */ + secp256k1_fe_mul(&jinv, &x3d, &wd); /* mag 1 */ + secp256k1_fe_inv(&jinv, &jinv); /* mag 1 */ + secp256k1_fe_mul(&x1, &x1n, &x3d); /* mag 1 */ + secp256k1_fe_mul(&x1, &x1, &jinv); /* mag 1 */ + secp256k1_fe_mul(&x2, &x2n, &x3d); /* mag 1 */ + secp256k1_fe_mul(&x2, &x2, &jinv); /* mag 1 */ + secp256k1_fe_mul(&x3, &x3n, &wd); /* mag 1 */ + secp256k1_fe_mul(&x3, &x3, &jinv); /* mag 1 */ + + secp256k1_fe_sqr(&alphain, &x1); /* mag 1 */ + secp256k1_fe_mul(&alphain, &alphain, &x1); /* mag 1 */ + secp256k1_fe_add(&alphain, &b); /* mag 2 */ + secp256k1_fe_sqr(&betain, &x2); /* mag 1 */ + secp256k1_fe_mul(&betain, &betain, &x2); /* mag 1 */ + secp256k1_fe_add(&betain, &b); /* mag 2 */ + secp256k1_fe_sqr(&gammain, &x3); /* mag 1 */ + secp256k1_fe_mul(&gammain, &gammain, &x3); /* mag 1 */ + secp256k1_fe_add(&gammain, &b); /* mag 2 */ + + alphaquad = secp256k1_fe_sqrt(&y1, &alphain); + betaquad = secp256k1_fe_sqrt(&y2, &betain); + secp256k1_fe_sqrt(&y3, &gammain); + + secp256k1_fe_cmov(&x1, &x2, (!alphaquad) & betaquad); + secp256k1_fe_cmov(&y1, &y2, (!alphaquad) & betaquad); + secp256k1_fe_cmov(&x1, &x3, (!alphaquad) & !betaquad); + secp256k1_fe_cmov(&y1, &y3, (!alphaquad) & !betaquad); + + secp256k1_ge_set_xy(ge, &x1, &y1); + + /* The linked algorithm from the paper uses the Jacobi symbol of t to + * determine the Jacobi symbol of the produced y coordinate. Since the + * rest of the algorithm only uses t^2, we can safely use another criterion + * as long as negation of t results in negation of the y coordinate. Here + * we choose to use t's oddness, as it is faster to determine. */ + secp256k1_fe_negate(&tmp, &ge->y, 1); + secp256k1_fe_cmov(&ge->y, &tmp, secp256k1_fe_is_odd(t)); +} + +static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) { + static const unsigned char prefix1[16] = "1st generation: "; + static const unsigned char prefix2[16] = "2nd generation: "; + secp256k1_fe t = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 4); + secp256k1_ge add; + secp256k1_gej accum; + int overflow; + secp256k1_sha256 sha256; + unsigned char b32[32]; + int ret = 1; + + if (blind32) { + secp256k1_scalar blind; + secp256k1_scalar_set_b32(&blind, blind32, &overflow); + ret = !overflow; + CHECK(ret); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &accum, &blind); + } + + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, prefix1, 16); + secp256k1_sha256_write(&sha256, key32, 32); + secp256k1_sha256_finalize(&sha256, b32); + ret &= secp256k1_fe_set_b32(&t, b32); + CHECK(ret); + shallue_van_de_woestijne(&add, &t); + if (blind32) { + secp256k1_gej_add_ge(&accum, &accum, &add); + } else { + secp256k1_gej_set_ge(&accum, &add); + } + + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, prefix2, 16); + secp256k1_sha256_write(&sha256, key32, 32); + secp256k1_sha256_finalize(&sha256, b32); + ret &= secp256k1_fe_set_b32(&t, b32); + CHECK(ret); + shallue_van_de_woestijne(&add, &t); + secp256k1_gej_add_ge(&accum, &accum, &add); + + secp256k1_ge_set_gej(&add, &accum); + secp256k1_generator_save(gen, &add); + return ret; +} + +int secp256k1_generator_generate(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(key32 != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + return secp256k1_generator_generate_internal(ctx, gen, key32, NULL); +} + +int secp256k1_generator_generate_blinded(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(key32 != NULL); + ARG_CHECK(blind32 != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + return secp256k1_generator_generate_internal(ctx, gen, key32, blind32); +} + +#endif diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h new file mode 100644 index 00000000..eee51fac --- /dev/null +++ b/src/modules/generator/tests_impl.h @@ -0,0 +1,139 @@ +/********************************************************************** + * Copyright (c) 2016 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_GENERATOR_TESTS +#define SECP256K1_MODULE_GENERATOR_TESTS + +#include +#include + +#include "group.h" +#include "scalar.h" +#include "testrand.h" +#include "util.h" + +#include "include/secp256k1_generator.h" + +void test_shallue_van_de_woestijne(void) { + /* Matches with the output of the shallue_van_de_woestijne.sage SAGE program */ + static const secp256k1_ge_storage results[32] = { + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0xa98e9f96, 0x97e7d794, 0x8d0fe5c1, 0xa1735358, 0xc9db6e9f, 0x38212962, 0x2ae6ec3b, 0xfc5d0198), + SECP256K1_GE_STORAGE_CONST(0x531f7239, 0xaebc780e, 0x179fbf8d, 0x412a1b01, 0x511f0abc, 0xe0c46151, 0x8b38db84, 0xcc2467f3, 0x82387d45, 0xec7bd5cc, 0x61fcb9df, 0x41cddd7b, 0x217d8114, 0x3577dc8f, 0x23de356a, 0x7e97704e), + SECP256K1_GE_STORAGE_CONST(0x531f7239, 0xaebc780e, 0x179fbf8d, 0x412a1b01, 0x511f0abc, 0xe0c46151, 0x8b38db84, 0xcc2467f3, 0x7dc782ba, 0x13842a33, 0x9e034620, 0xbe322284, 0xde827eeb, 0xca882370, 0xdc21ca94, 0x81688be1), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97), + SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0xa98e9f96, 0x97e7d794, 0x8d0fe5c1, 0xa1735358, 0xc9db6e9f, 0x38212962, 0x2ae6ec3b, 0xfc5d0198), + SECP256K1_GE_STORAGE_CONST(0x5e5936b1, 0x81db0b65, 0x8e33a8c6, 0x1aa687dd, 0x31d11e15, 0x85e35664, 0x6b4c2071, 0xcde7e942, 0x88bb5332, 0xa8e05654, 0x78d4f60c, 0x0cd979ec, 0x938558f2, 0xcac11216, 0x7c387a56, 0xe3a6d5f3), + SECP256K1_GE_STORAGE_CONST(0x5e5936b1, 0x81db0b65, 0x8e33a8c6, 0x1aa687dd, 0x31d11e15, 0x85e35664, 0x6b4c2071, 0xcde7e942, 0x7744accd, 0x571fa9ab, 0x872b09f3, 0xf3268613, 0x6c7aa70d, 0x353eede9, 0x83c785a8, 0x1c59263c), + SECP256K1_GE_STORAGE_CONST(0x657d438f, 0xfac34a50, 0x463fd07c, 0x3f09f320, 0x4c98e8ed, 0x6927e330, 0xc0c7735f, 0x76d32f6d, 0x577c2b11, 0xcaca2f6f, 0xd60bcaf0, 0x3e7cebe9, 0x5da6e1f4, 0xbb557f12, 0x2a397331, 0x81df897f), + SECP256K1_GE_STORAGE_CONST(0x657d438f, 0xfac34a50, 0x463fd07c, 0x3f09f320, 0x4c98e8ed, 0x6927e330, 0xc0c7735f, 0x76d32f6d, 0xa883d4ee, 0x3535d090, 0x29f4350f, 0xc1831416, 0xa2591e0b, 0x44aa80ed, 0xd5c68ccd, 0x7e2072b0), + SECP256K1_GE_STORAGE_CONST(0xbe0bc11b, 0x2bc639cb, 0xc28f72a8, 0xd07c21cc, 0xbc06cfa7, 0x4c2ff25e, 0x630c9740, 0x23128eab, 0x6f062fc8, 0x75148197, 0xd10375c3, 0xcc3fadb6, 0x20277e9c, 0x00579c55, 0xeddd7f95, 0xe95604db), + SECP256K1_GE_STORAGE_CONST(0xbe0bc11b, 0x2bc639cb, 0xc28f72a8, 0xd07c21cc, 0xbc06cfa7, 0x4c2ff25e, 0x630c9740, 0x23128eab, 0x90f9d037, 0x8aeb7e68, 0x2efc8a3c, 0x33c05249, 0xdfd88163, 0xffa863aa, 0x12228069, 0x16a9f754), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705), + SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a), + SECP256K1_GE_STORAGE_CONST(0xaee172d4, 0xce7c5010, 0xdb20a88f, 0x469598c1, 0xd7f7926f, 0xabb85cb5, 0x339f1403, 0x87e6b494, 0x38065980, 0x4de81b35, 0x098c7190, 0xe3380f9d, 0x95b2ed6c, 0x6c869e85, 0xc772bc5a, 0x7bc3d9d5), + SECP256K1_GE_STORAGE_CONST(0xaee172d4, 0xce7c5010, 0xdb20a88f, 0x469598c1, 0xd7f7926f, 0xabb85cb5, 0x339f1403, 0x87e6b494, 0xc7f9a67f, 0xb217e4ca, 0xf6738e6f, 0x1cc7f062, 0x6a4d1293, 0x9379617a, 0x388d43a4, 0x843c225a), + SECP256K1_GE_STORAGE_CONST(0xc28f5c28, 0xf5c28f5c, 0x28f5c28f, 0x5c28f5c2, 0x8f5c28f5, 0xc28f5c28, 0xf5c28f5b, 0x6666635a, 0x0c4da840, 0x1b2cf5be, 0x4604e6ec, 0xf92b2780, 0x063a5351, 0xe294bf65, 0xbb2f8b61, 0x00902db7), + SECP256K1_GE_STORAGE_CONST(0xc28f5c28, 0xf5c28f5c, 0x28f5c28f, 0x5c28f5c2, 0x8f5c28f5, 0xc28f5c28, 0xf5c28f5b, 0x6666635a, 0xf3b257bf, 0xe4d30a41, 0xb9fb1913, 0x06d4d87f, 0xf9c5acae, 0x1d6b409a, 0x44d0749d, 0xff6fce78), + SECP256K1_GE_STORAGE_CONST(0xecf56be6, 0x9c8fde26, 0x152832c6, 0xe043b3d5, 0xaf9a723f, 0x789854a0, 0xcb1b810d, 0xe2614ece, 0x66127ae4, 0xe4c17a75, 0x60a727e6, 0xffd2ea7f, 0xaed99088, 0xbec465c6, 0xbde56791, 0x37ed5572), + SECP256K1_GE_STORAGE_CONST(0xecf56be6, 0x9c8fde26, 0x152832c6, 0xe043b3d5, 0xaf9a723f, 0x789854a0, 0xcb1b810d, 0xe2614ece, 0x99ed851b, 0x1b3e858a, 0x9f58d819, 0x002d1580, 0x51266f77, 0x413b9a39, 0x421a986d, 0xc812a6bd), + SECP256K1_GE_STORAGE_CONST(0xba72860f, 0x10fcd142, 0x23f71e3c, 0x228deb9a, 0xc46c5ff5, 0x90b884e5, 0xcc60d51e, 0x0629d16e, 0x67999f31, 0x5a74ada3, 0x526832cf, 0x76b9fec3, 0xa348cc97, 0x33c3aa67, 0x02bd2516, 0x7814f635), + SECP256K1_GE_STORAGE_CONST(0xba72860f, 0x10fcd142, 0x23f71e3c, 0x228deb9a, 0xc46c5ff5, 0x90b884e5, 0xcc60d51e, 0x0629d16e, 0x986660ce, 0xa58b525c, 0xad97cd30, 0x8946013c, 0x5cb73368, 0xcc3c5598, 0xfd42dae8, 0x87eb05fa), + SECP256K1_GE_STORAGE_CONST(0x92ef5657, 0xdba51cc7, 0xf3e1b442, 0xa6a0916b, 0x8ce03079, 0x2ef5657d, 0xba51cc7e, 0xab2beb65, 0x782c65d2, 0x3f1e0eb2, 0x9179a994, 0xe5e8ff80, 0x5a0d50d9, 0xdeeaed90, 0xcec96ca5, 0x973e2ad3), + SECP256K1_GE_STORAGE_CONST(0x92ef5657, 0xdba51cc7, 0xf3e1b442, 0xa6a0916b, 0x8ce03079, 0x2ef5657d, 0xba51cc7e, 0xab2beb65, 0x87d39a2d, 0xc0e1f14d, 0x6e86566b, 0x1a17007f, 0xa5f2af26, 0x2115126f, 0x31369359, 0x68c1d15c), + SECP256K1_GE_STORAGE_CONST(0x9468ad22, 0xf921fc78, 0x8de3f1b0, 0x586c58eb, 0x5e6f0270, 0xe950b602, 0x7ada90d9, 0xd71ae323, 0x922a0c6a, 0x9ccc31d9, 0xc3bf87fd, 0x88381739, 0x35fe393f, 0xa64dfdec, 0x29f2846d, 0x12918d86), + SECP256K1_GE_STORAGE_CONST(0x9468ad22, 0xf921fc78, 0x8de3f1b0, 0x586c58eb, 0x5e6f0270, 0xe950b602, 0x7ada90d9, 0xd71ae323, 0x6dd5f395, 0x6333ce26, 0x3c407802, 0x77c7e8c6, 0xca01c6c0, 0x59b20213, 0xd60d7b91, 0xed6e6ea9), + SECP256K1_GE_STORAGE_CONST(0x76ddc7f5, 0xe029e59e, 0x22b0e54f, 0xa811db94, 0x5a209c4f, 0x5e912ca2, 0x8b4da6a7, 0x4c1e00a2, 0x1e8f516c, 0x91c20437, 0x50f6e24e, 0x8c2cf202, 0xacf68291, 0xbf8b66eb, 0xf7335b62, 0xec2c88fe), + SECP256K1_GE_STORAGE_CONST(0x76ddc7f5, 0xe029e59e, 0x22b0e54f, 0xa811db94, 0x5a209c4f, 0x5e912ca2, 0x8b4da6a7, 0x4c1e00a2, 0xe170ae93, 0x6e3dfbc8, 0xaf091db1, 0x73d30dfd, 0x53097d6e, 0x40749914, 0x08cca49c, 0x13d37331), + SECP256K1_GE_STORAGE_CONST(0xf75763bc, 0x2907e79b, 0x125e33c3, 0x9a027f48, 0x0f8c6409, 0x2153432f, 0x967bc2b1, 0x1d1f5cf0, 0xb4a8edc6, 0x36391b39, 0x9bc219c0, 0x3d033128, 0xdbcd463e, 0xd2506394, 0x061b87a5, 0x9e510235), + SECP256K1_GE_STORAGE_CONST(0xf75763bc, 0x2907e79b, 0x125e33c3, 0x9a027f48, 0x0f8c6409, 0x2153432f, 0x967bc2b1, 0x1d1f5cf0, 0x4b571239, 0xc9c6e4c6, 0x643de63f, 0xc2fcced7, 0x2432b9c1, 0x2daf9c6b, 0xf9e47859, 0x61aef9fa), + }; + + secp256k1_ge ge; + secp256k1_fe fe; + secp256k1_ge_storage ges; + int i, s; + for (i = 1; i <= 16; i++) { + secp256k1_fe_set_int(&fe, i); + + for (s = 0; s < 2; s++) { + if (s) { + secp256k1_fe_negate(&fe, &fe, 1); + secp256k1_fe_normalize(&fe); + } + shallue_van_de_woestijne(&ge, &fe); + secp256k1_ge_to_storage(&ges, &ge); + + CHECK(memcmp(&ges, &results[i * 2 + s - 2], sizeof(secp256k1_ge_storage)) == 0); + } + } +} + +void test_generator_generate(void) { + static const secp256k1_ge_storage results[32] = { + SECP256K1_GE_STORAGE_CONST(0x806cd8ed, 0xd6c153e3, 0x4aa9b9a0, 0x8755c4be, 0x4718b1ef, 0xb26cb93f, 0xfdd99e1b, 0x21f2af8e, 0xc7062208, 0xcc649a03, 0x1bdc1a33, 0x9d01f115, 0x4bcd0dca, 0xfe0b875d, 0x62f35f73, 0x28673006), + SECP256K1_GE_STORAGE_CONST(0xd91b15ec, 0x47a811f4, 0xaa189561, 0xd13f5c4d, 0x4e81f10d, 0xc7dc551f, 0x4fea9b84, 0x610314c4, 0x9b0ada1e, 0xb38efd67, 0x8bff0b6c, 0x7d7315f7, 0xb49b8cc5, 0xa679fad4, 0xc94f9dc6, 0x9da66382), + SECP256K1_GE_STORAGE_CONST(0x11c00de6, 0xf885035e, 0x76051430, 0xa3c38b2a, 0x5f86ab8c, 0xf66dae58, 0x04ea7307, 0x348b19bf, 0xe0858ae7, 0x61dcb1ba, 0xff247e37, 0xd38fcd88, 0xf3bd7911, 0xaa4ed6e0, 0x28d792dd, 0x3ee1ac09), + SECP256K1_GE_STORAGE_CONST(0x986b99eb, 0x3130e7f0, 0xe779f674, 0xb85cb514, 0x46a676bf, 0xb1dfb603, 0x4c4bb639, 0x7c406210, 0xdf900609, 0x8b3ef1e0, 0x30e32fb0, 0xd97a4329, 0xff98aed0, 0xcd278c3f, 0xe6078467, 0xfbd12f35), + SECP256K1_GE_STORAGE_CONST(0xae528146, 0x03fdf91e, 0xc592977e, 0x12461dc7, 0xb9e038f8, 0x048dcb62, 0xea264756, 0xd459ae42, 0x80ef658d, 0x92becb84, 0xdba8e4f9, 0x560d7a72, 0xbaf4c393, 0xfbcf6007, 0x11039f1c, 0x224faaad), + SECP256K1_GE_STORAGE_CONST(0x00df3d91, 0x35975eee, 0x91fab903, 0xe3128e4a, 0xca071dde, 0x270814e5, 0xcbda69ec, 0xcad58f46, 0x11b590aa, 0x92d89969, 0x2dbd932f, 0x08013b8b, 0x45afabc6, 0x43677db2, 0x143e0c0f, 0x5865fb03), + SECP256K1_GE_STORAGE_CONST(0x1168155b, 0x987e9bc8, 0x84c5f3f4, 0x92ebf784, 0xcc8c6735, 0x39d8e5e8, 0xa967115a, 0x2949da9b, 0x0858a470, 0xf403ca97, 0xb1827f6f, 0x544c2c67, 0x08f6cb83, 0xc510c317, 0x96c981ed, 0xb9f61780), + SECP256K1_GE_STORAGE_CONST(0xe8d7c0cf, 0x2bb4194c, 0x97bf2a36, 0xbd115ba0, 0x81a9afe8, 0x7663fa3c, 0x9c3cd253, 0x79fe2571, 0x2028ad04, 0xefa00119, 0x5a25d598, 0x67e79502, 0x49de7c61, 0x4751cd9d, 0x4fb317f6, 0xf76f1110), + SECP256K1_GE_STORAGE_CONST(0x9532c491, 0xa64851dd, 0xcd0d3e5a, 0x93e17267, 0xa10aca95, 0xa23781aa, 0x5087f340, 0xc45fecc3, 0xb691ddc2, 0x3143a7b6, 0x09969302, 0x258affb8, 0x5bbf8666, 0xe1192319, 0xeb174d88, 0x308bd57a), + SECP256K1_GE_STORAGE_CONST(0x6b20b6e2, 0x1ba6cc44, 0x3f2c3a0c, 0x5283ba44, 0xbee43a0a, 0x2799a6cf, 0xbecc0f8a, 0xf8c583ac, 0xf7021e76, 0xd51291a6, 0xf9396215, 0x686f25aa, 0xbec36282, 0x5e11eeea, 0x6e51a6e6, 0xd7d7c006), + SECP256K1_GE_STORAGE_CONST(0xde27e6ff, 0x219b3ab1, 0x2b0a9e4e, 0x51fc6092, 0x96e55af6, 0xc6f717d6, 0x12cd6cce, 0x65d6c8f2, 0x48166884, 0x4dc13fd2, 0xed7a7d81, 0x66a0839a, 0x8a960863, 0xfe0001c1, 0x35d206fd, 0x63b87c09), + SECP256K1_GE_STORAGE_CONST(0x79a96fb8, 0xd88a08d3, 0x055d38d1, 0x3346b0d4, 0x47d838ca, 0xfcc8fa40, 0x6d3a7157, 0xef84e7e3, 0x6bab9c45, 0x2871b51d, 0xb0df2369, 0xe7860e01, 0x2e37ffea, 0x6689fd1a, 0x9c6fe9cf, 0xb940acea), + SECP256K1_GE_STORAGE_CONST(0x06c4d4cb, 0xd32c0ddb, 0x67e988c6, 0x2bdbe6ad, 0xa39b80cc, 0x61afb347, 0x234abe27, 0xa689618c, 0x5b355949, 0xf904fe08, 0x569b2313, 0xe8f19f8d, 0xc5b79e27, 0x70da0832, 0x5fb7a229, 0x238ca6b6), + SECP256K1_GE_STORAGE_CONST(0x7027e566, 0x3e727c28, 0x42aa14e5, 0x52c2d2ec, 0x1d8beaa9, 0x8a22ceab, 0x15ccafc3, 0xb4f06249, 0x9b3dffbc, 0xdbd5e045, 0x6931fd03, 0x8b1c6a9b, 0x4c168c6d, 0xa6553897, 0xfe11ce49, 0xac728139), + SECP256K1_GE_STORAGE_CONST(0xee3520c3, 0x9f2b954d, 0xf8e15547, 0xdaeb6cc8, 0x04c8f3b0, 0x9301f53e, 0xe0c11ea1, 0xeace539d, 0x244ff873, 0x7e060c98, 0xe843c353, 0xcd35d2e4, 0x3cd8b082, 0xcffbc9ae, 0x81eafa70, 0x332f9748), + SECP256K1_GE_STORAGE_CONST(0xdaecd756, 0xf5b706a4, 0xc14e1095, 0x3e2f70df, 0xa81276e7, 0x71806b89, 0x4d8a5502, 0xa0ef4998, 0xbac906c0, 0x948b1d48, 0xe023f439, 0xfd3770b8, 0x837f60cc, 0x40552a51, 0x433d0b79, 0x6610da27), + SECP256K1_GE_STORAGE_CONST(0x55e1ca28, 0x750fe2d0, 0x57f7449b, 0x3f49d999, 0x3b9616dd, 0x5387bc2e, 0x6e6698f8, 0xc4ea49f4, 0xe339e0e9, 0xa4c7fa99, 0xd063e062, 0x6582bce2, 0x33c6b1ee, 0x17a5b47f, 0x6d43ecf8, 0x98b40120), + SECP256K1_GE_STORAGE_CONST(0xdd82cac2, 0x9e0e0135, 0x4964d3bc, 0x27469233, 0xf13bbd5e, 0xd7aff24b, 0x4902fca8, 0x17294b12, 0x561ab1d6, 0xcd9bcb6e, 0x805585cf, 0x3df8714c, 0x1bfa6304, 0x5efbf122, 0x1a3d8fd9, 0x3827764a), + SECP256K1_GE_STORAGE_CONST(0xda5cbfb7, 0x3522e9c7, 0xcb594436, 0x83677038, 0x0eaa64a9, 0x2eca3888, 0x0fe4c9d6, 0xdeb22dbf, 0x4f46de68, 0x0447c780, 0xc54a314b, 0x5389a926, 0xbba8910b, 0x869fc6cd, 0x42ee82e8, 0x5895e42a), + SECP256K1_GE_STORAGE_CONST(0x4e09830e, 0xc8894c58, 0x4e6278de, 0x167a96b0, 0x20d60463, 0xee48f788, 0x4974d66e, 0x871e35e9, 0x21259c4d, 0x332ca932, 0x2e187df9, 0xe7afbc23, 0x9d171ebc, 0x7d9e2560, 0x503f50b1, 0x9fe45834), + SECP256K1_GE_STORAGE_CONST(0xabfff6ca, 0x41dcfd17, 0x03cae629, 0x9d127971, 0xf19ee000, 0x2db332e6, 0x5cc209a3, 0xc21b8f54, 0x65991d60, 0xee54f5cc, 0xddf7a732, 0xa76b0303, 0xb9f519a6, 0x22ea0390, 0x8af23ffa, 0x35ae6632), + SECP256K1_GE_STORAGE_CONST(0xc6c9b92c, 0x91e045a5, 0xa1913277, 0x44d6fce2, 0x11b12c7c, 0x9b3112d6, 0xc61e14a6, 0xd6b1ae12, 0x04ab0396, 0xebdc4c6a, 0xc213cc3e, 0x077a2e80, 0xb4ba7b2b, 0x33907d56, 0x2c98ccf7, 0xb82a2e9f), + SECP256K1_GE_STORAGE_CONST(0x66f6e6d9, 0xc4bb9a5f, 0x99085781, 0x83cb9362, 0x2ea437d8, 0xccd31969, 0xffadca3a, 0xff1d3935, 0x50a5b06e, 0x39e039d7, 0x1dfb2723, 0x18db74e5, 0x5af64da1, 0xdfc34586, 0x6aac3bd0, 0x5792a890), + SECP256K1_GE_STORAGE_CONST(0x58ded03c, 0x98e1a890, 0x63fc7793, 0xe3ecd896, 0x235e75c9, 0x82e7008f, 0xddbf3ca8, 0x5b7e9ecb, 0x34594776, 0x58ab6821, 0xaf43a453, 0xa946fda9, 0x13d24999, 0xccf22df8, 0xd291ef59, 0xb08975c0), + SECP256K1_GE_STORAGE_CONST(0x74557864, 0x4f2b0486, 0xd5beea7c, 0x2d258ccb, 0x78a870e1, 0x848982d8, 0xed3f91a4, 0x9db83a36, 0xd84e940e, 0x1d33c28a, 0x62398ec8, 0xc493aee7, 0x7c2ba722, 0x42dee7ae, 0x3c35c256, 0xad00cf42), + SECP256K1_GE_STORAGE_CONST(0x7fc7963a, 0x16abc8fb, 0x5d61eb61, 0x0fc50a68, 0x754470d2, 0xf43df3be, 0x52228f66, 0x522fe61b, 0x499f9e7f, 0x462c6545, 0x29687af4, 0x9f7c732d, 0x48801ce5, 0x21acd546, 0xc6fb903c, 0x7c265032), + SECP256K1_GE_STORAGE_CONST(0xb2f6257c, 0xc58df82f, 0xb9ba4f36, 0x7ededf03, 0xf8ea10f3, 0x104d7ae6, 0x233b7ac4, 0x725e11de, 0x9c7a32df, 0x4842f33d, 0xaad84f0b, 0x62e88b40, 0x46ddcbde, 0xbbeec6f8, 0x93bfde27, 0x0561dc73), + SECP256K1_GE_STORAGE_CONST(0xe2cdfd27, 0x8a8e22be, 0xabf08b79, 0x1bc6ae38, 0x41d22a9a, 0x9472e266, 0x1a7c6e83, 0xa2f74725, 0x0e26c103, 0xe0dd93b2, 0x3724f3b7, 0x8bb7366e, 0x2c245768, 0xd64f3283, 0xd8316e8a, 0x1383b977), + SECP256K1_GE_STORAGE_CONST(0x757c13e7, 0xe866017e, 0xe6af61d7, 0x161d208a, 0xc438f712, 0x242fcd23, 0x63a10e59, 0xd67e41fb, 0xb550c6a9, 0x4ddb15f3, 0xfeea4bfe, 0xd2faa19f, 0x2aa2fbd3, 0x0c6ae785, 0xe357f365, 0xb30d12e0), + SECP256K1_GE_STORAGE_CONST(0x528d525e, 0xac30095b, 0x5e5f83ca, 0x4d3dea63, 0xeb608f2d, 0x18dd25a7, 0x2529c8e5, 0x1ae5f9f1, 0xfde2860b, 0x492a4106, 0x9f356c05, 0x3ebc045e, 0x4ad08b79, 0x3e264935, 0xf25785a9, 0x8690b5ee), + SECP256K1_GE_STORAGE_CONST(0x150df593, 0x5b6956a0, 0x0cfed843, 0xb9d6ffce, 0x4f790022, 0xea18730f, 0xc495111d, 0x91568e55, 0x6700a2ca, 0x9ff4ed32, 0xc1697312, 0x4eb51ce3, 0x5656344b, 0x65a1e3d5, 0xd6c1f7ce, 0x29233f82), + SECP256K1_GE_STORAGE_CONST(0x38e02eaf, 0x2c8774fd, 0x58b8b373, 0x732457f1, 0x16dbe53b, 0xea5683d9, 0xada20dd7, 0x14ce20a6, 0x6ac5362e, 0xbb425416, 0x8250f43f, 0xa4ee2b63, 0x0406324f, 0x1c876d60, 0xebe5be2c, 0x6eb1515b), + }; + secp256k1_generator gen; + secp256k1_ge ge; + secp256k1_ge_storage ges; + int i; + unsigned char v[32]; + static const unsigned char s[32] = {0}; + secp256k1_scalar sc; + secp256k1_scalar_set_b32(&sc, s, NULL); + for (i = 1; i <= 32; i++) { + memset(v, 0, 31); + v[31] = i; + CHECK(secp256k1_generator_generate_blinded(ctx, &gen, v, s)); + secp256k1_generator_load(&ge, &gen); + secp256k1_ge_to_storage(&ges, &ge); + CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); + CHECK(secp256k1_generator_generate(ctx, &gen, v)); + secp256k1_generator_load(&ge, &gen); + secp256k1_ge_to_storage(&ges, &ge); + CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); + } +} + +void run_generator_tests(void) { + test_shallue_van_de_woestijne(); + test_generator_generate(); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index b1042c35..29c9f856 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -24,6 +24,10 @@ # include #endif +#ifdef ENABLE_MODULE_GENERATOR +# include "include/secp256k1_generator.h" +#endif + #ifdef ENABLE_MODULE_RANGEPROOF # include "include/secp256k1_rangeproof.h" # include "modules/rangeproof/pedersen.h" @@ -748,6 +752,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/recovery/main_impl.h" #endif +#ifdef ENABLE_MODULE_GENERATOR +# include "modules/generator/main_impl.h" +#endif + #ifdef ENABLE_MODULE_RANGEPROOF # include "modules/rangeproof/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index ee30d8df..822c7067 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5325,6 +5325,10 @@ void run_ecdsa_openssl(void) { # include "modules/recovery/tests_impl.h" #endif +#ifdef ENABLE_MODULE_GENERATOR +# include "modules/generator/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RANGEPROOF # include "modules/rangeproof/tests_impl.h" #endif @@ -5636,6 +5640,10 @@ int main(int argc, char **argv) { run_recovery_tests(); #endif +#ifdef ENABLE_MODULE_GENERATOR + run_generator_tests(); +#endif + #ifdef ENABLE_MODULE_RANGEPROOF run_rangeproof_tests(); #endif From f6c84a02f331acb26adae9caee635b059ea70107 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 6 Jul 2016 13:46:23 +0200 Subject: [PATCH 006/381] Expose generator in pedersen/rangeproof API --- include/secp256k1_rangeproof.h | 32 +++++++++----- src/bench_rangeproof.c | 8 ++-- src/modules/rangeproof/main_impl.h | 47 +++++++++++++++----- src/modules/rangeproof/pedersen.h | 4 +- src/modules/rangeproof/pedersen_impl.h | 21 ++------- src/modules/rangeproof/rangeproof.h | 2 +- src/modules/rangeproof/rangeproof_impl.h | 39 ++++++++++------- src/modules/rangeproof/tests_impl.h | 56 ++++++++++++------------ 8 files changed, 119 insertions(+), 90 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 0afeed32..b8fca472 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -26,9 +26,14 @@ typedef struct { unsigned char data[33]; } secp256k1_pedersen_commitment; +/** + * Static constant generator 'h' maintained for historical reasons. + */ +extern const secp256k1_generator *secp256k1_generator_h; + /** Parse a 33-byte commitment into a commitment object. * - * Returns: 1 always + * Returns: 1 if input contains a valid commitment. * Args: ctx: a secp256k1 context object. * Out: commit: pointer to the output commitment object * In: input: pointer to a 33-byte serialized commitment key @@ -70,8 +75,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, - uint64_t value -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + uint64_t value, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); /** Computes the sum of multiple positive and negative blinding factors. * Returns 1: sum successfully computed. @@ -113,8 +119,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt, - int64_t excess -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + int64_t excess, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); /** Initialize a context for usage with Pedersen commitments. */ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); @@ -135,8 +142,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( uint64_t *max_value, const secp256k1_pedersen_commitment *commit, const unsigned char *proof, - size_t plen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + size_t plen, + const secp256k1_generator* gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7); /** Verify a range proof proof and rewind the proof to recover information sent by its author. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. @@ -164,8 +172,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( uint64_t *max_value, const secp256k1_pedersen_commitment *commit, const unsigned char *proof, - size_t plen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10); + size_t plen, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(12); /** Author a proof that a committed value is within a range. * Returns 1: Proof successfully created. @@ -201,8 +210,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( int min_bits, uint64_t value, const unsigned char *message, - size_t msg_len -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + size_t msg_len, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(13); /** Extract some basic information from a range-proof. * Returns 1: Information successfully extracted. diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index dc01835f..98aaaa10 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -28,10 +28,10 @@ static void bench_rangeproof_setup(void* arg) { data->v = 0; for (i = 0; i < 32; i++) data->blind[i] = i + 1; - CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v)); + CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v, secp256k1_generator_h)); data->len = 5134; - CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0)); - CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len)); + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, secp256k1_generator_h)); } static void bench_rangeproof(void* arg, int iters) { @@ -42,7 +42,7 @@ static void bench_rangeproof(void* arg, int iters) { int j; uint64_t minv; uint64_t maxv; - j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len); + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, secp256k1_generator_h); for (j = 0; j < 4; j++) { data->proof[j + 2 + 32 *((data->min_bits + 1) >> 1) - 4] = (i >> 8)&255; } diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index c743b6d7..34279887 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -13,6 +13,20 @@ #include "modules/rangeproof/borromean_impl.h" #include "modules/rangeproof/rangeproof_impl.h" +/** Alternative generator for secp256k1. + * This is the sha256 of 'g' after DER encoding (without compression), + * which happens to be a point on the curve. + * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16)) + * sage: '%x %x' % (11 - G2.xy()[1].is_square(), G2.xy()[0]) + */ +static const secp256k1_generator secp256k1_generator_h_internal = {{ + 0x11, + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0 +}}; + +const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; + static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { secp256k1_fe fe; secp256k1_fe_set_b32(&fe, &commit->data[1]); @@ -32,6 +46,9 @@ int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_ VERIFY_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(input != NULL); + if ((input[0] & 0xFE) != 8) { + return 0; + } memcpy(commit->data, input, sizeof(commit->data)); return 1; } @@ -45,7 +62,8 @@ int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsign } /* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ -int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value) { +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { + secp256k1_ge genp; secp256k1_gej rj; secp256k1_ge r; secp256k1_scalar sec; @@ -55,9 +73,10 @@ int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_c ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(commit != NULL); ARG_CHECK(blind != NULL); + secp256k1_generator_load(&genp, gen); secp256k1_scalar_set_b32(&sec, blind, &overflow); if (!overflow) { - secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value); + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); if (!secp256k1_gej_is_infinity(&rj)) { secp256k1_ge_set_gej(&r, &rj); secp256k1_pedersen_commitment_save(commit, &r); @@ -99,7 +118,8 @@ int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *bl } /* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ -int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt, int64_t excess) { +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt, int64_t excess, const secp256k1_generator* gen) { + secp256k1_ge genp; secp256k1_gej accj; secp256k1_ge add; size_t i; @@ -107,12 +127,13 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); secp256k1_gej_set_infinity(&accj); + secp256k1_generator_load(&genp, gen); if (excess) { uint64_t ex; int neg; /* Take the absolute value, and negate the result if the input was negative. */ neg = secp256k1_sign_and_abs64(&ex, excess); - secp256k1_pedersen_ecmult_small(&accj, ex); + secp256k1_pedersen_ecmult_small(&accj, ex, &genp); if (neg) { secp256k1_gej_neg(&accj, &accj); } @@ -146,8 +167,9 @@ int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *manti int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const secp256k1_generator* gen) { secp256k1_ge commitp; + secp256k1_ge genp; ARG_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(proof != NULL); @@ -156,13 +178,15 @@ int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen); + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, &genp); } int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const secp256k1_generator* gen) { secp256k1_ge commitp; + secp256k1_ge genp; ARG_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(proof != NULL); @@ -170,14 +194,16 @@ int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_valu ARG_CHECK(max_value != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, - NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen); + NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, &genp); } int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value, const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, - const unsigned char *message, size_t msg_len){ + const unsigned char *message, size_t msg_len, const secp256k1_generator* gen){ secp256k1_ge commitp; + secp256k1_ge genp; ARG_CHECK(ctx != NULL); ARG_CHECK(proof != NULL); ARG_CHECK(plen != NULL); @@ -187,8 +213,9 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len); + proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, &genp); } #endif diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h index 84dd20b4..14d9920e 100644 --- a/src/modules/rangeproof/pedersen.h +++ b/src/modules/rangeproof/pedersen.h @@ -14,9 +14,9 @@ #include /** Multiply a small number with the generator: r = gn*G2 */ -static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn); +static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp); /* sec * G + value * G2. */ -static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value); +static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp); #endif diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/rangeproof/pedersen_impl.h index 991c60b3..69f22e38 100644 --- a/src/modules/rangeproof/pedersen_impl.h +++ b/src/modules/rangeproof/pedersen_impl.h @@ -17,19 +17,6 @@ #include "scalar.h" #include "util.h" -/** Alternative generator for secp256k1. - * This is the sha256 of 'g' after DER encoding (without compression), - * which happens to be a point on the curve. - * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16)) - * sage: '%x %x'%G2.xy() - */ -static const secp256k1_ge secp256k1_ge_const_g2 = SECP256K1_GE_CONST( - 0x50929b74UL, 0xc1a04954UL, 0xb78b4b60UL, 0x35e97a5eUL, - 0x078a5a0fUL, 0x28ec96d5UL, 0x47bfee9aUL, 0xce803ac0UL, - 0x31d3c686UL, 0x3973926eUL, 0x049e637cUL, 0xb1b5f40aUL, - 0x36dac28aUL, 0xf1766968UL, 0xc30c2313UL, 0xf3a38904UL -); - static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t value) { unsigned char data[32]; int i; @@ -44,18 +31,18 @@ static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t va memset(data, 0, 32); } -static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn) { +static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) { secp256k1_scalar s; secp256k1_pedersen_scalar_set_u64(&s, gn); - secp256k1_ecmult_const(r, &secp256k1_ge_const_g2, &s, 64); + secp256k1_ecmult_const(r, genp, &s, 64); secp256k1_scalar_clear(&s); } /* sec * G + value * G2. */ -SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value) { +SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp) { secp256k1_gej vj; secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec); - secp256k1_pedersen_ecmult_small(&vj, value); + secp256k1_pedersen_ecmult_small(&vj, value, genp); /* FIXME: constant time. */ secp256k1_gej_add_var(rj, rj, &vj, NULL); secp256k1_gej_clear(&vj); diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index 85f94bc4..5aa3a68d 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -15,6 +15,6 @@ static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen); + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const secp256k1_ge* genp); #endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index efd43e12..1f824020 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -19,7 +19,7 @@ #include "modules/rangeproof/borromean.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, - int exp, size_t *rsizes, size_t rings) { + int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { secp256k1_gej base; size_t i; size_t j; @@ -28,7 +28,7 @@ SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs if (exp < 0) { exp = 0; } - secp256k1_gej_set_ge(&base, &secp256k1_ge_const_g2); + secp256k1_gej_set_ge(&base, genp); secp256k1_gej_neg(&base, &base); while (exp--) { /* Multiplication by 10 */ @@ -60,9 +60,9 @@ SECP256K1_INLINE static void secp256k1_rangeproof_serialize_point(unsigned char* } SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_scalar *s, unsigned char *message, - size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len) { + size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge* genp) { unsigned char tmp[32]; - unsigned char rngseed[32 + 33 + 10]; + unsigned char rngseed[32 + 33 + 33 + 10]; secp256k1_rfc6979_hmac_sha256 rng; secp256k1_scalar acc; int overflow; @@ -74,8 +74,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, VERIFY_CHECK(len <= 10); memcpy(rngseed, nonce, 32); secp256k1_rangeproof_serialize_point(rngseed + 32, commit); - memcpy(rngseed + 33 + 32, proof, len); - secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + len); + secp256k1_rangeproof_serialize_point(rngseed + 32 + 33, genp); + memcpy(rngseed + 33 + 33 + 32, proof, len); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + 33 + len); secp256k1_scalar_clear(&acc); npub = 0; ret = 1; @@ -192,7 +193,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *proof, size_t *plen, uint64_t min_value, const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, - const unsigned char *message, size_t msg_len){ + const unsigned char *message, size_t msg_len, const secp256k1_ge* genp){ secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ @@ -246,6 +247,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul secp256k1_sha256_initialize(&sha256_m); secp256k1_rangeproof_serialize_point(tmp, commit); secp256k1_sha256_write(&sha256_m, tmp, 33); + secp256k1_rangeproof_serialize_point(tmp, genp); + secp256k1_sha256_write(&sha256_m, tmp, 33); secp256k1_sha256_write(&sha256_m, proof, len); memset(prep, 0, 4096); @@ -265,7 +268,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } prep[idx] = 128; } - if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len)) { + if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len, genp)) { return 0; } memset(prep, 0, 4096); @@ -294,7 +297,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul npub = 0; for (i = 0; i < rings; i++) { /*OPT: Use the precomputed gen2 basis?*/ - secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2)); + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2), genp); if (secp256k1_gej_is_infinity(&pubs[npub])) { return 0; } @@ -314,7 +317,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } npub += rsizes[i]; } - secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings); + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); secp256k1_sha256_finalize(&sha256_m, tmp); if (!secp256k1_borromean_sign(ecmult_ctx, ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { return 0; @@ -357,7 +360,7 @@ SECP256K1_INLINE static void secp256k1_rangeproof_ch32xor(unsigned char *x, cons SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar *blind, uint64_t *v, unsigned char *m, size_t *mlen, secp256k1_scalar *ev, secp256k1_scalar *s, - size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len) { + size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge *genp) { secp256k1_scalar s_orig[128]; secp256k1_scalar sec[32]; secp256k1_scalar stmp; @@ -376,7 +379,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * VERIFY_CHECK(npub >= 1); memset(prep, 0, 4096); /* Reconstruct the provers random values. */ - secp256k1_rangeproof_genrand(sec, s_orig, prep, rsizes, rings, nonce, commit, proof, len); + secp256k1_rangeproof_genrand(sec, s_orig, prep, rsizes, rings, nonce, commit, proof, len, genp); *v = UINT64_MAX; secp256k1_scalar_clear(blind); if (rings == 1 && rsizes[0] == 1) { @@ -535,7 +538,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(size_t *offset, SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen) { + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const secp256k1_ge* genp) { secp256k1_gej accj; secp256k1_gej pubs[128]; secp256k1_ge c; @@ -583,6 +586,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm secp256k1_sha256_initialize(&sha256_m); secp256k1_rangeproof_serialize_point(m, commit); secp256k1_sha256_write(&sha256_m, m, 33); + secp256k1_rangeproof_serialize_point(m, genp); + secp256k1_sha256_write(&sha256_m, m, 33); secp256k1_sha256_write(&sha256_m, proof, offset); for(i = 0; i < rings - 1; i++) { signs[i] = (proof[offset + ( i>> 3)] & (1 << (i & 7))) != 0; @@ -597,7 +602,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm npub = 0; secp256k1_gej_set_infinity(&accj); if (*min_value) { - secp256k1_pedersen_ecmult_small(&accj, *min_value); + secp256k1_pedersen_ecmult_small(&accj, *min_value, genp); } for(i = 0; i < rings - 1; i++) { secp256k1_fe fe; @@ -620,7 +625,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm if (secp256k1_gej_is_infinity(&pubs[npub])) { return 0; } - secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings); + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); npub += rsizes[rings - 1]; e0 = &proof[offset]; offset += 32; @@ -644,13 +649,13 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm if (!ecmult_gen_ctx) { return 0; } - if (!secp256k1_rangeproof_rewind_inner(&blind, &vv, message_out, outlen, evalues, s, rsizes, rings, nonce, commit, proof, offset_post_header)) { + if (!secp256k1_rangeproof_rewind_inner(&blind, &vv, message_out, outlen, evalues, s, rsizes, rings, nonce, commit, proof, offset_post_header, genp)) { return 0; } /* Unwind apparently successful, see if the commitment can be reconstructed. */ /* FIXME: should check vv is in the mantissa's range. */ vv = (vv * scale) + *min_value; - secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv); + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv, genp); if (secp256k1_gej_is_infinity(&accj)) { return 0; } diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index c8815afb..82cf426d 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -63,10 +63,10 @@ void test_pedersen(void) { } CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); for (i = 0; i < total; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i])); + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); } - CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv)); - CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv, secp256k1_generator_h)); + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1, secp256k1_generator_h)); random_scalar_order(&s); for (i = 0; i < 4; i++) { secp256k1_scalar_get_b32(&blinds[i * 32], &s); @@ -75,14 +75,14 @@ void test_pedersen(void) { values[1] = 0; values[2] = 1; for (i = 0; i < 3; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i])); + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); } - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1, 0)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[1], 1, INT64_MAX)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1, 0)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[0], 1, -INT64_MAX)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1, 0, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[1], 1, INT64_MAX, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1, 0, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[0], 1, -INT64_MAX, secp256k1_generator_h)); } void test_borromean(void) { @@ -180,7 +180,7 @@ void test_rangeproof(void) { secp256k1_rand256(blind); for (i = 0; i < 11; i++) { v = testvs[i]; - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { const unsigned char *input_message = NULL; size_t input_message_len = 0; @@ -196,10 +196,10 @@ void test_rangeproof(void) { input_message_len = sizeof(message_long); } len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, secp256k1_generator_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); if (input_message != NULL) { CHECK(memcmp(message, input_message, input_message_len) == 0); } @@ -212,9 +212,9 @@ void test_rangeproof(void) { CHECK(minv <= v); CHECK(maxv >= v); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, secp256k1_generator_h)); CHECK(len <= 73); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); CHECK(memcmp(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); @@ -223,11 +223,11 @@ void test_rangeproof(void) { } secp256k1_rand256(blind); v = INT64_MAX - 1; - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); for (i = 0; i < 19; i++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0)); - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); CHECK(len <= 5134); CHECK(minv <= v); CHECK(maxv >= v); @@ -236,16 +236,16 @@ void test_rangeproof(void) { { /*Malleability test.*/ v = secp256k1_rands64(0, 255); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); for (i = 0; i < len*8; i++) { proof[i >> 3] ^= 1 << (i & 7); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); proof[i >> 3] ^= 1 << (i & 7); } - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); CHECK(minv <= v); CHECK(maxv >= v); } @@ -259,7 +259,7 @@ void test_rangeproof(void) { vmin = secp256k1_rands64(0, v); } secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); len = 5134; exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); if (exp < 0) { @@ -269,10 +269,10 @@ void test_rangeproof(void) { if (min_bits < 0) { min_bits = -min_bits; } - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); for (j = 0; j < mlen; j++) { CHECK(message[j] == 0); } @@ -281,7 +281,7 @@ void test_rangeproof(void) { CHECK(vout == v); CHECK(minv <= v); CHECK(maxv >= v); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); memcpy(&commit2, &commit, sizeof(commit)); } for (j = 0; j < 10; j++) { @@ -290,10 +290,10 @@ void test_rangeproof(void) { } for (k = 0; k < 128; k++) { len = k; - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, secp256k1_generator_h)); } len = secp256k1_rands64(0, 3072); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, secp256k1_generator_h)); } } From 94425d4a679a77f56a377305bbee98abb1bb5257 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 6 Jul 2016 15:44:09 +0000 Subject: [PATCH 007/381] rangeproof: several API changes * add summing function for blinded generators * drop `excess` and `gen` from `verify_tally` * add extra_commit to rangeproof sign and verify --- include/secp256k1_rangeproof.h | 88 ++++++++++---- src/bench_rangeproof.c | 6 +- src/modules/generator/main_impl.h | 1 + src/modules/rangeproof/main_impl.h | 80 ++++++++++--- src/modules/rangeproof/rangeproof.h | 3 +- src/modules/rangeproof/rangeproof_impl.h | 10 +- src/modules/rangeproof/tests_impl.h | 141 ++++++++++++++++------- src/scalar.h | 3 + src/scalar_4x64_impl.h | 9 ++ src/scalar_8x32_impl.h | 11 ++ src/scalar_low_impl.h | 1 + 11 files changed, 266 insertions(+), 87 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index b8fca472..528b6662 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -16,8 +16,7 @@ extern "C" { * guaranteed to be portable between different platforms or versions. It is * however guaranteed to be 33 bytes in size, and can be safely copied/moved. * If you need to convert to a format suitable for storage or transmission, use - * the secp256k1_pedersen_commitment_serialize_* and - * secp256k1_pedersen_commitment_serialize_* functions. + * secp256k1_pedersen_commitment_serialize and secp256k1_pedersen_commitment_parse. * * Furthermore, it is guaranteed to identical signatures will have identical * representation, so they can be memcmp'ed. @@ -71,7 +70,7 @@ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); * * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, @@ -88,7 +87,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( * nneg: how many of the initial factors should be treated with a positive sign. * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, @@ -104,24 +103,57 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( * pcnt: number of commitments pointed to by commits. * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) * ncnt: number of commitments pointed to by ncommits. - * excess: signed 64bit amount to add to the total to bring it to zero, can be negative. * - * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) - excess*H == 0. + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. * - * A pedersen commitment is xG + vH where G and H are generators for the secp256k1 group and x is a blinding factor, - * while v is the committed value. For a collection of commitments to sum to zero both their blinding factors and - * values must sum to zero. + * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator + * A all blinding factors and all values must sum to zero. * */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, - size_t ncnt, - int64_t excess, - const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + size_t ncnt +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Sets the final Pedersen blinding factor correctly when the generators themselves + * have blinding factors. + * + * Consider a generator of the form A' = A + rG, where A is the "real" generator + * but A' is the generator provided to verifiers. Then a Pedersen commitment + * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') + * to sum to zero for multiple commitments, we take three arrays consisting of + * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s + * and `blinding_factor`s, and sum them. + * + * The function then subtracts the sum of all (vr + r') from the last element + * of the `blinding_factor` array, setting the total sum to zero. + * + * Returns 1 always. + * + * In: ctx: pointer to a context object + * value: array of asset values, `v` in the above paragraph. + * May not be NULL unless `n_total` is 0. + * generator_blind: array of asset blinding factors, `r` in the above paragraph + * May not be NULL unless `n_total` is 0. + * n_total: Total size of the above arrays + * n_inputs: How many of the initial array elements represent commitments that + * will be negated in the final sum + * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph + * May not be NULL unless `n_total` is 0. + * the last value will be modified to get the total sum to zero. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( + const secp256k1_context* ctx, + const uint64_t *value, + const unsigned char* const* generator_blind, + unsigned char* const* blinding_factor, + size_t n_total, + size_t n_inputs +); /** Initialize a context for usage with Pedersen commitments. */ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); @@ -133,18 +165,22 @@ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); * commit: the commitment being proved. (cannot be NULL) * proof: pointer to character array with the proof. (cannot be NULL) * plen: length of proof in bytes. + * extra_commit: additional data covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, + size_t extra_commit_len, const secp256k1_generator* gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(9); /** Verify a range proof proof and rewind the proof to recover information sent by its author. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. @@ -154,6 +190,8 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( * proof: pointer to character array with the proof. (cannot be NULL) * plen: length of proof in bytes. * nonce: 32-byte secret nonce used by the prover (cannot be NULL) + * extra_commit: additional data covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment * value_out: pointer to an unsigned int64 which has the exact value of the commitment. * message_out: pointer to a 4096 byte character array to receive message data from the proof author. @@ -161,7 +199,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( const secp256k1_context* ctx, unsigned char *blind_out, uint64_t *value_out, @@ -173,8 +211,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, + size_t extra_commit_len, const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(12); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(14); /** Author a proof that a committed value is within a range. * Returns 1: Proof successfully created. @@ -189,6 +229,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * (-1 is a special case that makes the value public. 0 is the most private.) * min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64). * value: Actual value of the commitment. + * message: pointer to a byte array of data to be embedded in the rangeproof that can be recovered by rewinding the proof + * msg_len: size of the message to be embedded in the rangeproof + * extra_commit: additional data to be covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. * * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. @@ -198,7 +242,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding. * */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( const secp256k1_context* ctx, unsigned char *proof, size_t *plen, @@ -211,8 +255,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( uint64_t value, const unsigned char *message, size_t msg_len, + const unsigned char *extra_commit, + size_t extra_commit_len, const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(13); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(15); /** Extract some basic information from a range-proof. * Returns 1: Information successfully extracted. @@ -225,7 +271,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( const secp256k1_context* ctx, int *exp, int *mantissa, diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 98aaaa10..14e22e8a 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -30,8 +30,8 @@ static void bench_rangeproof_setup(void* arg) { for (i = 0; i < 32; i++) data->blind[i] = i + 1; CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v, secp256k1_generator_h)); data->len = 5134; - CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, secp256k1_generator_h)); - CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h)); } static void bench_rangeproof(void* arg, int iters) { @@ -42,7 +42,7 @@ static void bench_rangeproof(void* arg, int iters) { int j; uint64_t minv; uint64_t maxv; - j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, secp256k1_generator_h); + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h); for (j = 0; j < 4; j++) { data->proof[j + 2 + 32 *((data->min_bits + 1) >> 1) - 4] = (i >> 8)&255; } diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 07ad32e7..94cdc448 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -12,6 +12,7 @@ #include "field.h" #include "group.h" #include "hash.h" +#include "scalar.h" static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { secp256k1_fe fe; diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 34279887..4427667a 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -118,8 +118,7 @@ int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *bl } /* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ -int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt, int64_t excess, const secp256k1_generator* gen) { - secp256k1_ge genp; +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { secp256k1_gej accj; secp256k1_ge add; size_t i; @@ -127,17 +126,6 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); secp256k1_gej_set_infinity(&accj); - secp256k1_generator_load(&genp, gen); - if (excess) { - uint64_t ex; - int neg; - /* Take the absolute value, and negate the result if the input was negative. */ - neg = secp256k1_sign_and_abs64(&ex, excess); - secp256k1_pedersen_ecmult_small(&accj, ex, &genp); - if (neg) { - secp256k1_gej_neg(&accj, &accj); - } - } for (i = 0; i < ncnt; i++) { secp256k1_pedersen_commitment_load(&add, ncommits[i]); secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); @@ -150,6 +138,60 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k return secp256k1_gej_is_infinity(&accj); } +int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { + secp256k1_scalar sum; + secp256k1_scalar tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(n_total == 0 || value != NULL); + ARG_CHECK(n_total == 0 || generator_blind != NULL); + ARG_CHECK(n_total == 0 || blinding_factor != NULL); + ARG_CHECK(n_total > n_inputs); + (void) ctx; + + if (n_total == 0) { + return 1; + } + + secp256k1_scalar_set_int(&sum, 0); + for (i = 0; i < n_total; i++) { + int overflow = 0; + secp256k1_scalar addend; + secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ + + secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ + + secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ + secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ + secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ + secp256k1_scalar_clear(&addend); + } + + /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ + secp256k1_scalar_negate(&sum, &sum); + secp256k1_scalar_add(&tmp, &tmp, &sum); + secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); + + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&sum); + return 1; +} + int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { size_t offset; @@ -167,7 +209,7 @@ int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *manti int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const secp256k1_generator* gen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { secp256k1_ge commitp; secp256k1_ge genp; ARG_CHECK(ctx != NULL); @@ -180,11 +222,11 @@ int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, &genp); + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp); } int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const secp256k1_generator* gen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { secp256k1_ge commitp; secp256k1_ge genp; ARG_CHECK(ctx != NULL); @@ -196,12 +238,12 @@ int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_valu secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, - NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, &genp); + NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp); } int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value, const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, - const unsigned char *message, size_t msg_len, const secp256k1_generator* gen){ + const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen){ secp256k1_ge commitp; secp256k1_ge genp; ARG_CHECK(ctx != NULL); @@ -215,7 +257,7 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, &genp); + proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp); } #endif diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index 5aa3a68d..840a09ae 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -15,6 +15,7 @@ static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const secp256k1_ge* genp); + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp); #endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 1f824020..8d4dc654 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -193,7 +193,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *proof, size_t *plen, uint64_t min_value, const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, - const unsigned char *message, size_t msg_len, const secp256k1_ge* genp){ + const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp){ secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ @@ -318,6 +318,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul npub += rsizes[i]; } secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); + if (extra_commit != NULL) { + secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); + } secp256k1_sha256_finalize(&sha256_m, tmp); if (!secp256k1_borromean_sign(ecmult_ctx, ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { return 0; @@ -538,7 +541,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(size_t *offset, SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const secp256k1_ge* genp) { + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp) { secp256k1_gej accj; secp256k1_gej pubs[128]; secp256k1_ge c; @@ -640,6 +643,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm /*Extra data found, reject.*/ return 0; } + if (extra_commit != NULL) { + secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); + } secp256k1_sha256_finalize(&sha256_m, m); ret = secp256k1_borromean_verify(ecmult_ctx, nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); if (ret && nonce) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 82cf426d..29b0a659 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -16,7 +16,7 @@ #include "include/secp256k1_rangeproof.h" -void test_pedersen(void) { +static void test_pedersen(void) { secp256k1_pedersen_commitment commits[19]; const secp256k1_pedersen_commitment *cptr[19]; unsigned char blinds[32*19]; @@ -40,23 +40,12 @@ void test_pedersen(void) { values[i] = secp256k1_rands64(0, INT64_MAX - totalv); totalv += values[i]; } - if (secp256k1_rand32() & 1) { - for (i = 0; i < outputs; i++) { - int64_t max = INT64_MAX; - if (totalv < 0) { - max += totalv; - } - values[i + inputs] = secp256k1_rands64(0, max); - totalv -= values[i + inputs]; - } - } else { - for (i = 0; i < outputs - 1; i++) { - values[i + inputs] = secp256k1_rands64(0, totalv); - totalv -= values[i + inputs]; - } - values[total - 1] = totalv >> (secp256k1_rand32() & 1); - totalv -= values[total - 1]; + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_rands64(0, totalv); + totalv -= values[i + inputs]; } + values[total - 1] = totalv; + for (i = 0; i < total - 1; i++) { random_scalar_order(&s); secp256k1_scalar_get_b32(&blinds[i * 32], &s); @@ -65,8 +54,11 @@ void test_pedersen(void) { for (i = 0; i < total; i++) { CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); } - CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv, secp256k1_generator_h)); - CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); + if (inputs > 0 && values[0] > 0) { + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs)); + } random_scalar_order(&s); for (i = 0; i < 4; i++) { secp256k1_scalar_get_b32(&blinds[i * 32], &s); @@ -77,15 +69,11 @@ void test_pedersen(void) { for (i = 0; i < 3; i++) { CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); } - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1, 0, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[1], 1, INT64_MAX, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1, 0, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[0], 1, -INT64_MAX, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); } -void test_borromean(void) { +static void test_borromean(void) { unsigned char e0[32]; secp256k1_scalar s[64]; secp256k1_gej pubs[64]; @@ -150,7 +138,7 @@ void test_borromean(void) { } } -void test_rangeproof(void) { +static void test_rangeproof(void) { const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; secp256k1_pedersen_commitment commit; secp256k1_pedersen_commitment commit2; @@ -196,10 +184,10 @@ void test_rangeproof(void) { input_message_len = sizeof(message_long); } len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); if (input_message != NULL) { CHECK(memcmp(message, input_message, input_message_len) == 0); } @@ -212,9 +200,21 @@ void test_rangeproof(void) { CHECK(minv <= v); CHECK(maxv >= v); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 73); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv == v); + CHECK(maxv == v); + + /* Check with a committed message */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), secp256k1_generator_h)); + CHECK(len <= 73); + CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); CHECK(memcmp(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); @@ -226,11 +226,13 @@ void test_rangeproof(void) { CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); for (i = 0; i < 19; i++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, secp256k1_generator_h)); - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); CHECK(minv <= v); CHECK(maxv >= v); + /* Make sure it fails when validating with a committed message */ + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); } secp256k1_rand256(blind); { @@ -238,14 +240,14 @@ void test_rangeproof(void) { v = secp256k1_rands64(0, 255); CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); for (i = 0; i < len*8; i++) { proof[i >> 3] ^= 1 << (i & 7); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); proof[i >> 3] ^= 1 << (i & 7); } - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(minv <= v); CHECK(maxv >= v); } @@ -269,10 +271,10 @@ void test_rangeproof(void) { if (min_bits < 0) { min_bits = -min_bits; } - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); for (j = 0; j < mlen; j++) { CHECK(message[j] == 0); } @@ -281,7 +283,7 @@ void test_rangeproof(void) { CHECK(vout == v); CHECK(minv <= v); CHECK(maxv >= v); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); memcpy(&commit2, &commit, sizeof(commit)); } for (j = 0; j < 10; j++) { @@ -290,13 +292,69 @@ void test_rangeproof(void) { } for (k = 0; k < 128; k++) { len = k; - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } len = secp256k1_rands64(0, 3072); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } } +#define MAX_N_GENS 30 +void test_multiple_generators(void) { + const size_t n_inputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_outputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_generators = n_inputs + n_outputs; + unsigned char *generator_blind[MAX_N_GENS]; + unsigned char *pedersen_blind[MAX_N_GENS]; + secp256k1_generator generator[MAX_N_GENS]; + secp256k1_pedersen_commitment commit[MAX_N_GENS]; + const secp256k1_pedersen_commitment *commit_ptr[MAX_N_GENS]; + size_t i; + int64_t total_value; + uint64_t value[MAX_N_GENS]; + + secp256k1_scalar s; + + unsigned char generator_seed[32]; + random_scalar_order(&s); + secp256k1_scalar_get_b32(generator_seed, &s); + /* Create all the needed generators */ + for (i = 0; i < n_generators; i++) { + generator_blind[i] = (unsigned char*) malloc(32); + pedersen_blind[i] = (unsigned char*) malloc(32); + + random_scalar_order(&s); + secp256k1_scalar_get_b32(generator_blind[i], &s); + random_scalar_order(&s); + secp256k1_scalar_get_b32(pedersen_blind[i], &s); + + CHECK(secp256k1_generator_generate_blinded(ctx, &generator[i], generator_seed, generator_blind[i])); + + commit_ptr[i] = &commit[i]; + } + + /* Compute all the values -- can be positive or negative */ + total_value = 0; + for (i = 0; i < n_outputs; i++) { + value[n_inputs + i] = secp256k1_rands64(0, INT64_MAX - total_value); + total_value += value[n_inputs + i]; + } + for (i = 0; i < n_inputs - 1; i++) { + value[i] = secp256k1_rands64(0, total_value); + total_value -= value[i]; + } + value[i] = total_value; + + /* Correct for blinding factors and do the commitments */ + CHECK(secp256k1_pedersen_blind_generator_blind_sum(ctx, value, (const unsigned char * const *) generator_blind, pedersen_blind, n_generators, n_inputs)); + for (i = 0; i < n_generators; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commit[i], pedersen_blind[i], value[i], &generator[i])); + } + + /* Verify */ + CHECK(secp256k1_pedersen_verify_tally(ctx, &commit_ptr[0], n_inputs, &commit_ptr[n_inputs], n_outputs)); +} + void run_rangeproof_tests(void) { int i; for (i = 0; i < 10*count; i++) { @@ -306,6 +364,7 @@ void run_rangeproof_tests(void) { test_borromean(); } test_rangeproof(); + test_multiple_generators(); } #endif diff --git a/src/scalar.h b/src/scalar.h index 2a747035..566f3807 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -46,6 +46,9 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c /** Set a scalar to an unsigned integer. */ static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); +/** Set a scalar to an unsigned 64-bit integer */ +static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v); + /** Convert a scalar to a byte array. */ static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 8f539c4b..c59e5f26 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "scalar.h" + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) #define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) @@ -38,6 +40,13 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig r->d[3] = 0; } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 3c372f34..95a08783 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -56,6 +56,17 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig r->d[7] = 0; } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + r->d[0] = v; + r->d[1] = v >> 32; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index b79cf1ff..1ece2363 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -17,6 +17,7 @@ SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { *r = v % EXHAUSTIVE_TEST_ORDER; } SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { if (offset < 32) From 8c77fe15900c3a12ab16d058fddaac6e6fddc773 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 21 Apr 2016 22:22:39 +0000 Subject: [PATCH 008/381] Implement ring-signature based whitelist delegation scheme --- Makefile.am | 4 + configure.ac | 21 +++ include/secp256k1_whitelist.h | 146 +++++++++++++++++++ src/modules/whitelist/Makefile.am.include | 3 + src/modules/whitelist/main_impl.h | 164 ++++++++++++++++++++++ src/modules/whitelist/tests_impl.h | 108 ++++++++++++++ src/modules/whitelist/whitelist.md | 96 +++++++++++++ src/modules/whitelist/whitelist_impl.h | 129 +++++++++++++++++ src/secp256k1.c | 4 + src/tests.c | 9 ++ 10 files changed, 684 insertions(+) create mode 100644 include/secp256k1_whitelist.h create mode 100644 src/modules/whitelist/Makefile.am.include create mode 100644 src/modules/whitelist/main_impl.h create mode 100644 src/modules/whitelist/tests_impl.h create mode 100644 src/modules/whitelist/whitelist.md create mode 100644 src/modules/whitelist/whitelist_impl.h diff --git a/Makefile.am b/Makefile.am index 8b5911a6..a03b0781 100644 --- a/Makefile.am +++ b/Makefile.am @@ -160,3 +160,7 @@ endif if ENABLE_MODULE_RANGEPROOF include src/modules/rangeproof/Makefile.am.include endif + +if ENABLE_MODULE_WHITELIST +include src/modules/whitelist/Makefile.am.include +endif diff --git a/configure.ac b/configure.ac index d1fcff96..7f1e94ed 100644 --- a/configure.ac +++ b/configure.ac @@ -146,6 +146,11 @@ AC_ARG_ENABLE(module_rangeproof, [enable_module_rangeproof=$enableval], [enable_module_rangeproof=no]) +AC_ARG_ENABLE(module_whitelist, + AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module (default is no)]), + [enable_module_whitelist=$enableval], + [enable_module_whitelist=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -517,6 +522,10 @@ if test x"$enable_module_rangeproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) fi +if test x"$enable_module_whitelist" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_WHITELIST, 1, [Define this symbol to enable the key whitelisting module]) +fi + AC_C_BIGENDIAN() if test x"$use_external_asm" = x"yes"; then @@ -534,12 +543,20 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator]) AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) + AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([******]) + if test x"$enable_module_generator" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then AC_MSG_ERROR([Rangeproof module requires the generator module. Use --enable-module-generator to allow.]) fi fi + + if test x"$enable_module_whitelist" = x"yes"; then + if test x"$enable_module_rangeproof" != x"yes"; then + AC_MSG_ERROR([Whitelist module requires the rangeproof module. Use --enable-module-rangeproof to allow.]) + fi + fi else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) @@ -553,6 +570,9 @@ else if test x"$enable_module_rangeproof" = x"yes"; then AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_whitelist" = x"yes"; then + AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) + fi fi AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) @@ -570,6 +590,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_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h new file mode 100644 index 00000000..c3175ce0 --- /dev/null +++ b/include/secp256k1_whitelist.h @@ -0,0 +1,146 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_WHITELIST_ +#define _SECP256K1_WHITELIST_ + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECP256K1_WHITELIST_MAX_N_KEYS 256 + +/** Opaque data structure that holds a parsed whitelist proof + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. Nor is + * it guaranteed to have any particular size, nor that identical signatures + * will have identical representation. (That is, memcmp may return nonzero + * even for identical signatures.) + * + * To obtain these properties, instead use secp256k1_whitelist_signature_parse + * and secp256k1_whitelist_signature_serialize to encode/decode signatures + * into a well-defined format. + * + * The representation is exposed to allow creation of these objects on the + * stack; please *do not* use these internals directly. To learn the number + * of keys for a signature, use `secp256k1_whitelist_signature_n_keys`. + */ +typedef struct { + size_t n_keys; + /* e0, scalars */ + unsigned char data[32 * (1 + SECP256K1_WHITELIST_MAX_N_KEYS)]; +} secp256k1_whitelist_signature; + +/** Parse a whitelist signature + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the array to parse + * + * The signature must consist of a 1-byte n_keys value, followed by a 32-byte + * big endian e0 value, followed by n_keys many 32-byte big endian s values. + * If n_keys falls outside of [0..SECP256K1_WHITELIST_MAX_N_KEYS] the encoding + * is invalid. + * + * The total length of the input array must therefore be 33 + 32 * n_keys. + * + * After the call, sig will always be initialized. If parsing failed or any + * scalar values overflow or are zero, the resulting sig value is guaranteed + * to fail validation for any set of keys. + */ +SECP256K1_API int secp256k1_whitelist_signature_parse( + const secp256k1_context* ctx, + secp256k1_whitelist_signature *sig, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Returns the number of keys a signature expects to have. + * + * Returns: the number of keys for the given signature + * In: sig: a pointer to a signature object + */ +SECP256K1_API size_t secp256k1_whitelist_signature_n_keys( + const secp256k1_whitelist_signature *sig +) SECP256K1_ARG_NONNULL(1); + +/** Serialize a whitelist signature + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to an array to store the serialization + * In: sig: a pointer to an initialized signature object + * + * See secp256k1_whitelist_signature_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_whitelist_signature_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_whitelist_signature *sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute a whitelist signature + * Returns 1: signature was successfully created + * 0: signature was not successfully created + * In: ctx: pointer to a context object, initialized for signing and verification + * online_pubkeys: list of all online pubkeys + * offline_pubkeys: list of all offline pubkeys + * n_keys: the number of entries in each of the above two arrays + * sub_pubkey: the key to be whitelisted + * online_seckey: the secret key to the signer's online pubkey + * summed_seckey: the secret key to the sum of (whitelisted key, signer's offline pubkey) + * index: the signer's index in the lists of keys + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * Out: sig: The produced signature. + * + * The signatures are of the list of all passed pubkeys in the order + * ( whitelist, online_1, offline_1, online_2, offline_2, ... ) + * The verification key list consists of + * online_i + H(offline_i + whitelist)(offline_i + whitelist) + * for each public key pair (offline_i, offline_i). Here H means sha256 of the + * compressed serialization of the key. + */ +SECP256K1_API int secp256k1_whitelist_sign( + const secp256k1_context* ctx, + secp256k1_whitelist_signature *sig, + const secp256k1_pubkey *online_pubkeys, + const secp256k1_pubkey *offline_pubkeys, + const size_t n_keys, + const secp256k1_pubkey *sub_pubkey, + const unsigned char *online_seckey, + const unsigned char *summed_seckey, + const size_t index, + secp256k1_nonce_function noncefp, + const void *noncedata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); + +/** Verify a whitelist signature + * Returns 1: signature is valid + * 0: signature is not valid + * In: ctx: pointer to a context object, initialized for signing and verification + * sig: the signature to be verified + * online_pubkeys: list of all online pubkeys + * offline_pubkeys: list of all offline pubkeys + * n_keys: the number of entries in each of the above two arrays + * sub_pubkey: the key to be whitelisted + */ +SECP256K1_API int secp256k1_whitelist_verify( + const secp256k1_context* ctx, + const secp256k1_whitelist_signature *sig, + const secp256k1_pubkey *online_pubkeys, + const secp256k1_pubkey *offline_pubkeys, + const secp256k1_pubkey *sub_pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include new file mode 100644 index 00000000..e926ffce --- /dev/null +++ b/src/modules/whitelist/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_whitelist.h +noinst_HEADERS += src/modules/whitelist/main_impl.h +noinst_HEADERS += src/modules/whitelist/tests_impl.h diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h new file mode 100644 index 00000000..0de178fc --- /dev/null +++ b/src/modules/whitelist/main_impl.h @@ -0,0 +1,164 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_WHITELIST_MAIN +#define SECP256K1_MODULE_WHITELIST_MAIN + +#include "include/secp256k1_whitelist.h" +#include "modules/whitelist/whitelist_impl.h" + +#define MAX_KEYS SECP256K1_WHITELIST_MAX_N_KEYS /* shorter alias */ + +int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey, const unsigned char *online_seckey, const unsigned char *summed_seckey, const size_t index, secp256k1_nonce_function noncefp, const void *noncedata) { + secp256k1_gej pubs[MAX_KEYS]; + secp256k1_scalar s[MAX_KEYS]; + secp256k1_scalar sec, non; + unsigned char msg32[32]; + int ret; + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + /* Sanity checks */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(online_pubkeys != NULL); + ARG_CHECK(offline_pubkeys != NULL); + ARG_CHECK(n_keys <= MAX_KEYS); + ARG_CHECK(sub_pubkey != NULL); + ARG_CHECK(online_seckey != NULL); + ARG_CHECK(summed_seckey != NULL); + ARG_CHECK(index < n_keys); + + /* Compute pubkeys: online_pubkey + tweaked(offline_pubkey + address), and message */ + ret = secp256k1_whitelist_compute_keys_and_message(ctx, msg32, pubs, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey); + + /* Compute signing key: online_seckey + tweaked(summed_seckey) */ + if (ret) { + ret = secp256k1_whitelist_compute_tweaked_privkey(ctx, &sec, online_seckey, summed_seckey); + } + /* Compute nonce and random s-values */ + if (ret) { + unsigned char seckey32[32]; + unsigned int count = 0; + int overflow = 0; + + secp256k1_scalar_get_b32(seckey32, &sec); + while (1) { + size_t i; + unsigned char nonce32[32]; + int done; + ret = noncefp(nonce32, msg32, seckey32, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + memset(nonce32, 0, 32); + if (overflow || secp256k1_scalar_is_zero(&non)) { + count++; + continue; + } + done = 1; + for (i = 0; i < n_keys; i++) { + msg32[0] ^= i + 1; + msg32[1] ^= (i + 1) / 0x100; + ret = noncefp(&sig->data[32 * (i + 1)], msg32, seckey32, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&s[i], &sig->data[32 * (i + 1)], &overflow); + msg32[0] ^= i + 1; + msg32[1] ^= (i + 1) / 0x100; + if (overflow || secp256k1_scalar_is_zero(&s[i])) { + count++; + done = 0; + break; + } + } + if (done) { + break; + } + } + memset(seckey32, 0, 32); + } + /* Actually sign */ + if (ret) { + sig->n_keys = n_keys; + ret = secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &non, &sec, &n_keys, &index, 1, msg32, 32); + /* Signing will change s[index], so update in the sig structure */ + secp256k1_scalar_get_b32(&sig->data[32 * (index + 1)], &s[index]); + } + + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_whitelist_verify(const secp256k1_context* ctx, const secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey) { + secp256k1_scalar s[MAX_KEYS]; + secp256k1_gej pubs[MAX_KEYS]; + unsigned char msg32[32]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(online_pubkeys != NULL); + ARG_CHECK(offline_pubkeys != NULL); + ARG_CHECK(sub_pubkey != NULL); + + if (sig->n_keys > MAX_KEYS) { + return 0; + } + for (i = 0; i < sig->n_keys; i++) { + int overflow = 0; + secp256k1_scalar_set_b32(&s[i], &sig->data[32 * (i + 1)], &overflow); + if (overflow || secp256k1_scalar_is_zero(&s[i])) { + return 0; + } + } + + /* Compute pubkeys: online_pubkey + tweaked(offline_pubkey + address), and message */ + if (!secp256k1_whitelist_compute_keys_and_message(ctx, msg32, pubs, online_pubkeys, offline_pubkeys, sig->n_keys, sub_pubkey)) { + return 0; + } + /* Do verification */ + return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &sig->data[0], s, pubs, &sig->n_keys, 1, msg32, 32); +} + +size_t secp256k1_whitelist_signature_n_keys(const secp256k1_whitelist_signature *sig) { + return sig->n_keys; +} + +int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const unsigned char *input) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + sig->n_keys = input[0]; + if (sig->n_keys >= MAX_KEYS) { + return 0; + } + memcpy(&sig->data[0], &input[1], 32 * (sig->n_keys + 1)); + + return 1; +} + +int secp256k1_whitelist_signature_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_whitelist_signature *sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(sig != NULL); + + output[0] = sig->n_keys; + memcpy(&output[1], &sig->data[0], 32 * (sig->n_keys + 1)); + + return 1; +} + +#endif diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h new file mode 100644 index 00000000..e307de16 --- /dev/null +++ b/src/modules/whitelist/tests_impl.h @@ -0,0 +1,108 @@ +/********************************************************************** + * Copyright (c) 2014-2016 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_WHITELIST_TESTS +#define SECP256K1_MODULE_WHITELIST_TESTS + +#include "include/secp256k1_whitelist.h" + +void test_whitelist_end_to_end(const size_t n_keys) { + unsigned char **online_seckey = (unsigned char **) malloc(n_keys * sizeof(*online_seckey)); + unsigned char **summed_seckey = (unsigned char **) malloc(n_keys * sizeof(*summed_seckey)); + secp256k1_pubkey *online_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*online_pubkeys)); + secp256k1_pubkey *offline_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*offline_pubkeys)); + + secp256k1_scalar ssub; + unsigned char csub[32]; + secp256k1_pubkey sub_pubkey; + + /* Generate random keys */ + size_t i; + /* Start with subkey */ + random_scalar_order_test(&ssub); + secp256k1_scalar_get_b32(csub, &ssub); + CHECK(secp256k1_ec_seckey_verify(ctx, csub) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &sub_pubkey, csub) == 1); + /* Then offline and online whitelist keys */ + for (i = 0; i < n_keys; i++) { + secp256k1_scalar son, soff; + + online_seckey[i] = (unsigned char *) malloc(32); + summed_seckey[i] = (unsigned char *) malloc(32); + + /* Create two keys */ + random_scalar_order_test(&son); + secp256k1_scalar_get_b32(online_seckey[i], &son); + CHECK(secp256k1_ec_seckey_verify(ctx, online_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &online_pubkeys[i], online_seckey[i]) == 1); + + random_scalar_order_test(&soff); + secp256k1_scalar_get_b32(summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(ctx, summed_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &offline_pubkeys[i], summed_seckey[i]) == 1); + + /* Make summed_seckey correspond to the sum of offline_pubkey and sub_pubkey */ + secp256k1_scalar_add(&soff, &soff, &ssub); + secp256k1_scalar_get_b32(summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(ctx, summed_seckey[i]) == 1); + } + + /* Sign/verify with each one */ + for (i = 0; i < n_keys; i++) { + unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; + secp256k1_whitelist_signature sig; + secp256k1_whitelist_signature sig1; + + CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey, online_seckey[i], summed_seckey[i], i, NULL, NULL)); + CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, &sub_pubkey) == 1); + /* Check that exchanging keys causes a failure */ + CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); + /* Serialization round trip */ + CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &sig) == 1); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized) == 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, &sub_pubkey) == 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); + /* Test n_keys */ + CHECK(secp256k1_whitelist_signature_n_keys(&sig) == n_keys); + CHECK(secp256k1_whitelist_signature_n_keys(&sig1) == n_keys); + } + + for (i = 0; i < n_keys; i++) { + free(online_seckey[i]); + free(summed_seckey[i]); + } + free(online_seckey); + free(summed_seckey); + free(online_pubkeys); + free(offline_pubkeys); +} + +void test_whitelist_bad_parse(void) { + const unsigned char serialized[] = { + /* Hash */ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + /* Length in excess of maximum */ + 0x00, 0x00, 0x01, 0x00 + /* No room for s-values; parse should be rejected before reading past length */ + }; + secp256k1_whitelist_signature sig; + + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized) == 0); +} + +void run_whitelist_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_whitelist_end_to_end(1); + test_whitelist_end_to_end(10); + test_whitelist_end_to_end(50); + } +} + +#endif diff --git a/src/modules/whitelist/whitelist.md b/src/modules/whitelist/whitelist.md new file mode 100644 index 00000000..15ab998c --- /dev/null +++ b/src/modules/whitelist/whitelist.md @@ -0,0 +1,96 @@ +Address Whitelisting Module +=========================== + +This module implements a scheme by which members of some group, having fixed +signing keys, can prove control of an arbitrary other key without associating +their own identity (only that they belong to the group) to the new key. The +application is to patch ring-signature-like behaviour onto systems such as +Bitcoin or PGP which do not directly support this. + +We refer to such delegation as "whitelisting" because we expect it to be used +to build a dynamic whitelist of authorized keys. + +For example, imagine a private sidechain with a fixed membership set but +stronger privacy properties than Bitcoin. When moving coins from this system +to Bitcoin, it is desirable that the destination Bitcoin addresses be provably +in control of some user of the sidechain. This prevents malicious or erroneous +behaviour on the sidechain, which can likely be resolved by its participants, +from translating to theft on the wider Bitcoin network, which is irreversible. + +### Unused Schemes and Design Rationale + +#### Direct Signing + +An obvious scheme for such delegation is to simply have participants sign the +key they want to whitelist. To avoid revealing their specific identity, they +could use a ring signature. The problem with this is that it really only proves +that a participant *signed off* on a key, not that they control it. Thus any +security failure that allows text substitution could be used to subvert this +and redirect coins to an attacker-controlled address. + +#### Signing with Difference-of-Keys + +A less obvious scheme is to have a participant sign an arbitrary message with +the sum of her key `P` and the whitelisted key `W`. Such a signature with the key +`P + W` proves knowledge of either (a) discrete logarithms of both `P` and `W`; +or (b) neither. This makes directly attacking participants' signing schemes much +harder, but allows an attacker to whitelist arbitrary "garbage" keys by computing +`W` as the difference between an attacker-controlled key and `P`. For Bitcoin, +the effect of garbage keys is to "burn" stolen coins, destroying them. + +In an important sense, this "burning coins" attack is a good thing: it enables +*offline delegation*. That is, the key `P` does not need to be available at the +time of delegation. Instead, participants could choose `S = P + W`, sign with +this to delegate, and only later compute the discrete logarithm of `W = P - S`. +This allows `P` to be in cold storage or be otherwise inaccessible, improving +the overall system security. + +#### Signing with Tweaked-Difference-of-Keys + +A modification of this scheme, which prevents this "garbage key" attack, is to +instead have participants sign some message with the key `P + H(W)W`, for `H` +some random-oracle hash that maps group elements to scalars. This key, and its +discrete logarithm, cannot be known until after `W` is chosen, so `W` cannot +be selected as the difference between it and `P`. (Note that `P` could still +be some chosen difference; however `P` is a fixed key and must be verified +out-of-band to have come from a legitimate participant anyway.) + +This scheme is almost what we want, but it no longer supports offline +delegation. However, we can get this back by introducing a new key, `P'`, +and signing with the key `P + H(W + P')(W + P')`. This gives us the best +of both worlds: `P'` does not need to be online to delegate, allowing it +to be securely stored and preventing real-time attacks; `P` does need to +be online, but its compromise only allows an attacker to whitelist "garbage +keys", not attacker-controlled ones. + +### Our Scheme + +Our scheme works as follows: each participant `i` chooses two keys, `P_i` and `Q_i`. +We refer to `P_i` as the "online key" and `Q_i` as the "offline key". To whitelist +a key `W`, the participant computes the key `L_j = P_j + H(W + Q_j)(W + Q_j)` for +every participant `j`. Then she will know the discrete logarithm of `L_i` for her +own `i`. + +Next, she signs a message containing every `P_i` and `Q_i` as well as `W` with +a ring signature over all the keys `L_j`. This proves that she knows the discrete +logarithm of some `L_i` (though it is zero-knowledge which one), and therefore +knows: +1. The discrete logarithms of all of `W`, `P_i` and `Q_i`; or +2. The discrete logarithm of `P_i` but of *neither* `W` nor `Q_i`. +In other words, compromise of the online key `P_i` allows an attacker to whitelist +"garbage keys" for which nobody knows the discrete logarithm; to whitelist an +attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult +because by design, only the sum `S = W + Q_i` is used when signing; then by choosing +`S` freely, a participant can delegate without the secret key to `Q_i` ever being online. +(Later, when she wants to actually use `W`, she will need to compute its key as the +difference between `S` and `Q_i`; but this can be done offline and much later +and with more expensive security requirements.) + +The message to be signed contains all public keys to prevent a class of attacks +centered around choosing keys to match pre-computed signatures. In our proposed +use case, whitelisted keys already must be computed before they are signed, and +the remaining public keys are verified out-of-band when setting up the system, +so there is no direct benefit to this. We do it only to reduce fragility and +increase safety of unforeseen uses. + + diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h new file mode 100644 index 00000000..ff8d87f4 --- /dev/null +++ b/src/modules/whitelist/whitelist_impl.h @@ -0,0 +1,129 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_WHITELIST_IMPL_H_ +#define _SECP256K1_WHITELIST_IMPL_H_ + +static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_gej* pubkey) { + unsigned char h[32]; + unsigned char c[33]; + secp256k1_sha256 sha; + int overflow = 0; + size_t size = 33; + secp256k1_ge ge; + + secp256k1_ge_set_gej(&ge, pubkey); + + secp256k1_sha256_initialize(&sha); + if (!secp256k1_eckey_pubkey_serialize(&ge, c, &size, SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(&sha, c, size); + secp256k1_sha256_finalize(&sha, h); + + secp256k1_scalar_set_b32(output, h, &overflow); + if (overflow || secp256k1_scalar_is_zero(output)) { + /* This return path is mathematically impossible to hit */ + secp256k1_scalar_clear(output); + return 0; + } + return 1; +} + +static int secp256k1_whitelist_tweak_pubkey(const secp256k1_context* ctx, secp256k1_gej* pub_tweaked) { + secp256k1_scalar tweak; + secp256k1_scalar zero; + int ret; + + secp256k1_scalar_set_int(&zero, 0); + + ret = secp256k1_whitelist_hash_pubkey(&tweak, pub_tweaked); + if (ret) { + secp256k1_ecmult(&ctx->ecmult_ctx, pub_tweaked, pub_tweaked, &tweak, &zero); + } + return ret; +} + +static int secp256k1_whitelist_compute_tweaked_privkey(const secp256k1_context* ctx, secp256k1_scalar* skey, const unsigned char *online_key, const unsigned char *summed_key) { + secp256k1_scalar tweak; + int ret = 1; + int overflow = 0; + + secp256k1_scalar_set_b32(skey, summed_key, &overflow); + if (overflow || secp256k1_scalar_is_zero(skey)) { + ret = 0; + } + if (ret) { + secp256k1_gej pkeyj; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkeyj, skey); + ret = secp256k1_whitelist_hash_pubkey(&tweak, &pkeyj); + } + if (ret) { + secp256k1_scalar sonline; + secp256k1_scalar_mul(skey, skey, &tweak); + + secp256k1_scalar_set_b32(&sonline, online_key, &overflow); + if (overflow || secp256k1_scalar_is_zero(&sonline)) { + ret = 0; + } + secp256k1_scalar_add(skey, skey, &sonline); + secp256k1_scalar_clear(&sonline); + secp256k1_scalar_clear(&tweak); + } + + if (!ret) { + secp256k1_scalar_clear(skey); + } + return ret; +} + +/* Takes a list of pubkeys and combines them to form the public keys needed + * for the ring signature; also produce a commitment to every one that will + * be our "message". */ +static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* ctx, unsigned char *msg32, secp256k1_gej *keys, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const int n_keys, const secp256k1_pubkey *sub_pubkey) { + unsigned char c[33]; + size_t size = 33; + secp256k1_sha256 sha; + int i; + secp256k1_ge subkey_ge; + + secp256k1_sha256_initialize(&sha); + secp256k1_pubkey_load(ctx, &subkey_ge, sub_pubkey); + + /* commit to sub-key */ + if (!secp256k1_eckey_pubkey_serialize(&subkey_ge, c, &size, SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(&sha, c, size); + for (i = 0; i < n_keys; i++) { + secp256k1_ge offline_ge; + secp256k1_ge online_ge; + secp256k1_gej tweaked_gej; + + /* commit to fixed keys */ + secp256k1_pubkey_load(ctx, &offline_ge, &offline_pubkeys[i]); + if (!secp256k1_eckey_pubkey_serialize(&offline_ge, c, &size, SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(&sha, c, size); + secp256k1_pubkey_load(ctx, &online_ge, &online_pubkeys[i]); + if (!secp256k1_eckey_pubkey_serialize(&online_ge, c, &size, SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(&sha, c, size); + + /* compute tweaked keys */ + secp256k1_gej_set_ge(&tweaked_gej, &offline_ge); + secp256k1_gej_add_ge_var(&tweaked_gej, &tweaked_gej, &subkey_ge, NULL); + secp256k1_whitelist_tweak_pubkey(ctx, &tweaked_gej); + secp256k1_gej_add_ge_var(&keys[i], &tweaked_gej, &online_ge, NULL); + } + secp256k1_sha256_finalize(&sha, msg32); + return 1; +} + + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 29c9f856..74c70b8c 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -759,3 +759,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_RANGEPROOF # include "modules/rangeproof/main_impl.h" #endif + +#ifdef ENABLE_MODULE_WHITELIST +# include "modules/whitelist/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index 822c7067..6ec70024 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5333,6 +5333,10 @@ void run_ecdsa_openssl(void) { # include "modules/rangeproof/tests_impl.h" #endif +#ifdef ENABLE_MODULE_WHITELIST +# include "modules/whitelist/tests_impl.h" +#endif + void run_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5648,6 +5652,11 @@ int main(int argc, char **argv) { run_rangeproof_tests(); #endif +#ifdef ENABLE_MODULE_WHITELIST + /* Key whitelisting tests */ + run_whitelist_tests(); +#endif + /* util tests */ run_memczero_test(); From ba8b4f53efef8b22eca6a76ba464f19aa9fcd525 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 1 Jul 2016 15:51:07 +0000 Subject: [PATCH 009/381] add surjection proof module Includes fix and tests by Jonas Nick. --- Makefile.am | 4 + configure.ac | 27 +- include/secp256k1_surjectionproof.h | 212 +++++++++++++ src/modules/surjection/Makefile.am.include | 6 + src/modules/surjection/main_impl.h | 334 ++++++++++++++++++++ src/modules/surjection/surjection.h | 19 ++ src/modules/surjection/surjection.md | 108 +++++++ src/modules/surjection/surjection_impl.h | 86 ++++++ src/modules/surjection/tests_impl.h | 336 +++++++++++++++++++++ src/secp256k1.c | 4 + src/tests.c | 8 + 11 files changed, 1142 insertions(+), 2 deletions(-) create mode 100644 include/secp256k1_surjectionproof.h create mode 100644 src/modules/surjection/Makefile.am.include create mode 100644 src/modules/surjection/main_impl.h create mode 100644 src/modules/surjection/surjection.h create mode 100644 src/modules/surjection/surjection.md create mode 100644 src/modules/surjection/surjection_impl.h create mode 100644 src/modules/surjection/tests_impl.h diff --git a/Makefile.am b/Makefile.am index a03b0781..4f3808b7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -164,3 +164,7 @@ endif if ENABLE_MODULE_WHITELIST include src/modules/whitelist/Makefile.am.include endif + +if ENABLE_MODULE_SURJECTIONPROOF +include src/modules/surjection/Makefile.am.include +endif diff --git a/configure.ac b/configure.ac index 7f1e94ed..1fe39ee8 100644 --- a/configure.ac +++ b/configure.ac @@ -156,6 +156,11 @@ AC_ARG_ENABLE(external_default_callbacks, [use_external_default_callbacks=$enableval], [use_external_default_callbacks=no]) +AC_ARG_ENABLE(module_surjectionproof, + AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module (default is no)]), + [enable_module_surjectionproof=$enableval], + [enable_module_surjectionproof=no]) + AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], [finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto]) @@ -198,6 +203,12 @@ else CFLAGS="-O2 $CFLAGS" fi +AC_MSG_CHECKING([for __builtin_popcount]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_POPCOUNT,1,[Define this symbol if __builtin_popcount is available]) ], + [ AC_MSG_RESULT([no]) + ]) + if test x"$use_ecmult_static_precomputation" != x"no"; then # Temporarily switch to an environment for the native compiler save_cross_compiling=$cross_compiling @@ -526,6 +537,10 @@ if test x"$enable_module_whitelist" = x"yes"; then AC_DEFINE(ENABLE_MODULE_WHITELIST, 1, [Define this symbol to enable the key whitelisting module]) fi +if test x"$enable_module_surjectionproof" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_SURJECTIONPROOF, 1, [Define this symbol to enable the surjection proof module]) +fi + AC_C_BIGENDIAN() if test x"$use_external_asm" = x"yes"; then @@ -544,6 +559,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator]) AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) + AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) AC_MSG_NOTICE([******]) if test x"$enable_module_generator" != x"yes"; then @@ -552,10 +568,13 @@ if test x"$enable_experimental" = x"yes"; then fi fi - if test x"$enable_module_whitelist" = x"yes"; then - if test x"$enable_module_rangeproof" != x"yes"; then + if test x"$enable_module_rangeproof" != x"yes"; then + if test x"$enable_module_whitelist" = x"yes"; then AC_MSG_ERROR([Whitelist module requires the rangeproof module. Use --enable-module-rangeproof to allow.]) fi + if test x"$enable_module_surjectionproof" = x"yes"; then + AC_MSG_ERROR([Surjection proof module requires the rangeproof module. Use --enable-module-rangeproof to allow.]) + fi fi else if test x"$enable_module_ecdh" = x"yes"; then @@ -573,6 +592,9 @@ else if test x"$enable_module_whitelist" = x"yes"; then AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_surjectionproof" = x"yes"; then + AC_MSG_ERROR([Surjection proof module is experimental. Use --enable-experimental to allow.]) + fi fi AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) @@ -593,6 +615,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) +AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) dnl make sure nothing new is exported so that we don't break the cache PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h new file mode 100644 index 00000000..57f2afb6 --- /dev/null +++ b/include/secp256k1_surjectionproof.h @@ -0,0 +1,212 @@ +#ifndef _SECP256K1_SURJECTIONPROOF_ +#define _SECP256K1_SURJECTIONPROOF_ + +#include "secp256k1.h" +#include "secp256k1_rangeproof.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Maximum number of inputs that may be given in a surjection proof */ +#define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256 + +/** Number of bytes a serialized surjection proof requires given the + * number of inputs and the number of used inputs. + */ +#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used_inputs) \ + (2 + (n_inputs + 7)/8 + 32 * (1 + (n_used_inputs))) + +/** Maximum number of bytes a serialized surjection proof requires. */ +#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX \ + SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) + +/** Opaque data structure that holds a parsed surjection proof + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. Nor is + * it guaranteed to have any particular size, nor that identical proofs + * will have identical representation. (That is, memcmp may return nonzero + * even for identical proofs.) + * + * To obtain these properties, instead use secp256k1_surjectionproof_parse + * and secp256k1_surjectionproof_serialize to encode/decode proofs into a + * well-defined format. + * + * The representation is exposed to allow creation of these objects on the + * stack; please *do not* use these internals directly. + */ +typedef struct { +#ifdef VERIFY + /** Mark whether this proof has gone through `secp256k1_surjectionproof_initialize` */ + int initialized; +#endif + /** Total number of input asset tags */ + size_t n_inputs; + /** Bitmap of which input tags are used in the surjection proof */ + unsigned char used_inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8]; + /** Borromean signature: e0, scalars */ + unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)]; +} secp256k1_surjectionproof; + +/** Parse a surjection proof + * + * Returns: 1 when the proof could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: proof: a pointer to a proof object + * In: input: a pointer to the array to parse + * inputlen: length of the array pointed to by input + * + * The proof must consist of: + * - A 2-byte little-endian total input count `n` + * - A ceil(n/8)-byte bitmap indicating which inputs are used. + * - A big-endian 32-byte borromean signature e0 value + * - `m` big-endian 32-byte borromean signature s values, where `m` + * is the number of set bits in the bitmap + */ +SECP256K1_API int secp256k1_surjectionproof_parse( + const secp256k1_context* ctx, + secp256k1_surjectionproof *proof, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a surjection proof + * + * Returns: 1 if enough space was available to serialize, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: output: a pointer to an array to store the serialization + * In/Out: outputlen: a pointer to an integer which is initially set to the + * size of output, and is overwritten with the written + * size. + * In: proof: a pointer to an initialized proof object + * + * See secp256k1_surjectionproof_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_surjectionproof_serialize( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_surjectionproof *proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Data structure that holds a fixed asset tag. + * + * This data type is *not* opaque. It will always be 32 bytes of whatever + * data the API user wants to use as an asset tag. Its contents have no + * semantic meaning to libsecp whatsoever. + */ +typedef struct { + unsigned char data[32]; +} secp256k1_fixed_asset_tag; + +/** Returns the total number of inputs a proof expects to be over. + * + * Returns: the number of inputs for the given proof + * In: ctx: pointer to a context object + * proof: a pointer to a proof object + */ +SECP256K1_API size_t secp256k1_surjectionproof_n_total_inputs( + const secp256k1_context* ctx, + const secp256k1_surjectionproof* proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Returns the actual number of inputs that a proof uses + * + * Returns: the number of inputs for the given proof + * In: ctx: pointer to a context object + * proof: a pointer to a proof object + */ +SECP256K1_API size_t secp256k1_surjectionproof_n_used_inputs( + const secp256k1_context* ctx, + const secp256k1_surjectionproof* proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Returns the total size this proof would take, in bytes, when serialized + * + * Returns: the total size + * In: ctx: pointer to a context object + * proof: a pointer to a proof object + */ +SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( + const secp256k1_context* ctx, + const secp256k1_surjectionproof* proof +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Surjection proof initialization function; decides on inputs to use + * Returns 0: inputs could not be selected + * n: inputs were selected after n iterations of random selection + * + * In: ctx: pointer to a context object + * fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known, + * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; + * this won't match the output tag but might be used in the anonymity set.) + * n_input_tags: the number of entries in the fixed_input_tags array + * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * fixed_output_tag: fixed output tag + * max_n_iterations: the maximum number of iterations to do before giving up + * random_seed32: a random seed to be used for input selection + * Out: proof: The proof whose bitvector will be initialized. In case of failure, + * the state of the proof is undefined. + * input_index: The index of the actual input that is secretly mapped to the output + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initialize( + const secp256k1_context* ctx, + secp256k1_surjectionproof* proof, + size_t *input_index, + const secp256k1_fixed_asset_tag* fixed_input_tags, + const size_t n_input_tags, + const size_t n_input_tags_to_use, + const secp256k1_fixed_asset_tag* fixed_output_tag, + const size_t n_max_iterations, + const unsigned char *random_seed32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + +/** Surjection proof generation function + * Returns 0: proof could not be created + * 1: proof was successfully created + * + * In: ctx: pointer to a context object, initialized for signing and verification + * ephemeral_input_tags: the ephemeral asset tag of all inputs + * n_ephemeral_input_tags: the number of entries in the ephemeral_input_tags array + * ephemeral_output_tag: the ephemeral asset tag of the output + * input_index: the index of the input that actually maps to the output + * input_blinding_key: the blinding key of the input + * output_blinding_key: the blinding key of the output + * In/Out: proof: The produced surjection proof. Must have already gone through `secp256k1_surjectionproof_initialize` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_generate( + const secp256k1_context* ctx, + secp256k1_surjectionproof* proof, + const secp256k1_generator* ephemeral_input_tags, + size_t n_ephemeral_input_tags, + const secp256k1_generator* ephemeral_output_tag, + size_t input_index, + const unsigned char *input_blinding_key, + const unsigned char *output_blinding_key +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); + + +/** Surjection proof verification function + * Returns 0: proof was invalid + * 1: proof was valid + * + * In: ctx: pointer to a context object, initialized for signing and verification + * proof: proof to be verified + * ephemeral_input_tags: the ephemeral asset tag of all inputs + * n_ephemeral_input_tags: the number of entries in the ephemeral_input_tags array + * ephemeral_output_tag: the ephemeral asset tag of the output + */ +SECP256K1_API int secp256k1_surjectionproof_verify( + const secp256k1_context* ctx, + const secp256k1_surjectionproof* proof, + const secp256k1_generator* ephemeral_input_tags, + size_t n_ephemeral_input_tags, + const secp256k1_generator* ephemeral_output_tag +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/modules/surjection/Makefile.am.include b/src/modules/surjection/Makefile.am.include new file mode 100644 index 00000000..51ece21f --- /dev/null +++ b/src/modules/surjection/Makefile.am.include @@ -0,0 +1,6 @@ +include_HEADERS += include/secp256k1_surjectionproof.h +noinst_HEADERS += src/modules/surjection/main_impl.h +noinst_HEADERS += src/modules/surjection/surjection.h +noinst_HEADERS += src/modules/surjection/surjection_impl.h +noinst_HEADERS += src/modules/surjection/tests_impl.h + diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h new file mode 100644 index 00000000..2a70c50c --- /dev/null +++ b/src/modules/surjection/main_impl.h @@ -0,0 +1,334 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#ifndef SECP256K1_MODULE_SURJECTION_MAIN +#define SECP256K1_MODULE_SURJECTION_MAIN + +#include +#include + +#include "modules/rangeproof/borromean.h" +#include "modules/surjection/surjection_impl.h" +#include "hash.h" +#include "include/secp256k1_rangeproof.h" +#include "include/secp256k1_surjectionproof.h" + +static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) { + size_t ret = 0; + size_t i; + for (i = 0; i < count; i++) { +#ifdef HAVE_BUILTIN_POPCOUNT + ret += __builtin_popcount(data[i]); +#else + ret += !!(data[i] & 0x1); + ret += !!(data[i] & 0x2); + ret += !!(data[i] & 0x4); + ret += !!(data[i] & 0x8); + ret += !!(data[i] & 0x10); + ret += !!(data[i] & 0x20); + ret += !!(data[i] & 0x40); + ret += !!(data[i] & 0x80); +#endif + } + return ret; +} + +int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surjectionproof *proof, const unsigned char *input, size_t inputlen) { + size_t n_inputs; + size_t signature_len; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(input != NULL); + (void) ctx; + + if (inputlen < 2) { + return 0; + } + n_inputs = ((size_t) (input[1] << 8)) + input[0]; + if (n_inputs > SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) { + return 0; + } + if (inputlen < 2 + (n_inputs + 7) / 8) { + return 0; + } + + signature_len = 32 * (1 + secp256k1_count_bits_set(&input[2], (n_inputs + 7) / 8)); + if (inputlen < 2 + (n_inputs + 7) / 8 + signature_len) { + return 0; + } + proof->n_inputs = n_inputs; + memcpy(proof->used_inputs, &input[2], (n_inputs + 7) / 8); + memcpy(proof->data, &input[2 + (n_inputs + 7) / 8], signature_len); + + return 1; +} + +int secp256k1_surjectionproof_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_surjectionproof *proof) { + size_t signature_len; + size_t serialized_len; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(proof != NULL); + (void) ctx; + + signature_len = 32 * (1 + secp256k1_count_bits_set(proof->used_inputs, (proof->n_inputs + 7) / 8)); + serialized_len = 2 + (proof->n_inputs + 7) / 8 + signature_len; + if (*outputlen < serialized_len) { + return 0; + } + + output[0] = proof->n_inputs % 0x100; + output[1] = proof->n_inputs / 0x100; + memcpy(&output[2], proof->used_inputs, (proof->n_inputs + 7) / 8); + memcpy(&output[2 + (proof->n_inputs + 7) / 8], proof->data, signature_len); + *outputlen = serialized_len; + + return 1; +} + +size_t secp256k1_surjectionproof_n_total_inputs(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + (void) ctx; + return proof->n_inputs; +} + +size_t secp256k1_surjectionproof_n_used_inputs(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + (void) ctx; + return secp256k1_count_bits_set(proof->used_inputs, (proof->n_inputs + 7) / 8); +} + +size_t secp256k1_surjectionproof_serialized_size(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + return 2 + (proof->n_inputs + 7) / 8 + 32 * (1 + secp256k1_surjectionproof_n_used_inputs(ctx, proof)); +} + +typedef struct { + unsigned char state[32]; + size_t state_i; +} secp256k1_surjectionproof_csprng; + +static void secp256k1_surjectionproof_csprng_init(secp256k1_surjectionproof_csprng *csprng, const unsigned char* state) { + memcpy(csprng->state, state, 32); + csprng->state_i = 0; +} + +static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_csprng *csprng, size_t rand_max) { + /* The number of random bytes to read for each random sample */ + const size_t increment = rand_max > 256 ? 2 : 1; + /* The maximum value expressable by the number of random bytes we read */ + const size_t selection_range = rand_max > 256 ? 0xffff : 0xff; + /* The largest multiple of rand_max that fits within selection_range */ + const size_t limit = ((selection_range + 1) / rand_max) * rand_max; + + while (1) { + size_t val; + if (csprng->state_i + increment >= 32) { + secp256k1_sha256 sha; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, csprng->state, 32); + secp256k1_sha256_finalize(&sha, csprng->state); + csprng->state_i = 0; + } + val = csprng->state[csprng->state_i]; + if (increment > 1) { + val = (val << 8) + csprng->state[csprng->state_i + 1]; + } + csprng->state_i += increment; + /* Accept only values below our limit. Values equal to or above the limit are + * biased because they comprise only a subset of the range (0, rand_max - 1) */ + if (val < limit) { + return val % rand_max; + } + } +} + +int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) { + secp256k1_surjectionproof_csprng csprng; + size_t n_iterations = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(input_index != NULL); + ARG_CHECK(fixed_input_tags != NULL); + ARG_CHECK(fixed_output_tag != NULL); + ARG_CHECK(random_seed32 != NULL); + ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + ARG_CHECK(n_input_tags_to_use <= n_input_tags); + + secp256k1_surjectionproof_csprng_init(&csprng, random_seed32); + memset(proof->data, 0, sizeof(proof->data)); + proof->n_inputs = n_input_tags; + + while (1) { + int has_output_tag = 0; + size_t i; + + /* obtain a random set of indices */ + memset(proof->used_inputs, 0, sizeof(proof->used_inputs)); + for (i = 0; i < n_input_tags_to_use; i++) { + while (1) { + size_t next_input_index; + next_input_index = secp256k1_surjectionproof_csprng_next(&csprng, n_input_tags); + if (memcmp(&fixed_input_tags[next_input_index], fixed_output_tag, sizeof(*fixed_output_tag)) == 0) { + *input_index = next_input_index; + has_output_tag = 1; + } + + if (!(proof->used_inputs[next_input_index / 8] & (1 << (next_input_index % 8)))) { + proof->used_inputs[next_input_index / 8] |= (1 << (next_input_index % 8)); + break; + } + } + } + + /* Check if we succeeded */ + n_iterations++; + if (has_output_tag) { +#ifdef VERIFY + proof->initialized = 1; +#endif + return n_iterations; + } + if (n_iterations >= n_max_iterations) { +#ifdef VERIFY + proof->initialized = 0; +#endif + return 0; + } + } +} + +int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag, size_t input_index, const unsigned char *input_blinding_key, const unsigned char *output_blinding_key) { + secp256k1_scalar blinding_key; + secp256k1_scalar tmps; + secp256k1_scalar nonce; + int overflow = 0; + size_t rsizes[1]; /* array needed for borromean sig API */ + size_t indices[1]; /* array needed for borromean sig API */ + size_t i; + size_t n_total_pubkeys; + size_t n_used_pubkeys; + size_t ring_input_index = 0; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_ge output; + unsigned char msg32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(ephemeral_input_tags != NULL); + ARG_CHECK(ephemeral_output_tag != NULL); + ARG_CHECK(input_blinding_key != NULL); + ARG_CHECK(output_blinding_key != NULL); +#ifdef VERIFY + CHECK(proof->initialized == 1); +#endif + + /* Compute secret key */ + secp256k1_scalar_set_b32(&tmps, input_blinding_key, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&blinding_key, output_blinding_key, &overflow); + if (overflow) { + return 0; + } + /* The only time the input may equal the output is if neither one was blinded in the first place, + * i.e. both blinding keys are zero. Otherwise this is a privacy leak. */ + if (secp256k1_scalar_eq(&tmps, &blinding_key) && !secp256k1_scalar_is_zero(&blinding_key)) { + return 0; + } + secp256k1_scalar_negate(&tmps, &tmps); + secp256k1_scalar_add(&blinding_key, &blinding_key, &tmps); + + /* Compute public keys */ + n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof); + n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof); + if (n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) { + return 0; + } + + secp256k1_generator_load(&output, ephemeral_output_tag); + for (i = 0; i < n_total_pubkeys; i++) { + secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]); + } + + secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, input_index, &ring_input_index); + + /* Produce signature */ + rsizes[0] = (int) n_used_pubkeys; + indices[0] = (int) ring_input_index; + secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output); + if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) { + return 0; + } + /* Borromean sign will overwrite one of the s values we just generated, so use + * it as a nonce instead. This avoids extra random generation and also is an + * homage to the rangeproof code which does this very cleverly to encode messages. */ + nonce = borromean_s[ring_input_index]; + secp256k1_scalar_clear(&borromean_s[ring_input_index]); + if (secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &nonce, &blinding_key, rsizes, indices, 1, msg32, 32) == 0) { + return 0; + } + for (i = 0; i < n_used_pubkeys; i++) { + secp256k1_scalar_get_b32(&proof->data[32 + 32 * i], &borromean_s[i]); + } + return 1; +} + +int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag) { + size_t rsizes[1]; /* array needed for borromean sig API */ + size_t i; + size_t n_total_pubkeys; + size_t n_used_pubkeys; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_ge output; + unsigned char msg32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(ephemeral_input_tags != NULL); + ARG_CHECK(ephemeral_output_tag != NULL); + + /* Compute public keys */ + n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof); + n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof); + if (n_used_pubkeys == 0 || n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) { + return 0; + } + + secp256k1_generator_load(&output, ephemeral_output_tag); + for (i = 0; i < n_total_pubkeys; i++) { + secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]); + } + + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, 0, NULL) == 0) { + return 0; + } + + /* Verify signature */ + rsizes[0] = (int) n_used_pubkeys; + for (i = 0; i < n_used_pubkeys; i++) { + int overflow = 0; + secp256k1_scalar_set_b32(&borromean_s[i], &proof->data[32 + 32 * i], &overflow); + if (overflow == 1) { + return 0; + } + } + secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output); + return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32); +} + +#endif diff --git a/src/modules/surjection/surjection.h b/src/modules/surjection/surjection.h new file mode 100644 index 00000000..20ac4931 --- /dev/null +++ b/src/modules/surjection/surjection.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SURJECTION_H_ +#define _SECP256K1_SURJECTION_H_ + +#include "group.h" +#include "scalar.h" + +SECP256K1_INLINE static int secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag); + +SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key); + +SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index); + +#endif diff --git a/src/modules/surjection/surjection.md b/src/modules/surjection/surjection.md new file mode 100644 index 00000000..e7bd4db1 --- /dev/null +++ b/src/modules/surjection/surjection.md @@ -0,0 +1,108 @@ +Surjection Proof Module +=========================== + +This module implements a scheme by which a given point can be proven to be +equal to one of a set of points, plus a known difference. This is used in +Confidential Assets when reblinding "asset commitments", which are NUMS +points, to prove that the underlying NUMS point does not change during +reblinding. + +Assets are represented, in general, by a 32-byte seed (a hash of some +transaction data) which is hashed to form a NUMS generator, which appears +on the blockchain only in blinded form. We refer to the seed as an +"asset ID" and the blinded generator as an "(ephemeral) asset commitment". +These asset commitments are unique per-output, and their NUMS components +are in general known only to the holder of the output. + +The result is that within a transaction, all outputs are able to have +a new uniformly-random asset commitment which cannot be associated with +any individual input asset id, but verifiers are nonetheless assured that +all assets coming out of a transaction are ones that went in. + +### Terminology + +Assets are identified by a 32-byte "asset ID". In this library these IDs +are used as input to a point-valued hash function `H`. We usually refer +to the hash output as `A`, since this output is the only thing that appears +in the algebra. + +Then transaction outputs have "asset commitments", which are curvepoints +of the form `A + rG`, where `A` is the hash of the asset ID and `r` is +some random "blinding factor". + +### Design Rationale + +Confidential Assets essentially works by replacing the second NUMS generator +`H` in Confidental Transactions with a per-asset unique NUMS generator. This +allows the same verification equation (the sum of all blinded inputs must +equal the sum of all blinded outputs) to imply that quantity of *every* asset +type is preserved in each transaction. + +It turns out that even if outputs are reblinded by the addition of `rG` for +some known `r`, this verification equation has the same meaning, with one +caveat: verifiers must be assured that the reblinding preserves the original +generators (and does not, for example, negate them). + +This assurance is what surjection proofs provide. + +### Limitations + +The naive scheme works as follows: every output asset is shown to have come +from some input asset. However, the proofs scale with the number of input +assets, so for all outputs the total size of all surjection proofs is `O(mn)` +for `m`, `n` the number of inputs and outputs. + +We therefore restrict the number of inputs that each output may have come +from to 3 (well, some fixed number, which is passed into the API), which +provides a weaker form of blinding, but gives `O(n)` scaling. Over many +transactions, the privacy afforded by this increases exponentially. + +### Our Scheme + +Our scheme works as follows. Proofs are generated in two steps, "initialization" +which selects a subset of inputs and "generation" which does the mathematical +part of proof generation. + +Every input has an asset commitment for which we know the blinding key and +underlying asset ID. + +#### Initialization + +The initialization function takes a list of input asset IDs and one output +asset ID. It chooses an input subset of some fixed size repeatedly until it +the output ID appears at least once in its subset. + +It stores a bitmap representing this subset in the proof object and returns +the number of iterations it needed to choose the subset. The reciprocal of +this represents the probability that a uniformly random input-output +mapping would correspond to the actual input-output mapping, and therefore +gives a measure of privacy. (Lower iteration counts are better.) + +It also informs the caller the index of the input whose ID matches the output. + +As the API works on only a single output at a time, the total probability +should be computed by multiplying together the counts for each output. + +#### Generation + +The generation function takes a list of input asset commitments, an output +asset commitment, the input index returned by the initialization step, and +blinding keys for (a) the output commitment, (b) the input commitment. Here +"the input commitment" refers specifically to the input whose index was +chosen during initialization. + +Next, it computes a ring signature over the differences between the output +commitment and every input commitment chosen during initialization. Since +the discrete log of one of these is the difference between the output and +input blinding keys, it is possible to create a ring signature over every +differences will be the blinding factor of the output. We create such a +signature, which completes the proof. + +#### Verification + +Verification takes a surjection proof object, a list of input commitments, +and an output commitment. The proof object contains a ring signature and +a bitmap describing which input commitments to use, and verification +succeeds iff the signature verifies. + + diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h new file mode 100644 index 00000000..f58026de --- /dev/null +++ b/src/modules/surjection/surjection_impl.h @@ -0,0 +1,86 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SURJECTION_IMPL_H_ +#define _SECP256K1_SURJECTION_IMPL_H_ + +#include +#include + +#include "eckey.h" +#include "group.h" +#include "scalar.h" +#include "hash.h" + +SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag) { + /* compute message */ + size_t i; + unsigned char pk_ser[33]; + size_t pk_len = sizeof(pk_ser); + secp256k1_sha256 sha256_en; + + secp256k1_sha256_initialize(&sha256_en); + for (i = 0; i < n_input_tags; i++) { + secp256k1_eckey_pubkey_serialize(&ephemeral_input_tags[i], pk_ser, &pk_len, 1); + assert(pk_len == sizeof(pk_ser)); + secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); + } + secp256k1_eckey_pubkey_serialize(ephemeral_output_tag, pk_ser, &pk_len, 1); + assert(pk_len == sizeof(pk_ser)); + secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); + secp256k1_sha256_finalize(&sha256_en, msg32); +} + +SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key) { + size_t i; + unsigned char sec_input[36]; + secp256k1_sha256 sha256_en; + + /* compute s values */ + secp256k1_scalar_get_b32(&sec_input[4], blinding_key); + for (i = 0; i < ns; i++) { + int overflow = 0; + sec_input[0] = i; + sec_input[1] = i >> 8; + sec_input[2] = i >> 16; + sec_input[3] = i >> 24; + + secp256k1_sha256_initialize(&sha256_en); + secp256k1_sha256_write(&sha256_en, sec_input, 36); + secp256k1_sha256_finalize(&sha256_en, sec_input); + secp256k1_scalar_set_b32(&s[i], sec_input, &overflow); + if (overflow == 1) { + memset(sec_input, 0, 32); + return 0; + } + } + memset(sec_input, 0, 32); + return 1; +} + +SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index) { + size_t i; + size_t j = 0; + for (i = 0; i < n_input_tags; i++) { + if (used_tags[i / 8] & (1 << (i % 8))) { + secp256k1_ge tmpge; + secp256k1_ge_neg(&tmpge, &input_tags[i]); + secp256k1_gej_set_ge(&pubkeys[j], &tmpge); + secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], output_tag, NULL); + if (ring_input_index != NULL && input_index == i) { + *ring_input_index = j; + } + j++; + if (j > n_pubkeys) { + return 0; + } + } + } + return 1; +} + + +#endif diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h new file mode 100644 index 00000000..13397830 --- /dev/null +++ b/src/modules/surjection/tests_impl.h @@ -0,0 +1,336 @@ +/********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS +#define SECP256K1_MODULE_SURJECTIONPROOF_TESTS + +#include + +#include "testrand.h" +#include "group.h" +#include "include/secp256k1_generator.h" +#include "include/secp256k1_rangeproof.h" +#include "include/secp256k1_surjectionproof.h" + +static void run_input_selection_tests(size_t n_inputs) { + unsigned char seed[32]; + size_t i; + size_t result; + size_t input_index; + size_t try_count = n_inputs * 100; + secp256k1_surjectionproof proof; + secp256k1_fixed_asset_tag fixed_input_tags[1000]; + const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1; + + assert(n_inputs < max_n_inputs); + secp256k1_rand256(seed); + + for (i = 0; i < n_inputs + 1; i++) { + secp256k1_rand256(fixed_input_tags[i].data); + } + + /* cannot match output when told to use zero keys */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], try_count, seed); + assert(result == 0); + assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 0); + assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 34 + (n_inputs + 7) / 8); + if (n_inputs > 0) { + /* succeed in 100*n_inputs tries (probability of failure e^-100) */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 1, &fixed_input_tags[0], try_count, seed); + assert(result > 0); + assert(result < n_inputs * 10); + assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 1); + assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 66 + (n_inputs + 7) / 8); + assert(input_index == 0); + } + + if (n_inputs >= 3) { + /* succeed in 10*n_inputs tries (probability of failure e^-10) */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[1], try_count, seed); + assert(result > 0); + assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 3); + assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 130 + (n_inputs + 7) / 8); + assert(input_index == 1); + + /* fail, key not found */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[n_inputs], try_count, seed); + assert(result == 0); + + /* succeed on first try when told to use all keys */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], try_count, seed); + assert(result == 1); + assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs); + assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs + 1) + (n_inputs + 7) / 8); + assert(input_index == 0); + + /* succeed in less than 64 tries when told to use half keys. (probability of failure 2^-64) */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs / 2, &fixed_input_tags[0], 64, seed); + assert(result > 0); + assert(result < 64); + assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs / 2); + assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs / 2 + 1) + (n_inputs + 7) / 8); + assert(input_index == 0); + } +} + +/** Runs surjectionproof_initilize multiple times and records the number of times each input was used. + */ +static void run_input_selection_distribution_tests_helper(const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, size_t *used_inputs) { + secp256k1_surjectionproof proof; + size_t input_index; + size_t i; + size_t j; + unsigned char seed[32]; + size_t result; + for (i = 0; i < n_input_tags; i++) { + used_inputs[i] = 0; + } + for(j = 0; j < 10000; j++) { + secp256k1_rand256(seed); + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, &fixed_input_tags[0], 64, seed); + assert(result > 0); + + for (i = 0; i < n_input_tags; i++) { + if (proof.used_inputs[i / 8] & (1 << (i % 8))) { + used_inputs[i] += 1; + } + } + } +} + +/** Probabilistic test of the distribution of used_inputs after surjectionproof_initialize. + * Each confidence interval assertion fails incorrectly with a probability of 2^-128. + */ +static void run_input_selection_distribution_tests(void) { + size_t i; + size_t n_input_tags_to_use; + const size_t n_inputs = 4; + secp256k1_fixed_asset_tag fixed_input_tags[4]; + size_t used_inputs[4]; + + for (i = 0; i < n_inputs; i++) { + secp256k1_rand256(fixed_input_tags[i].data); + } + + /* If there is one input tag to use, initialize must choose the one equal to fixed_output_tag. */ + n_input_tags_to_use = 1; + run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + assert(used_inputs[0] == 10000); + assert(used_inputs[1] == 0); + assert(used_inputs[2] == 0); + assert(used_inputs[3] == 0); + + n_input_tags_to_use = 2; + /* The input equal to the fixed_output_tag must be included in all used_inputs sets. + * For each fixed_input_tag != fixed_output_tag the probability that it's included + * in the used_inputs set is P(used_input|not fixed_output_tag) = 1/3. + */ + run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + assert(used_inputs[0] == 10000); + assert(used_inputs[1] > 2725 && used_inputs[1] < 3961); + assert(used_inputs[2] > 2725 && used_inputs[2] < 3961); + assert(used_inputs[3] > 2725 && used_inputs[3] < 3961); + + n_input_tags_to_use = 3; + /* P(used_input|not fixed_output_tag) = 2/3 */ + run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + assert(used_inputs[0] == 10000); + assert(used_inputs[1] > 6039 && used_inputs[1] < 7275); + assert(used_inputs[2] > 6039 && used_inputs[2] < 7275); + assert(used_inputs[3] > 6039 && used_inputs[3] < 7275); + + + n_input_tags_to_use = 1; + /* Create second input tag that is equal to the output tag. Therefore, when using only + * one input we have P(used_input|fixed_output_tag) = 1/2 and P(used_input|not fixed_output_tag) = 0 + */ + memcpy(fixed_input_tags[0].data, fixed_input_tags[1].data, 32); + run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + assert(used_inputs[0] > 4345 && used_inputs[0] < 5655); + assert(used_inputs[1] > 4345 && used_inputs[1] < 5655); + assert(used_inputs[2] == 0); + assert(used_inputs[3] == 0); + + n_input_tags_to_use = 2; + /* When choosing 2 inputs in initialization there are 5 possible combinations of + * input indexes {(0, 1), (1, 2), (0, 3), (1, 3), (0, 2)}. Therefore we have + * P(used_input|fixed_output_tag) = 3/5 and P(used_input|not fixed_output_tag) = 2/5. + */ + run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + assert(used_inputs[0] > 5352 && used_inputs[0] < 6637); + assert(used_inputs[1] > 5352 && used_inputs[1] < 6637); + assert(used_inputs[2] > 3363 && used_inputs[2] < 4648); + assert(used_inputs[3] > 3363 && used_inputs[3] < 4648); + + n_input_tags_to_use = 3; + /* There are 4 combinations, each with all inputs except one. Therefore we have + * P(used_input|fixed_output_tag) = 3/4 and P(used_input|not fixed_output_tag) = 3/4. + */ + run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + assert(used_inputs[0] > 6918 && used_inputs[0] < 8053); + assert(used_inputs[1] > 6918 && used_inputs[1] < 8053); + assert(used_inputs[2] > 6918 && used_inputs[2] < 8053); + assert(used_inputs[3] > 6918 && used_inputs[3] < 8053); +} + +static void run_gen_verify(size_t n_inputs, size_t n_used) { + unsigned char seed[32]; + secp256k1_surjectionproof proof; + unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + size_t serialized_len = SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX; + secp256k1_fixed_asset_tag fixed_input_tags[1000]; + secp256k1_generator ephemeral_input_tags[1000]; + unsigned char *input_blinding_key[1000]; + const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1; + size_t try_count = n_inputs * 100; + size_t key_index; + size_t input_index; + size_t i; + int result; + + /* setup */ + assert(n_used <= n_inputs); + assert(n_inputs < max_n_inputs); + secp256k1_rand256(seed); + + key_index = (((size_t) seed[0] << 8) + seed[1]) % n_inputs; + + for (i = 0; i < n_inputs + 1; i++) { + input_blinding_key[i] = malloc(32); + secp256k1_rand256(input_blinding_key[i]); + /* choose random fixed tag, except that for the output one copy from the key_index */ + if (i < n_inputs) { + secp256k1_rand256(fixed_input_tags[i].data); + } else { + memcpy(&fixed_input_tags[i], &fixed_input_tags[key_index], sizeof(fixed_input_tags[i])); + } + assert(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i])); + } + + /* test */ + result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_used, &fixed_input_tags[key_index], try_count, seed); + if (n_used == 0) { + assert(result == 0); + return; + } + assert(result > 0); + assert(input_index == key_index); + + result = secp256k1_surjectionproof_generate(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]); + assert(result == 1); + + assert(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof)); + assert(serialized_len == secp256k1_surjectionproof_serialized_size(ctx, &proof)); + assert(serialized_len == SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used)); + assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); + assert(result == 1); + /* various fail cases */ + if (n_inputs > 1) { + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); + assert(result == 0); + + /* number of entries in ephemeral_input_tags array is less than proof.n_inputs */ + n_inputs -= 1; + result = secp256k1_surjectionproof_generate(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]); + assert(result == 0); + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); + assert(result == 0); + n_inputs += 1; + } + + /* cleanup */ + for (i = 0; i < n_inputs + 1; i++) { + free(input_blinding_key[i]); + } +} + +/* check that a proof with empty n_used_inputs is invalid */ +static void run_no_used_inputs_verify(void) { + secp256k1_surjectionproof proof; + secp256k1_fixed_asset_tag fixed_input_tag; + secp256k1_fixed_asset_tag fixed_output_tag; + secp256k1_generator ephemeral_input_tags[1]; + size_t n_ephemeral_input_tags = 1; + secp256k1_generator ephemeral_output_tag; + unsigned char blinding_key[32]; + secp256k1_ge inputs[1]; + secp256k1_ge output; + secp256k1_sha256 sha256_e0; + int result; + + /* Create proof that doesn't use inputs. secp256k1_surjectionproof_initialize + * will not work here since it insists on selecting an input that matches the output. */ + proof.n_inputs = 1; + memset(proof.used_inputs, 0, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8); + + /* create different fixed input and output tags */ + secp256k1_rand256(fixed_input_tag.data); + secp256k1_rand256(fixed_output_tag.data); + + /* blind fixed output tags with random blinding key */ + secp256k1_rand256(blinding_key); + assert(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[0], fixed_input_tag.data, blinding_key)); + assert(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, blinding_key)); + + /* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */ + secp256k1_generator_load(&output, &ephemeral_output_tag); + secp256k1_generator_load(&inputs[0], &ephemeral_input_tags[0]); + secp256k1_surjection_genmessage(proof.data, inputs, 1, &output); + secp256k1_sha256_initialize(&sha256_e0); + secp256k1_sha256_write(&sha256_e0, proof.data, 32); + secp256k1_sha256_finalize(&sha256_e0, proof.data); + + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_ephemeral_input_tags, &ephemeral_output_tag); + assert(result == 0); +} + +void run_bad_serialize(void) { + secp256k1_surjectionproof proof; + unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + size_t serialized_len; + + proof.n_inputs = 0; + serialized_len = 2 + 31; + /* e0 is one byte too short */ + assert(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof) == 0); +} + +void run_bad_parse(void) { + secp256k1_surjectionproof proof; + unsigned char serialized_proof0[] = { 0x00 }; + unsigned char serialized_proof1[] = { 0x01, 0x00 }; + unsigned char serialized_proof2[33] = { 0 }; + + /* Missing total input count */ + assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof0, sizeof(serialized_proof0)) == 0); + /* Missing bitmap */ + assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof1, sizeof(serialized_proof1)) == 0); + /* Missing e0 value */ + assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0); +} + +void run_surjection_tests(void) { + run_input_selection_tests(0); + run_input_selection_tests(1); + run_input_selection_tests(5); + run_input_selection_tests(100); + run_input_selection_tests(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + + run_input_selection_distribution_tests(); + run_gen_verify(10, 3); + run_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + run_no_used_inputs_verify(); + run_bad_serialize(); + run_bad_parse(); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 74c70b8c..d4b4ac83 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -763,3 +763,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_WHITELIST # include "modules/whitelist/main_impl.h" #endif + +#ifdef ENABLE_MODULE_SURJECTIONPROOF +# include "modules/surjection/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index 6ec70024..fda9c1f9 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5337,6 +5337,10 @@ void run_ecdsa_openssl(void) { # include "modules/whitelist/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF +# include "modules/surjection/tests_impl.h" +#endif + void run_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5657,6 +5661,10 @@ int main(int argc, char **argv) { run_whitelist_tests(); #endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF + run_surjection_tests(); +#endif + /* util tests */ run_memczero_test(); From 002002e7354112a7535afcfbc941f8554537fb43 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 22 Apr 2017 18:31:28 +0000 Subject: [PATCH 010/381] rangeproof: fix memory leak in unit tests --- src/modules/rangeproof/tests_impl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 29b0a659..ea3c12e9 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -353,6 +353,12 @@ void test_multiple_generators(void) { /* Verify */ CHECK(secp256k1_pedersen_verify_tally(ctx, &commit_ptr[0], n_inputs, &commit_ptr[n_inputs], n_outputs)); + + /* Cleanup */ + for (i = 0; i < n_generators; i++) { + free(generator_blind[i]); + free(pedersen_blind[i]); + } } void run_rangeproof_tests(void) { From f858a4e3d5f1c249731957733b2e2bace79aa0c7 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 May 2017 13:50:58 +0000 Subject: [PATCH 011/381] surjectionproof: tests_impl.h s/assert/CHECK/g --- src/modules/surjection/tests_impl.h | 152 ++++++++++++++-------------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 13397830..25afaa0c 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -7,8 +7,6 @@ #ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS #define SECP256K1_MODULE_SURJECTIONPROOF_TESTS -#include - #include "testrand.h" #include "group.h" #include "include/secp256k1_generator.h" @@ -25,7 +23,7 @@ static void run_input_selection_tests(size_t n_inputs) { secp256k1_fixed_asset_tag fixed_input_tags[1000]; const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1; - assert(n_inputs < max_n_inputs); + CHECK(n_inputs < max_n_inputs); secp256k1_rand256(seed); for (i = 0; i < n_inputs + 1; i++) { @@ -34,50 +32,50 @@ static void run_input_selection_tests(size_t n_inputs) { /* cannot match output when told to use zero keys */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], try_count, seed); - assert(result == 0); - assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 0); - assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); - assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 34 + (n_inputs + 7) / 8); + CHECK(result == 0); + CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 0); + CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 34 + (n_inputs + 7) / 8); if (n_inputs > 0) { /* succeed in 100*n_inputs tries (probability of failure e^-100) */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 1, &fixed_input_tags[0], try_count, seed); - assert(result > 0); - assert(result < n_inputs * 10); - assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 1); - assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); - assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 66 + (n_inputs + 7) / 8); - assert(input_index == 0); + CHECK(result > 0); + CHECK(result < n_inputs * 10); + CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 1); + CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 66 + (n_inputs + 7) / 8); + CHECK(input_index == 0); } if (n_inputs >= 3) { /* succeed in 10*n_inputs tries (probability of failure e^-10) */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[1], try_count, seed); - assert(result > 0); - assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 3); - assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); - assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 130 + (n_inputs + 7) / 8); - assert(input_index == 1); + CHECK(result > 0); + CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 3); + CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 130 + (n_inputs + 7) / 8); + CHECK(input_index == 1); /* fail, key not found */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[n_inputs], try_count, seed); - assert(result == 0); + CHECK(result == 0); /* succeed on first try when told to use all keys */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], try_count, seed); - assert(result == 1); - assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs); - assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); - assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs + 1) + (n_inputs + 7) / 8); - assert(input_index == 0); + CHECK(result == 1); + CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs + 1) + (n_inputs + 7) / 8); + CHECK(input_index == 0); /* succeed in less than 64 tries when told to use half keys. (probability of failure 2^-64) */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs / 2, &fixed_input_tags[0], 64, seed); - assert(result > 0); - assert(result < 64); - assert(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs / 2); - assert(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); - assert(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs / 2 + 1) + (n_inputs + 7) / 8); - assert(input_index == 0); + CHECK(result > 0); + CHECK(result < 64); + CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs / 2); + CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs); + CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs / 2 + 1) + (n_inputs + 7) / 8); + CHECK(input_index == 0); } } @@ -96,7 +94,7 @@ static void run_input_selection_distribution_tests_helper(const secp256k1_fixed_ for(j = 0; j < 10000; j++) { secp256k1_rand256(seed); result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, &fixed_input_tags[0], 64, seed); - assert(result > 0); + CHECK(result > 0); for (i = 0; i < n_input_tags; i++) { if (proof.used_inputs[i / 8] & (1 << (i % 8))) { @@ -123,10 +121,10 @@ static void run_input_selection_distribution_tests(void) { /* If there is one input tag to use, initialize must choose the one equal to fixed_output_tag. */ n_input_tags_to_use = 1; run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); - assert(used_inputs[0] == 10000); - assert(used_inputs[1] == 0); - assert(used_inputs[2] == 0); - assert(used_inputs[3] == 0); + CHECK(used_inputs[0] == 10000); + CHECK(used_inputs[1] == 0); + CHECK(used_inputs[2] == 0); + CHECK(used_inputs[3] == 0); n_input_tags_to_use = 2; /* The input equal to the fixed_output_tag must be included in all used_inputs sets. @@ -134,18 +132,18 @@ static void run_input_selection_distribution_tests(void) { * in the used_inputs set is P(used_input|not fixed_output_tag) = 1/3. */ run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); - assert(used_inputs[0] == 10000); - assert(used_inputs[1] > 2725 && used_inputs[1] < 3961); - assert(used_inputs[2] > 2725 && used_inputs[2] < 3961); - assert(used_inputs[3] > 2725 && used_inputs[3] < 3961); + CHECK(used_inputs[0] == 10000); + CHECK(used_inputs[1] > 2725 && used_inputs[1] < 3961); + CHECK(used_inputs[2] > 2725 && used_inputs[2] < 3961); + CHECK(used_inputs[3] > 2725 && used_inputs[3] < 3961); n_input_tags_to_use = 3; /* P(used_input|not fixed_output_tag) = 2/3 */ run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); - assert(used_inputs[0] == 10000); - assert(used_inputs[1] > 6039 && used_inputs[1] < 7275); - assert(used_inputs[2] > 6039 && used_inputs[2] < 7275); - assert(used_inputs[3] > 6039 && used_inputs[3] < 7275); + CHECK(used_inputs[0] == 10000); + CHECK(used_inputs[1] > 6039 && used_inputs[1] < 7275); + CHECK(used_inputs[2] > 6039 && used_inputs[2] < 7275); + CHECK(used_inputs[3] > 6039 && used_inputs[3] < 7275); n_input_tags_to_use = 1; @@ -154,10 +152,10 @@ static void run_input_selection_distribution_tests(void) { */ memcpy(fixed_input_tags[0].data, fixed_input_tags[1].data, 32); run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); - assert(used_inputs[0] > 4345 && used_inputs[0] < 5655); - assert(used_inputs[1] > 4345 && used_inputs[1] < 5655); - assert(used_inputs[2] == 0); - assert(used_inputs[3] == 0); + CHECK(used_inputs[0] > 4345 && used_inputs[0] < 5655); + CHECK(used_inputs[1] > 4345 && used_inputs[1] < 5655); + CHECK(used_inputs[2] == 0); + CHECK(used_inputs[3] == 0); n_input_tags_to_use = 2; /* When choosing 2 inputs in initialization there are 5 possible combinations of @@ -165,20 +163,20 @@ static void run_input_selection_distribution_tests(void) { * P(used_input|fixed_output_tag) = 3/5 and P(used_input|not fixed_output_tag) = 2/5. */ run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); - assert(used_inputs[0] > 5352 && used_inputs[0] < 6637); - assert(used_inputs[1] > 5352 && used_inputs[1] < 6637); - assert(used_inputs[2] > 3363 && used_inputs[2] < 4648); - assert(used_inputs[3] > 3363 && used_inputs[3] < 4648); + CHECK(used_inputs[0] > 5352 && used_inputs[0] < 6637); + CHECK(used_inputs[1] > 5352 && used_inputs[1] < 6637); + CHECK(used_inputs[2] > 3363 && used_inputs[2] < 4648); + CHECK(used_inputs[3] > 3363 && used_inputs[3] < 4648); n_input_tags_to_use = 3; /* There are 4 combinations, each with all inputs except one. Therefore we have * P(used_input|fixed_output_tag) = 3/4 and P(used_input|not fixed_output_tag) = 3/4. */ run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); - assert(used_inputs[0] > 6918 && used_inputs[0] < 8053); - assert(used_inputs[1] > 6918 && used_inputs[1] < 8053); - assert(used_inputs[2] > 6918 && used_inputs[2] < 8053); - assert(used_inputs[3] > 6918 && used_inputs[3] < 8053); + CHECK(used_inputs[0] > 6918 && used_inputs[0] < 8053); + CHECK(used_inputs[1] > 6918 && used_inputs[1] < 8053); + CHECK(used_inputs[2] > 6918 && used_inputs[2] < 8053); + CHECK(used_inputs[3] > 6918 && used_inputs[3] < 8053); } static void run_gen_verify(size_t n_inputs, size_t n_used) { @@ -197,8 +195,8 @@ static void run_gen_verify(size_t n_inputs, size_t n_used) { int result; /* setup */ - assert(n_used <= n_inputs); - assert(n_inputs < max_n_inputs); + CHECK(n_used <= n_inputs); + CHECK(n_inputs < max_n_inputs); secp256k1_rand256(seed); key_index = (((size_t) seed[0] << 8) + seed[1]) % n_inputs; @@ -212,38 +210,38 @@ static void run_gen_verify(size_t n_inputs, size_t n_used) { } else { memcpy(&fixed_input_tags[i], &fixed_input_tags[key_index], sizeof(fixed_input_tags[i])); } - assert(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i])); + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i])); } /* test */ result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_used, &fixed_input_tags[key_index], try_count, seed); if (n_used == 0) { - assert(result == 0); + CHECK(result == 0); return; } - assert(result > 0); - assert(input_index == key_index); + CHECK(result > 0); + CHECK(input_index == key_index); result = secp256k1_surjectionproof_generate(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]); - assert(result == 1); + CHECK(result == 1); - assert(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof)); - assert(serialized_len == secp256k1_surjectionproof_serialized_size(ctx, &proof)); - assert(serialized_len == SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used)); - assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); + CHECK(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof)); + CHECK(serialized_len == secp256k1_surjectionproof_serialized_size(ctx, &proof)); + CHECK(serialized_len == SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used)); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); - assert(result == 1); + CHECK(result == 1); /* various fail cases */ if (n_inputs > 1) { result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); - assert(result == 0); + CHECK(result == 0); /* number of entries in ephemeral_input_tags array is less than proof.n_inputs */ n_inputs -= 1; result = secp256k1_surjectionproof_generate(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]); - assert(result == 0); + CHECK(result == 0); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); - assert(result == 0); + CHECK(result == 0); n_inputs += 1; } @@ -278,8 +276,8 @@ static void run_no_used_inputs_verify(void) { /* blind fixed output tags with random blinding key */ secp256k1_rand256(blinding_key); - assert(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[0], fixed_input_tag.data, blinding_key)); - assert(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, blinding_key)); + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[0], fixed_input_tag.data, blinding_key)); + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, blinding_key)); /* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */ secp256k1_generator_load(&output, &ephemeral_output_tag); @@ -290,7 +288,7 @@ static void run_no_used_inputs_verify(void) { secp256k1_sha256_finalize(&sha256_e0, proof.data); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_ephemeral_input_tags, &ephemeral_output_tag); - assert(result == 0); + CHECK(result == 0); } void run_bad_serialize(void) { @@ -301,7 +299,7 @@ void run_bad_serialize(void) { proof.n_inputs = 0; serialized_len = 2 + 31; /* e0 is one byte too short */ - assert(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof) == 0); + CHECK(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof) == 0); } void run_bad_parse(void) { @@ -311,11 +309,11 @@ void run_bad_parse(void) { unsigned char serialized_proof2[33] = { 0 }; /* Missing total input count */ - assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof0, sizeof(serialized_proof0)) == 0); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof0, sizeof(serialized_proof0)) == 0); /* Missing bitmap */ - assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof1, sizeof(serialized_proof1)) == 0); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof1, sizeof(serialized_proof1)) == 0); /* Missing e0 value */ - assert(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0); } void run_surjection_tests(void) { From 5f1ad03d007e0dfe33150d5daa64632ca94b0b3b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 May 2017 16:54:14 +0000 Subject: [PATCH 012/381] surjectionproof: add API unit tests --- src/modules/surjection/main_impl.h | 4 + src/modules/surjection/tests_impl.h | 153 ++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 2a70c50c..f57ddba1 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -163,6 +163,7 @@ int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1 ARG_CHECK(random_seed32 != NULL); ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); ARG_CHECK(n_input_tags_to_use <= n_input_tags); + (void) ctx; secp256k1_surjectionproof_csprng_init(&csprng, random_seed32); memset(proof->data, 0, sizeof(proof->data)); @@ -225,6 +226,8 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(proof != NULL); ARG_CHECK(ephemeral_input_tags != NULL); ARG_CHECK(ephemeral_output_tag != NULL); @@ -298,6 +301,7 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(proof != NULL); ARG_CHECK(ephemeral_input_tags != NULL); ARG_CHECK(ephemeral_output_tag != NULL); diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 25afaa0c..b24f0c86 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -13,6 +13,154 @@ #include "include/secp256k1_rangeproof.h" #include "include/secp256k1_surjectionproof.h" +static void run_surjectionproof_api_tests(void) { + unsigned char seed[32]; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_fixed_asset_tag fixed_input_tags[10]; + secp256k1_fixed_asset_tag fixed_output_tag; + secp256k1_generator ephemeral_input_tags[10]; + secp256k1_generator ephemeral_output_tag; + unsigned char input_blinding_key[10][32]; + unsigned char output_blinding_key[32]; + unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + size_t serialized_len; + secp256k1_surjectionproof proof; + size_t n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]); + size_t input_index; + int32_t ecount = 0; + size_t i; + + secp256k1_rand256(seed); + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + for (i = 0; i < n_inputs; i++) { + secp256k1_rand256(input_blinding_key[i]); + secp256k1_rand256(fixed_input_tags[i].data); + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i])); + } + secp256k1_rand256(output_blinding_key); + memcpy(&fixed_output_tag, &fixed_input_tags[0], sizeof(fixed_input_tags[0])); + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, output_blinding_key)); + + /* check initialize */ + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 0); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + CHECK(ecount == 0); + CHECK(secp256k1_surjectionproof_initialize(none, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0); + CHECK(ecount == 4); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed) == 0); + CHECK(ecount == 6); + CHECK((secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL) == 0); + CHECK(ecount == 7); + + CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + /* check generate */ + CHECK(secp256k1_surjectionproof_generate(none, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_surjectionproof_generate(vrfy, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 9); + + CHECK(secp256k1_surjectionproof_generate(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0); + CHECK(ecount == 10); + + CHECK(secp256k1_surjectionproof_generate(both, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_surjectionproof_generate(both, &proof, NULL, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, 0, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, NULL, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 1, input_blinding_key[0], output_blinding_key) != 0); + CHECK(ecount == 13); /* the above line "succeeds" but generates an invalid proof as the input_index is wrong. it is fairly expensive to detect this. should we? */ + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, n_inputs + 1, input_blinding_key[0], output_blinding_key) != 0); + CHECK(ecount == 13); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, NULL, output_blinding_key) == 0); + CHECK(ecount == 14); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], NULL) == 0); + CHECK(ecount == 15); + + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0); + /* check verify */ + CHECK(secp256k1_surjectionproof_verify(none, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0); + CHECK(ecount == 16); + CHECK(secp256k1_surjectionproof_verify(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0); + CHECK(ecount == 17); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) != 0); + CHECK(ecount == 17); + + CHECK(secp256k1_surjectionproof_verify(vrfy, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0); + CHECK(ecount == 18); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, NULL, n_inputs, &ephemeral_output_tag) == 0); + CHECK(ecount == 19); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag) == 0); + CHECK(ecount == 19); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag) == 0); + CHECK(ecount == 19); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, NULL) == 0); + CHECK(ecount == 20); + + /* Check serialize */ + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0); + CHECK(ecount == 20); + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(none, NULL, &serialized_len, &proof) == 0); + CHECK(ecount == 21); + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, NULL, &proof) == 0); + CHECK(ecount == 22); + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, NULL) == 0); + CHECK(ecount == 23); + + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0); + /* Check parse */ + CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, serialized_len) != 0); + CHECK(ecount == 23); + CHECK(secp256k1_surjectionproof_parse(none, NULL, serialized_proof, serialized_len) == 0); + CHECK(ecount == 24); + CHECK(secp256k1_surjectionproof_parse(none, &proof, NULL, serialized_len) == 0); + CHECK(ecount == 25); + CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, 0) == 0); + CHECK(ecount == 25); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + static void run_input_selection_tests(size_t n_inputs) { unsigned char seed[32]; size_t i; @@ -317,6 +465,11 @@ void run_bad_parse(void) { } void run_surjection_tests(void) { + int i; + for (i = 0; i < count; i++) { + run_surjectionproof_api_tests(); + } + run_input_selection_tests(0); run_input_selection_tests(1); run_input_selection_tests(5); From 18c5c62b45a62a182217de4859e06661398b5640 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 3 May 2017 17:06:39 +0000 Subject: [PATCH 013/381] surjectionproof: rename unit test functions to be more consistent with other modules --- src/modules/surjection/tests_impl.h | 52 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index b24f0c86..08742e14 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -13,7 +13,7 @@ #include "include/secp256k1_rangeproof.h" #include "include/secp256k1_surjectionproof.h" -static void run_surjectionproof_api_tests(void) { +static void test_surjectionproof_api(void) { unsigned char seed[32]; secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); @@ -161,7 +161,7 @@ static void run_surjectionproof_api_tests(void) { secp256k1_context_destroy(both); } -static void run_input_selection_tests(size_t n_inputs) { +static void test_input_selection(size_t n_inputs) { unsigned char seed[32]; size_t i; size_t result; @@ -229,7 +229,7 @@ static void run_input_selection_tests(size_t n_inputs) { /** Runs surjectionproof_initilize multiple times and records the number of times each input was used. */ -static void run_input_selection_distribution_tests_helper(const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, size_t *used_inputs) { +static void test_input_selection_distribution_helper(const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, size_t *used_inputs) { secp256k1_surjectionproof proof; size_t input_index; size_t i; @@ -255,7 +255,7 @@ static void run_input_selection_distribution_tests_helper(const secp256k1_fixed_ /** Probabilistic test of the distribution of used_inputs after surjectionproof_initialize. * Each confidence interval assertion fails incorrectly with a probability of 2^-128. */ -static void run_input_selection_distribution_tests(void) { +static void test_input_selection_distribution(void) { size_t i; size_t n_input_tags_to_use; const size_t n_inputs = 4; @@ -268,7 +268,7 @@ static void run_input_selection_distribution_tests(void) { /* If there is one input tag to use, initialize must choose the one equal to fixed_output_tag. */ n_input_tags_to_use = 1; - run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); CHECK(used_inputs[0] == 10000); CHECK(used_inputs[1] == 0); CHECK(used_inputs[2] == 0); @@ -279,7 +279,7 @@ static void run_input_selection_distribution_tests(void) { * For each fixed_input_tag != fixed_output_tag the probability that it's included * in the used_inputs set is P(used_input|not fixed_output_tag) = 1/3. */ - run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); CHECK(used_inputs[0] == 10000); CHECK(used_inputs[1] > 2725 && used_inputs[1] < 3961); CHECK(used_inputs[2] > 2725 && used_inputs[2] < 3961); @@ -287,7 +287,7 @@ static void run_input_selection_distribution_tests(void) { n_input_tags_to_use = 3; /* P(used_input|not fixed_output_tag) = 2/3 */ - run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); CHECK(used_inputs[0] == 10000); CHECK(used_inputs[1] > 6039 && used_inputs[1] < 7275); CHECK(used_inputs[2] > 6039 && used_inputs[2] < 7275); @@ -299,7 +299,7 @@ static void run_input_selection_distribution_tests(void) { * one input we have P(used_input|fixed_output_tag) = 1/2 and P(used_input|not fixed_output_tag) = 0 */ memcpy(fixed_input_tags[0].data, fixed_input_tags[1].data, 32); - run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); CHECK(used_inputs[0] > 4345 && used_inputs[0] < 5655); CHECK(used_inputs[1] > 4345 && used_inputs[1] < 5655); CHECK(used_inputs[2] == 0); @@ -310,7 +310,7 @@ static void run_input_selection_distribution_tests(void) { * input indexes {(0, 1), (1, 2), (0, 3), (1, 3), (0, 2)}. Therefore we have * P(used_input|fixed_output_tag) = 3/5 and P(used_input|not fixed_output_tag) = 2/5. */ - run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); CHECK(used_inputs[0] > 5352 && used_inputs[0] < 6637); CHECK(used_inputs[1] > 5352 && used_inputs[1] < 6637); CHECK(used_inputs[2] > 3363 && used_inputs[2] < 4648); @@ -320,14 +320,14 @@ static void run_input_selection_distribution_tests(void) { /* There are 4 combinations, each with all inputs except one. Therefore we have * P(used_input|fixed_output_tag) = 3/4 and P(used_input|not fixed_output_tag) = 3/4. */ - run_input_selection_distribution_tests_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); + test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs); CHECK(used_inputs[0] > 6918 && used_inputs[0] < 8053); CHECK(used_inputs[1] > 6918 && used_inputs[1] < 8053); CHECK(used_inputs[2] > 6918 && used_inputs[2] < 8053); CHECK(used_inputs[3] > 6918 && used_inputs[3] < 8053); } -static void run_gen_verify(size_t n_inputs, size_t n_used) { +static void test_gen_verify(size_t n_inputs, size_t n_used) { unsigned char seed[32]; secp256k1_surjectionproof proof; unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; @@ -400,7 +400,7 @@ static void run_gen_verify(size_t n_inputs, size_t n_used) { } /* check that a proof with empty n_used_inputs is invalid */ -static void run_no_used_inputs_verify(void) { +static void test_no_used_inputs_verify(void) { secp256k1_surjectionproof proof; secp256k1_fixed_asset_tag fixed_input_tag; secp256k1_fixed_asset_tag fixed_output_tag; @@ -439,7 +439,7 @@ static void run_no_used_inputs_verify(void) { CHECK(result == 0); } -void run_bad_serialize(void) { +void test_bad_serialize(void) { secp256k1_surjectionproof proof; unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; size_t serialized_len; @@ -450,7 +450,7 @@ void run_bad_serialize(void) { CHECK(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof) == 0); } -void run_bad_parse(void) { +void test_bad_parse(void) { secp256k1_surjectionproof proof; unsigned char serialized_proof0[] = { 0x00 }; unsigned char serialized_proof1[] = { 0x01, 0x00 }; @@ -467,21 +467,21 @@ void run_bad_parse(void) { void run_surjection_tests(void) { int i; for (i = 0; i < count; i++) { - run_surjectionproof_api_tests(); + test_surjectionproof_api(); } - run_input_selection_tests(0); - run_input_selection_tests(1); - run_input_selection_tests(5); - run_input_selection_tests(100); - run_input_selection_tests(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + test_input_selection(0); + test_input_selection(1); + test_input_selection(5); + test_input_selection(100); + test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); - run_input_selection_distribution_tests(); - run_gen_verify(10, 3); - run_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); - run_no_used_inputs_verify(); - run_bad_serialize(); - run_bad_parse(); + test_input_selection_distribution(); + test_gen_verify(10, 3); + test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + test_no_used_inputs_verify(); + test_bad_serialize(); + test_bad_parse(); } #endif From e13bdf2f23c20b8b1e32a8c48159d5e60ac9bbf5 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 3 May 2017 18:08:31 +0000 Subject: [PATCH 014/381] rangeproof: add API tests --- include/secp256k1_rangeproof.h | 2 +- src/modules/rangeproof/main_impl.h | 27 ++- src/modules/rangeproof/tests_impl.h | 244 ++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 7 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 528b6662..a41d2be5 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -98,7 +98,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( /** Verify a tally of pedersen commitments * Returns 1: commitments successfully sum to zero. * 0: Commitments do not sum to zero or other error. - * In: ctx: pointer to a context object, initialized for Pedersen commitment (cannot be NULL) + * In: ctx: pointer to a context object (cannot be NULL) * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) * pcnt: number of commitments pointed to by commits. * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 4427667a..f16a0abf 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -46,6 +46,7 @@ int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_ VERIFY_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(input != NULL); + (void) ctx; if ((input[0] & 0xFE) != 8) { return 0; } @@ -69,10 +70,11 @@ int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_c secp256k1_scalar sec; int overflow; int ret = 0; - ARG_CHECK(ctx != NULL); + VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(commit != NULL); ARG_CHECK(blind != NULL); + ARG_CHECK(gen != NULL); secp256k1_generator_load(&genp, gen); secp256k1_scalar_set_b32(&sec, blind, &overflow); if (!overflow) { @@ -97,9 +99,11 @@ int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *bl secp256k1_scalar x; size_t i; int overflow; - ARG_CHECK(ctx != NULL); + VERIFY_CHECK(ctx != NULL); ARG_CHECK(blind_out != NULL); ARG_CHECK(blinds != NULL); + ARG_CHECK(npositive <= n); + (void) ctx; secp256k1_scalar_set_int(&acc, 0); for (i = 0; i < n; i++) { secp256k1_scalar_set_b32(&x, blinds[i], &overflow); @@ -122,9 +126,10 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k secp256k1_gej accj; secp256k1_ge add; size_t i; - ARG_CHECK(ctx != NULL); + VERIFY_CHECK(ctx != NULL); ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); + (void) ctx; secp256k1_gej_set_infinity(&accj); for (i = 0; i < ncnt; i++) { secp256k1_pedersen_commitment_load(&add, ncommits[i]); @@ -200,6 +205,7 @@ int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *manti ARG_CHECK(mantissa != NULL); ARG_CHECK(min_value != NULL); ARG_CHECK(max_value != NULL); + ARG_CHECK(proof != NULL); offset = 0; scale = 1; (void)ctx; @@ -212,11 +218,15 @@ int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { secp256k1_ge commitp; secp256k1_ge genp; - ARG_CHECK(ctx != NULL); + VERIFY_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(proof != NULL); ARG_CHECK(min_value != NULL); ARG_CHECK(max_value != NULL); + ARG_CHECK(message_out != NULL || outlen == NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(gen != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); @@ -229,11 +239,13 @@ int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_valu const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { secp256k1_ge commitp; secp256k1_ge genp; - ARG_CHECK(ctx != NULL); + VERIFY_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(proof != NULL); ARG_CHECK(min_value != NULL); ARG_CHECK(max_value != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(gen != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); @@ -246,12 +258,15 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen){ secp256k1_ge commitp; secp256k1_ge genp; - ARG_CHECK(ctx != NULL); + VERIFY_CHECK(ctx != NULL); ARG_CHECK(proof != NULL); ARG_CHECK(plen != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(blind != NULL); ARG_CHECK(nonce != NULL); + ARG_CHECK(message != NULL || msg_len == 0); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(gen != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_pedersen_commitment_load(&commitp, commit); diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index ea3c12e9..f604aa60 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -16,6 +16,249 @@ #include "include/secp256k1_rangeproof.h" +static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const int32_t *ecount) { + secp256k1_pedersen_commitment commit; + const secp256k1_pedersen_commitment *commit_ptr = &commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + const unsigned char *blind_ptr = blind; + unsigned char *blind_out_ptr = blind_out; + uint64_t val = secp256k1_rand32(); + + secp256k1_rand256(blind); + CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) == 0); + CHECK(*ecount == 1); + CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) == 0); + CHECK(*ecount == 2); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(*ecount == 2); + + CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0); + CHECK(*ecount == 3); + CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); + CHECK(*ecount == 4); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); + CHECK(*ecount == 5); + + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0); + CHECK(*ecount == 5); + CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); + CHECK(*ecount == 6); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); + CHECK(*ecount == 7); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); + CHECK(*ecount == 8); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); + CHECK(*ecount == 8); + + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0); + CHECK(*ecount == 8); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); + CHECK(*ecount == 9); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); + CHECK(*ecount == 10); + + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); + CHECK(*ecount == 10); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); + CHECK(*ecount == 11); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); + CHECK(*ecount == 12); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); + CHECK(*ecount == 13); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); + CHECK(*ecount == 14); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); + CHECK(*ecount == 15); +} + +static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const int32_t *ecount) { + unsigned char proof[5134]; + unsigned char blind[32]; + secp256k1_pedersen_commitment commit; + uint64_t vmin = secp256k1_rand32(); + uint64_t val = vmin + secp256k1_rand32(); + size_t len = sizeof(proof); + /* we'll switch to dylan thomas for this one */ + const unsigned char message[68] = "My tears are like the quiet drift / Of petals from some magic rose;"; + size_t mlen = sizeof(message); + const unsigned char ext_commit[72] = "And all my grief flows from the rift / Of unremembered skies and snows."; + size_t ext_commit_len = sizeof(ext_commit); + + secp256k1_rand256(blind); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, secp256k1_generator_h)); + + CHECK(secp256k1_rangeproof_sign(none, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 1); + CHECK(secp256k1_rangeproof_sign(sign, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 2); + CHECK(secp256k1_rangeproof_sign(vrfy, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 3); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 3); + + CHECK(secp256k1_rangeproof_sign(both, NULL, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 4); + CHECK(secp256k1_rangeproof_sign(both, proof, NULL, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 5); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, NULL, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 6); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, NULL, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 7); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, NULL, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 8); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, vmin - 1, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 8); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 9); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 9); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 10); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, secp256k1_generator_h) != 0); + CHECK(*ecount == 10); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, NULL) == 0); + CHECK(*ecount == 11); + + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + { + int exp; + int mantissa; + uint64_t min_value; + uint64_t max_value; + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, proof, len) != 0); + CHECK(exp == 0); + CHECK(((uint64_t) 1 << mantissa) > val - vmin); + CHECK(((uint64_t) 1 << (mantissa - 1)) <= val - vmin); + CHECK(min_value == vmin); + CHECK(max_value >= val); + + CHECK(secp256k1_rangeproof_info(none, NULL, &mantissa, &min_value, &max_value, proof, len) == 0); + CHECK(*ecount == 12); + CHECK(secp256k1_rangeproof_info(none, &exp, NULL, &min_value, &max_value, proof, len) == 0); + CHECK(*ecount == 13); + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, NULL, &max_value, proof, len) == 0); + CHECK(*ecount == 14); + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, NULL, proof, len) == 0); + CHECK(*ecount == 15); + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, NULL, len) == 0); + CHECK(*ecount == 16); + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, proof, 0) == 0); + CHECK(*ecount == 16); + } + { + uint64_t min_value; + uint64_t max_value; + CHECK(secp256k1_rangeproof_verify(none, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 17); + CHECK(secp256k1_rangeproof_verify(sign, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 18); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 18); + + CHECK(secp256k1_rangeproof_verify(vrfy, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 19); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 20); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 21); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 22); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 22); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 23); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK(*ecount == 23); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); + CHECK(*ecount == 24); + } + { + unsigned char blind_out[32]; + unsigned char message_out[68]; + uint64_t value_out; + uint64_t min_value; + uint64_t max_value; + size_t message_len = sizeof(message_out); + + CHECK(secp256k1_rangeproof_rewind(none, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 25); + CHECK(secp256k1_rangeproof_rewind(sign, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 26); + CHECK(secp256k1_rangeproof_rewind(vrfy, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 27); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 27); + + CHECK(min_value == vmin); + CHECK(max_value >= val); + CHECK(value_out == val); + CHECK(message_len == sizeof(message_out)); + CHECK(memcmp(message, message_out, sizeof(message_out)) == 0); + + CHECK(secp256k1_rangeproof_rewind(both, NULL, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 27); /* blindout may be NULL */ + CHECK(secp256k1_rangeproof_rewind(both, blind_out, NULL, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 27); /* valueout may be NULL */ + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 28); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 28); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, NULL, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 29); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 30); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 31); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 32); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 33); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 33); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 34); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK(*ecount == 34); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); + CHECK(*ecount == 35); + } +} + +static void test_api(void) { + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int32_t ecount; + int i; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + for (i = 0; i < count; i++) { + ecount = 0; + test_pedersen_api(none, sign, vrfy, &ecount); + ecount = 0; + test_rangeproof_api(none, sign, vrfy, both, &ecount); + } + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + static void test_pedersen(void) { secp256k1_pedersen_commitment commits[19]; const secp256k1_pedersen_commitment *cptr[19]; @@ -363,6 +606,7 @@ void test_multiple_generators(void) { void run_rangeproof_tests(void) { int i; + test_api(); for (i = 0; i < 10*count; i++) { test_pedersen(); } From 660ad39fb330abb43037adf452b84923da8c8e9d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 9 May 2017 01:46:55 +0200 Subject: [PATCH 015/381] Fix include/secp256k1_rangeproof.h function argument documentation. --- include/secp256k1_rangeproof.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index a41d2be5..c71f432e 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -66,6 +66,7 @@ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) * blind: pointer to a 32-byte blinding factor (cannot be NULL) * value: unsigned 64-bit integer value to commit to. + * gen: additional generator 'h' * Out: commit: pointer to the commitment (cannot be NULL) * * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. @@ -84,7 +85,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( * In: ctx: pointer to a context object (cannot be NULL) * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) * n: number of factors pointed to by blinds. - * nneg: how many of the initial factors should be treated with a positive sign. + * npositive: how many of the initial factors should be treated with a positive sign. * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( @@ -167,6 +168,7 @@ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); * plen: length of proof in bytes. * extra_commit: additional data covered in rangeproof signature * extra_commit_len: length of extra_commit byte array (0 if NULL) + * gen: additional generator 'h' * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ @@ -192,6 +194,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( * nonce: 32-byte secret nonce used by the prover (cannot be NULL) * extra_commit: additional data covered in rangeproof signature * extra_commit_len: length of extra_commit byte array (0 if NULL) + * gen: additional generator 'h' * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment * value_out: pointer to an unsigned int64 which has the exact value of the commitment. * message_out: pointer to a 4096 byte character array to receive message data from the proof author. @@ -233,6 +236,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * msg_len: size of the message to be embedded in the rangeproof * extra_commit: additional data to be covered in rangeproof signature * extra_commit_len: length of extra_commit byte array (0 if NULL) + * gen: additional generator 'h' * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. * * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. From 29d0d562dcb5f0764b6358a5e395a2b5c297a224 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 26 Jun 2017 17:08:47 +0000 Subject: [PATCH 016/381] whitelist: fix serialize/parse API to take serialized length --- include/secp256k1_whitelist.h | 13 +++++++++---- src/modules/whitelist/main_impl.h | 11 ++++++++--- src/modules/whitelist/tests_impl.h | 12 +++++++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index c3175ce0..19412883 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -43,6 +43,7 @@ typedef struct { * Args: ctx: a secp256k1 context object * Out: sig: a pointer to a signature object * In: input: a pointer to the array to parse + * input_len: the length of the above array * * The signature must consist of a 1-byte n_keys value, followed by a 32-byte * big endian e0 value, followed by n_keys many 32-byte big endian s values. @@ -50,6 +51,7 @@ typedef struct { * is invalid. * * The total length of the input array must therefore be 33 + 32 * n_keys. + * If the length `input_len` does not match this value, parsing will fail. * * After the call, sig will always be initialized. If parsing failed or any * scalar values overflow or are zero, the resulting sig value is guaranteed @@ -58,7 +60,8 @@ typedef struct { SECP256K1_API int secp256k1_whitelist_signature_parse( const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, - const unsigned char *input + const unsigned char *input, + size_t input_len ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Returns the number of keys a signature expects to have. @@ -73,15 +76,17 @@ SECP256K1_API size_t secp256k1_whitelist_signature_n_keys( /** Serialize a whitelist signature * * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: output64: a pointer to an array to store the serialization - * In: sig: a pointer to an initialized signature object + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to an array to store the serialization + * In/Out: output_len: length of the above array, updated with the actual serialized length + * In: sig: a pointer to an initialized signature object * * See secp256k1_whitelist_signature_parse for details about the encoding. */ SECP256K1_API int secp256k1_whitelist_signature_serialize( const secp256k1_context* ctx, unsigned char *output, + size_t *output_len, const secp256k1_whitelist_signature *sig ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 0de178fc..7445d6e3 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -136,13 +136,13 @@ size_t secp256k1_whitelist_signature_n_keys(const secp256k1_whitelist_signature return sig->n_keys; } -int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const unsigned char *input) { +int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const unsigned char *input, size_t input_len) { VERIFY_CHECK(ctx != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(input != NULL); sig->n_keys = input[0]; - if (sig->n_keys >= MAX_KEYS) { + if (sig->n_keys >= MAX_KEYS || input_len != 1 + 32 * (sig->n_keys + 1)) { return 0; } memcpy(&sig->data[0], &input[1], 32 * (sig->n_keys + 1)); @@ -150,13 +150,18 @@ int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_ return 1; } -int secp256k1_whitelist_signature_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_whitelist_signature *sig) { +int secp256k1_whitelist_signature_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *output_len, const secp256k1_whitelist_signature *sig) { VERIFY_CHECK(ctx != NULL); ARG_CHECK(output != NULL); ARG_CHECK(sig != NULL); + if (*output_len < 1 + 32 * (sig->n_keys + 1)) { + return 0; + } + output[0] = sig->n_keys; memcpy(&output[1], &sig->data[0], 32 * (sig->n_keys + 1)); + *output_len = 1 + 32 * (sig->n_keys + 1); return 1; } diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index e307de16..dcaf3baa 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -53,6 +53,7 @@ void test_whitelist_end_to_end(const size_t n_keys) { /* Sign/verify with each one */ for (i = 0; i < n_keys; i++) { unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; + size_t slen = sizeof(serialized); secp256k1_whitelist_signature sig; secp256k1_whitelist_signature sig1; @@ -61,8 +62,13 @@ void test_whitelist_end_to_end(const size_t n_keys) { /* Check that exchanging keys causes a failure */ CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); /* Serialization round trip */ - CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &sig) == 1); - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized) == 1); + CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &slen, &sig) == 1); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen) == 1); + /* (Check various bad-length conditions) */ + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 32) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 1) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen - 1) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, 0) == 0); CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, &sub_pubkey) == 1); CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); /* Test n_keys */ @@ -93,7 +99,7 @@ void test_whitelist_bad_parse(void) { }; secp256k1_whitelist_signature sig; - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized, sizeof(serialized)) == 0); } void run_whitelist_tests(void) { From dbf3d752a85e2e6751b4e89f734ac39010286138 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 27 Jun 2017 12:14:29 +0200 Subject: [PATCH 017/381] Fix checks of whitelist serialize/parse arguments --- include/secp256k1_whitelist.h | 2 +- src/modules/whitelist/main_impl.h | 5 +++ src/modules/whitelist/tests_impl.h | 53 +++++++++++++++++++++++------- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index 19412883..e1e17022 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -88,7 +88,7 @@ SECP256K1_API int secp256k1_whitelist_signature_serialize( unsigned char *output, size_t *output_len, const secp256k1_whitelist_signature *sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Compute a whitelist signature * Returns 1: signature was successfully created diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 7445d6e3..8ac93ded 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -141,6 +141,10 @@ int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_ ARG_CHECK(sig != NULL); ARG_CHECK(input != NULL); + if (input_len == 0) { + return 0; + } + sig->n_keys = input[0]; if (sig->n_keys >= MAX_KEYS || input_len != 1 + 32 * (sig->n_keys + 1)) { return 0; @@ -153,6 +157,7 @@ int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_ int secp256k1_whitelist_signature_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *output_len, const secp256k1_whitelist_signature *sig) { VERIFY_CHECK(ctx != NULL); ARG_CHECK(output != NULL); + ARG_CHECK(output_len != NULL); ARG_CHECK(sig != NULL); if (*output_len < 1 + 32 * (sig->n_keys + 1)) { diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index dcaf3baa..647e237b 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -63,6 +63,7 @@ void test_whitelist_end_to_end(const size_t n_keys) { CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); /* Serialization round trip */ CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &slen, &sig) == 1); + CHECK(slen == 33 + 32 * n_keys); CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen) == 1); /* (Check various bad-length conditions) */ CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 32) == 0); @@ -87,23 +88,53 @@ void test_whitelist_end_to_end(const size_t n_keys) { } void test_whitelist_bad_parse(void) { - const unsigned char serialized[] = { - /* Hash */ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - /* Length in excess of maximum */ - 0x00, 0x00, 0x01, 0x00 - /* No room for s-values; parse should be rejected before reading past length */ - }; secp256k1_whitelist_signature sig; - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized, sizeof(serialized)) == 0); + const unsigned char serialized0[] = { 1+32*(0+1) }; + const unsigned char serialized1[] = { + 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 + }; + const unsigned char serialized2[] = { + 0x01, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + + /* Empty input */ + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized0, 0) == 0); + /* Misses one byte of e0 */ + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized1, sizeof(serialized1)) == 0); + /* Enough bytes for e0, but there is no s value */ + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized2, sizeof(serialized2)) == 0); +} + +void test_whitelist_bad_serialize(void) { + unsigned char serialized[] = { + 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + size_t serialized_len; + secp256k1_whitelist_signature sig; + + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized, sizeof(serialized)) == 1); + serialized_len = sizeof(serialized) - 1; + /* Output buffer is one byte too short */ + CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &serialized_len, &sig) == 0); } void run_whitelist_tests(void) { int i; + test_whitelist_bad_parse(); + test_whitelist_bad_serialize(); for (i = 0; i < count; i++) { test_whitelist_end_to_end(1); test_whitelist_end_to_end(10); From 04f4c091114066c81f178633d1996bd7c7eacfd5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 10 Jul 2017 18:51:16 +0200 Subject: [PATCH 018/381] Add n_keys argument to whitelist_verify --- include/secp256k1_whitelist.h | 3 ++- src/modules/whitelist/main_impl.h | 4 ++-- src/modules/whitelist/tests_impl.h | 14 ++++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index e1e17022..c536c11a 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -141,8 +141,9 @@ SECP256K1_API int secp256k1_whitelist_verify( const secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, + const size_t n_keys, const secp256k1_pubkey *sub_pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6); #ifdef __cplusplus } diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 8ac93ded..0b2d6c9c 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -100,7 +100,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s return ret; } -int secp256k1_whitelist_verify(const secp256k1_context* ctx, const secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey) { +int secp256k1_whitelist_verify(const secp256k1_context* ctx, const secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey) { secp256k1_scalar s[MAX_KEYS]; secp256k1_gej pubs[MAX_KEYS]; unsigned char msg32[32]; @@ -113,7 +113,7 @@ int secp256k1_whitelist_verify(const secp256k1_context* ctx, const secp256k1_whi ARG_CHECK(offline_pubkeys != NULL); ARG_CHECK(sub_pubkey != NULL); - if (sig->n_keys > MAX_KEYS) { + if (sig->n_keys > MAX_KEYS || sig->n_keys != n_keys) { return 0; } for (i = 0; i < sig->n_keys; i++) { diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 647e237b..7cf1fb09 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -58,9 +58,9 @@ void test_whitelist_end_to_end(const size_t n_keys) { secp256k1_whitelist_signature sig1; CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey, online_seckey[i], summed_seckey[i], i, NULL, NULL)); - CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, &sub_pubkey) == 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey) == 1); /* Check that exchanging keys causes a failure */ - CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1); /* Serialization round trip */ CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &slen, &sig) == 1); CHECK(slen == 33 + 32 * n_keys); @@ -70,11 +70,17 @@ void test_whitelist_end_to_end(const size_t n_keys) { CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 1) == 0); CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen - 1) == 0); CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, 0) == 0); - CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, &sub_pubkey) == 1); - CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, &sub_pubkey) != 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey) == 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1); + /* Test n_keys */ CHECK(secp256k1_whitelist_signature_n_keys(&sig) == n_keys); CHECK(secp256k1_whitelist_signature_n_keys(&sig1) == n_keys); + + /* Test bad number of keys in signature */ + sig.n_keys = n_keys + 1; + CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1); + sig.n_keys = n_keys; } for (i = 0; i < n_keys; i++) { From 3997128ad91ee98c291074f76f9a0b7d3181fdcb Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 10 Jul 2017 18:56:00 +0200 Subject: [PATCH 019/381] Fix pedersen_blind_generator_blind_sum return value documentation --- include/secp256k1_rangeproof.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index c71f432e..866fdca5 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -61,8 +61,10 @@ SECP256K1_API int secp256k1_pedersen_commitment_serialize( void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); /** Generate a pedersen commitment. - * Returns 1: commitment successfully created. - * 0: error + * Returns 1: Commitment successfully created. + * 0: Error. The blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127) or results in the + * point at infinity. Retry with a different factor. * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) * blind: pointer to a 32-byte blinding factor (cannot be NULL) * value: unsigned 64-bit integer value to commit to. @@ -80,8 +82,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); /** Computes the sum of multiple positive and negative blinding factors. - * Returns 1: sum successfully computed. - * 0: error + * Returns 1: Sum successfully computed. + * 0: Error. A blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127). Retry with + * different factors. * In: ctx: pointer to a context object (cannot be NULL) * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) * n: number of factors pointed to by blinds. @@ -133,7 +137,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( * The function then subtracts the sum of all (vr + r') from the last element * of the `blinding_factor` array, setting the total sum to zero. * - * Returns 1 always. + * Returns 1: Blinding factor successfully computed. + * 0: Error. A blinding_factor or generator_blind are larger than the group + * order (probability for random 32 byte number < 2^-127). Retry with + * different values. * * In: ctx: pointer to a context object * value: array of asset values, `v` in the above paragraph. From 253f13131083bec2e5db534e9b89a9451cd8b535 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 15 Aug 2017 22:39:26 -0400 Subject: [PATCH 020/381] Fix generator makefile Include test_impl.h --- src/modules/generator/Makefile.am.include | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include index bc3c514f..69933e99 100644 --- a/src/modules/generator/Makefile.am.include +++ b/src/modules/generator/Makefile.am.include @@ -1,5 +1,6 @@ include_HEADERS += include/secp256k1_generator.h noinst_HEADERS += src/modules/generator/main_impl.h +noinst_HEADERS += src/modules/generator/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_generator bench_generator_SOURCES = src/bench_generator.c From 126493ef014c9e6d26521180939b733660241fef Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 30 Aug 2017 17:59:26 +0000 Subject: [PATCH 021/381] generator: remove unnecessary ARG_CHECK from generate() --- src/modules/generator/main_impl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 94cdc448..46032f44 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -191,7 +191,6 @@ int secp256k1_generator_generate(const secp256k1_context* ctx, secp256k1_generat VERIFY_CHECK(ctx != NULL); ARG_CHECK(gen != NULL); ARG_CHECK(key32 != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); return secp256k1_generator_generate_internal(ctx, gen, key32, NULL); } From 4320490e88e0a2178d5c7f966103436a724360d1 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 30 Aug 2017 18:08:40 +0000 Subject: [PATCH 022/381] generator: add API tests --- include/secp256k1_generator.h | 2 +- src/modules/generator/tests_impl.h | 60 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index 7743b06e..57f6ca07 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -73,7 +73,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( * * Returns: 0 in the highly unlikely case the seed is not acceptable or when * blind is out of range. 1 otherwise. - * Args: ctx: a secp256k1 context object + * Args: ctx: a secp256k1 context object, initialized for signing * Out: gen: a generator object * In: seed32: a 32-byte seed * blind32: a 32-byte secret value to blind the generator with. diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index eee51fac..8b1a5acb 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -17,6 +17,65 @@ #include "include/secp256k1_generator.h" +void test_generator_api(void) { + unsigned char key[32]; + unsigned char blind[32]; + unsigned char sergen[33]; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_generator gen; + int32_t ecount = 0; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_rand256(key); + secp256k1_rand256(blind); + + CHECK(secp256k1_generator_generate(none, &gen, key) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_generator_generate(none, NULL, key) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_generator_generate(none, &gen, NULL) == 0); + CHECK(ecount == 2); + + CHECK(secp256k1_generator_generate_blinded(sign, &gen, key, blind) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, key, blind) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_generator_generate_blinded(none, &gen, key, blind) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_generator_generate_blinded(vrfy, NULL, key, blind) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, NULL, blind) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, key, NULL) == 0); + CHECK(ecount == 7); + + CHECK(secp256k1_generator_serialize(none, sergen, &gen) == 1); + CHECK(ecount == 7); + CHECK(secp256k1_generator_serialize(none, NULL, &gen) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_generator_serialize(none, sergen, NULL) == 0); + CHECK(ecount == 9); + + CHECK(secp256k1_generator_serialize(none, sergen, &gen) == 1); + CHECK(secp256k1_generator_parse(none, &gen, sergen) == 1); + CHECK(ecount == 9); + CHECK(secp256k1_generator_parse(none, NULL, sergen) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_generator_parse(none, &gen, NULL) == 0); + CHECK(ecount == 11); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + void test_shallue_van_de_woestijne(void) { /* Matches with the output of the shallue_van_de_woestijne.sage SAGE program */ static const secp256k1_ge_storage results[32] = { @@ -133,6 +192,7 @@ void test_generator_generate(void) { void run_generator_tests(void) { test_shallue_van_de_woestijne(); + test_generator_api(); test_generator_generate(); } From edc7cb6cdd986e18e5756ec94ce8f7dfc8e420c8 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 13 Feb 2018 16:28:30 -0500 Subject: [PATCH 023/381] add whitelist_impl.h to include for dist --- src/modules/whitelist/Makefile.am.include | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include index e926ffce..8d0bea63 100644 --- a/src/modules/whitelist/Makefile.am.include +++ b/src/modules/whitelist/Makefile.am.include @@ -1,3 +1,4 @@ include_HEADERS += include/secp256k1_whitelist.h +noinst_HEADERS += src/modules/whitelist/whitelist_impl.h noinst_HEADERS += src/modules/whitelist/main_impl.h noinst_HEADERS += src/modules/whitelist/tests_impl.h From fc3dc94049fafa169a306df98380bec9815db2f5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 16 Mar 2018 13:55:55 +0000 Subject: [PATCH 024/381] Add whitelisting benchmark --- src/bench_whitelist.c | 108 ++++++++++++++++++++++ src/modules/whitelist/Makefile.am.include | 6 ++ 2 files changed, 114 insertions(+) create mode 100644 src/bench_whitelist.c diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c new file mode 100644 index 00000000..074f2ce8 --- /dev/null +++ b/src/bench_whitelist.c @@ -0,0 +1,108 @@ +/********************************************************************** + * Copyright (c) 2017 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.h" + +#include "include/secp256k1_whitelist.h" +#include "util.h" +#include "bench.h" +#include "hash_impl.h" +#include "num_impl.h" +#include "scalar_impl.h" +#include "testrand_impl.h" + +#define MAX_N_KEYS 30 + +typedef struct { + secp256k1_context* ctx; + unsigned char online_seckey[MAX_N_KEYS][32]; + unsigned char summed_seckey[MAX_N_KEYS][32]; + secp256k1_pubkey online_pubkeys[MAX_N_KEYS]; + secp256k1_pubkey offline_pubkeys[MAX_N_KEYS]; + unsigned char csub[32]; + secp256k1_pubkey sub_pubkey; + secp256k1_whitelist_signature sig; + size_t n_keys; +} bench_data; + +static void bench_whitelist(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + for (i = 0; i < iters; i++) { + CHECK(secp256k1_whitelist_verify(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey) == 1); + } +} + +static void bench_whitelist_setup(void* arg) { + bench_data* data = (bench_data*)arg; + int i = 0; + CHECK(secp256k1_whitelist_sign(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey, data->online_seckey[i], data->summed_seckey[i], i, NULL, NULL)); +} + +static void run_test(bench_data* data, int iters) { + char str[32]; + sprintf(str, "whitelist_%i", (int)data->n_keys); + run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, iters); +} + +void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +int main(void) { + bench_data data; + size_t i; + size_t n_keys = 30; + secp256k1_scalar ssub; + int iters = get_iters(5); + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + /* Start with subkey */ + random_scalar_order(&ssub); + secp256k1_scalar_get_b32(data.csub, &ssub); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.csub) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.sub_pubkey, data.csub) == 1); + /* Then offline and online whitelist keys */ + for (i = 0; i < n_keys; i++) { + secp256k1_scalar son, soff; + + /* Create two keys */ + random_scalar_order(&son); + secp256k1_scalar_get_b32(data.online_seckey[i], &son); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.online_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.online_pubkeys[i], data.online_seckey[i]) == 1); + + random_scalar_order(&soff); + secp256k1_scalar_get_b32(data.summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.offline_pubkeys[i], data.summed_seckey[i]) == 1); + + /* Make summed_seckey correspond to the sum of offline_pubkey and sub_pubkey */ + secp256k1_scalar_add(&soff, &soff, &ssub); + secp256k1_scalar_get_b32(data.summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); + } + + /* Run test */ + for (i = 1; i <= n_keys; ++i) { + data.n_keys = i; + run_test(&data, iters); + } + + secp256k1_context_destroy(data.ctx); + return(0); +} diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include index 8d0bea63..0dc5a64d 100644 --- a/src/modules/whitelist/Makefile.am.include +++ b/src/modules/whitelist/Makefile.am.include @@ -2,3 +2,9 @@ include_HEADERS += include/secp256k1_whitelist.h noinst_HEADERS += src/modules/whitelist/whitelist_impl.h noinst_HEADERS += src/modules/whitelist/main_impl.h noinst_HEADERS += src/modules/whitelist/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_whitelist +bench_whitelist_SOURCES = src/bench_whitelist.c +bench_whitelist_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_generator_LDFLAGS = -static +endif From c87618157ef41d20e4bb363123f6612b763798cc Mon Sep 17 00:00:00 2001 From: datavetaren Date: Wed, 16 May 2018 05:02:21 +0200 Subject: [PATCH 025/381] Minor bugfix. Wrong length due to NUL character. --- src/modules/generator/main_impl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 46032f44..8646cf9c 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -142,8 +142,8 @@ static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { } static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) { - static const unsigned char prefix1[16] = "1st generation: "; - static const unsigned char prefix2[16] = "2nd generation: "; + static const unsigned char prefix1[17] = "1st generation: "; + static const unsigned char prefix2[17] = "2nd generation: "; secp256k1_fe t = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 4); secp256k1_ge add; secp256k1_gej accum; From 949e994cb339d7330d6f9baf9c165104bb1164d2 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 May 2018 14:56:14 +0200 Subject: [PATCH 026/381] Reject surjection proofs with trailing garbage --- src/modules/surjection/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index f57ddba1..c67d4c0d 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -56,7 +56,7 @@ int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surj } signature_len = 32 * (1 + secp256k1_count_bits_set(&input[2], (n_inputs + 7) / 8)); - if (inputlen < 2 + (n_inputs + 7) / 8 + signature_len) { + if (inputlen != 2 + (n_inputs + 7) / 8 + signature_len) { return 0; } proof->n_inputs = n_inputs; From 16aaa4a02ce80f203e216ccc376c2163556aabeb Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 May 2018 15:59:01 +0200 Subject: [PATCH 027/381] Test for rejection of trailing bytes in surjection proofs --- src/modules/surjection/tests_impl.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 08742e14..a0856e22 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -331,6 +331,7 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { unsigned char seed[32]; secp256k1_surjectionproof proof; unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; + unsigned char serialized_proof_trailing[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX + 1]; size_t serialized_len = SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX; secp256k1_fixed_asset_tag fixed_input_tags[1000]; secp256k1_generator ephemeral_input_tags[1000]; @@ -376,6 +377,12 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { CHECK(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof)); CHECK(serialized_len == secp256k1_surjectionproof_serialized_size(ctx, &proof)); CHECK(serialized_len == SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used)); + + /* trailing garbage */ + memcpy(&serialized_proof_trailing, &serialized_proof, serialized_len); + serialized_proof_trailing[serialized_len] = seed[0]; + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len + 1) == 0); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); CHECK(result == 1); From 47be098bac129dd039c00af53728ecc5621a0540 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 24 May 2018 13:23:08 +0200 Subject: [PATCH 028/381] Test for rejection of trailing bytes in range proofs --- src/modules/rangeproof/tests_impl.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index f604aa60..429ed5b5 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -385,7 +385,7 @@ static void test_rangeproof(void) { const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; secp256k1_pedersen_commitment commit; secp256k1_pedersen_commitment commit2; - unsigned char proof[5134]; + unsigned char proof[5134 + 1]; /* One additional byte to test if trailing bytes are rejected */ unsigned char blind[32]; unsigned char blindout[32]; unsigned char message[4096]; @@ -485,6 +485,9 @@ static void test_rangeproof(void) { len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); + /* Test if trailing bytes are rejected. */ + proof[len] = v; + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len + 1, NULL, 0, secp256k1_generator_h)); for (i = 0; i < len*8; i++) { proof[i >> 3] ^= 1 << (i & 7); CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); From dbc49df80c04e60e5454985974fb9c1c7d3ca60b Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 20 Jun 2018 11:43:18 -0400 Subject: [PATCH 029/381] fix spelling in documentation --- include/secp256k1_generator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index 57f6ca07..14a68754 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -58,9 +58,9 @@ SECP256K1_API int secp256k1_generator_serialize( * Out: gen: a generator object * In: seed32: a 32-byte seed * - * If succesful, a valid generator will be placed in gen. The produced + * If successful a valid generator will be placed in gen. The produced * generators are distributed uniformly over the curve, and will not have a - * known dicrete logarithm with respect to any other generator produced, + * known discrete logarithm with respect to any other generator produced, * or to the base generator G. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( From 0c5cb7cd087082d5e64e7863b3d227704c08e321 Mon Sep 17 00:00:00 2001 From: "Frank V. Castellucci" Date: Wed, 25 Jul 2018 13:30:11 -0400 Subject: [PATCH 030/381] Expose generator in shared library Was failing linking to `*.so` library --- include/secp256k1_rangeproof.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 866fdca5..bdd2bcc1 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -28,7 +28,7 @@ typedef struct { /** * Static constant generator 'h' maintained for historical reasons. */ -extern const secp256k1_generator *secp256k1_generator_h; +SECP256K1_API extern const secp256k1_generator *secp256k1_generator_h; /** Parse a 33-byte commitment into a commitment object. * From c33e59724520aa1941416655eded6afea046ee35 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 17:58:39 +0000 Subject: [PATCH 031/381] rangeproof: add fixed vector test case --- src/modules/rangeproof/tests_impl.h | 66 +++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 429ed5b5..ef7cf222 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -607,9 +607,75 @@ void test_multiple_generators(void) { } } +void test_rangeproof_fixed_vectors(void) { + const unsigned char vector_1[] = { + 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x02, 0x2a, 0x5c, 0x42, 0x0e, 0x1d, + 0x51, 0xe1, 0xb7, 0xf3, 0x69, 0x04, 0xb5, 0xbb, 0x9b, 0x41, 0x66, 0x14, 0xf3, 0x64, 0x42, 0x26, + 0xe3, 0xa7, 0x6a, 0x06, 0xbb, 0xa8, 0x5a, 0x49, 0x6f, 0x19, 0x76, 0xfb, 0xe5, 0x75, 0x77, 0x88, + 0xab, 0xa9, 0x66, 0x44, 0x80, 0xea, 0x29, 0x95, 0x7f, 0xdf, 0x72, 0x4a, 0xaf, 0x02, 0xbe, 0xdd, + 0x5d, 0x15, 0xd8, 0xae, 0xff, 0x74, 0xc9, 0x8c, 0x1a, 0x67, 0x0e, 0xb2, 0x57, 0x22, 0x99, 0xc3, + 0x21, 0x46, 0x6f, 0x15, 0x58, 0x0e, 0xdb, 0xe6, 0x6e, 0xc4, 0x0d, 0xfe, 0x6f, 0x04, 0x6b, 0x0d, + 0x18, 0x3d, 0x78, 0x40, 0x98, 0x56, 0x4e, 0xe4, 0x4a, 0x74, 0x90, 0xa7, 0xac, 0x9c, 0x16, 0xe0, + 0x3e, 0x81, 0xaf, 0x0f, 0xe3, 0x4f, 0x34, 0x99, 0x52, 0xf7, 0xa7, 0xf6, 0xd3, 0x83, 0xa0, 0x17, + 0x4b, 0x2d, 0xa7, 0xd4, 0xfd, 0xf7, 0x84, 0x45, 0xc4, 0x11, 0x71, 0x3d, 0x4a, 0x22, 0x34, 0x09, + 0x9c, 0xa7, 0xe5, 0xc8, 0xba, 0x04, 0xbf, 0xfd, 0x25, 0x11, 0x7d, 0xa4, 0x43, 0x45, 0xc7, 0x62, + 0x9e, 0x7b, 0x80, 0xf6, 0x09, 0xbb, 0x1b, 0x2e, 0xf3, 0xcd, 0x23, 0xe0, 0xed, 0x81, 0x43, 0x42, + 0xbe, 0xc4, 0x9f, 0x58, 0x8a, 0x0d, 0x66, 0x79, 0x09, 0x70, 0x11, 0x68, 0x3d, 0x87, 0x38, 0x1c, + 0x3c, 0x85, 0x52, 0x5b, 0x62, 0xf7, 0x3e, 0x7e, 0x87, 0xa2, 0x99, 0x24, 0xd0, 0x7d, 0x18, 0x63, + 0x56, 0x48, 0xa4, 0x3a, 0xfe, 0x65, 0xfa, 0xa4, 0xd0, 0x67, 0xaa, 0x98, 0x65, 0x4d, 0xe4, 0x22, + 0x75, 0x45, 0x52, 0xe8, 0x41, 0xc7, 0xed, 0x38, 0xeb, 0xf5, 0x02, 0x90, 0xc9, 0x45, 0xa3, 0xb0, + 0x4d, 0x03, 0xd7, 0xab, 0x43, 0xe4, 0x21, 0xfc, 0x83, 0xd6, 0x12, 0x1d, 0x76, 0xb1, 0x3c, 0x67, + 0x63, 0x1f, 0x52, 0x9d, 0xc3, 0x23, 0x5c, 0x4e, 0xa6, 0x8d, 0x01, 0x4a, 0xba, 0x9a, 0xf4, 0x16, + 0x5b, 0x67, 0xc8, 0xe1, 0xd2, 0x42, 0x6d, 0xdf, 0xcd, 0x08, 0x6a, 0x73, 0x41, 0x6a, 0xc2, 0x84, + 0xc6, 0x31, 0xbe, 0x57, 0xcb, 0x0e, 0xde, 0xbf, 0x71, 0xd5, 0x8a, 0xf7, 0x24, 0xb2, 0xa7, 0x89, + 0x96, 0x62, 0x4f, 0xd9, 0xf7, 0xc3, 0xde, 0x4c, 0xab, 0x13, 0x72, 0xb4, 0xb3, 0x35, 0x04, 0x82, + 0xa8, 0x75, 0x1d, 0xde, 0x46, 0xa8, 0x0d, 0xb8, 0x23, 0x44, 0x00, 0x44, 0xfa, 0x53, 0x6c, 0x2d, + 0xce, 0xd3, 0xa6, 0x80, 0xa1, 0x20, 0xca, 0xd1, 0x63, 0xbb, 0xbe, 0x39, 0x5f, 0x9d, 0x27, 0x69, + 0xb3, 0x33, 0x1f, 0xdb, 0xda, 0x67, 0x05, 0x37, 0xbe, 0x65, 0xe9, 0x7e, 0xa9, 0xc3, 0xff, 0x37, + 0x8a, 0xb4, 0x2d, 0xfe, 0xf2, 0x16, 0x85, 0xc7, 0x0f, 0xd9, 0xbe, 0x14, 0xd1, 0x80, 0x14, 0x9f, + 0x58, 0x56, 0x98, 0x41, 0xf6, 0x26, 0xf7, 0xa2, 0x71, 0x66, 0xb4, 0x7a, 0x9c, 0x12, 0x73, 0xd3, + 0xdf, 0x77, 0x2b, 0x49, 0xe5, 0xca, 0x50, 0x57, 0x44, 0x6e, 0x3f, 0x58, 0x56, 0xbc, 0x21, 0x70, + 0x4f, 0xc6, 0xaa, 0x12, 0xff, 0x7c, 0xa7, 0x3d, 0xed, 0x46, 0xc1, 0x40, 0xe6, 0x58, 0x09, 0x2a, + 0xda, 0xb3, 0x76, 0xab, 0x44, 0xb5, 0x4e, 0xb3, 0x12, 0xe0, 0x26, 0x8a, 0x52, 0xac, 0x49, 0x1d, + 0xe7, 0x06, 0x53, 0x3a, 0x01, 0x35, 0x21, 0x2e, 0x86, 0x48, 0xc5, 0x75, 0xc1, 0xa2, 0x7d, 0x22, + 0x53, 0xf6, 0x3f, 0x41, 0xc5, 0xb3, 0x08, 0x7d, 0xa3, 0x67, 0xc0, 0xbb, 0xb6, 0x8d, 0xf0, 0xd3, + 0x01, 0x72, 0xd3, 0x63, 0x82, 0x01, 0x1a, 0xe7, 0x1d, 0x22, 0xfa, 0x95, 0x33, 0xf6, 0xf2, 0xde, + 0xa2, 0x53, 0x86, 0x55, 0x5a, 0xb4, 0x2e, 0x75, 0x75, 0xc6, 0xd5, 0x93, 0x9c, 0x57, 0xa9, 0x1f, + 0xb9, 0x3e, 0xe8, 0x1c, 0xbf, 0xac, 0x1c, 0x54, 0x6f, 0xf5, 0xab, 0x41, 0xee, 0xb3, 0x0e, 0xd0, + 0x76, 0xc4, 0x1a, 0x45, 0xcd, 0xf1, 0xd6, 0xcc, 0xb0, 0x83, 0x70, 0x73, 0xbc, 0x88, 0x74, 0xa0, + 0x5b, 0xe7, 0x98, 0x10, 0x36, 0xbf, 0xec, 0x23, 0x1c, 0xc2, 0xb5, 0xba, 0x4b, 0x9d, 0x7f, 0x8c, + 0x8a, 0xe2, 0xda, 0x18, 0xdd, 0xab, 0x27, 0x8a, 0x15, 0xeb, 0xb0, 0xd4, 0x3a, 0x8b, 0x77, 0x00, + 0xc7, 0xbb, 0xcc, 0xfa, 0xba, 0xa4, 0x6a, 0x17, 0x5c, 0xf8, 0x51, 0x5d, 0x8d, 0x16, 0xcd, 0xa7, + 0x0e, 0x71, 0x97, 0x98, 0x78, 0x5a, 0x41, 0xb3, 0xf0, 0x1f, 0x87, 0x2d, 0x65, 0xcd, 0x29, 0x49, + 0xd2, 0x87, 0x2c, 0x91, 0xa9, 0x5f, 0xcc, 0xa9, 0xd8, 0xbb, 0x53, 0x18, 0xe7, 0xd6, 0xec, 0x65, + 0xa6, 0x45, 0xf6, 0xce, 0xcf, 0x48, 0xf6, 0x1e, 0x3d, 0xd2, 0xcf, 0xcb, 0x3a, 0xcd, 0xbb, 0x92, + 0x29, 0x24, 0x16, 0x7f, 0x8a, 0xa8, 0x5c, 0x0c, 0x45, 0x71, 0x33 + }; + const unsigned char commit_1[] = { + 0x08, + 0xf5, 0x1e, 0x0d, 0xc5, 0x86, 0x78, 0x51, 0xa9, 0x00, 0x00, 0xef, 0x4d, 0xe2, 0x94, 0x60, 0x89, + 0x83, 0x04, 0xb4, 0x0e, 0x90, 0x10, 0x05, 0x1c, 0x7f, 0xd7, 0x33, 0x92, 0x1f, 0xe7, 0x74, 0x59 + }; + size_t min_value_1; + size_t max_value_1; + secp256k1_pedersen_commitment pc; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_1)); + + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_value_1, &max_value_1, + &pc, + vector_1, sizeof(vector_1), + NULL, 0, + secp256k1_generator_h + )); +} + void run_rangeproof_tests(void) { int i; test_api(); + test_rangeproof_fixed_vectors(); for (i = 0; i < 10*count; i++) { test_pedersen(); } From c50b218698886df74bf2df4912926503b986f20d Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 16:23:08 +0000 Subject: [PATCH 032/381] rangeproof: check that points deserialize correctly when verifying rangeproof --- src/modules/rangeproof/rangeproof_impl.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 8d4dc654..8056f0a7 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -609,8 +609,10 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm } for(i = 0; i < rings - 1; i++) { secp256k1_fe fe; - secp256k1_fe_set_b32(&fe, &proof[offset]); - secp256k1_ge_set_xquad(&c, &fe); + if (!secp256k1_fe_set_b32(&fe, &proof[offset]) || + !secp256k1_ge_set_xquad(&c, &fe)) { + return 0; + } if (signs[i]) { secp256k1_ge_neg(&c, &c); } From fca4c3b62f386ece6a09de6373b1750e91f04ca9 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 16:23:35 +0000 Subject: [PATCH 033/381] generator: verify correctness of point when parsing --- include/secp256k1_generator.h | 11 +++----- src/modules/generator/main_impl.h | 45 +++++++++++++++++++++--------- src/modules/rangeproof/main_impl.h | 9 +++--- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index 14a68754..c2743a6e 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -13,15 +13,12 @@ extern "C" { * * The exact representation of data inside is implementation defined and not * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 33 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage or transmission, use - * the secp256k1_generator_serialize_*. - * - * Furthermore, it is guaranteed to identical points will have identical - * representation, so they can be memcmp'ed. + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_generator_serialize and secp256k1_generator_parse. */ typedef struct { - unsigned char data[33]; + unsigned char data[64]; } secp256k1_generator; /** Parse a 33-byte generator byte sequence into a generator object. diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 8646cf9c..12447591 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -15,36 +15,55 @@ #include "scalar.h" static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { - secp256k1_fe fe; - secp256k1_fe_set_b32(&fe, &gen->data[1]); - secp256k1_ge_set_xquad(ge, &fe); - if (gen->data[0] & 1) { - secp256k1_ge_neg(ge, ge); - } + int succeed; + succeed = secp256k1_fe_set_b32(&ge->x, &gen->data[0]); + VERIFY_CHECK(succeed != 0); + succeed = secp256k1_fe_set_b32(&ge->y, &gen->data[32]); + VERIFY_CHECK(succeed != 0); + ge->infinity = 0; + (void) succeed; } -static void secp256k1_generator_save(secp256k1_generator* commit, secp256k1_ge* ge) { - secp256k1_fe_normalize(&ge->x); - secp256k1_fe_get_b32(&commit->data[1], &ge->x); - commit->data[0] = 11 ^ secp256k1_fe_is_quad_var(&ge->y); +static void secp256k1_generator_save(secp256k1_generator *gen, secp256k1_ge* ge) { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(&gen->data[0], &ge->x); + secp256k1_fe_get_b32(&gen->data[32], &ge->y); } int secp256k1_generator_parse(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(gen != NULL); ARG_CHECK(input != NULL); - if ((input[0] & 0xFE) != 10) { + + if ((input[0] & 0xFE) != 10 || + !secp256k1_fe_set_b32(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { return 0; } - memcpy(gen->data, input, sizeof(gen->data)); + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_generator_save(gen, &ge); return 1; } int secp256k1_generator_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_generator* gen) { + secp256k1_ge ge; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(output != NULL); ARG_CHECK(gen != NULL); - memcpy(output, gen->data, sizeof(gen->data)); + + secp256k1_generator_load(&ge, gen); + + output[0] = 11 ^ secp256k1_fe_is_quad_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); return 1; } diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index f16a0abf..021a5bf9 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -16,13 +16,14 @@ /** Alternative generator for secp256k1. * This is the sha256 of 'g' after DER encoding (without compression), * which happens to be a point on the curve. - * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16)) - * sage: '%x %x' % (11 - G2.xy()[1].is_square(), G2.xy()[0]) + * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16))) + * sage: '%x %x' % G2.xy() */ static const secp256k1_generator secp256k1_generator_h_internal = {{ - 0x11, 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, - 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0 + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, + 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, + 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 }}; const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; From edb879f57893c6d815db16d5793ad9143b4bd6b9 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 18:03:05 +0000 Subject: [PATCH 034/381] rangeproof: verify correctness of pedersen commitments when parsing --- include/secp256k1_rangeproof.h | 12 +++++------- src/modules/rangeproof/main_impl.h | 22 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index bdd2bcc1..22cc53eb 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -14,15 +14,13 @@ extern "C" { * * The exact representation of data inside is implementation defined and not * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 33 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage or transmission, use - * secp256k1_pedersen_commitment_serialize and secp256k1_pedersen_commitment_parse. - * - * Furthermore, it is guaranteed to identical signatures will have identical - * representation, so they can be memcmp'ed. + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_pedersen_commitment_serialize and + * secp256k1_pedersen_commitment_parse. */ typedef struct { - unsigned char data[33]; + unsigned char data[64]; } secp256k1_pedersen_commitment; /** diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 021a5bf9..3fe1693f 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -44,22 +44,38 @@ static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* co } int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); ARG_CHECK(input != NULL); (void) ctx; - if ((input[0] & 0xFE) != 8) { + + if ((input[0] & 0xFE) != 8 || + !secp256k1_fe_set_b32(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { return 0; } - memcpy(commit->data, input, sizeof(commit->data)); + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_pedersen_commitment_save(commit, &ge); return 1; } int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { + secp256k1_ge ge; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(output != NULL); ARG_CHECK(commit != NULL); - memcpy(output, commit->data, sizeof(commit->data)); + + secp256k1_pedersen_commitment_load(&ge, commit); + + output[0] = 11 ^ secp256k1_fe_is_quad_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); return 1; } From e06540de8c52a3402a33da5c279ecbd27b4c84e3 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Oct 2018 05:17:26 +0000 Subject: [PATCH 035/381] rangeproof: fix serialization of pedersen commintments --- src/modules/rangeproof/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 3fe1693f..d3f1dd33 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -73,7 +73,7 @@ int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsign secp256k1_pedersen_commitment_load(&ge, commit); - output[0] = 11 ^ secp256k1_fe_is_quad_var(&ge.y); + output[0] = 9 ^ secp256k1_fe_is_quad_var(&ge.y); secp256k1_fe_normalize_var(&ge.x); secp256k1_fe_get_b32(&output[1], &ge.x); return 1; From 936d62f2486a7bc92fe718ff22b6cba05965f3f4 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Oct 2018 05:15:34 +0000 Subject: [PATCH 036/381] add unit test for generator and pedersen commitment roundtripping --- src/modules/generator/tests_impl.h | 20 ++++++++++++++++++++ src/modules/rangeproof/tests_impl.h | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 8b1a5acb..20acf2e7 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -190,8 +190,28 @@ void test_generator_generate(void) { } } +void test_generator_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x0b, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_generator parse; + + CHECK(secp256k1_generator_parse(ctx, &parse, two_g)); + CHECK(secp256k1_generator_serialize(ctx, result, &parse)); + CHECK(memcmp(two_g, result, 33) == 0); + + result[0] = 0x0a; + CHECK(secp256k1_generator_parse(ctx, &parse, result)); + result[0] = 0x08; + CHECK(!secp256k1_generator_parse(ctx, &parse, result)); +} + void run_generator_tests(void) { test_shallue_van_de_woestijne(); + test_generator_fixed_vector(); test_generator_api(); test_generator_generate(); } diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index ef7cf222..e8cf1f17 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -672,10 +672,30 @@ void test_rangeproof_fixed_vectors(void) { )); } +void test_pedersen_commitment_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x09, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); + CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); + CHECK(memcmp(two_g, result, 33) == 0); + + result[0] = 0x08; + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); + result[0] = 0x0c; + CHECK(!secp256k1_pedersen_commitment_parse(ctx, &parse, result)); +} + void run_rangeproof_tests(void) { int i; test_api(); test_rangeproof_fixed_vectors(); + test_pedersen_commitment_fixed_vector(); for (i = 0; i < 10*count; i++) { test_pedersen(); } From f416e039bbf934b6b33346add0b2816f8a059ee2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 17 Apr 2018 22:34:01 +0000 Subject: [PATCH 037/381] Add comment to explain effect of max_n_iterations in surjectionproof_init --- include/secp256k1_surjectionproof.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index 57f2afb6..38f67990 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -144,7 +144,11 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( * n_input_tags: the number of entries in the fixed_input_tags array * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set * fixed_output_tag: fixed output tag - * max_n_iterations: the maximum number of iterations to do before giving up + * max_n_iterations: the maximum number of iterations to do before giving up. Because the + * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is + * limited to 256 the probability of giving up is smaller than + * (255/256)^(n_input_tags_to_use*max_n_iterations). + * * random_seed32: a random seed to be used for input selection * Out: proof: The proof whose bitvector will be initialized. In case of failure, * the state of the proof is undefined. From 4c231568fbd6396057a86816872b11399ac35103 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 19 Nov 2018 15:43:08 +0000 Subject: [PATCH 038/381] Add explanation about how BIP32 unhardened derivation can be used to simplify whitelisting --- src/modules/whitelist/whitelist.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/modules/whitelist/whitelist.md b/src/modules/whitelist/whitelist.md index 15ab998c..89d19caf 100644 --- a/src/modules/whitelist/whitelist.md +++ b/src/modules/whitelist/whitelist.md @@ -93,4 +93,13 @@ the remaining public keys are verified out-of-band when setting up the system, so there is no direct benefit to this. We do it only to reduce fragility and increase safety of unforeseen uses. - +Having to access the offline key `Q_i` to compute the secret to the sum `W + +Q_i` for every authorization is onerous. Instead, if the whitelisted keys are +created using +[BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) +unhardened derivation, the sum can be computed on an online machine. In order +to achieve that, the offline key `Q_j` is set to the negated last hardened +BIP32 derived parent key (typically, the public key corresponding to the xpub). +As a result `W + Q_i = I_L*G` where `I_L` is the public tweak used +to derive `W` and can be easily computed online using the extended public key +and the derivation path. From 0dfb356f951d82c312e0710a52ca758755da5776 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 3 Jan 2019 13:45:36 -0500 Subject: [PATCH 039/381] Enable more builds with rest of experimental flags --- .travis.yml | 4 +++- contrib/travis.sh | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a6ad6fb2..67ce4628 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,10 @@ compiler: - gcc env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no matrix: + - SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes + - FIELD=64bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes - SCALAR=32bit RECOVERY=yes - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes - SCALAR=64bit diff --git a/contrib/travis.sh b/contrib/travis.sh index 3909d16a..315ee5be 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -20,7 +20,9 @@ fi --enable-experimental="$EXPERIMENTAL" --enable-endomorphism="$ENDOMORPHISM" \ --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ - --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" "$EXTRAFLAGS" "$USE_HOST" + --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ + "$EXTRAFLAGS" "$USE_HOST" if [ -n "$BUILD" ] then From cf21c9d7153787939fc42b3b2d31acd8810e5e99 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 3 Jan 2019 19:17:05 +0000 Subject: [PATCH 040/381] rangeproof: reduce iteration count in unit tests --- src/modules/rangeproof/tests_impl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index e8cf1f17..99c86d6e 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -498,7 +498,7 @@ static void test_rangeproof(void) { CHECK(maxv >= v); } memcpy(&commit2, &commit, sizeof(commit)); - for (i = 0; i < 10 * (size_t) count; i++) { + for (i = 0; i < (size_t) 2*count; i++) { int exp; int min_bits; v = secp256k1_rands64(0, UINT64_MAX >> (secp256k1_rand32()&63)); @@ -526,13 +526,13 @@ static void test_rangeproof(void) { } CHECK(mlen <= 4096); CHECK(memcmp(blindout, blind, 32) == 0); - CHECK(vout == v); + CHECK(minv <= v); CHECK(maxv >= v); CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); memcpy(&commit2, &commit, sizeof(commit)); } - for (j = 0; j < 10; j++) { + for (j = 0; j < 5; j++) { for (i = 0; i < 96; i++) { secp256k1_rand256(&proof[i * 32]); } From 3cdc02ef8ac32dcc5254341c9c23fc3271725994 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 3 Jan 2019 14:18:39 -0500 Subject: [PATCH 041/381] use proper types for rangeproof min/max --- src/modules/rangeproof/tests_impl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 99c86d6e..c4cc666f 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -656,8 +656,8 @@ void test_rangeproof_fixed_vectors(void) { 0xf5, 0x1e, 0x0d, 0xc5, 0x86, 0x78, 0x51, 0xa9, 0x00, 0x00, 0xef, 0x4d, 0xe2, 0x94, 0x60, 0x89, 0x83, 0x04, 0xb4, 0x0e, 0x90, 0x10, 0x05, 0x1c, 0x7f, 0xd7, 0x33, 0x92, 0x1f, 0xe7, 0x74, 0x59 }; - size_t min_value_1; - size_t max_value_1; + uint64_t min_value_1; + uint64_t max_value_1; secp256k1_pedersen_commitment pc; CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_1)); From a1f16a0a535be84c44861d2f943febb1f1b607d7 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 3 Apr 2018 22:06:07 +0000 Subject: [PATCH 042/381] add chacha20 function --- src/scalar.h | 3 ++ src/scalar_4x64_impl.h | 91 ++++++++++++++++++++++++++++++++++ src/scalar_8x32_impl.h | 100 +++++++++++++++++++++++++++++++++++++ src/scalar_low_impl.h | 5 ++ src/tests.c | 110 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 309 insertions(+) diff --git a/src/scalar.h b/src/scalar.h index 566f3807..bf609a1b 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -117,4 +117,7 @@ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_ /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); +/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index c59e5f26..3275605a 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_SCALAR_REPR_IMPL_H #include "scalar.h" +#include /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) @@ -966,4 +967,94 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define BE32(p) (p) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[3] = LE32((uint64_t) x0) << 32 | LE32(x1); + r1->d[2] = LE32((uint64_t) x2) << 32 | LE32(x3); + r1->d[1] = LE32((uint64_t) x4) << 32 | LE32(x5); + r1->d[0] = LE32((uint64_t) x6) << 32 | LE32(x7); + r2->d[3] = LE32((uint64_t) x8) << 32 | LE32(x9); + r2->d[2] = LE32((uint64_t) x10) << 32 | LE32(x11); + r2->d[1] = LE32((uint64_t) x12) << 32 | LE32(x13); + r2->d[0] = LE32((uint64_t) x14) << 32 | LE32(x15); + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef BE32 +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 95a08783..ac3789e0 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) #define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) @@ -744,4 +746,102 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define BE32(p) (p) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[7] = LE32(x0); + r1->d[6] = LE32(x1); + r1->d[5] = LE32(x2); + r1->d[4] = LE32(x3); + r1->d[3] = LE32(x4); + r1->d[2] = LE32(x5); + r1->d[1] = LE32(x6); + r1->d[0] = LE32(x7); + r2->d[7] = LE32(x8); + r2->d[6] = LE32(x9); + r2->d[5] = LE32(x10); + r2->d[4] = LE32(x11); + r2->d[3] = LE32(x12); + r2->d[2] = LE32(x13); + r2->d[1] = LE32(x14); + r2->d[0] = LE32(x15); + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef BE32 +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 1ece2363..60e8f6e7 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -123,4 +123,9 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se *r = (*r & mask0) | (*a & mask1); } +SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { + *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; + *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/tests.c b/src/tests.c index fda9c1f9..79d73a85 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1135,6 +1135,114 @@ void run_scalar_set_b32_seckey_tests(void) { CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } +void scalar_chacha_tests(void) { + /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A + * Note that scalar_set_b32 and scalar_get_b32 represent integers + * underlying the scalar in big-endian format. */ + unsigned char expected1[64] = { + 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, + 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, + 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, + 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, + 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, + 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, + 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, + 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 + }; + unsigned char expected2[64] = { + 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, + 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, + 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, + 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, + 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, + 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, + 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, + 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b + }; + unsigned char seed3[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + unsigned char expected3[64] = { + 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, + 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, + 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, + 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, + 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, + 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, + 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, + 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 + }; + unsigned char seed4[32] = { + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char expected4[64] = { + 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, + 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, + 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, + 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, + 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, + 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, + 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, + 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e + }; + unsigned char seed5[32] = { + 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, + 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, + 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, + 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 + }; + unsigned char expected5[64] = { + 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, + 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, + 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, + 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, + 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, + 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, + 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, + 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 + }; + + secp256k1_scalar exp_r1, exp_r2; + secp256k1_scalar r1, r2; + unsigned char seed0[32] = { 0 }; + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); + secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); + secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); + secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); +} + void run_scalar_tests(void) { int i; for (i = 0; i < 128 * count; i++) { @@ -1144,6 +1252,8 @@ void run_scalar_tests(void) { run_scalar_set_b32_seckey_tests(); } + scalar_chacha_tests(); + { /* (-1)+1 should be zero. */ secp256k1_scalar s, o; From c59c602dd6408ee75fb5224068efc84dd0b11ed8 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 9 May 2018 15:37:35 +0000 Subject: [PATCH 043/381] Add schnorrsig module which implements BIP-schnorr [0] compatible signing, verification and batch verification. [0] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki --- .gitignore | 2 +- .travis.yml | 10 +- Makefile.am | 4 + configure.ac | 15 + contrib/travis.sh | 2 +- include/secp256k1.h | 6 + include/secp256k1_schnorrsig.h | 129 ++++ src/bench_schnorrsig.c | 129 ++++ src/modules/schnorrsig/Makefile.am.include | 8 + src/modules/schnorrsig/main_impl.h | 338 ++++++++++ src/modules/schnorrsig/tests_impl.h | 726 +++++++++++++++++++++ src/scalar_4x64_impl.h | 19 +- src/scalar_8x32_impl.h | 35 +- src/secp256k1.c | 28 + src/tests.c | 9 + 15 files changed, 1423 insertions(+), 37 deletions(-) create mode 100644 include/secp256k1_schnorrsig.h create mode 100644 src/bench_schnorrsig.c create mode 100644 src/modules/schnorrsig/Makefile.am.include create mode 100644 src/modules/schnorrsig/main_impl.h create mode 100644 src/modules/schnorrsig/tests_impl.h diff --git a/.gitignore b/.gitignore index cb4331aa..85fe89aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ bench_inv bench_ecdh bench_ecmult +bench_schnorrsig bench_sign bench_verify -bench_schnorr_verify bench_recover bench_internal tests diff --git a/.travis.yml b/.travis.yml index 67ce4628..49e6a7b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,21 +17,21 @@ compiler: - gcc env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no matrix: - - SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes - - FIELD=64bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes + - SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes + - FIELD=64bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes - SCALAR=32bit RECOVERY=yes - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes - SCALAR=64bit - FIELD=64bit RECOVERY=yes - FIELD=64bit ENDOMORPHISM=yes - - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck CTIMETEST= BENCH= - CPPFLAGS=-DDETERMINISTIC diff --git a/Makefile.am b/Makefile.am index 4f3808b7..29330833 100644 --- a/Makefile.am +++ b/Makefile.am @@ -149,6 +149,10 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif +if ENABLE_MODULE_SCHNORRSIG +include src/modules/schnorrsig/Makefile.am.include +endif + if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif diff --git a/configure.ac b/configure.ac index 1fe39ee8..219f7b6b 100644 --- a/configure.ac +++ b/configure.ac @@ -131,6 +131,11 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) +AC_ARG_ENABLE(module_schnorrsig, + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), + [enable_module_schnorrsig=$enableval], + [enable_module_schnorrsig=no]) + AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [enable_module_recovery=$enableval], @@ -521,6 +526,10 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi +if test x"$enable_module_schnorrsig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) +fi + if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi @@ -560,6 +569,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) + AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) if test x"$enable_module_generator" != x"yes"; then @@ -580,6 +590,9 @@ else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_schnorrsig" = x"yes"; then + AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -609,6 +622,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) @@ -633,6 +647,7 @@ echo " with benchmarks = $use_benchmark" echo " with coverage = $enable_coverage" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" +echo " module schnorrsig = $enable_module_schnorrsig" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/contrib/travis.sh b/contrib/travis.sh index 315ee5be..8140dc14 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -21,7 +21,7 @@ fi --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ - --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ + --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" --enable-module-schnorrsig="$SCHNORRSIG" \ "$EXTRAFLAGS" "$USE_HOST" if [ -n "$BUILD" ] diff --git a/include/secp256k1.h b/include/secp256k1.h index 2ba2dca3..54769287 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -525,6 +525,12 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; +/** An implementation of the nonce generation function as defined in BIP-schnorr. + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. + */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr; + /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h new file mode 100644 index 00000000..4c0f263d --- /dev/null +++ b/include/secp256k1_schnorrsig.h @@ -0,0 +1,129 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements a variant of Schnorr signatures compliant with + * BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki). + */ + +/** Opaque data structure that holds a parsed Schnorr signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the `secp256k1_schnorrsig_serialize` and + * `secp256k1_schnorrsig_parse` functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_schnorrsig; + +/** Serialize a Schnorr signature. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: out64: pointer to a 64-byte array to store the serialized signature + * In: sig: pointer to the signature + * + * See secp256k1_schnorrsig_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_schnorrsig_serialize( + const secp256k1_context* ctx, + unsigned char *out64, + const secp256k1_schnorrsig* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a Schnorr signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in64: pointer to the 64-byte signature to be parsed + * + * The signature is serialized in the form R||s, where R is a 32-byte public + * key (x-coordinate only; the y-coordinate is considered to be the unique + * y-coordinate satisfying the curve equation that is a quadratic residue) + * and s is a 32-byte big-endian scalar. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_schnorrsig_parse( + const secp256k1_context* ctx, + secp256k1_schnorrsig* sig, + const unsigned char *in64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Create a Schnorr signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to the returned signature (cannot be NULL) + * nonce_is_negated: a pointer to an integer indicates if signing algorithm negated the + * nonce (can be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context* ctx, + secp256k1_schnorrsig *sig, + int *nonce_is_negated, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to a public key to verify with (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( + const secp256k1_context* ctx, + const secp256k1_schnorrsig *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verifies a set of Schnorr signatures. + * + * Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0. + * + * Args: ctx: a secp256k1 context object, initialized for verification. + * scratch: scratch space used for the multiexponentiation + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays. Must be smaller than + * 2^31 and smaller than half the maximum size_t value. Must be 0 + * if above arrays are NULL. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_schnorrsig *const *sig, + const unsigned char *const *msg32, + const secp256k1_pubkey *const *pk, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c new file mode 100644 index 00000000..a22e3496 --- /dev/null +++ b/src/bench_schnorrsig.c @@ -0,0 +1,129 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_scratch_space *scratch; + int n; + const unsigned char **pk; + const secp256k1_schnorrsig **sigs; + const unsigned char **msgs; +} bench_schnorrsig_data; + +void bench_schnorrsig_sign(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i; + unsigned char sk[32] = "benchmarkexample secrettemplate"; + unsigned char msg[32] = "benchmarkexamplemessagetemplate"; + secp256k1_schnorrsig sig; + + for (i = 0; i < iters; i++) { + msg[0] = i; + msg[1] = i >> 8; + sk[0] = i; + sk[1] = i >> 8; + CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, NULL, msg, sk, NULL, NULL)); + } +} + +void bench_schnorrsig_verify(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i; + + for (i = 0; i < iters; i++) { + secp256k1_pubkey pk; + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pk, data->pk[i], 33) == 1); + CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk)); + } +} + +void bench_schnorrsig_verify_n(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i, j; + const secp256k1_pubkey **pk = (const secp256k1_pubkey **)malloc(data->n * sizeof(*pk)); + + CHECK(pk != NULL); + for (j = 0; j < iters/data->n; j++) { + for (i = 0; i < data->n; i++) { + secp256k1_pubkey *pk_nonconst = (secp256k1_pubkey *)malloc(sizeof(*pk_nonconst)); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, pk_nonconst, data->pk[i], 33) == 1); + pk[i] = pk_nonconst; + } + CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n)); + for (i = 0; i < data->n; i++) { + free((void *)pk[i]); + } + } + free(pk); +} + +int main(void) { + int i; + bench_schnorrsig_data data; + int iters = get_iters(1000); + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024); + data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.sigs = (const secp256k1_schnorrsig **)malloc(iters * sizeof(secp256k1_schnorrsig *)); + + for (i = 0; i < iters; i++) { + unsigned char sk[32]; + unsigned char *msg = (unsigned char *)malloc(32); + secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig)); + unsigned char *pk_char = (unsigned char *)malloc(33); + secp256k1_pubkey pk; + size_t pk_len = 33; + msg[0] = sk[0] = i; + msg[1] = sk[1] = i >> 8; + msg[2] = sk[2] = i >> 16; + msg[3] = sk[3] = i >> 24; + memset(&msg[4], 'm', 28); + memset(&sk[4], 's', 28); + + data.pk[i] = pk_char; + data.msgs[i] = msg; + data.sigs[i] = sig; + + CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk)); + CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, NULL, msg, sk, NULL, NULL)); + } + + run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters); + run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters); + for (i = 1; i <= iters; i *= 2) { + char name[64]; + int divisible_iters; + sprintf(name, "schnorrsig_batch_verify_%d", (int) i); + + data.n = i; + divisible_iters = iters - (iters % data.n); + run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, divisible_iters); + } + + for (i = 0; i < iters; i++) { + free((void *)data.pk[i]); + free((void *)data.msgs[i]); + free((void *)data.sigs[i]); + } + free(data.pk); + free(data.msgs); + free(data.sigs); + + secp256k1_scratch_space_destroy(data.ctx, data.scratch); + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include new file mode 100644 index 00000000..a82bafe4 --- /dev/null +++ b/src/modules/schnorrsig/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_schnorrsig.h +noinst_HEADERS += src/modules/schnorrsig/main_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_schnorrsig +bench_schnorrsig_SOURCES = src/bench_schnorrsig.c +bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h new file mode 100644 index 00000000..b0310ab9 --- /dev/null +++ b/src/modules/schnorrsig/main_impl.h @@ -0,0 +1,338 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ +#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "hash.h" + +int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out64 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out64, sig->data, 64); + return 1; +} + +int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in64 != NULL); + memcpy(sig->data, in64, 64); + return 1; +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, int *nonce_is_negated, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { + secp256k1_scalar x; + secp256k1_scalar e; + secp256k1_scalar k; + secp256k1_gej pkj; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_ge r; + secp256k1_sha256 sha; + int overflow; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_bipschnorr; + } + secp256k1_scalar_set_b32(&x, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (overflow || secp256k1_scalar_is_zero(&x)) { + memset(sig, 0, sizeof(*sig)); + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); + secp256k1_ge_set_gej(&pk, &pkj); + + if (!noncefp(buf, msg32, seckey, NULL, (void*)ndata, 0)) { + return 0; + } + secp256k1_scalar_set_b32(&k, buf, NULL); + if (secp256k1_scalar_is_zero(&k)) { + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + + if (nonce_is_negated != NULL) { + *nonce_is_negated = 0; + } + if (!secp256k1_fe_is_quad_var(&r.y)) { + secp256k1_scalar_negate(&k, &k); + if (nonce_is_negated != NULL) { + *nonce_is_negated = 1; + } + } + secp256k1_fe_normalize(&r.x); + secp256k1_fe_get_b32(&sig->data[0], &r.x); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_eckey_pubkey_serialize(&pk, buf, &buflen, 1); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(&e, buf, NULL); + secp256k1_scalar_mul(&e, &e, &x); + secp256k1_scalar_add(&e, &e, &k); + + secp256k1_scalar_get_b32(&sig->data[32], &e); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&x); + + return 1; +} + +/* Helper function for verification and batch verification. + * Computes R = sG - eP. */ +static int secp256k1_schnorrsig_real_verify(const secp256k1_context* ctx, secp256k1_gej *rj, const secp256k1_scalar *s, const secp256k1_scalar *e, const secp256k1_pubkey *pk) { + secp256k1_scalar nege; + secp256k1_ge pkp; + secp256k1_gej pkj; + + secp256k1_scalar_negate(&nege, e); + + if (!secp256k1_pubkey_load(ctx, &pkp, pk)) { + return 0; + } + secp256k1_gej_set_ge(&pkj, &pkp); + + /* rj = s*G + (-e)*pkj */ + secp256k1_ecmult(&ctx->ecmult_ctx, rj, &pkj, &nege, s); + return 1; +} + +int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_fe rx; + secp256k1_sha256 sha; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + + if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig->data[32], &overflow); + if (overflow) { + return 0; + } + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(&e, buf, NULL); + + if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pk) + || !secp256k1_gej_has_quad_y_var(&rj) /* fails if rj is infinity */ + || !secp256k1_gej_eq_x_var(&rx, &rj)) { + return 0; + } + + return 1; +} + +/* Data that is used by the batch verification ecmult callback */ +typedef struct { + const secp256k1_context *ctx; + /* Seed for the random number generator */ + unsigned char chacha_seed[32]; + /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching + * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and + * the PRNG is called at every odd indexed schnorrsig to fill the cache. */ + secp256k1_scalar randomizer_cache[2]; + /* Signature, message, public key tuples to verify */ + const secp256k1_schnorrsig *const *sig; + const unsigned char *const *msg32; + const secp256k1_pubkey *const *pk; + size_t n_sigs; +} secp256k1_schnorrsig_verify_ecmult_context; + +/* Callback function which is called by ecmult_multi in order to convert the ecmult_context + * consisting of signature, message and public key tuples into scalars and points. */ +static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data; + + if (idx % 4 == 2) { + /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4 + * consecutive tuples before we need to call the RNG for new randomizers: + * (-randomizer_cache[0], R1) + * (-randomizer_cache[0]*e1, P1) + * (-randomizer_cache[1], R2) + * (-randomizer_cache[1]*e2, P2) */ + secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4); + } + + /* R */ + if (idx % 2 == 0) { + secp256k1_fe rx; + *sc = ecmult_context->randomizer_cache[(idx / 2) % 2]; + if (!secp256k1_fe_set_b32(&rx, &ecmult_context->sig[idx / 2]->data[0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(pt, &rx)) { + return 0; + } + /* eP */ + } else { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256 sha; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2]->data[0], 32); + secp256k1_ec_pubkey_serialize(ecmult_context->ctx, buf, &buflen, ecmult_context->pk[idx / 2], SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, ecmult_context->msg32[idx / 2], 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(sc, buf, NULL); + secp256k1_scalar_mul(sc, sc, &ecmult_context->randomizer_cache[(idx / 2) % 2]); + + if (!secp256k1_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])) { + return 0; + } + } + return 1; +} + +/** Helper function for batch verification. Hashes signature verification data into the + * randomization seed and initializes ecmult_context. + * + * Returns 1 if the randomizer was successfully initialized. + * + * Args: ctx: a secp256k1 context object + * Out: ecmult_context: context for batch_ecmult_callback + * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a + * seed for the randomizer PRNG + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_init_randomizer(const secp256k1_context *ctx, secp256k1_schnorrsig_verify_ecmult_context *ecmult_context, secp256k1_sha256 *sha, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { + size_t i; + + if (n_sigs > 0) { + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + } + + for (i = 0; i < n_sigs; i++) { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256_write(sha, sig[i]->data, 64); + secp256k1_sha256_write(sha, msg32[i], 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk[i], SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(sha, buf, buflen); + } + ecmult_context->ctx = ctx; + ecmult_context->sig = sig; + ecmult_context->msg32 = msg32; + ecmult_context->pk = pk; + ecmult_context->n_sigs = n_sigs; + + return 1; +} + +/** Helper function for batch verification. Sums the s part of all signatures multiplied by their + * randomizer. + * + * Returns 1 if s is successfully summed. + * + * In/Out: s: the s part of the input sigs is added to this s argument + * In: chacha_seed: PRNG seed for computing randomizers + * sig: array of signatures, or NULL if there are no signatures + * n_sigs: number of signatures in above array (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *chacha_seed, const secp256k1_schnorrsig *const *sig, size_t n_sigs) { + secp256k1_scalar randomizer_cache[2]; + size_t i; + + secp256k1_scalar_set_int(&randomizer_cache[0], 1); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + if (i % 2 == 1) { + secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2); + } + + secp256k1_scalar_set_b32(&term, &sig[i]->data[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_mul(&term, &term, &randomizer_cache[i % 2]); + secp256k1_scalar_add(s, s, &term); + } + return 1; +} + +/* schnorrsig batch verification. + * Seeds a random number generator with the inputs and derives a random number ai for every + * signature i. Fails if y-coordinate of any R is not a quadratic residue or if + * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */ +int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_scratch *scratch, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { + secp256k1_schnorrsig_verify_ecmult_context ecmult_context; + secp256k1_sha256 sha; + secp256k1_scalar s; + secp256k1_gej rj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because + * the number of points given to ecmult_multi is 2*n_sigs. */ + ARG_CHECK(n_sigs <= SIZE_MAX / 2); + /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit + * and 64-bit platforms. */ + ARG_CHECK(n_sigs < ((uint32_t)1 << 31)); + + secp256k1_sha256_initialize(&sha); + if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) { + return 0; + } + secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed); + secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1); + + secp256k1_scalar_clear(&s); + if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) { + return 0; + } + secp256k1_scalar_negate(&s, &s); + + return secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs) + && secp256k1_gej_is_infinity(&rj); +} + +#endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h new file mode 100644 index 00000000..670b2d1a --- /dev/null +++ b/src/modules/schnorrsig/tests_impl.h @@ -0,0 +1,726 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ +#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ + +#include "secp256k1_schnorrsig.h" + +void test_schnorrsig_serialize(void) { + secp256k1_schnorrsig sig; + unsigned char in[64]; + unsigned char out[64]; + + memset(in, 0x12, 64); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, in)); + CHECK(secp256k1_schnorrsig_serialize(ctx, out, &sig)); + CHECK(memcmp(in, out, 64) == 0); +} + +void test_schnorrsig_api(secp256k1_scratch_space *scratch) { + unsigned char sk1[32]; + unsigned char sk2[32]; + unsigned char sk3[32]; + unsigned char msg[32]; + unsigned char sig64[64]; + secp256k1_pubkey pk[3]; + secp256k1_schnorrsig sig; + const secp256k1_schnorrsig *sigptr = &sig; + const unsigned char *msgptr = msg; + const secp256k1_pubkey *pkptr = &pk[0]; + int nonce_is_negated; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_rand256(sk1); + secp256k1_rand256(sk2); + secp256k1_rand256(sk3); + secp256k1_rand256(msg); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk1) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk2) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[2], sk3) == 1); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_schnorrsig_sign(none, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, NULL, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, msg, sk1, NULL, NULL) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, NULL, sk1, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_schnorrsig_serialize(none, NULL, &sig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_serialize(none, sig64, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, &sig, sig64) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, NULL, sig64) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); + CHECK(ecount == 4); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify(sign, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, NULL, &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_batch(none, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_batch(sign, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, NULL, NULL, 0) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (uint32_t)1 << 31) == 0); + CHECK(ecount == 7); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +/* Helper function for schnorrsig_bip_vectors + * Signs the message and checks that it's the same as expected_sig. */ +void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *msg, const unsigned char *expected_sig, const int expected_nonce_is_negated) { + secp256k1_schnorrsig sig; + unsigned char serialized_sig[64]; + secp256k1_pubkey pk; + int nonce_is_negated; + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &nonce_is_negated, msg, sk, NULL, NULL)); + CHECK(nonce_is_negated == expected_nonce_is_negated); + CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); + CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); +} + +/* Helper function for schnorrsig_bip_vectors + * Checks that both verify and verify_batch return the same value as expected. */ +void test_schnorrsig_bip_vectors_check_verify(secp256k1_scratch_space *scratch, const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig_serialized, int expected) { + const unsigned char *msg_arr[1]; + const secp256k1_schnorrsig *sig_arr[1]; + const secp256k1_pubkey *pk_arr[1]; + secp256k1_pubkey pk; + secp256k1_schnorrsig sig; + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, sig_serialized)); + + sig_arr[0] = &sig; + msg_arr[0] = msg32; + pk_arr[0] = &pk; + + CHECK(expected == secp256k1_schnorrsig_verify(ctx, &sig, msg32, &pk)); + CHECK(expected == secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); +} + +/* Test vectors according to BIP-schnorr + * (https://github.com/sipa/bips/blob/7f6a73e53c8bbcf2d008ea0546f76433e22094a8/bip-schnorr/test-vectors.csv). + */ +void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { + { + /* Test vector 1 */ + const unsigned char sk1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + const unsigned char pk1[33] = { + 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, + 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, + 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98 + }; + const unsigned char msg1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig1[64] = { + 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, + 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, + 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, + 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, + 0x70, 0x31, 0xA9, 0x88, 0x31, 0x85, 0x9D, 0xC3, + 0x4D, 0xFF, 0xEE, 0xDD, 0xA8, 0x68, 0x31, 0x84, + 0x2C, 0xCD, 0x00, 0x79, 0xE1, 0xF9, 0x2A, 0xF1, + 0x77, 0xF7, 0xF2, 0x2C, 0xC1, 0xDC, 0xED, 0x05 + }; + test_schnorrsig_bip_vectors_check_signing(sk1, pk1, msg1, sig1, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk1, msg1, sig1, 1); + } + { + /* Test vector 2 */ + const unsigned char sk2[32] = { + 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, + 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, + 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, + 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF + }; + const unsigned char pk2[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg2[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig2[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_signing(sk2, pk2, msg2, sig2, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk2, msg2, sig2, 1); + } + { + /* Test vector 3 */ + const unsigned char sk3[32] = { + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC7 + }; + const unsigned char pk3[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg3[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig3[64] = { + 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, + 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, + 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, + 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, + 0x00, 0x88, 0x03, 0x71, 0xD0, 0x17, 0x66, 0x93, + 0x5B, 0x92, 0xD2, 0xAB, 0x4C, 0xD5, 0xC8, 0xA2, + 0xA5, 0x83, 0x7E, 0xC5, 0x7F, 0xED, 0x76, 0x60, + 0x77, 0x3A, 0x05, 0xF0, 0xDE, 0x14, 0x23, 0x80 + }; + test_schnorrsig_bip_vectors_check_signing(sk3, pk3, msg3, sig3, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk3, msg3, sig3, 1); + } + { + /* Test vector 4 */ + const unsigned char pk4[33] = { + 0x03, 0xDE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, + 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, + 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, + 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, + 0x34 + }; + const unsigned char msg4[32] = { + 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, + 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, + 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, + 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 + }; + const unsigned char sig4[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, + 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, + 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, + 0x02, 0xA8, 0xDC, 0x32, 0xE6, 0x4E, 0x86, 0xA3, + 0x33, 0xF2, 0x0E, 0xF5, 0x6E, 0xAC, 0x9B, 0xA3, + 0x0B, 0x72, 0x46, 0xD6, 0xD2, 0x5E, 0x22, 0xAD, + 0xB8, 0xC6, 0xBE, 0x1A, 0xEB, 0x08, 0xD4, 0x9D + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk4, msg4, sig4, 1); + } + { + /* Test vector 5 */ + const unsigned char pk5[33] = { + 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 + }; + const unsigned char msg5[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig5[64] = { + 0x52, 0x81, 0x85, 0x79, 0xAC, 0xA5, 0x97, 0x67, + 0xE3, 0x29, 0x1D, 0x91, 0xB7, 0x6B, 0x63, 0x7B, + 0xEF, 0x06, 0x20, 0x83, 0x28, 0x49, 0x92, 0xF2, + 0xD9, 0x5F, 0x56, 0x4C, 0xA6, 0xCB, 0x4E, 0x35, + 0x30, 0xB1, 0xDA, 0x84, 0x9C, 0x8E, 0x83, 0x04, + 0xAD, 0xC0, 0xCF, 0xE8, 0x70, 0x66, 0x03, 0x34, + 0xB3, 0xCF, 0xC1, 0x8E, 0x82, 0x5E, 0xF1, 0xDB, + 0x34, 0xCF, 0xAE, 0x3D, 0xFC, 0x5D, 0x81, 0x87 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk5, msg5, sig5, 1); + } + { + /* Test vector 6 */ + const unsigned char pk6[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg6[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char sig6[64] = { + 0x57, 0x0D, 0xD4, 0xCA, 0x83, 0xD4, 0xE6, 0x31, + 0x7B, 0x8E, 0xE6, 0xBA, 0xE8, 0x34, 0x67, 0xA1, + 0xBF, 0x41, 0x9D, 0x07, 0x67, 0x12, 0x2D, 0xE4, + 0x09, 0x39, 0x44, 0x14, 0xB0, 0x50, 0x80, 0xDC, + 0xE9, 0xEE, 0x5F, 0x23, 0x7C, 0xBD, 0x10, 0x8E, + 0xAB, 0xAE, 0x1E, 0x37, 0x75, 0x9A, 0xE4, 0x7F, + 0x8E, 0x42, 0x03, 0xDA, 0x35, 0x32, 0xEB, 0x28, + 0xDB, 0x86, 0x0F, 0x33, 0xD6, 0x2D, 0x49, 0xBD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk6, msg6, sig6, 1); + } + { + /* Test vector 7 */ + const unsigned char pk7[33] = { + 0x03, 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, + 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, + 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, + 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, + 0x34 + }; + secp256k1_pubkey pk7_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_ec_pubkey_parse(ctx, &pk7_parsed, pk7, 33)); + } + { + /* Test vector 8 */ + const unsigned char pk8[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg8[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig8[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0xFA, 0x16, 0xAE, 0xE0, 0x66, 0x09, 0x28, 0x0A, + 0x19, 0xB6, 0x7A, 0x24, 0xE1, 0x97, 0x7E, 0x46, + 0x97, 0x71, 0x2B, 0x5F, 0xD2, 0x94, 0x39, 0x14, + 0xEC, 0xD5, 0xF7, 0x30, 0x90, 0x1B, 0x4A, 0xB7 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk8, msg8, sig8, 0); + } + { + /* Test vector 9 */ + const unsigned char pk9[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg9[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig9[64] = { + 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, + 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, + 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, + 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, + 0xD0, 0x92, 0xF9, 0xD8, 0x60, 0xF1, 0x77, 0x6A, + 0x1F, 0x74, 0x12, 0xAD, 0x8A, 0x1E, 0xB5, 0x0D, + 0xAC, 0xCC, 0x22, 0x2B, 0xC8, 0xC0, 0xE2, 0x6B, + 0x20, 0x56, 0xDF, 0x2F, 0x27, 0x3E, 0xFD, 0xEC + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk9, msg9, sig9, 0); + } + { + /* Test vector 10 */ + const unsigned char pk10[33] = { + 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, + 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, + 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98 + }; + const unsigned char msg10[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig10[64] = { + 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, + 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, + 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, + 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, + 0x8F, 0xCE, 0x56, 0x77, 0xCE, 0x7A, 0x62, 0x3C, + 0xB2, 0x00, 0x11, 0x22, 0x57, 0x97, 0xCE, 0x7A, + 0x8D, 0xE1, 0xDC, 0x6C, 0xCD, 0x4F, 0x75, 0x4A, + 0x47, 0xDA, 0x6C, 0x60, 0x0E, 0x59, 0x54, 0x3C + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk10, msg10, sig10, 0); + } + { + /* Test vector 11 */ + const unsigned char pk11[33] = { + 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg11[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig11[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk11, msg11, sig11, 0); + } + { + /* Test vector 12 */ + const unsigned char pk12[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg12[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig12[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9E, 0x9D, 0x01, 0xAF, 0x98, 0x8B, 0x5C, 0xED, + 0xCE, 0x47, 0x22, 0x1B, 0xFA, 0x9B, 0x22, 0x27, + 0x21, 0xF3, 0xFA, 0x40, 0x89, 0x15, 0x44, 0x4A, + 0x4B, 0x48, 0x90, 0x21, 0xDB, 0x55, 0x77, 0x5F + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk12, msg12, sig12, 0); + } + { + /* Test vector 13 */ + const unsigned char pk13[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg13[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig13[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xD3, 0x7D, 0xDF, 0x02, 0x54, 0x35, 0x18, 0x36, + 0xD8, 0x4B, 0x1B, 0xD6, 0xA7, 0x95, 0xFD, 0x5D, + 0x52, 0x30, 0x48, 0xF2, 0x98, 0xC4, 0x21, 0x4D, + 0x18, 0x7F, 0xE4, 0x89, 0x29, 0x47, 0xF7, 0x28 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk13, msg13, sig13, 0); + } + { + /* Test vector 14 */ + const unsigned char pk14[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg14[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x14, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig14[64] = { + 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk14, msg14, sig14, 0); + } + { + /* Test vector 15 */ + const unsigned char pk15[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg15[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig15[64] = { + 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, 0xFC, 0x2F, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk15, msg15, sig15, 0); + } + { + /* Test vector 16 */ + const unsigned char pk16[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg16[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig16[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 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 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk16, msg16, sig16, 0); + } +} + +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + + memset(nonce32, 0, 32); + return 1; +} + +void test_schnorrsig_sign(void) { + unsigned char sk[32]; + const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + secp256k1_schnorrsig sig; + + memset(sk, 23, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 1); + + /* Overflowing secret key */ + memset(sk, 0xFF, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 0); + memset(sk, 23, sizeof(sk)); + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_failing, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_0, NULL) == 0); +} + +#define N_SIGS 200 +/* Creates N_SIGS valid signatures and verifies them with verify and verify_batch. Then flips some + * bits and checks that verification now fails. */ +void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { + const unsigned char sk[32] = "shhhhhhhh! this key is a secret."; + unsigned char msg[N_SIGS][32]; + secp256k1_schnorrsig sig[N_SIGS]; + size_t i; + const secp256k1_schnorrsig *sig_arr[N_SIGS]; + const unsigned char *msg_arr[N_SIGS]; + const secp256k1_pubkey *pk_arr[N_SIGS]; + secp256k1_pubkey pk; + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk)); + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, NULL, NULL, NULL, 0)); + + for (i = 0; i < N_SIGS; i++) { + secp256k1_rand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], NULL, msg[i], sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[i], msg[i], &pk)); + sig_arr[i] = &sig[i]; + msg_arr[i] = msg[i]; + pk_arr[i] = &pk; + } + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 2)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, N_SIGS)); + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch fail */ + size_t sig_idx = secp256k1_rand_int(4); + size_t byte_idx = secp256k1_rand_int(32); + unsigned char xorbyte = secp256k1_rand_int(254)+1; + sig[sig_idx].data[byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + msg[sig_idx][byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + msg[sig_idx][byte_idx] ^= xorbyte; + + /* Check that above bitflips have been reversed correctly */ + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + } +} +#undef N_SIGS + +void run_schnorrsig_tests(void) { + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + test_schnorrsig_serialize(); + test_schnorrsig_api(scratch); + test_schnorrsig_bip_vectors(scratch); + test_schnorrsig_sign(); + test_schnorrsig_sign_verify(scratch); + + secp256k1_scratch_space_destroy(ctx, scratch); +} + +#endif diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 3275605a..733299be 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -976,9 +976,7 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se #ifdef WORDS_BIGENDIAN #define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#define BE32(p) (p) #else -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #define LE32(p) (p) #endif @@ -1037,14 +1035,14 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 x14 += 0; x15 += over_count; - r1->d[3] = LE32((uint64_t) x0) << 32 | LE32(x1); - r1->d[2] = LE32((uint64_t) x2) << 32 | LE32(x3); - r1->d[1] = LE32((uint64_t) x4) << 32 | LE32(x5); - r1->d[0] = LE32((uint64_t) x6) << 32 | LE32(x7); - r2->d[3] = LE32((uint64_t) x8) << 32 | LE32(x9); - r2->d[2] = LE32((uint64_t) x10) << 32 | LE32(x11); - r2->d[1] = LE32((uint64_t) x12) << 32 | LE32(x13); - r2->d[0] = LE32((uint64_t) x14) << 32 | LE32(x15); + r1->d[3] = (((uint64_t) x0) << 32) | x1; + r1->d[2] = (((uint64_t) x2) << 32) | x3; + r1->d[1] = (((uint64_t) x4) << 32) | x5; + r1->d[0] = (((uint64_t) x6) << 32) | x7; + r2->d[3] = (((uint64_t) x8) << 32) | x9; + r2->d[2] = (((uint64_t) x10) << 32) | x11; + r2->d[1] = (((uint64_t) x12) << 32) | x13; + r2->d[0] = (((uint64_t) x14) << 32) | x15; over1 = secp256k1_scalar_check_overflow(r1); over2 = secp256k1_scalar_check_overflow(r2); @@ -1054,7 +1052,6 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 #undef ROTL32 #undef QUARTERROUND -#undef BE32 #undef LE32 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index ac3789e0..9a905645 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -755,9 +755,7 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se #ifdef WORDS_BIGENDIAN #define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#define BE32(p) (p) #else -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #define LE32(p) (p) #endif @@ -816,22 +814,22 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 x14 += 0; x15 += over_count; - r1->d[7] = LE32(x0); - r1->d[6] = LE32(x1); - r1->d[5] = LE32(x2); - r1->d[4] = LE32(x3); - r1->d[3] = LE32(x4); - r1->d[2] = LE32(x5); - r1->d[1] = LE32(x6); - r1->d[0] = LE32(x7); - r2->d[7] = LE32(x8); - r2->d[6] = LE32(x9); - r2->d[5] = LE32(x10); - r2->d[4] = LE32(x11); - r2->d[3] = LE32(x12); - r2->d[2] = LE32(x13); - r2->d[1] = LE32(x14); - r2->d[0] = LE32(x15); + r1->d[7] = x0; + r1->d[6] = x1; + r1->d[5] = x2; + r1->d[4] = x3; + r1->d[3] = x4; + r1->d[2] = x5; + r1->d[1] = x6; + r1->d[0] = x7; + r2->d[7] = x8; + r2->d[6] = x9; + r2->d[5] = x10; + r2->d[4] = x11; + r2->d[3] = x12; + r2->d[2] = x13; + r2->d[1] = x14; + r2->d[0] = x15; over1 = secp256k1_scalar_check_overflow(r1); over2 = secp256k1_scalar_check_overflow(r2); @@ -841,7 +839,6 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 #undef ROTL32 #undef QUARTERROUND -#undef BE32 #undef LE32 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index d4b4ac83..51d75139 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -444,6 +444,29 @@ static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *off *offset += len; } +/* This nonce function is described in BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) */ +static int nonce_function_bipschnorr(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + secp256k1_sha256 sha; + (void) counter; + VERIFY_CHECK(counter == 0); + + /* Hash x||msg as per the spec */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, key32, 32); + secp256k1_sha256_write(&sha, msg32, 32); + /* Hash in algorithm, which is not in the spec, but may be critical to + * users depending on it to avoid nonce reuse across algorithms. */ + if (algo16 != NULL) { + secp256k1_sha256_write(&sha, algo16, 16); + } + if (data != NULL) { + secp256k1_sha256_write(&sha, data, 32); + } + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; unsigned int offset = 0; @@ -474,6 +497,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m return 1; } +const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr = nonce_function_bipschnorr; const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; @@ -748,6 +772,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index 79d73a85..ca2bdff5 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5431,6 +5431,10 @@ void run_ecdsa_openssl(void) { # include "modules/ecdh/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/tests_impl.h" #endif @@ -5743,6 +5747,11 @@ int main(int argc, char **argv) { run_ecdh_tests(); #endif +#ifdef ENABLE_MODULE_SCHNORRSIG + /* Schnorrsig tests */ + run_schnorrsig_tests(); +#endif + /* ecdsa tests */ run_random_pubkeys(); run_ecdsa_der_parse(); From b86c2107478e7509902bbdd0df16292dc51ef6f5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Dec 2018 22:12:35 +0000 Subject: [PATCH 044/381] Add MuSig module which allows creating n-of-n multisignatures and adaptor signatures. --- Makefile.am | 4 + configure.ac | 21 + include/secp256k1_musig.h | 429 +++++++++++++++ src/modules/musig/Makefile.am.include | 3 + src/modules/musig/main_impl.h | 629 +++++++++++++++++++++ src/modules/musig/tests_impl.h | 757 ++++++++++++++++++++++++++ src/secp256k1.c | 4 + src/tests.c | 8 + 8 files changed, 1855 insertions(+) create mode 100644 include/secp256k1_musig.h create mode 100644 src/modules/musig/Makefile.am.include create mode 100644 src/modules/musig/main_impl.h create mode 100644 src/modules/musig/tests_impl.h diff --git a/Makefile.am b/Makefile.am index 29330833..c29a9051 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,6 +153,10 @@ if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif +if ENABLE_MODULE_MUSIG +include src/modules/musig/Makefile.am.include +endif + if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif diff --git a/configure.ac b/configure.ac index 219f7b6b..e46fd9f2 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,11 @@ AC_ARG_ENABLE(module_schnorrsig, [enable_module_schnorrsig=$enableval], [enable_module_schnorrsig=no]) +AC_ARG_ENABLE(module_musig, + AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), + [enable_module_musig=$enableval], + [enable_module_musig=no]) + AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [enable_module_recovery=$enableval], @@ -530,6 +535,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) fi +if test x"$enable_module_musig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) +fi + if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi @@ -570,8 +579,16 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) + AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([******]) + + if test x"$enable_module_schnorrsig" != x"yes"; then + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) + fi + fi + if test x"$enable_module_generator" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then AC_MSG_ERROR([Rangeproof module requires the generator module. Use --enable-module-generator to allow.]) @@ -593,6 +610,9 @@ else if test x"$enable_module_schnorrsig" = x"yes"; then AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -623,6 +643,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) 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_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h new file mode 100644 index 00000000..035adfe0 --- /dev/null +++ b/include/secp256k1_musig.h @@ -0,0 +1,429 @@ +#ifndef SECP256K1_MUSIG_H +#define SECP256K1_MUSIG_H + +#include + +/** This module implements a Schnorr-based multi-signature scheme called MuSig + * (https://eprint.iacr.org/2018/068.pdf). + */ + +/** Data structure containing data related to a signing session resulting in a single + * signature. + * + * This structure is not opaque, but it MUST NOT be copied or read or written to it + * 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. + * + * A signer who goes offline and needs to import/export or save/load this structure + * **must** take measures prevent replay attacks wherein an old state is loaded and + * the signing protocol forked from that point. One straightforward way to accomplish + * this is to attach the output of a monotonic non-resettable counter (hardware + * support is needed for this). Increment the counter before each output and + * encrypt+sign the entire package. If a package is deserialized with an old counter + * state or bad signature it should be rejected. + * + * Observe that an independent counter is needed for each concurrent signing session + * such a device is involved in. To avoid fragility, it is therefore recommended that + * any offline signer be usable for only a single session at once. + * + * Given access to such a counter, its output should be used as (or mixed into) the + * session ID to ensure uniqueness. + * + * Fields: + * combined_pk: MuSig-computed combined public key + * n_signers: Number of signers + * pk_hash: The 32-byte hash of the original public keys + * combined_nonce: Summed combined public nonce (undefined if `nonce_is_set` is false) + * nonce_is_set: Whether the above nonce has been set + * nonce_is_negated: If `nonce_is_set`, whether the above nonce was negated after + * summing the participants' nonces. Needed to ensure the nonce's y + * coordinate has a quadratic-residue y coordinate + * msg: The 32-byte message (hash) to be signed + * msg_is_set: Whether the above message has been set + * has_secret_data: Whether this session object has a signers' secret data; if this + * is `false`, it may still be used for verification purposes. + * seckey: If `has_secret_data`, the signer's secret key + * secnonce: If `has_secret_data`, the signer's secret nonce + * nonce: If `has_secret_data`, the signer's public nonce + * nonce_commitments_hash: If `has_secret_data` and `nonce_commitments_hash_is_set`, + * the hash of all signers' commitments + * nonce_commitments_hash_is_set: If `has_secret_data`, whether the + * nonce_commitments_hash has been set + */ +typedef struct { + secp256k1_pubkey combined_pk; + uint32_t n_signers; + unsigned char pk_hash[32]; + secp256k1_pubkey combined_nonce; + int nonce_is_set; + int nonce_is_negated; + unsigned char msg[32]; + int msg_is_set; + int has_secret_data; + unsigned char seckey[32]; + unsigned char secnonce[32]; + secp256k1_pubkey nonce; + unsigned char nonce_commitments_hash[32]; + int nonce_commitments_hash_is_set; +} secp256k1_musig_session; + +/** Data structure containing data on all signers in a single session. + * + * The workflow for this structure is as follows: + * + * 1. This structure is initialized with `musig_session_initialize` or + * `musig_session_initialize_verifier`, which set the `index` field, and zero out + * all other fields. The public session is initialized with the signers' + * nonce_commitments. + * + * 2. In a non-public session the nonce_commitments are set with the function + * `musig_get_public_nonce`, which also returns the signer's public nonce. This + * ensures that the public nonce is not exposed until all commitments have been + * received. + * + * 3. Each individual data struct should be updated with `musig_set_nonce` once a + * nonce is available. This function takes a single signer data struct rather than + * an array because it may fail in the case that the provided nonce does not match + * the commitment. In this case, it is desirable to identify the exact party whose + * nonce was inconsistent. + * + * Fields: + * present: indicates whether the signer's nonce is set + * index: index of the signer in the MuSig key aggregation + * nonce: public nonce, must be a valid curvepoint if the signer is `present` + * nonce_commitment: commitment to the nonce, or all-bits zero if a commitment + * has not yet been set + */ +typedef struct { + int present; + uint32_t index; + secp256k1_pubkey nonce; + unsigned char nonce_commitment[32]; +} secp256k1_musig_session_signer_data; + +/** Opaque data structure that holds a MuSig partial signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is however + * guaranteed to be 32 bytes in size, and can be safely copied/moved. If you need + * to convert to a format suitable for storage, transmission, or comparison, use the + * `musig_partial_signature_serialize` and `musig_partial_signature_parse` + * functions. + */ +typedef struct { + unsigned char data[32]; +} secp256k1_musig_partial_signature; + +/** Computes a combined public key and the hash of the given public keys + * + * Returns: 1 if the public keys were successfully combined, 0 otherwise + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * scratch: scratch space used to compute the combined pubkey by + * multiexponentiation. If NULL, an inefficient algorithm is used. + * Out: combined_pk: the MuSig-combined public key (cannot be NULL) + * pk_hash32: if non-NULL, filled with the 32-byte hash of all input public + * keys in order to be used in `musig_session_initialize`. + * In: pubkeys: input array of public keys to combine. The order is important; + * a different order will result in a different combined public + * key (cannot be NULL) + * n_pubkeys: length of pubkeys array + */ +SECP256K1_API int secp256k1_musig_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + secp256k1_pubkey *combined_pk, + unsigned char *pk_hash32, + const secp256k1_pubkey *pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Initializes a signing session for a signer + * + * Returns: 1: session is successfully initialized + * 0: session could not be initialized: secret key or secret nonce overflow + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers` (cannot be NULL) + * nonce_commitment32: filled with a 32-byte commitment to the generated nonce + * (cannot be NULL) + * In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be + * NULL). If a non-unique session_id32 was given then a partial + * signature will LEAK THE SECRET KEY. + * msg32: the 32-byte message to be signed. Shouldn't be NULL unless you + * require sharing public nonces before the message is known + * because it reduces nonce misuse resistance. If NULL, must be + * set with `musig_session_set_msg` before signing and verifying. + * combined_pk: the combined public key of all signers (cannot be NULL) + * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be + * NULL) + * n_signers: length of signers array. Number of signers participating in + * the MuSig. Must be greater than 0 and at most 2^32 - 1. + * my_index: index of this signer in the signers array + * seckey: the signer's 32-byte secret key (cannot be NULL) + */ +SECP256K1_API int secp256k1_musig_session_initialize( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + unsigned char *nonce_commitment32, + const unsigned char *session_id32, + const unsigned char *msg32, + const secp256k1_pubkey *combined_pk, + const unsigned char *pk_hash32, + size_t n_signers, + size_t my_index, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); + +/** Gets the signer's public nonce given a list of all signers' data with commitments + * + * Returns: 1: public nonce is written in nonce + * 0: signer data is missing commitments or session isn't initialized + * for signing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: the signing session to get the nonce from (cannot be NULL) + * signers: an array of signers' data initialized with + * `musig_session_initialize`. Array length must equal to + * `n_commitments` (cannot be NULL) + * Out: nonce: the nonce (cannot be NULL) + * In: commitments: array of 32-byte nonce commitments (cannot be NULL) + * n_commitments: the length of commitments and signers array. Must be the total + * number of signers participating in the MuSig. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + secp256k1_pubkey *nonce, + const unsigned char *const *commitments, + size_t n_commitments +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Initializes a verifier session that can be used for verifying nonce commitments + * and partial signatures. It does not have secret key material and therefore can not + * be used to create signatures. + * + * Returns: 1 when session is successfully initialized, 0 otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers`(cannot be NULL) + * In: msg32: the 32-byte message to be signed If NULL, must be set with + * `musig_session_set_msg` before using the session for verifying + * partial signatures. + * combined_pk: the combined public key of all signers (cannot be NULL) + * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) + * commitments: array of 32-byte nonce commitments. Array length must equal to + * `n_signers` (cannot be NULL) + * n_signers: length of signers and commitments array. Number of signers + * participating in the MuSig. Must be greater than 0 and at most + * 2^32 - 1. + */ +SECP256K1_API int secp256k1_musig_session_initialize_verifier( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + const unsigned char *msg32, + const secp256k1_pubkey *combined_pk, + const unsigned char *pk_hash32, + const unsigned char *const *commitments, + size_t n_signers +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Checks a signer's public nonce against a commitment to said nonce, and update + * data structure if they match + * + * Returns: 1: commitment was valid, data structure updated + * 0: commitment was invalid, nothing happened + * Args: ctx: pointer to a context object (cannot be NULL) + * signer: pointer to the signer data to update (cannot be NULL). Must have + * been used with `musig_session_get_public_nonce` or initialized + * with `musig_session_initialize_verifier`. + * In: nonce: signer's alleged public nonce (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( + const secp256k1_context* ctx, + secp256k1_musig_session_signer_data *signer, + const secp256k1_pubkey *nonce +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates a session with the combined public nonce of all signers. The combined + * public nonce is the sum of every signer's public nonce. + * + * Returns: 1: nonces are successfully combined + * 0: a signer's nonce is missing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: session to update with the combined public nonce (cannot be + * NULL) + * signers: an array of signers' data, which must have had public nonces + * set with `musig_set_nonce`. Array length must equal to `n_signers` + * (cannot be NULL) + * n_signers: the length of the signers array. Must be the total number of + * signers participating in the MuSig. + * Out: nonce_is_negated: a pointer to an integer that indicates if the combined + * public nonce had to be negated. + * adaptor: point to add to the combined public nonce. If NULL, nothing is + * added to the combined nonce. + */ +SECP256K1_API int secp256k1_musig_session_combine_nonces( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + const secp256k1_musig_session_signer_data *signers, + size_t n_signers, + int *nonce_is_negated, + const secp256k1_pubkey *adaptor +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Sets the message of a session if previously unset + * + * Returns 1 if the message was not set yet and is now successfully set + * 0 otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * session: the session structure to update with the message (cannot be NULL) + * In: msg32: the 32-byte message to be signed (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_set_msg( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a MuSig partial signature or adaptor signature + * + * Returns: 1 when the signature could be serialized, 0 otherwise + * Args: ctx: a secp256k1 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_musig_partial_signature_serialize( + const secp256k1_context* ctx, + unsigned char *out32, + const secp256k1_musig_partial_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse and verify a MuSig partial signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in32: pointer to the 32-byte signature to be parsed + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature verification with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_musig_partial_signature_parse( + const secp256k1_context* ctx, + secp256k1_musig_partial_signature* sig, + const unsigned char *in32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Produces a partial signature + * + * Returns: 1: partial signature constructed + * 0: session in incorrect or inconsistent state + * Args: ctx: pointer to a context object (cannot be NULL) + * session: active signing session for which the combined nonce has been + * computed (cannot be NULL) + * Out: partial_sig: partial signature (cannot be NULL) + */ +SECP256K1_API int secp256k1_musig_partial_sign( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + secp256k1_musig_partial_signature *partial_sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Checks that an individual partial signature verifies + * + * This function is essential when using protocols with adaptor signatures. + * However, it is not essential for regular MuSig's, in the sense that if any + * partial signatures does not verify, the full signature will also not verify, so the + * problem will be caught. But this function allows determining the specific party + * who produced an invalid signature, so that signing can be restarted without them. + * + * Returns: 1: partial signature verifies + * 0: invalid signature or bad data + * Args: ctx: pointer to a context object (cannot be NULL) + * session: active session for which the combined nonce has been computed + * (cannot be NULL) + * signer: data for the signer who produced this signature (cannot be NULL) + * In: partial_sig: signature to verify (cannot be NULL) + * pubkey: public key of the signer who produced the signature (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + const secp256k1_musig_session_signer_data *signer, + const secp256k1_musig_partial_signature *partial_sig, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Combines partial signatures + * + * Returns: 1: all partial signatures have values in range. Does NOT mean the + * resulting signature verifies. + * 0: some partial signature had s/r out of range + * Args: ctx: pointer to a context object (cannot be NULL) + * session: initialized session for which the combined nonce has been + * computed (cannot be NULL) + * Out: sig: complete signature (cannot be NULL) + * In: partial_sigs: array of partial signatures to combine (cannot be NULL) + * n_sigs: number of signatures in the partial_sigs array + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + secp256k1_schnorrsig *sig, + const secp256k1_musig_partial_signature *partial_sigs, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Converts a partial signature to an adaptor signature by adding a given secret + * adaptor. + * + * Returns: 1: signature and secret adaptor contained valid values + * 0: otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: adaptor_sig: adaptor signature to produce (cannot be NULL) + * In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL) + * sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot + * be NULL) + * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` + */ +SECP256K1_API int secp256k1_musig_partial_sig_adapt( + const secp256k1_context* ctx, + secp256k1_musig_partial_signature *adaptor_sig, + const secp256k1_musig_partial_signature *partial_sig, + const unsigned char *sec_adaptor32, + int nonce_is_negated +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Extracts a secret adaptor from a MuSig, given all parties' partial + * signatures. This function will not fail unless given grossly invalid data; if it + * is merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier steps of + * any protocol that uses this function. + * + * Returns: 1: signatures contained valid data such that an adaptor could be extracted + * 0: otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL) + * In: sig: complete 2-of-2 signature (cannot be NULL) + * partial_sigs: array of partial signatures (cannot be NULL) + * n_partial_sigs: number of elements in partial_sigs array + * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( + const secp256k1_context* ctx, + unsigned char *sec_adaptor32, + const secp256k1_schnorrsig *sig, + const secp256k1_musig_partial_signature *partial_sigs, + size_t n_partial_sigs, + int nonce_is_negated +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#endif diff --git a/src/modules/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include new file mode 100644 index 00000000..6099ab72 --- /dev/null +++ b/src/modules/musig/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_musig.h +noinst_HEADERS += src/modules/musig/main_impl.h +noinst_HEADERS += src/modules/musig/tests_impl.h diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h new file mode 100644 index 00000000..7215f4a5 --- /dev/null +++ b/src/modules/musig/main_impl.h @@ -0,0 +1,629 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_MUSIG_MAIN_ +#define _SECP256K1_MODULE_MUSIG_MAIN_ + +#include "include/secp256k1.h" +#include "include/secp256k1_musig.h" +#include "hash.h" + +/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ +static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_pubkey *pk, size_t np) { + secp256k1_sha256 sha; + size_t i; + + secp256k1_sha256_initialize(&sha); + for (i = 0; i < np; i++) { + unsigned char ser[33]; + size_t serlen = sizeof(ser); + if (!secp256k1_ec_pubkey_serialize(ctx, ser, &serlen, &pk[i], SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(&sha, ser, serlen); + } + secp256k1_sha256_finalize(&sha, ell); + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */ +static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + + sha->s[0] = 0x0fd0690cul; + sha->s[1] = 0xfefeae97ul; + sha->s[2] = 0x996eac7ful; + sha->s[3] = 0x5c30d864ul; + sha->s[4] = 0x8c4a0573ul; + sha->s[5] = 0xaca1a22ful; + sha->s[6] = 0x6f43b801ul; + sha->s[7] = 0x85ce27cdul; + sha->bytes = 64; +} + +/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */ +static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) { + secp256k1_sha256 sha; + unsigned char buf[32]; + size_t i; + + secp256k1_musig_sha256_init_tagged(&sha); + secp256k1_sha256_write(&sha, ell, 32); + /* We're hashing the index of the signer instead of its public key as specified + * in the MuSig paper. This reduces the total amount of data that needs to be + * hashed. + * Additionally, it prevents creating identical musig_coefficients for identical + * public keys. A participant Bob could choose his public key to be the same as + * Alice's, then replay Alice's messages (nonce and partial signature) to create + * a valid partial signature. This is not a problem for MuSig per se, but could + * result in subtle issues with protocols building on threshold signatures. + * With the assumption that public keys are unique, hashing the index is + * equivalent to hashing the public key. Because the public key can be + * identified by the index given the ordered list of public keys (included in + * ell), the index is just a different encoding of the public key.*/ + for (i = 0; i < sizeof(uint32_t); i++) { + unsigned char c = idx; + secp256k1_sha256_write(&sha, &c, 1); + idx >>= 8; + } + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(r, buf, NULL); +} + +typedef struct { + const secp256k1_context *ctx; + unsigned char ell[32]; + const secp256k1_pubkey *pks; +} secp256k1_musig_pubkey_combine_ecmult_data; + +/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ +static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; + secp256k1_musig_coefficient(sc, ctx->ell, idx); + return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); +} + + +static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { + uint32_t i; + for (i = 0; i < n_signers; i++) { + memset(&signers[i], 0, sizeof(signers[i])); + signers[i].index = i; + signers[i].present = 0; + } +} + +int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys) { + secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; + secp256k1_gej pkj; + secp256k1_ge pkp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(n_pubkeys > 0); + + ecmult_data.ctx = ctx; + ecmult_data.pks = pubkeys; + if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) { + return 0; + } + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) { + return 0; + } + secp256k1_ge_set_gej(&pkp, &pkj); + secp256k1_pubkey_save(combined_pk, &pkp); + + if (pk_hash32 != NULL) { + memcpy(pk_hash32, ecmult_data.ell, 32); + } + return 1; +} + +int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey) { + unsigned char combined_ser[33]; + size_t combined_ser_size = sizeof(combined_ser); + int overflow; + secp256k1_scalar secret; + secp256k1_scalar mu; + secp256k1_sha256 sha; + secp256k1_gej rj; + secp256k1_ge rp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(nonce_commitment32 != NULL); + ARG_CHECK(session_id32 != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(seckey != NULL); + + memset(session, 0, sizeof(*session)); + + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + } else { + session->msg_is_set = 0; + } + memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + memcpy(session->pk_hash, pk_hash32, 32); + session->nonce_is_set = 0; + session->has_secret_data = 1; + if (n_signers == 0 || my_index >= n_signers) { + return 0; + } + if (n_signers > UINT32_MAX) { + return 0; + } + session->n_signers = (uint32_t) n_signers; + secp256k1_musig_signers_init(signers, session->n_signers); + session->nonce_commitments_hash_is_set = 0; + + /* Compute secret key */ + secp256k1_scalar_set_b32(&secret, seckey, &overflow); + if (overflow) { + secp256k1_scalar_clear(&secret); + return 0; + } + secp256k1_musig_coefficient(&mu, pk_hash32, (uint32_t) my_index); + secp256k1_scalar_mul(&secret, &secret, &mu); + secp256k1_scalar_get_b32(session->seckey, &secret); + + /* Compute secret nonce */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, session_id32, 32); + if (session->msg_is_set) { + secp256k1_sha256_write(&sha, msg32, 32); + } + secp256k1_ec_pubkey_serialize(ctx, combined_ser, &combined_ser_size, combined_pk, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, combined_ser, combined_ser_size); + secp256k1_sha256_write(&sha, seckey, 32); + secp256k1_sha256_finalize(&sha, session->secnonce); + secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow); + if (overflow) { + secp256k1_scalar_clear(&secret); + return 0; + } + + /* Compute public nonce and commitment */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &secret); + secp256k1_ge_set_gej(&rp, &rj); + secp256k1_pubkey_save(&session->nonce, &rp); + + if (nonce_commitment32 != NULL) { + unsigned char commit[33]; + size_t commit_size = sizeof(commit); + secp256k1_sha256_initialize(&sha); + secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, &session->nonce, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, commit, commit_size); + secp256k1_sha256_finalize(&sha, nonce_commitment32); + } + + secp256k1_scalar_clear(&secret); + return 1; +} + +int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments) { + secp256k1_sha256 sha; + unsigned char nonce_commitments_hash[32]; + size_t i; + (void) ctx; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(commitments != NULL); + + if (!session->has_secret_data || n_commitments != session->n_signers) { + return 0; + } + for (i = 0; i < n_commitments; i++) { + ARG_CHECK(commitments[i] != NULL); + } + + secp256k1_sha256_initialize(&sha); + for (i = 0; i < n_commitments; i++) { + memcpy(signers[i].nonce_commitment, commitments[i], 32); + secp256k1_sha256_write(&sha, commitments[i], 32); + } + secp256k1_sha256_finalize(&sha, nonce_commitments_hash); + if (session->nonce_commitments_hash_is_set + && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { + /* Abort if get_public_nonce has been called before with a different array of + * commitments. */ + return 0; + } + memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); + session->nonce_commitments_hash_is_set = 1; + memcpy(nonce, &session->nonce, sizeof(*nonce)); + return 1; +} + +int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, const unsigned char *const *commitments, size_t n_signers) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(commitments != NULL); + /* Check n_signers before checking commitments to allow testing the case where + * n_signers is big without allocating the space. */ + if (n_signers > UINT32_MAX) { + return 0; + } + for (i = 0; i < n_signers; i++) { + ARG_CHECK(commitments[i] != NULL); + } + (void) ctx; + + memset(session, 0, sizeof(*session)); + + memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + if (n_signers == 0) { + return 0; + } + session->n_signers = (uint32_t) n_signers; + secp256k1_musig_signers_init(signers, session->n_signers); + + memcpy(session->pk_hash, pk_hash32, 32); + session->nonce_is_set = 0; + session->msg_is_set = 0; + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + } + session->has_secret_data = 0; + session->nonce_commitments_hash_is_set = 0; + + for (i = 0; i < n_signers; i++) { + memcpy(signers[i].nonce_commitment, commitments[i], 32); + } + return 1; +} + +int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const secp256k1_pubkey *nonce) { + unsigned char commit[33]; + size_t commit_size = sizeof(commit); + secp256k1_sha256 sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(signer != NULL); + ARG_CHECK(nonce != NULL); + + secp256k1_sha256_initialize(&sha); + secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, nonce, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, commit, commit_size); + secp256k1_sha256_finalize(&sha, commit); + + if (memcmp(commit, signer->nonce_commitment, 32) != 0) { + return 0; + } + memcpy(&signer->nonce, nonce, sizeof(*nonce)); + signer->present = 1; + return 1; +} + +int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_is_negated, const secp256k1_pubkey *adaptor) { + secp256k1_gej combined_noncej; + secp256k1_ge combined_noncep; + secp256k1_ge noncep; + secp256k1_sha256 sha; + unsigned char nonce_commitments_hash[32]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + + if (n_signers != session->n_signers) { + return 0; + } + secp256k1_sha256_initialize(&sha); + secp256k1_gej_set_infinity(&combined_noncej); + for (i = 0; i < n_signers; i++) { + if (!signers[i].present) { + return 0; + } + secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32); + secp256k1_pubkey_load(ctx, &noncep, &signers[i].nonce); + secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); + } + secp256k1_sha256_finalize(&sha, nonce_commitments_hash); + /* Either the session is a verifier session or or the nonce_commitments_hash has + * been set in `musig_session_get_public_nonce`. */ + VERIFY_CHECK(!session->has_secret_data || session->nonce_commitments_hash_is_set); + if (session->has_secret_data + && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { + /* If the signers' commitments changed between get_public_nonce and now we + * have to abort because in that case they may have seen our nonce before + * creating their commitment. That can happen if the signer_data given to + * this function is different to the signer_data given to get_public_nonce. + * */ + return 0; + } + + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_pubkey_load(ctx, &noncep, adaptor); + secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); + } + secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); + if (secp256k1_fe_is_quad_var(&combined_noncep.y)) { + session->nonce_is_negated = 0; + } else { + session->nonce_is_negated = 1; + secp256k1_ge_neg(&combined_noncep, &combined_noncep); + } + if (nonce_is_negated != NULL) { + *nonce_is_negated = session->nonce_is_negated; + } + secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep); + session->nonce_is_set = 1; + return 1; +} + +int secp256k1_musig_session_set_msg(const secp256k1_context* ctx, secp256k1_musig_session *session, const unsigned char *msg32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(msg32 != NULL); + + if (session->msg_is_set) { + return 0; + } + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + return 1; +} + +int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out32 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out32, sig->data, 32); + return 1; +} + +int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp256k1_musig_partial_signature* sig, const unsigned char *in32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in32 != NULL); + memcpy(sig->data, in32, 32); + return 1; +} + +/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ +static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { + unsigned char buf[33]; + size_t bufsize = 33; + secp256k1_ge rp; + secp256k1_sha256 sha; + + secp256k1_sha256_initialize(&sha); + if (!session->nonce_is_set) { + return 0; + } + secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce); + secp256k1_fe_get_b32(buf, &rp.x); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &bufsize, &session->combined_pk, SECP256K1_EC_COMPRESSED); + VERIFY_CHECK(bufsize == 33); + secp256k1_sha256_write(&sha, buf, bufsize); + if (!session->msg_is_set) { + return 0; + } + secp256k1_sha256_write(&sha, session->msg, 32); + secp256k1_sha256_finalize(&sha, msghash); + return 1; +} + +int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) { + unsigned char msghash[32]; + int overflow; + secp256k1_scalar sk; + secp256k1_scalar e, k; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(session != NULL); + + if (!session->nonce_is_set || !session->has_secret_data) { + return 0; + } + + /* build message hash */ + if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { + return 0; + } + secp256k1_scalar_set_b32(&e, msghash, NULL); + + secp256k1_scalar_set_b32(&sk, session->seckey, &overflow); + if (overflow) { + secp256k1_scalar_clear(&sk); + return 0; + } + + secp256k1_scalar_set_b32(&k, session->secnonce, &overflow); + if (overflow || secp256k1_scalar_is_zero(&k)) { + secp256k1_scalar_clear(&sk); + secp256k1_scalar_clear(&k); + return 0; + } + if (session->nonce_is_negated) { + secp256k1_scalar_negate(&k, &k); + } + + /* Sign */ + secp256k1_scalar_mul(&e, &e, &sk); + secp256k1_scalar_add(&e, &e, &k); + secp256k1_scalar_get_b32(&partial_sig->data[0], &e); + secp256k1_scalar_clear(&sk); + secp256k1_scalar_clear(&k); + + return 1; +} + +int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { + size_t i; + secp256k1_scalar s; + secp256k1_ge noncep; + (void) ctx; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(partial_sigs != NULL); + ARG_CHECK(session != NULL); + + if (!session->nonce_is_set) { + return 0; + } + if (n_sigs != session->n_signers) { + return 0; + } + secp256k1_scalar_clear(&s); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + + secp256k1_scalar_set_b32(&term, partial_sigs[i].data, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&s, &s, &term); + } + + secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); + VERIFY_CHECK(secp256k1_fe_is_quad_var(&noncep.y)); + secp256k1_fe_normalize(&noncep.x); + secp256k1_fe_get_b32(&sig->data[0], &noncep.x); + secp256k1_scalar_get_b32(&sig->data[32], &s); + + return 1; +} + +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_pubkey *pubkey) { + unsigned char msghash[32]; + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_scalar mu; + secp256k1_gej rj; + secp256k1_ge rp; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(session != NULL); + ARG_CHECK(signer != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(pubkey != NULL); + + if (!session->nonce_is_set || !signer->present) { + return 0; + } + secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); + if (overflow) { + return 0; + } + if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { + return 0; + } + secp256k1_scalar_set_b32(&e, msghash, NULL); + + /* Multiplying the messagehash by the musig coefficient is equivalent + * to multiplying the signer's public key by the coefficient, except + * much easier to do. */ + secp256k1_musig_coefficient(&mu, session->pk_hash, signer->index); + secp256k1_scalar_mul(&e, &e, &mu); + + if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { + return 0; + } + + if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pubkey)) { + return 0; + } + if (!session->nonce_is_negated) { + secp256k1_ge_neg(&rp, &rp); + } + secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL); + + return secp256k1_gej_is_infinity(&rj); +} + +int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_is_negated) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(adaptor_sig != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + + secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + if (overflow) { + secp256k1_scalar_clear(&t); + return 0; + } + + if (nonce_is_negated) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(adaptor_sig->data, &s); + secp256k1_scalar_clear(&t); + return 1; +} + +int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + size_t i; + + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(partial_sigs != NULL); + + secp256k1_scalar_set_b32(&t, &sig->data[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_negate(&t, &t); + + for (i = 0; i < n_partial_sigs; i++) { + secp256k1_scalar_set_b32(&s, partial_sigs[i].data, &overflow); + if (overflow) { + secp256k1_scalar_clear(&t); + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + } + + if (!nonce_is_negated) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return 1; +} + +#endif diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h new file mode 100644 index 00000000..28301fa8 --- /dev/null +++ b/src/modules/musig/tests_impl.h @@ -0,0 +1,757 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_MUSIG_TESTS_ +#define _SECP256K1_MODULE_MUSIG_TESTS_ + +#include "secp256k1_musig.h" + +void musig_api_tests(secp256k1_scratch_space *scratch) { + secp256k1_scratch_space *scratch_small; + secp256k1_musig_session session[2]; + secp256k1_musig_session verifier_session; + secp256k1_musig_session_signer_data signer0[2]; + secp256k1_musig_session_signer_data signer1[2]; + secp256k1_musig_session_signer_data verifier_signer_data[2]; + secp256k1_musig_partial_signature partial_sig[2]; + secp256k1_musig_partial_signature partial_sig_adapted[2]; + secp256k1_musig_partial_signature partial_sig_overflow; + secp256k1_schnorrsig final_sig; + secp256k1_schnorrsig final_sig_cmp; + + unsigned char buf[32]; + unsigned char sk[2][32]; + unsigned char ones[32]; + unsigned char session_id[2][32]; + unsigned char nonce_commitment[2][32]; + int nonce_is_negated; + const unsigned char *ncs[2]; + unsigned char msg[32]; + unsigned char msghash[32]; + secp256k1_pubkey combined_pk; + unsigned char pk_hash[32]; + secp256k1_pubkey pk[2]; + + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor1[32]; + secp256k1_pubkey adaptor; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + + memset(ones, 0xff, 32); + + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(msg); + secp256k1_rand256(sec_adaptor); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); + + /** main test body **/ + + /* Key combination */ + ecount = 0; + CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(ecount == 2); + /* pubkey_combine does not require a scratch space */ + CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(ecount == 2); + /* A small scratch space works too, but will result in using an ineffecient algorithm */ + scratch_small = secp256k1_scratch_space_create(ctx, 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, pk_hash, pk, 2) == 1); + secp256k1_scratch_space_destroy(ctx, scratch_small); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, pk_hash, pk, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 0) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 0) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + + /** Session creation **/ + ecount = 0; + CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 0, 0, sk[0]) == 0); + CHECK(ecount == 8); + /* If more than UINT32_MAX fits in a size_t, test that session_initialize + * rejects n_signers that high. */ + if (SIZE_MAX > UINT32_MAX) { + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + } + CHECK(ecount == 8); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, NULL) == 0); + CHECK(ecount == 9); + /* secret key overflows */ + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, ones) == 0); + CHECK(ecount == 9); + + + { + secp256k1_musig_session session_without_msg; + CHECK(secp256k1_musig_session_initialize(sign, &session_without_msg, signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_set_msg(none, &session_without_msg, msg) == 1); + CHECK(secp256k1_musig_session_set_msg(none, &session_without_msg, msg) == 0); + } + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + + ecount = 0; + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, pk_hash, ncs, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 0) == 0); + CHECK(ecount == 4); + if (SIZE_MAX > UINT32_MAX) { + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, ((size_t) UINT32_MAX) + 2) == 0); + } + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + + CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0); + CHECK(secp256k1_musig_compute_messagehash(none, msghash, &session[0]) == 0); + + /** Signing step 0 -- exchange nonce commitments */ + ecount = 0; + { + secp256k1_pubkey nonce; + + /* Can obtain public nonce after commitments have been exchanged; still can't sign */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 0); + CHECK(ecount == 0); + } + + /** Signing step 1 -- exchange nonces */ + ecount = 0; + { + secp256k1_pubkey public_nonce[3]; + + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], NULL, &public_nonce[0], ncs, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, NULL, ncs, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], NULL, 2) == 0); + CHECK(ecount == 4); + /* Number of commitments and number of signers are different */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 1) == 0); + CHECK(ecount == 4); + + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2) == 1); + + CHECK(secp256k1_musig_set_nonce(none, &signer0[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); + CHECK(ecount == 4); + + CHECK(secp256k1_musig_set_nonce(none, NULL, &public_nonce[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer1[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], &public_nonce[1]) == 1); + + ecount = 0; + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], NULL, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 2); + /* Number of signers differs from number during intialization */ + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 1, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, NULL, &adaptor) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, NULL) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &nonce_is_negated, &adaptor) == 1); + } + + /** Signing step 2 -- partial signatures */ + ecount = 0; + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); + CHECK(ecount == 2); + + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); + /* observer can't sign */ + CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); + CHECK(ecount == 2); + + ecount = 0; + CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_signature_serialize(none, NULL, &partial_sig[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_signature_serialize(none, buf, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], buf) == 1); + CHECK(secp256k1_musig_partial_signature_parse(none, NULL, buf) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig_overflow, ones) == 1); + + /** Partial signature verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_verify(none, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_verify(sign, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[1], &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1); + CHECK(ecount == 6); + + /** Adaptor signature verification */ + memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); + ecount = 0; + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, nonce_is_negated) == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, nonce_is_negated) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, nonce_is_negated) == 0); + CHECK(ecount == 3); + + /** Signing combining and verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); + + CHECK(secp256k1_musig_partial_sig_combine(none, NULL, &final_sig, partial_sig_adapted, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, NULL, 2) == 0); + CHECK(ecount == 3); + { + secp256k1_musig_partial_signature partial_sig_tmp[2]; + partial_sig_tmp[0] = partial_sig_adapted[0]; + partial_sig_tmp[1] = partial_sig_overflow; + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_tmp, 2) == 0); + } + CHECK(ecount == 3); + /* Wrong number of partial sigs */ + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 1) == 0); + CHECK(ecount == 3); + + CHECK(secp256k1_schnorrsig_verify(vrfy, &final_sig, msg, &combined_pk) == 1); + + /** Secret adaptor can be extracted from signature */ + ecount = 0; + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, nonce_is_negated) == 1); + CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, &final_sig, partial_sig, 2, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0); + CHECK(ecount == 2); + { + secp256k1_schnorrsig final_sig_tmp = final_sig; + memcpy(&final_sig_tmp.data[32], ones, 32); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); + } + CHECK(ecount == 2); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, NULL, 2, 0) == 0); + CHECK(ecount == 3); + { + secp256k1_musig_partial_signature partial_sig_tmp[2]; + partial_sig_tmp[0] = partial_sig[0]; + partial_sig_tmp[1] = partial_sig_overflow; + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); + } + CHECK(ecount == 3); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 0, 0) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, 1) == 1); + + /** cleanup **/ + memset(&session, 0, sizeof(session)); + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + +/* Initializes two sessions, one use the given parameters (session_id, + * nonce_commitments, etc.) except that `session_tmp` uses new signers with different + * public keys. The point of this test is to call `musig_session_get_public_nonce` + * with signers from `session_tmp` who have different public keys than the correct + * ones and return the resulting messagehash. This should not result in a different + * messagehash because the public keys of the signers are only used during session + * initialization. */ +int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { + secp256k1_musig_session session; + secp256k1_musig_session session_tmp; + unsigned char nonce_commitment[32]; + secp256k1_musig_session_signer_data signers[2]; + secp256k1_musig_session_signer_data signers_tmp[2]; + unsigned char sk_dummy[32]; + secp256k1_pubkey pks_tmp[2]; + secp256k1_pubkey combined_pk_tmp; + unsigned char pk_hash_tmp[32]; + secp256k1_pubkey nonce; + + /* Set up signers with different public keys */ + secp256k1_rand256(sk_dummy); + pks_tmp[0] = pks[0]; + CHECK(secp256k1_ec_pubkey_create(ctx, &pks_tmp[1], sk_dummy) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, pk_hash_tmp, pks_tmp, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, pk_hash_tmp, 2, 0, sk_dummy) == 1); + + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); + /* Call get_public_nonce with different signers than the signers the session was + * initialized with. */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, &nonce, nonce_commitments, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, &nonce, nonce_commitments, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + + return secp256k1_musig_compute_messagehash(ctx, msghash, &session); +} + +/* Creates a new session (with a different session id) and tries to use that session + * to combine nonces with given signers_other. This should fail, because the nonce + * commitments of signers_other do not match the nonce commitments the new session + * was initialized with. If do_test is 0, the correct signers are being used and + * therefore the function should return 1. */ +int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + secp256k1_musig_session_signer_data *signers_to_use; + unsigned char nonce_commitment[32]; + unsigned char session_id[32]; + secp256k1_pubkey nonce; + const unsigned char *ncs[2]; + + /* Initialize new signers */ + secp256k1_rand256(session_id); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL); + if (do_test) { + signers_to_use = signers_other; + } else { + signers_to_use = signers; + } + return secp256k1_musig_session_combine_nonces(ctx, &session, signers_to_use, 2, NULL, NULL); +} + +/* Recreates a session with the given session_id, signers, pk, msg etc. parameters + * and tries to sign and verify the other signers partial signature. Both should fail + * if msg is NULL. */ +int musig_state_machine_missing_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + unsigned char nonce_commitment[32]; + const unsigned char *ncs[2]; + secp256k1_pubkey nonce; + secp256k1_musig_partial_signature partial_sig; + int partial_sign, partial_verify; + + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + partial_sign = secp256k1_musig_partial_sign(ctx, &session, &partial_sig); + partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, &signers[0], partial_sig_other, &pks[0]); + if (msg != NULL) { + /* Return 1 if both succeeded */ + return partial_sign && partial_verify; + } + /* Return 0 if both failed */ + return partial_sign || partial_verify; +} + +/* Recreates a session with the given session_id, signers, pk, msg etc. parameters + * and tries to verify and combine partial sigs. If do_combine is 0, the + * combine_nonces step is left out. In that case verify and combine should fail and + * this function should return 0. */ +int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + unsigned char nonce_commitment[32]; + const unsigned char *ncs[2]; + secp256k1_pubkey nonce; + secp256k1_musig_partial_signature partial_sigs[2]; + secp256k1_schnorrsig sig; + int partial_verify, sig_combine; + + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + + partial_sigs[0] = *partial_sig_other; + partial_sigs[1] = *partial_sig; + if (do_combine != 0) { + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + } + partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]); + sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, &sig, partial_sigs, 2); + if (do_combine != 0) { + /* Return 1 if both succeeded */ + return partial_verify && sig_combine; + } + /* Return 0 if both failed */ + return partial_verify || sig_combine; +} + +void musig_state_machine_tests(secp256k1_scratch_space *scratch) { + size_t i; + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signers0[2]; + secp256k1_musig_session_signer_data signers1[2]; + unsigned char nonce_commitment[2][32]; + unsigned char session_id[2][32]; + unsigned char msg[32]; + unsigned char sk[2][32]; + secp256k1_pubkey pk[2]; + secp256k1_pubkey combined_pk; + unsigned char pk_hash[32]; + secp256k1_pubkey nonce[2]; + const unsigned char *ncs[2]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char msghash1[32]; + unsigned char msghash2[32]; + + /* Run state machine with the same objects twice to test that it's allowed to + * reinitialize session and session_signer_data. */ + for (i = 0; i < 2; i++) { + /* Setup */ + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(msg); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + + /* Set nonce commitments */ + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + /* Changing a nonce commitment is not okay */ + ncs[1] = (unsigned char*) "this isn't a nonce commitment..."; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 0); + /* Repeating with the same nonce commitments is okay */ + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + + /* Get nonce for signer 1 */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2) == 1); + + /* Set nonces */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); + /* Can't set nonce that doesn't match nonce commitment */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[0]) == 0); + /* Set correct nonce */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); + + /* Combine nonces */ + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); + /* Not everyone is present from signer 1's view */ + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0); + /* Make everyone present */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); + + /* Can't combine nonces from signers of a different session */ + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); + + /* Partially sign */ + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + /* Can't verify or sign until nonce is combined */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 0); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); + /* messagehash should be the same as a session whose get_public_nonce was called + * with different signers (i.e. they diff in public keys). This is because the + * public keys of the signers is set in stone when initializing the session. */ + CHECK(secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]) == 1); + CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, pk_hash, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); + CHECK(memcmp(msghash1, msghash2, 32) == 0); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[1], &pk[1]) == 1); + /* Wrong signature */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); + /* Can't sign or verify until msg is set */ + CHECK(musig_state_machine_missing_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], sk[1], session_id[1], NULL) == 0); + CHECK(musig_state_machine_missing_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], sk[1], session_id[1], msg) == 1); + + /* Can't verify and combine partial sigs until nonces are combined */ + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); + } +} + +void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { + /* Throughout this test "a" and "b" refer to two hypothetical blockchains, + * while the indices 0 and 1 refer to the two signers. Here signer 0 is + * sending a-coins to signer 1, while signer 1 is sending b-coins to signer + * 0. Signer 0 produces the adaptor signatures. */ + secp256k1_schnorrsig final_sig_a; + secp256k1_schnorrsig final_sig_b; + secp256k1_musig_partial_signature partial_sig_a[2]; + secp256k1_musig_partial_signature partial_sig_b_adapted[2]; + secp256k1_musig_partial_signature partial_sig_b[2]; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor_extracted[32]; + secp256k1_pubkey pub_adaptor; + + unsigned char seckey_a[2][32]; + unsigned char seckey_b[2][32]; + secp256k1_pubkey pk_a[2]; + secp256k1_pubkey pk_b[2]; + unsigned char pk_hash_a[32]; + unsigned char pk_hash_b[32]; + secp256k1_pubkey combined_pk_a; + secp256k1_pubkey combined_pk_b; + secp256k1_musig_session musig_session_a[2]; + secp256k1_musig_session musig_session_b[2]; + unsigned char noncommit_a[2][32]; + unsigned char noncommit_b[2][32]; + const unsigned char *noncommit_a_ptr[2]; + const unsigned char *noncommit_b_ptr[2]; + secp256k1_pubkey pubnon_a[2]; + secp256k1_pubkey pubnon_b[2]; + int nonce_is_negated_a; + int nonce_is_negated_b; + secp256k1_musig_session_signer_data data_a[2]; + secp256k1_musig_session_signer_data data_b[2]; + + const unsigned char seed[32] = "still tired of choosing seeds..."; + const unsigned char msg32_a[32] = "this is the message blockchain a"; + const unsigned char msg32_b[32] = "this is the message blockchain b"; + + /* Step 1: key setup */ + secp256k1_rand256(seckey_a[0]); + secp256k1_rand256(seckey_a[1]); + secp256k1_rand256(seckey_b[0]); + secp256k1_rand256(seckey_b[1]); + secp256k1_rand256(sec_adaptor); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[0], seckey_a[0])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[1], seckey_a[1])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[0], seckey_b[0])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[1], seckey_b[1])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, pk_hash_a, pk_a, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, pk_hash_b, pk_b, 2)); + + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 0, seckey_a[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 1, seckey_a[1])); + noncommit_a_ptr[0] = noncommit_a[0]; + noncommit_a_ptr[1] = noncommit_a[1]; + + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 0, seckey_b[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 1, seckey_b[1])); + noncommit_b_ptr[0] = noncommit_b[0]; + noncommit_b_ptr[1] = noncommit_b[1]; + + /* Step 2: Exchange nonces */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, &pubnon_a[0], noncommit_a_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, &pubnon_a[1], noncommit_a_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, &pubnon_b[0], noncommit_b_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, &pubnon_b[1], noncommit_b_ptr, 2)); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], &pubnon_a[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], &pubnon_a[1])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], &pubnon_b[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], &pubnon_b[1])); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &nonce_is_negated_a, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &nonce_is_negated_b, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor)); + + /* Step 3: Signer 0 produces partial signatures for both chains. */ + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[0], &partial_sig_a[0])); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[0], &partial_sig_b[0])); + + /* Step 4: Signer 1 receives partial signatures, verifies them and creates a + * partial signature to send B-coins to signer 0. */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_a[1], data_a, &partial_sig_a[0], &pk_a[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_b[1], data_b, &partial_sig_b[0], &pk_b[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[1], &partial_sig_b[1])); + + /* Step 5: Signer 0 adapts its own partial signature and combines it with the + * partial signature from signer 1. This results in a complete signature which + * is broadcasted by signer 0 to take B-coins. */ + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); + memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], &final_sig_b, partial_sig_b_adapted, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_b, msg32_b, &combined_pk_b) == 1); + + /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to + * other partial signature, and takes A-coins. */ + CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, &final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); + CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], &final_sig_a, partial_sig_a, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_a, msg32_a, &combined_pk_a) == 1); +} + +/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the + * expected state. */ +void sha256_tag_test(void) { + char tag[17] = "MuSig coefficient"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_tagged; + unsigned char buf[32]; + unsigned char buf2[32]; + size_t i; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, (unsigned char *) tag, 17); + secp256k1_sha256_finalize(&sha, buf); + /* buf = SHA256("MuSig coefficient") */ + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha, buf, 32); + /* Is buffer fully consumed? */ + CHECK((sha.bytes & 0x3F) == 0); + + /* Compare with tagged SHA */ + secp256k1_musig_sha256_init_tagged(&sha_tagged); + for (i = 0; i < 8; i++) { + CHECK(sha_tagged.s[i] == sha.s[i]); + } + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha_tagged, buf, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_finalize(&sha_tagged, buf2); + CHECK(memcmp(buf, buf2, 32) == 0); +} + +void run_musig_tests(void) { + int i; + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + musig_api_tests(scratch); + musig_state_machine_tests(scratch); + for (i = 0; i < count; i++) { + /* Run multiple times to ensure that the nonce is negated in some tests */ + scriptless_atomic_swap(scratch); + } + sha256_tag_test(); + + secp256k1_scratch_space_destroy(ctx, scratch); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 51d75139..676d8064 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -776,6 +776,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/schnorrsig/main_impl.h" #endif +#ifdef ENABLE_MODULE_MUSIG +# include "modules/musig/main_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index ca2bdff5..f0619f38 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5435,6 +5435,10 @@ void run_ecdsa_openssl(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_MUSIG +# include "modules/musig/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/tests_impl.h" #endif @@ -5752,6 +5756,10 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_MUSIG + run_musig_tests(); +#endif + /* ecdsa tests */ run_random_pubkeys(); run_ecdsa_der_parse(); From 13ef4457215b35e665b732b4b6e0b7ed38218fd5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Dec 2018 22:15:19 +0000 Subject: [PATCH 045/381] Add 3-of-3 MuSig example --- include/secp256k1_musig.h | 4 +- src/modules/musig/Makefile.am.include | 13 ++ src/modules/musig/example.c | 165 ++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/modules/musig/example.c diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 035adfe0..ead762d2 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -4,7 +4,9 @@ #include /** This module implements a Schnorr-based multi-signature scheme called MuSig - * (https://eprint.iacr.org/2018/068.pdf). + * (https://eprint.iacr.org/2018/068.pdf). There's an example C source file in the + * module's directory (src/modules/musig/example.c) that demonstrates how it can be + * used. */ /** Data structure containing data related to a signing session resulting in a single diff --git a/src/modules/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include index 6099ab72..0cd254d8 100644 --- a/src/modules/musig/Makefile.am.include +++ b/src/modules/musig/Makefile.am.include @@ -1,3 +1,16 @@ include_HEADERS += include/secp256k1_musig.h noinst_HEADERS += src/modules/musig/main_impl.h noinst_HEADERS += src/modules/musig/tests_impl.h + +noinst_PROGRAMS += example_musig +example_musig_SOURCES = src/modules/musig/example.c +example_musig_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include $(SECP_INCLUDES) +if !ENABLE_COVERAGE +example_musig_CPPFLAGS += -DVERIFY +endif +example_musig_LDADD = libsecp256k1.la $(SECP_LIBS) +example_musig_LDFLAGS = -static + +if USE_TESTS +TESTS += example_musig +endif diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c new file mode 100644 index 00000000..5aebfa20 --- /dev/null +++ b/src/modules/musig/example.c @@ -0,0 +1,165 @@ +/********************************************************************** + * Copyright (c) 2018 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * This file demonstrates how to use the MuSig module to create a multisignature. + * Additionally, see the documentation in include/secp256k1_musig.h. + */ + +#include +#include +#include +#include +#include + + /* Number of public keys involved in creating the aggregate signature */ +#define N_SIGNERS 3 + /* Create a key pair and store it in seckey and pubkey */ +int create_key(const secp256k1_context* ctx, unsigned char* seckey, secp256k1_pubkey* pubkey) { + int ret; + FILE *frand = fopen("/dev/urandom", "r"); + if (frand == NULL) { + return 0; + } + do { + if(!fread(seckey, 32, 1, frand)) { + fclose(frand); + return 0; + } + /* The probability that this not a valid secret key is approximately 2^-128 */ + } while (!secp256k1_ec_seckey_verify(ctx, seckey)); + fclose(frand); + ret = secp256k1_ec_pubkey_create(ctx, pubkey, seckey); + return ret; +} + +/* Sign a message hash with the given key pairs and store the result in sig */ +int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_pubkey* pubkeys, const unsigned char* msg32, secp256k1_schnorrsig *sig) { + secp256k1_musig_session musig_session[N_SIGNERS]; + unsigned char nonce_commitment[N_SIGNERS][32]; + const unsigned char *nonce_commitment_ptr[N_SIGNERS]; + secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; + secp256k1_pubkey nonce[N_SIGNERS]; + int i, j; + secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; + + for (i = 0; i < N_SIGNERS; i++) { + FILE *frand; + unsigned char session_id32[32]; + unsigned char pk_hash[32]; + secp256k1_pubkey combined_pk; + + /* Create combined pubkey and initialize signer data */ + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, pk_hash, pubkeys, N_SIGNERS)) { + return 0; + } + /* Create random session ID. It is absolutely necessary that the session ID + * is unique for every call of secp256k1_musig_session_initialize. Otherwise + * it's trivial for an attacker to extract the secret key! */ + frand = fopen("/dev/urandom", "r"); + if(frand == NULL) { + return 0; + } + if (!fread(session_id32, 32, 1, frand)) { + fclose(frand); + return 0; + } + fclose(frand); + /* Initialize session */ + if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, pk_hash, N_SIGNERS, i, seckeys[i])) { + return 0; + } + nonce_commitment_ptr[i] = &nonce_commitment[i][0]; + } + /* Communication round 1: Exchange nonce commitments */ + for (i = 0; i < N_SIGNERS; i++) { + /* Set nonce commitments in the signer data and get the own public nonce */ + if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], &nonce[i], nonce_commitment_ptr, N_SIGNERS)) { + return 0; + } + } + /* Communication round 2: Exchange nonces */ + for (i = 0; i < N_SIGNERS; i++) { + for (j = 0; j < N_SIGNERS; j++) { + if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], &nonce[j])) { + /* Signer j's nonce does not match the nonce commitment. In this case + * abort the protocol. If you make another attempt at finishing the + * protocol, create a new session (with a fresh session ID!). */ + return 0; + } + } + if (!secp256k1_musig_session_combine_nonces(ctx, &musig_session[i], signer_data[i], N_SIGNERS, NULL, NULL)) { + return 0; + } + } + for (i = 0; i < N_SIGNERS; i++) { + if (!secp256k1_musig_partial_sign(ctx, &musig_session[i], &partial_sig[i])) { + return 0; + } + } + /* Communication round 3: Exchange partial signatures */ + for (i = 0; i < N_SIGNERS; i++) { + for (j = 0; j < N_SIGNERS; j++) { + /* To check whether signing was successful, it suffices to either verify + * the the combined signature with the combined public key using + * secp256k1_schnorrsig_verify, or verify all partial signatures of all + * signers individually. Verifying the combined 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 combined + * signature to be invalid and thus the protocol run to fail. It's also + * fine to first verify the combined sig, and only verify the individual + * sigs if it does not work. + */ + if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], &pubkeys[j])) { + return 0; + } + } + } + return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig, partial_sig, N_SIGNERS); +} + + int main(void) { + secp256k1_context* ctx; + int i; + unsigned char seckeys[N_SIGNERS][32]; + secp256k1_pubkey pubkeys[N_SIGNERS]; + secp256k1_pubkey combined_pk; + unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; + secp256k1_schnorrsig sig; + + /* Create a context for signing and verification */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + printf("Creating key pairs......"); + for (i = 0; i < N_SIGNERS; i++) { + if (!create_key(ctx, seckeys[i], &pubkeys[i])) { + printf("FAILED\n"); + return 1; + } + } + printf("ok\n"); + printf("Combining public keys..."); + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys, N_SIGNERS)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Signing message........."); + if (!sign(ctx, seckeys, pubkeys, msg, &sig)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Verifying signature....."); + if (!secp256k1_schnorrsig_verify(ctx, &sig, msg, &combined_pk)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + secp256k1_context_destroy(ctx); + return 0; +} + From 3424cb1fa3964f3772921c9a48a8de120abf3861 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 8 Feb 2019 19:31:28 +0000 Subject: [PATCH 046/381] musig: add user documentation --- include/secp256k1_musig.h | 23 ++--- src/modules/musig/musig.md | 199 +++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 src/modules/musig/musig.md diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index ead762d2..657bacab 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -7,6 +7,10 @@ * (https://eprint.iacr.org/2018/068.pdf). There's an example C source file in the * module's directory (src/modules/musig/example.c) that demonstrates how it can be * used. + * + * The documentation in this include file is for reference and may not be sufficient + * for users to begin using the library. A full description of API usage can be found + * in src/modules/musig/musig.md */ /** Data structure containing data related to a signing session resulting in a single @@ -15,22 +19,9 @@ * This structure is not opaque, but it MUST NOT be copied or read or written to it * 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. - * - * A signer who goes offline and needs to import/export or save/load this structure - * **must** take measures prevent replay attacks wherein an old state is loaded and - * the signing protocol forked from that point. One straightforward way to accomplish - * this is to attach the output of a monotonic non-resettable counter (hardware - * support is needed for this). Increment the counter before each output and - * encrypt+sign the entire package. If a package is deserialized with an old counter - * state or bad signature it should be rejected. - * - * Observe that an independent counter is needed for each concurrent signing session - * such a device is involved in. To avoid fragility, it is therefore recommended that - * any offline signer be usable for only a single session at once. - * - * Given access to such a counter, its output should be used as (or mixed into) the - * session ID to ensure uniqueness. + * workflow. See https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ + * for more details about the risks associated with serializing or deserializing this + * structure. * * Fields: * combined_pk: MuSig-computed combined public key diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md new file mode 100644 index 00000000..015ce91b --- /dev/null +++ b/src/modules/musig/musig.md @@ -0,0 +1,199 @@ +MuSig - Rogue-Key-Resistant Multisignatures Module +=========================== + +This module implements the MuSig [1] multisignature scheme. The majority of +the module is an API designed to be used by signing or auditing participants +in a multisignature scheme. This involves a somewhat complex state machine +and significant effort has been taken to prevent accidental misuse of the +API in ways that could lead to accidental signatures or loss of key material. + +The resulting signatures are valid Schnorr signatures as described in [2]. + +# Theory + +In MuSig all signers contribute key material to a single signing key, +using the equation + + P = sum_i µ_i - P_i + +where `P_i` is the public key of the `i`th signer and `µ_i` is a so-called +_MuSig coefficient_ computed according to the following equation + + L = H(P_1 || P_2 || ... || P_n) + µ_i = H(L || i) + +where H is a hash function modelled as a random oracle. + +To produce a multisignature `(s, R)` on a message `m` using verification key +`P`, signers act as follows: + +1. Each computes a nonce, or ephemeral keypair, `(k_i, R_i)`. Every signer + communicates `H(R_i)` to every participant (both signers and auditors). +2. Upon receipt of every `H(R_i)`, each signer communicates `R_i` to every + participant. The recipients check that each `R_i` is consistent with the + previously-communicated hash. +3. Each signer computes a combined nonce + `R = sum_i R_i` + and shared challenge + `e = H(R || P || m)` + and partial signature + `s_i = k_i + µ_i*x_i*e` + where `x_i` is the secret key corresponding to `P_i`. + +The complete signature is then the `(s, R)` where `s = sum_i s_i` and `R = sum_i R_i`. + +# API Usage + +The following sections describe use of our API, and are mirrored in code in `src/modules/musig/example.c`. + +It is essential to security that signers use a unique uniformly random nonce for all +signing sessions, and that they do not reuse these nonces even in the case that a +signing session fails to complete. To that end, all signing state is encapsulated +in the data structure `secp256k1_musig_session`. The API does not expose any +functionality to serialize or deserialize this structure; it is designed to exist +only in memory. + +Users who need to persist this structure must take additional security measures +which cannot be enforced by a C API. Some guidance is provided in the documentation +for this data structure in `include/secp256k1_musig.h`. + +## Key Generation + +To use MuSig, users must first compute their combined public key `P`, which is +suitable for use on a blockchain or other public key repository. They do this +by calling `secp256k1_musig_pubkey_combine`. + +This function takes as input a list of public keys `P_i` in the argument +`pubkeys`. It outputs the combined public key `P` in the out-pointer `combined_pk` +and hash `L` in the out-pointer `pk_hash32`, if this pointer is non-NULL. + +## Signing + +A participant who wishes to sign a message (as opposed to observing/auditing the +signature process, which is also a supported mode) acts as follows. + +### Signing Participant + +1. The signer starts the session by calling `secp256k1_musig_session_initialize`. + This function outputs + - an initialized session state in the out-pointer `session` + - an array of initialized signer data in the out-pointer `signers` + - a commitment `H(R_i)` to a nonce in the out-pointer `nonce_commitment32` + It takes as input + - a unique session ID `session_id32` + - (optionally) a message to be signed `msg32` + - the combined public key output from `secp256k1_musig_pubkey_combine` + - the public key hash output from `secp256k1_musig_pubkey_combine` + - the signer's index `i` `my_index` + - the signer's secret key `seckey` +2. The signer then communicates `H(R_i)` to all other signers, and receives + commitments `H(R_j)` from all other signers `j`. These hashes are simply + length-32 byte arrays which can be communicated however is communicated. +3. Once all signers nonce commitments have been received, the signer records + these commitments with the function `secp256k1_musig_session_get_public_nonce`. + This function updates in place + - the session state `session` + - the array of signer data `signers` + taking in as input the list of commitments `commitments` and outputting the + signer's public nonce `R_i` in the out-pointer `nonce`. +4. The signer then communicates `R_i` to all other signers, and receives `R_j` + from each signer `j`. On receipt of a nonce `R_j` he calls the function + `secp256k1_musig_set_nonce` to record this fact. This function checks that + the received nonce is consistent with the previously-received nonce and will + return 0 in this case. The signer must also call this function with his own + nonce and his own index `i`. + These nonces `R_i` are secp256k1 public keys; they should be serialized using + `secp256k1_ec_pubkey_serialize` and parsed with `secp256k1_ec_pubkey_parse`. +5. Once all nonces have been exchanged in this way, signers are able to compute + their partial signatures. They do so by calling `secp256k1_musig_session_combine_nonces` + which updates in place + - the session state `session` + - the array of signer data `signers` + It outputs an auxiliary integer `nonce_is_negated` and has an auxiliary input + `adaptor`. Both of these may be set to NULL for ordinary signing purposes. + If the signer did not provide a message to `secp256k1_musig_session_initialize`, + a message must be provided now by calling `secp256k1_musig_session_set_msg` which + updates the session state in place. +6. The signer computes a partial signature `s_i` using the function + `secp256k1_musig_partial_sign` which takes the session state as input and + partial signature as output. +7. The signer then communicates the partial signature `s_i` to all other signers, or + to a central coordinator. These partial signatures should be serialized using + `musig_partial_signature_serialize` and parsed using `musig_partial_signature_parse`. +8. Each signer calls `secp256k1_musig_partial_sig_verify` on the other signers' partial + signatures to verify their correctness. If only the validity of the final signature + is important, not assigning blame, this step can be skipped. +9. Any signer, or central coordinator, may combine the partial signatures to obtain + a complete signature using `secp256k1_musig_partial_sig_combine`. This function takes + a signing session and array of MuSig partial signatures, and outputs a single + Schnorr signature. + +### Non-signing Participant + +A participant who wants to verify the signing process, i.e. check that nonce commitments +are consistent and partial signatures are correct without contributing a partial signature, +may do so using the above instructions except for the following changes: + +1. A signing session should be produced using `musig_session_initialize_verifier` + rather than `musig_session_initialize`; this function takes no secret data or + signer index. +2. The participant receives nonce commitments, public nonces and partial signatures, + but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce` + and `secp256k1_musig_partial_sign` are not called. + +### Verifier + +The final signature is simply a valid Schnorr signature using the combined public key. It +can be verified using the `secp256k1_schnorrsig_verify` with the correct message and +public key output from `secp256k1_musig_pubkey_combine`. + +## Atomic Swaps + +The signing API supports the production of "adaptor signatures", modified partial signatures +which are offset by an auxiliary secret known to one party. That is, +1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`. +2. When combining nonces, each party adds `T` to the total nonce used in the signature. +3. The party who knows `t` must "adapt" their partial signature with `t` to complete the + signature. +4. Any party who sees both the final signature and the original partial signatures + can compute `t`. + +Using these adaptor signatures, two 2-of-2 MuSig signing protocols can be executed in +parallel such that one party's partial signatures are made atomic. That is, when the other +party learns one partial signature, she automatically learns the other. This has applications +in cross-chain atomic swaps. + +Such a protocol can be executed as follows. Consider two participants, Alice and Bob, who +are simultaneously producing 2-of-2 multisignatures for two blockchains A and B. They act +as follows. + +1. Before the protocol begins, Bob chooses a 32-byte auxiliary secret `t` at random and + computes a corresponding public point `T` by calling `secp256k1_ec_pubkey_create`. + He communicates `T` to Alice. +2. Together, the parties execute steps 1-4 of the signing protocol above. +3. At step 5, when combining the two parties' public nonces, both parties call + `secp256k1_musig_session_combine_nonces` with `adaptor` set to `T` and `nonce_is_negated` + set to a non-NULL pointer to int. +4. Steps 6 and 7 proceed as before. Step 8, verifying the partial signatures, is now + essential to the security of the protocol and must not be omitted! + +The above steps are executed identically for both signing sessions. However, step 9 will +not work as before, since the partial signatures will not add up to a valid total signature. +Additional steps must be taken, and it is at this point that the two signing sessions +diverge. From here on we consider "Session A" which benefits Alice (e.g. which sends her +coins) and "Session B" which benefits Bob (e.g. which sends him coins). + +5. In Session B, Bob calls `secp256k1_musig_partial_sig_adapt` with his partial signature + and `t`, to produce an adaptor signature. He can then call `secp256k1_musig_partial_sig_combine` + with this adaptor signature and Alice's partial signature, to produce a complete + signature for blockchain B. +6. Alice reads this signature from blockchain B. She calls `secp256k1_musig_extract_secret_adaptor`, + passing the complete signature along with her and Bob's partial signatures from Session B. + This function outputs `t`, which until this point was only known to Bob. +7. In Session A, Alice is now able to replicate Bob's action, calling + `secp256k1_musig_partial_sig_adapt` with her own partial signature and `t`, ultimately + producing a complete signature on blockchain A. + +[1] https://eprint.iacr.org/2018/068 +[2] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki + From 068f03c35befa80eed782142a72a1a48e3de3a20 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 27 Mar 2019 20:07:15 +0000 Subject: [PATCH 047/381] generator: remove `CHECK` abort calls exposed by public API --- src/modules/generator/main_impl.h | 3 --- src/modules/generator/tests_impl.h | 10 +++++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 12447591..9217169c 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -175,7 +175,6 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s secp256k1_scalar blind; secp256k1_scalar_set_b32(&blind, blind32, &overflow); ret = !overflow; - CHECK(ret); secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &accum, &blind); } @@ -184,7 +183,6 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s secp256k1_sha256_write(&sha256, key32, 32); secp256k1_sha256_finalize(&sha256, b32); ret &= secp256k1_fe_set_b32(&t, b32); - CHECK(ret); shallue_van_de_woestijne(&add, &t); if (blind32) { secp256k1_gej_add_ge(&accum, &accum, &add); @@ -197,7 +195,6 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s secp256k1_sha256_write(&sha256, key32, 32); secp256k1_sha256_finalize(&sha256, b32); ret &= secp256k1_fe_set_b32(&t, b32); - CHECK(ret); shallue_van_de_woestijne(&add, &t); secp256k1_gej_add_ge(&accum, &accum, &add); diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 20acf2e7..006168d7 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -173,7 +173,7 @@ void test_generator_generate(void) { secp256k1_ge_storage ges; int i; unsigned char v[32]; - static const unsigned char s[32] = {0}; + unsigned char s[32] = {0}; secp256k1_scalar sc; secp256k1_scalar_set_b32(&sc, s, NULL); for (i = 1; i <= 32; i++) { @@ -188,6 +188,14 @@ void test_generator_generate(void) { secp256k1_ge_to_storage(&ges, &ge); CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); } + + /* There is no range restriction on the value, but the blinder must be a + * valid scalar. Check that an invalid blinder causes the call to fail + * but not crash. */ + memset(v, 0xff, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, v)); + memset(s, 0xff, 32); + CHECK(!secp256k1_generator_generate_blinded(ctx, &gen, v, s)); } void test_generator_fixed_vector(void) { From f35b5e271f4ad9da478cfac564a8d90981e3505d Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Thu, 11 Apr 2019 17:59:58 +0300 Subject: [PATCH 048/381] Fix a small typo in the generator parameter name --- include/secp256k1_generator.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index c2743a6e..5b5ee647 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -25,12 +25,12 @@ typedef struct { * * Returns: 1 if input contains a valid generator. * Args: ctx: a secp256k1 context object. - * Out: commit: pointer to the output generator object + * Out: gen: pointer to the output generator object * In: input: pointer to a 33-byte serialized generator */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse( const secp256k1_context* ctx, - secp256k1_generator* commit, + secp256k1_generator* gen, const unsigned char *input ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); @@ -39,12 +39,12 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse( * Returns: 1 always. * Args: ctx: a secp256k1 context object. * Out: output: a pointer to a 33-byte byte array - * In: commit: a pointer to a generator + * In: gen: a pointer to a generator */ SECP256K1_API int secp256k1_generator_serialize( const secp256k1_context* ctx, unsigned char *output, - const secp256k1_generator* commit + const secp256k1_generator* gen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Generate a generator for the curve. From 9dd117fd2be1a979d94a1c4d5ea2c5ae386426fc Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 5 Apr 2019 21:26:19 +0200 Subject: [PATCH 049/381] Clean up ./configure help strings (zkp extensions) --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index e46fd9f2..7e9ecb89 100644 --- a/configure.ac +++ b/configure.ac @@ -147,17 +147,17 @@ AC_ARG_ENABLE(module_recovery, [enable_module_recovery=no]) AC_ARG_ENABLE(module_generator, - AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module (default is no)]), + AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]), [enable_module_generator=$enableval], [enable_module_generator=no]) AC_ARG_ENABLE(module_rangeproof, - AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module (default is no)]), + AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]), [enable_module_rangeproof=$enableval], [enable_module_rangeproof=no]) AC_ARG_ENABLE(module_whitelist, - AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module (default is no)]), + AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), [enable_module_whitelist=$enableval], [enable_module_whitelist=no]) @@ -167,7 +167,7 @@ AC_ARG_ENABLE(external_default_callbacks, [use_external_default_callbacks=no]) AC_ARG_ENABLE(module_surjectionproof, - AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module (default is no)]), + AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), [enable_module_surjectionproof=$enableval], [enable_module_surjectionproof=no]) From ed7394f0051962d386a0c9f592a4109c670ba4cb Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Tue, 14 May 2019 22:04:23 +0300 Subject: [PATCH 050/381] Add bench_generator and bench_rangeproof to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 85fe89aa..5b2e4ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ bench_inv bench_ecdh bench_ecmult +bench_generator +bench_rangeproof bench_schnorrsig bench_sign bench_verify From 2a1750dedd50a871b18d95fae7b7f1a90bdc8351 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 12 May 2019 11:13:18 +0000 Subject: [PATCH 051/381] Clarify how to derive alternative generator H --- src/modules/rangeproof/main_impl.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index d3f1dd33..12cfc80f 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -15,9 +15,14 @@ /** Alternative generator for secp256k1. * This is the sha256 of 'g' after DER encoding (without compression), - * which happens to be a point on the curve. - * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16))) - * sage: '%x %x' % G2.xy() + * which happens to be a point on the curve. More precisely, the generator is + * derived by running the following script with the sage mathematics software. + + import hashlib + F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + G_DER = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' + G2 = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G_DER.decode('hex')).hexdigest(),16))) + print('%x %x' % G2.xy()) */ static const secp256k1_generator secp256k1_generator_h_internal = {{ 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, From 0d4ee3c62d4d31cb7d27475466884ede94518dd9 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 28 Mar 2019 19:32:17 +0000 Subject: [PATCH 052/381] Improve explanation of key cancellation attack in whitelist.md --- src/modules/whitelist/whitelist.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/modules/whitelist/whitelist.md b/src/modules/whitelist/whitelist.md index 89d19caf..28307f4b 100644 --- a/src/modules/whitelist/whitelist.md +++ b/src/modules/whitelist/whitelist.md @@ -34,11 +34,13 @@ A less obvious scheme is to have a participant sign an arbitrary message with the sum of her key `P` and the whitelisted key `W`. Such a signature with the key `P + W` proves knowledge of either (a) discrete logarithms of both `P` and `W`; or (b) neither. This makes directly attacking participants' signing schemes much -harder, but allows an attacker to whitelist arbitrary "garbage" keys by computing -`W` as the difference between an attacker-controlled key and `P`. For Bitcoin, -the effect of garbage keys is to "burn" stolen coins, destroying them. +harder, but allows an attacker to whitelist arbitrary "cancellation" keys by +computing `W` as the difference between an attacker-controlled key and `P`. +Because to spend the funds the attacker must produce a signature with `W`, the +coins will be unspendable until attacker and the legitimate participant owning +`P` cooperate. -In an important sense, this "burning coins" attack is a good thing: it enables +In an important sense, this "cancellation" attack is a good thing: it enables *offline delegation*. That is, the key `P` does not need to be available at the time of delegation. Instead, participants could choose `S = P + W`, sign with this to delegate, and only later compute the discrete logarithm of `W = P - S`. @@ -47,7 +49,7 @@ the overall system security. #### Signing with Tweaked-Difference-of-Keys -A modification of this scheme, which prevents this "garbage key" attack, is to +A modification of this scheme, which prevents this "cancellation" attack, is to instead have participants sign some message with the key `P + H(W)W`, for `H` some random-oracle hash that maps group elements to scalars. This key, and its discrete logarithm, cannot be known until after `W` is chosen, so `W` cannot @@ -60,8 +62,8 @@ delegation. However, we can get this back by introducing a new key, `P'`, and signing with the key `P + H(W + P')(W + P')`. This gives us the best of both worlds: `P'` does not need to be online to delegate, allowing it to be securely stored and preventing real-time attacks; `P` does need to -be online, but its compromise only allows an attacker to whitelist "garbage -keys", not attacker-controlled ones. +be online, but its compromise only allows an attacker to whitelist keys he does +not control alone. ### Our Scheme @@ -78,8 +80,8 @@ knows: 1. The discrete logarithms of all of `W`, `P_i` and `Q_i`; or 2. The discrete logarithm of `P_i` but of *neither* `W` nor `Q_i`. In other words, compromise of the online key `P_i` allows an attacker to whitelist -"garbage keys" for which nobody knows the discrete logarithm; to whitelist an -attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult +"cancellation keys" for which the attacker alone does not know the discrete logarithm; +to whitelist an attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult because by design, only the sum `S = W + Q_i` is used when signing; then by choosing `S` freely, a participant can delegate without the secret key to `Q_i` ever being online. (Later, when she wants to actually use `W`, she will need to compute its key as the From 2dc868f35b5790ca74c1dfc4880f802d3f8f52af Mon Sep 17 00:00:00 2001 From: Dmitry Petukhov Date: Sun, 21 Apr 2019 21:23:13 +0500 Subject: [PATCH 053/381] work in progress: add _allocate_initialized/destroy funcs --- include/secp256k1_surjectionproof.h | 46 +++++++++++++++++++++++++++++ src/modules/surjection/main_impl.h | 31 +++++++++++++++++++ src/modules/surjection/tests_impl.h | 41 +++++++++++++++++++++++++ 3 files changed, 118 insertions(+) diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index 38f67990..d9a38e40 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -134,6 +134,7 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); /** Surjection proof initialization function; decides on inputs to use + * To be used to initialize stack-allocated secp256k1_surjectionproof struct * Returns 0: inputs could not be selected * n: inputs were selected after n iterations of random selection * @@ -166,6 +167,51 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initial const unsigned char *random_seed32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + +/** Surjection proof allocation and initialization function; decides on inputs to use + * Returns 0: inputs could not be selected, or malloc failure + * n: inputs were selected after n iterations of random selection + * + * In: ctx: pointer to a context object + * proof_out_p: a pointer to a pointer to `secp256k1_surjectionproof*`. + * the newly-allocated struct pointer will be saved here. + * fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known, + * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; + * this won't match the output tag but might be used in the anonymity set.) + * n_input_tags: the number of entries in the fixed_input_tags array + * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * fixed_output_tag: fixed output tag + * max_n_iterations: the maximum number of iterations to do before giving up. Because the + * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is + * limited to 256 the probability of giving up is smaller than + * (255/256)^(n_input_tags_to_use*max_n_iterations). + * + * random_seed32: a random seed to be used for input selection + * Out: proof_out_p: The pointer to newly-allocated proof whose bitvector will be initialized. + * In case of failure, the pointer will be NULL. + * input_index: The index of the actual input that is secretly mapped to the output + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_allocate_initialized( + const secp256k1_context* ctx, + secp256k1_surjectionproof** proof_out_p, + size_t *input_index, + const secp256k1_fixed_asset_tag* fixed_input_tags, + const size_t n_input_tags, + const size_t n_input_tags_to_use, + const secp256k1_fixed_asset_tag* fixed_output_tag, + const size_t n_max_iterations, + const unsigned char *random_seed32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + +/** Surjection proof destroy function + * deallocates the struct that was allocated with secp256k1_surjectionproof_allocate_initialized + * + * In: proof: pointer to secp256k1_surjectionproof struct + */ +SECP256K1_API void secp256k1_surjectionproof_destroy( + secp256k1_surjectionproof* proof +) SECP256K1_ARG_NONNULL(1); + /** Surjection proof generation function * Returns 0: proof could not be created * 1: proof was successfully created diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index c67d4c0d..ce13e86b 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -151,6 +151,37 @@ static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_cs } } +/* XXX secp256k1_surjectionproof_create is not a good name, because it can be confused with secp256k1_surjectionproof_generate */ +int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, secp256k1_surjectionproof** proof_out_p, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) { + int ret = 0; + secp256k1_surjectionproof* proof; + + VERIFY_CHECK(ctx != NULL); + + ARG_CHECK(proof_out_p != NULL); + *proof_out_p = 0; + + proof = (secp256k1_surjectionproof*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_surjectionproof)); + if (proof != NULL) { + ret = secp256k1_surjectionproof_initialize(ctx, proof, input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, fixed_output_tag, n_max_iterations, random_seed32); + if (ret) { + *proof_out_p = proof; + } + else { + free(proof); + } + } + return ret; +} + +/* XXX add checks to prevent destroy of stack-allocated struct ? */ +void secp256k1_surjectionproof_destroy(secp256k1_surjectionproof* proof) { + if (proof != NULL) { + VERIFY_CHECK(proof->n_inputs <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + free(proof); + } +} + int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) { secp256k1_surjectionproof_csprng csprng; size_t n_iterations = 0; diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index a0856e22..ee959f38 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -28,6 +28,7 @@ static void test_surjectionproof_api(void) { unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX]; size_t serialized_len; secp256k1_surjectionproof proof; + secp256k1_surjectionproof* proof_on_heap; size_t n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]); size_t input_index; int32_t ecount = 0; @@ -52,6 +53,46 @@ static void test_surjectionproof_api(void) { memcpy(&fixed_output_tag, &fixed_input_tags[0], sizeof(fixed_input_tags[0])); CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, output_blinding_key)); + /* check allocate_initialized */ + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 0); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + CHECK(proof_on_heap != 0); + secp256k1_surjectionproof_destroy(proof_on_heap); + CHECK(ecount == 0); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 2); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 3); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 4); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0); + CHECK(proof_on_heap != 0); + secp256k1_surjectionproof_destroy(proof_on_heap); + CHECK(ecount == 4); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 5); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 6); + CHECK((secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 6); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 7); + + /* we are now going to test essentially the same functions, just without heap allocation. + * reset ecount. */ + ecount = 0; + /* check initialize */ CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0); CHECK(ecount == 0); From 00fffeb172baa9fd79be1b8d98aeb1d8b98d51e8 Mon Sep 17 00:00:00 2001 From: Dmitry Petukhov Date: Sun, 26 May 2019 18:37:29 +0500 Subject: [PATCH 054/381] Improve comments for surctionproof init+alloc/destroy funcs The comments with 'XXX' was intended to indicate that the listed concerns was subject to review and change, but the code with these comments was merged straight away. This commit replaces comments with more complete text describing the issues. This also signifies that the commit that this code was introduced in is not anymore 'work in progress'. --- src/modules/surjection/main_impl.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index ce13e86b..1b0e5359 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -151,7 +151,10 @@ static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_cs } } -/* XXX secp256k1_surjectionproof_create is not a good name, because it can be confused with secp256k1_surjectionproof_generate */ +/* While '_allocate_initialized' may be a wordy suffix for this function, and '_create' + * may have been more appropriate, '_create' could be confused with '_generate', + * as the meanings for the words are close. Therefore, more wordy, but less + * ambiguous suffix was chosen. */ int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, secp256k1_surjectionproof** proof_out_p, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) { int ret = 0; secp256k1_surjectionproof* proof; @@ -174,7 +177,15 @@ int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, return ret; } -/* XXX add checks to prevent destroy of stack-allocated struct ? */ +/* secp256k1_surjectionproof structure may also be allocated on the stack, + * and initialized explicitly via secp256k1_surjectionproof_initialize(). + * Supplying stack-allocated struct to _destroy() will result in calling + * free() with the pointer that points at the stack, with disasterous + * consequences. Thus, it is not advised to mix heap- and stack-allocating + * approaches to working with this struct. It is possible to detect this + * situation by using additional field in the struct that can be set to + * special value depending on the allocation path, and check it here. + * But currently, it is not seen as big enough concern to warrant this extra code .*/ void secp256k1_surjectionproof_destroy(secp256k1_surjectionproof* proof) { if (proof != NULL) { VERIFY_CHECK(proof->n_inputs <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); From c0415eb0cb444998aa1200d83c1910882cd8c7fb Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 4 Jun 2019 14:33:08 +0200 Subject: [PATCH 055/381] Fix read of wrong buffer (and OOB) in surjectionproof tests --- src/modules/surjection/tests_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index ee959f38..bec251dd 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -422,7 +422,7 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { /* trailing garbage */ memcpy(&serialized_proof_trailing, &serialized_proof, serialized_len); serialized_proof_trailing[serialized_len] = seed[0]; - CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len + 1) == 0); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof_trailing, serialized_len + 1) == 0); CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); From b0644d4ab3076e8191997737b3b491015ba43f95 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 30 Apr 2019 21:39:14 +0000 Subject: [PATCH 056/381] surjectionproof: add fixed test vectors --- src/modules/surjection/tests_impl.h | 127 ++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index bec251dd..648d4aea 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -512,11 +512,138 @@ void test_bad_parse(void) { CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0); } +void test_fixed_vectors(void) { + const unsigned char tag0_ser[] = { + 0x0a, + 0x1c, 0xa3, 0xdd, 0x12, 0x48, 0xdd, 0x4d, 0xd0, 0x04, 0x30, 0x47, 0x48, 0x75, 0xf5, 0xf5, 0xff, + 0x2a, 0xd5, 0x0d, 0x1d, 0x86, 0x2b, 0xa4, 0xa4, 0x2f, 0x46, 0xe9, 0xb4, 0x54, 0x21, 0xf0, 0x85 + }; + const unsigned char tag1_ser[] = { + 0x0a, + 0x09, 0x0d, 0x5a, 0xd4, 0xed, 0xae, 0x9c, 0x0c, 0x69, 0x79, 0xf3, 0x8d, 0x22, 0x03, 0x0a, 0x3d, + 0x38, 0xd4, 0x78, 0xe1, 0x03, 0x0d, 0x70, 0x57, 0xd9, 0x9a, 0x23, 0x03, 0xf0, 0x7f, 0xfb, 0xef + }; + const unsigned char tag2_ser[] = { + 0x0a, + 0xfd, 0xed, 0xba, 0x15, 0x20, 0x8a, 0xb2, 0xaf, 0x0b, 0x76, 0x6d, 0xd2, 0x5f, 0xd4, 0x15, 0x11, + 0x90, 0xec, 0xcb, 0x3f, 0xcd, 0x08, 0xb5, 0x35, 0xd9, 0x24, 0x18, 0xb1, 0xd3, 0x47, 0x83, 0x54 + }; + const unsigned char tag3_ser[] = { + 0x0b, + 0x8b, 0x47, 0xca, 0xee, 0x20, 0x52, 0x17, 0xbf, 0xee, 0xcc, 0x84, 0xcd, 0x34, 0x32, 0x6c, 0x36, + 0xf1, 0xd9, 0x3f, 0xe1, 0x6f, 0x77, 0xfe, 0x89, 0x3e, 0x4a, 0xc8, 0x2a, 0x75, 0xfa, 0x2d, 0x36 + }; + const unsigned char tag4_ser[] = { + 0x0b, + 0x3c, 0x5c, 0xf4, 0x61, 0x45, 0xa8, 0x53, 0xc1, 0x64, 0x32, 0x0e, 0x92, 0x68, 0x52, 0xbd, 0x12, + 0xe9, 0x45, 0x31, 0xeb, 0x04, 0x4c, 0xf4, 0xe2, 0x9e, 0x9f, 0x60, 0x26, 0x50, 0xbf, 0xd6, 0x9f + }; + const unsigned char output_tag_ser[] = { + 0x0b, + 0xf7, 0x3c, 0x03, 0xed, 0xae, 0x83, 0xa1, 0xa6, 0x94, 0x8c, 0xe3, 0xb8, 0x54, 0x02, 0xa8, 0xbd, + 0x66, 0xca, 0x28, 0xef, 0x44, 0xf5, 0x3a, 0xcb, 0xc7, 0x5b, 0x16, 0xac, 0xce, 0x29, 0x4b, 0xc6 + }; + + const unsigned char total1_used1[] = { + 0x01, 0x00, 0x01, 0x8e, 0x6b, 0x8d, 0x8b, 0x96, 0x29, 0x10, 0x29, 0xcb, 0xf8, 0x48, 0xd9, 0xc8, + 0x5b, 0x77, 0xdc, 0xdf, 0x16, 0x67, 0x19, 0xfe, 0x8d, 0xee, 0x8f, 0x56, 0x6f, 0x9c, 0xe9, 0xae, + 0xb9, 0xd9, 0x12, 0xb8, 0x95, 0x6c, 0xf1, 0x48, 0x07, 0x7d, 0x49, 0xe4, 0x3e, 0x7f, 0xc1, 0x2c, + 0xe2, 0xe1, 0x94, 0x10, 0xb1, 0xda, 0x86, 0x5f, 0xbc, 0x03, 0x59, 0xe1, 0x09, 0xd2, 0x1b, 0x18, + 0xce, 0x58, 0x15 + }; + const size_t total1_used1_len = sizeof(total1_used1); + + const unsigned char total2_used1[] = { + 0x02, 0x00, 0x01, 0x35, 0x3a, 0x29, 0x4b, 0xe4, 0x99, 0xc6, 0xbf, 0x99, 0x4d, 0x6c, 0xc8, 0x18, + 0x14, 0xad, 0x10, 0x22, 0x3a, 0xb8, 0x1c, 0xb9, 0xc5, 0x77, 0xda, 0xe0, 0x8a, 0x71, 0x2d, 0x0d, + 0x8e, 0x80, 0xf5, 0x8d, 0x74, 0xf9, 0x01, 0x6b, 0x35, 0x88, 0xf4, 0x8e, 0x43, 0xa5, 0x9c, 0x0f, + 0x7e, 0x37, 0x86, 0x77, 0x44, 0x72, 0x7c, 0xaa, 0xff, 0x14, 0x5b, 0x7a, 0x42, 0x41, 0x75, 0xb2, + 0x5e, 0x3d, 0x6c + }; + const size_t total2_used1_len = sizeof(total2_used1); + + const unsigned char total3_used2[] = { + 0x03, 0x00, 0x03, 0xf2, 0x3f, 0xca, 0x49, 0x52, 0x05, 0xaf, 0x81, 0x83, 0x01, 0xd7, 0xf4, 0x92, + 0xc0, 0x50, 0xe3, 0x15, 0xfc, 0x94, 0xc1, 0x27, 0x10, 0xd7, 0x8f, 0x57, 0xb1, 0x23, 0xcf, 0x68, + 0x31, 0xf8, 0xcb, 0x58, 0x3d, 0xca, 0x2f, 0x7a, 0x3b, 0x0b, 0xb6, 0x10, 0x52, 0x94, 0xc8, 0x5f, + 0x0a, 0xf8, 0xca, 0x5d, 0x4c, 0x38, 0x44, 0x92, 0xb3, 0xc7, 0xe4, 0x46, 0x9f, 0x96, 0x64, 0xbd, + 0xd2, 0xda, 0x40, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91 + }; + const size_t total3_used2_len = sizeof(total3_used2); + + const unsigned char total5_used3[] = { + 0x05, 0x00, 0x15, 0x36, 0x3b, 0x92, 0x97, 0x84, 0x25, 0x75, 0xd6, 0xa6, 0xaf, 0xb7, 0x32, 0x5b, + 0x2c, 0xf8, 0x31, 0xe2, 0x15, 0x3a, 0x9b, 0xb7, 0x20, 0x14, 0xc0, 0x67, 0x96, 0x7d, 0xa9, 0xc4, + 0xa2, 0xb4, 0x22, 0x57, 0x5f, 0xb8, 0x20, 0xf1, 0xe8, 0x82, 0xaf, 0xbc, 0x8a, 0xbc, 0x01, 0xc9, + 0x35, 0xf2, 0x7f, 0x6f, 0x0c, 0x0d, 0xba, 0x87, 0xa4, 0xc3, 0xec, 0x60, 0x54, 0x49, 0x35, 0xeb, + 0x1e, 0x48, 0x2c, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b, + 0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee, + 0x9c, 0xf5, 0xb9 + }; + const size_t total5_used3_len = sizeof(total5_used3); + + const unsigned char total5_used5[] = { + 0x05, 0x00, 0x1f, 0xfd, 0xbb, 0xb6, 0xc2, 0x78, 0x82, 0xad, 0xe1, 0x66, 0x6d, 0x20, 0x4d, 0xfe, + 0x6b, 0xd2, 0x0b, 0x21, 0x6e, 0xa8, 0x5b, 0xc8, 0xe4, 0x88, 0x42, 0x11, 0x30, 0x3b, 0x6b, 0x02, + 0xc9, 0x7f, 0x44, 0x1c, 0xee, 0xd8, 0x37, 0x6a, 0xf8, 0xfd, 0xc8, 0x4b, 0x0b, 0xa1, 0x43, 0x1f, + 0x68, 0x77, 0x8d, 0x1b, 0xac, 0x9e, 0xc1, 0xc1, 0xda, 0x60, 0xa8, 0xcf, 0x10, 0x9d, 0x80, 0x07, + 0x90, 0x57, 0xb6, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b, + 0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee, + 0x9c, 0xf5, 0xb9, 0x5a, 0xc8, 0x03, 0x8d, 0x4f, 0xe3, 0x1d, 0x79, 0x38, 0x5a, 0xfa, 0xe5, 0xa8, + 0x9d, 0x56, 0x77, 0xb3, 0xf9, 0xa8, 0x70, 0x46, 0x27, 0x26, 0x6c, 0x6e, 0x54, 0xaf, 0xf9, 0xd0, + 0x37, 0xa4, 0x86, 0x68, 0x8f, 0xac, 0x3e, 0x78, 0xaa, 0x3d, 0x83, 0x1a, 0xca, 0x05, 0xfe, 0x10, + 0x95, 0xa4, 0x6a, 0x10, 0xc6, 0x62, 0xf3, 0xf7, 0xf3, 0x4d, 0x0b, 0xd4, 0x94, 0xe5, 0x51, 0x6c, + 0x85, 0xd7, 0xc7 + }; + const size_t total5_used5_len = sizeof(total5_used5); + + secp256k1_generator input_tags[5]; + secp256k1_generator output_tag; + secp256k1_surjectionproof proof; + + CHECK(secp256k1_generator_parse(ctx, &input_tags[0], tag0_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[1], tag1_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[2], tag2_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[3], tag3_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[4], tag4_ser)); + CHECK(secp256k1_generator_parse(ctx, &output_tag, output_tag_ser)); + + /* check 1-of-1 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &output_tag)); + /* check 1-of-2 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total2_used1, total2_used1_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 2, &output_tag)); + /* check 2-of-3 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total3_used2, total3_used2_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 3, &output_tag)); + /* check 3-of-5 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total5_used3, total5_used3_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 5, &output_tag)); + /* check 5-of-5 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total5_used5, total5_used5_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 5, &output_tag)); + + /* check invalid length fails */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, total5_used5, total5_used3_len)); + /* check invalid keys fail */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len)); + CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, &input_tags[1], 1, &output_tag)); + CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &input_tags[0])); +} + void run_surjection_tests(void) { int i; for (i = 0; i < count; i++) { test_surjectionproof_api(); } + test_fixed_vectors(); test_input_selection(0); test_input_selection(1); From 41bc9ce129e535c8b2498fddb2c02094f64fa61c Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 3 Jun 2019 21:45:48 +0000 Subject: [PATCH 057/381] surjectionproof: add test vectors for "set padding bits" --- src/modules/surjection/tests_impl.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 648d4aea..c2fc8237 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -603,6 +603,8 @@ void test_fixed_vectors(void) { }; const size_t total5_used5_len = sizeof(total5_used5); + unsigned char bad[sizeof(total5_used5) + 32] = { 0 }; + secp256k1_generator input_tags[5]; secp256k1_generator output_tag; secp256k1_surjectionproof proof; @@ -636,6 +638,24 @@ void test_fixed_vectors(void) { CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len)); CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, &input_tags[1], 1, &output_tag)); CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &input_tags[0])); + + /* Try setting 6 bits on the total5-used-5; check that parsing fails */ + memcpy(bad, total5_used5, total5_used5_len); + bad[2] = 0x3f; /* 0x1f -> 0x3f */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); + /* Correct for the length */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len + 32)); /* FIXME */ + /* Alternately just turn off one of the "legit" bits */ + bad[2] = 0x37; /* 0x1f -> 0x37 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); /* FIXME */ + + /* Similarly try setting 4 bits on the total5-used-3, with one bit out of range */ + memcpy(bad, total5_used3, total5_used3_len); + bad[2] = 0x35; /* 0x15 -> 0x35 */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len + 32)); /* FIXME */ + bad[2] = 0x34; /* 0x15 -> 0x34 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); /* FIXME */ } void run_surjection_tests(void) { From 68d937fe11005ba1ab574afaa8790a093d0dd249 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 3 Jun 2019 21:50:40 +0000 Subject: [PATCH 058/381] surjectionproof: fix malleability in surjection proof parsing --- src/modules/surjection/main_impl.h | 9 +++++++++ src/modules/surjection/tests_impl.h | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 1b0e5359..13e8f97e 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -55,6 +55,15 @@ int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surj return 0; } + /* Check that the bitvector of used inputs is of the claimed + * length; i.e. the final byte has no "padding bits" set */ + if (n_inputs % 8 != 0) { + const unsigned char padding_mask = (~0U) << (n_inputs % 8); + if ((input[2 + (n_inputs + 7) / 8 - 1] & padding_mask) != 0) { + return 0; + } + } + signature_len = 32 * (1 + secp256k1_count_bits_set(&input[2], (n_inputs + 7) / 8)); if (inputlen != 2 + (n_inputs + 7) / 8 + signature_len) { return 0; diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index c2fc8237..c6f0c64b 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -644,18 +644,18 @@ void test_fixed_vectors(void) { bad[2] = 0x3f; /* 0x1f -> 0x3f */ CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); /* Correct for the length */ - CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len + 32)); /* FIXME */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len + 32)); /* Alternately just turn off one of the "legit" bits */ bad[2] = 0x37; /* 0x1f -> 0x37 */ - CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); /* FIXME */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); /* Similarly try setting 4 bits on the total5-used-3, with one bit out of range */ memcpy(bad, total5_used3, total5_used3_len); bad[2] = 0x35; /* 0x15 -> 0x35 */ CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); - CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len + 32)); /* FIXME */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len + 32)); bad[2] = 0x34; /* 0x15 -> 0x34 */ - CHECK(secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); /* FIXME */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); } void run_surjection_tests(void) { From b8a3ff5f3ba85fadd342561c84ffa7263a64f97b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 30 Apr 2019 22:46:05 +0000 Subject: [PATCH 059/381] surjectionproof: reduce stack usage --- src/modules/surjection/main_impl.h | 22 ++++------------------ src/modules/surjection/surjection_impl.h | 19 +++++++++++-------- src/modules/surjection/tests_impl.h | 4 +--- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 13e8f97e..d08712a0 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -272,8 +272,6 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s size_t ring_input_index = 0; secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge output; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); @@ -312,17 +310,12 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s return 0; } - secp256k1_generator_load(&output, ephemeral_output_tag); - for (i = 0; i < n_total_pubkeys; i++) { - secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]); - } - - secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, input_index, &ring_input_index); + secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index); /* Produce signature */ rsizes[0] = (int) n_used_pubkeys; indices[0] = (int) ring_input_index; - secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output); + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) { return 0; } @@ -347,8 +340,6 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 size_t n_used_pubkeys; secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge output; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); @@ -364,12 +355,7 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return 0; } - secp256k1_generator_load(&output, ephemeral_output_tag); - for (i = 0; i < n_total_pubkeys; i++) { - secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]); - } - - if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, 0, NULL) == 0) { + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 0, NULL) == 0) { return 0; } @@ -382,7 +368,7 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return 0; } } - secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output); + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32); } diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index f58026de..c90c5767 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -15,7 +15,7 @@ #include "scalar.h" #include "hash.h" -SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag) { +SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, const secp256k1_generator *ephemeral_input_tags, size_t n_input_tags, const secp256k1_generator *ephemeral_output_tag) { /* compute message */ size_t i; unsigned char pk_ser[33]; @@ -24,12 +24,12 @@ SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg3 secp256k1_sha256_initialize(&sha256_en); for (i = 0; i < n_input_tags; i++) { - secp256k1_eckey_pubkey_serialize(&ephemeral_input_tags[i], pk_ser, &pk_len, 1); - assert(pk_len == sizeof(pk_ser)); + pk_ser[0] = 2 + (ephemeral_input_tags[i].data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_input_tags[i].data[0], 32); secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); } - secp256k1_eckey_pubkey_serialize(ephemeral_output_tag, pk_ser, &pk_len, 1); - assert(pk_len == sizeof(pk_ser)); + pk_ser[0] = 2 + (ephemeral_output_tag->data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_output_tag->data[0], 32); secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); secp256k1_sha256_finalize(&sha256_en, msg32); } @@ -61,15 +61,18 @@ SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, si return 1; } -SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index) { +SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_generator *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_generator *output_tag, size_t input_index, size_t *ring_input_index) { size_t i; size_t j = 0; for (i = 0; i < n_input_tags; i++) { if (used_tags[i / 8] & (1 << (i % 8))) { secp256k1_ge tmpge; - secp256k1_ge_neg(&tmpge, &input_tags[i]); + secp256k1_generator_load(&tmpge, &input_tags[i]); + secp256k1_ge_neg(&tmpge, &tmpge); secp256k1_gej_set_ge(&pubkeys[j], &tmpge); - secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], output_tag, NULL); + + secp256k1_generator_load(&tmpge, output_tag); + secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], &tmpge, NULL); if (ring_input_index != NULL && input_index == i) { *ring_input_index = j; } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index c6f0c64b..49f36847 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -456,7 +456,6 @@ static void test_no_used_inputs_verify(void) { size_t n_ephemeral_input_tags = 1; secp256k1_generator ephemeral_output_tag; unsigned char blinding_key[32]; - secp256k1_ge inputs[1]; secp256k1_ge output; secp256k1_sha256 sha256_e0; int result; @@ -477,8 +476,7 @@ static void test_no_used_inputs_verify(void) { /* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */ secp256k1_generator_load(&output, &ephemeral_output_tag); - secp256k1_generator_load(&inputs[0], &ephemeral_input_tags[0]); - secp256k1_surjection_genmessage(proof.data, inputs, 1, &output); + secp256k1_surjection_genmessage(proof.data, ephemeral_input_tags, 1, &ephemeral_output_tag); secp256k1_sha256_initialize(&sha256_e0); secp256k1_sha256_write(&sha256_e0, proof.data, 32); secp256k1_sha256_finalize(&sha256_e0, proof.data); From 56f69d979f173f58da7d45cdb551e821605ef72b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 30 Apr 2019 23:04:08 +0000 Subject: [PATCH 060/381] surjectionproof: introduce `SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS` constant and set it to 16 --- include/secp256k1_surjectionproof.h | 10 +++++++--- src/modules/surjection/main_impl.h | 17 ++++++++++------- src/modules/surjection/surjection_impl.h | 2 +- src/modules/surjection/tests_impl.h | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index d9a38e40..4f83458d 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -11,6 +11,9 @@ extern "C" { /** Maximum number of inputs that may be given in a surjection proof */ #define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256 +/** Maximum number of inputs that may be used in a surjection proof */ +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 + /** Number of bytes a serialized surjection proof requires given the * number of inputs and the number of used inputs. */ @@ -19,7 +22,7 @@ extern "C" { /** Maximum number of bytes a serialized surjection proof requires. */ #define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX \ - SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) + SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) /** Opaque data structure that holds a parsed surjection proof * @@ -46,7 +49,7 @@ typedef struct { /** Bitmap of which input tags are used in the surjection proof */ unsigned char used_inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8]; /** Borromean signature: e0, scalars */ - unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)]; + unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)]; } secp256k1_surjectionproof; /** Parse a surjection proof @@ -143,7 +146,8 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; * this won't match the output tag but might be used in the anonymity set.) * n_input_tags: the number of entries in the fixed_input_tags array - * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * Must be <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS * fixed_output_tag: fixed output tag * max_n_iterations: the maximum number of iterations to do before giving up. Because the * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index d08712a0..832377bc 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -9,11 +9,12 @@ #include #include +#include "include/secp256k1_rangeproof.h" +#include "include/secp256k1_surjectionproof.h" + #include "modules/rangeproof/borromean.h" #include "modules/surjection/surjection_impl.h" #include "hash.h" -#include "include/secp256k1_rangeproof.h" -#include "include/secp256k1_surjectionproof.h" static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) { size_t ret = 0; @@ -270,8 +271,8 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s size_t n_total_pubkeys; size_t n_used_pubkeys; size_t ring_input_index = 0; - secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); @@ -310,7 +311,9 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s return 0; } - secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index); + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index) == 0) { + return 0; + } /* Produce signature */ rsizes[0] = (int) n_used_pubkeys; @@ -338,8 +341,8 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 size_t i; size_t n_total_pubkeys; size_t n_used_pubkeys; - secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index c90c5767..1a839ff8 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -77,7 +77,7 @@ SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_g *ring_input_index = j; } j++; - if (j > n_pubkeys) { + if (j > n_pubkeys || j > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { return 0; } } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 49f36847..dbab9576 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -671,7 +671,7 @@ void run_surjection_tests(void) { test_input_selection_distribution(); test_gen_verify(10, 3); - test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); test_no_used_inputs_verify(); test_bad_serialize(); test_bad_parse(); From bd708201233abeee4de85dc84262b6954e169448 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Thu, 30 May 2019 09:04:40 +0300 Subject: [PATCH 061/381] allow reducing surjection proof size (to lower generation stack usage) --- configure.ac | 10 ++++++++++ include/secp256k1_surjectionproof.h | 6 +++++- src/modules/surjection/main_impl.h | 17 ++++++++++++++++- src/modules/surjection/tests_impl.h | 3 +-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 7e9ecb89..811aea0d 100644 --- a/configure.ac +++ b/configure.ac @@ -171,6 +171,11 @@ AC_ARG_ENABLE(module_surjectionproof, [enable_module_surjectionproof=$enableval], [enable_module_surjectionproof=no]) +AC_ARG_ENABLE(reduced_surjection_proof_size, + AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), + [use_reduced_surjection_proof_size=$enableval], + [use_reduced_surjection_proof_size=no]) + AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], [finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto]) @@ -569,6 +574,10 @@ if test x"$use_external_default_callbacks" = x"yes"; then AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used]) fi +if test x"$use_reduced_surjection_proof_size" = x"yes"; then + AC_DEFINE(USE_REDUCED_SURJECTION_PROOF_SIZE, 1, [Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, disabling parsing and verification]) +fi + if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([******]) AC_MSG_NOTICE([WARNING: experimental build]) @@ -651,6 +660,7 @@ AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x" AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) +AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = x"yes"]) dnl make sure nothing new is exported so that we don't break the cache PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index 4f83458d..ab7a4a9e 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -12,7 +12,7 @@ extern "C" { #define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256 /** Maximum number of inputs that may be used in a surjection proof */ -#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 256 /** Number of bytes a serialized surjection proof requires given the * number of inputs and the number of used inputs. @@ -52,6 +52,7 @@ typedef struct { unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)]; } secp256k1_surjectionproof; +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE /** Parse a surjection proof * * Returns: 1 when the proof could be parsed, 0 otherwise. @@ -73,6 +74,7 @@ SECP256K1_API int secp256k1_surjectionproof_parse( const unsigned char *input, size_t inputlen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +#endif /** Serialize a surjection proof * @@ -241,6 +243,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_generat ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE /** Surjection proof verification function * Returns 0: proof was invalid * 1: proof was valid @@ -258,6 +261,7 @@ SECP256K1_API int secp256k1_surjectionproof_verify( size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); +#endif #ifdef __cplusplus } diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 832377bc..e76ebbf9 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -9,13 +9,21 @@ #include #include +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + #include "include/secp256k1_rangeproof.h" #include "include/secp256k1_surjectionproof.h" - #include "modules/rangeproof/borromean.h" #include "modules/surjection/surjection_impl.h" #include "hash.h" +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +#undef SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 +#endif + static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) { size_t ret = 0; size_t i; @@ -36,6 +44,9 @@ static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) return ret; } +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surjectionproof *proof, const unsigned char *input, size_t inputlen) { size_t n_inputs; size_t signature_len; @@ -214,6 +225,7 @@ int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1 ARG_CHECK(fixed_output_tag != NULL); ARG_CHECK(random_seed32 != NULL); ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + ARG_CHECK(n_input_tags_to_use <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); ARG_CHECK(n_input_tags_to_use <= n_input_tags); (void) ctx; @@ -336,6 +348,9 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s return 1; } +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag) { size_t rsizes[1]; /* array needed for borromean sig API */ size_t i; diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index dbab9576..4885a8e8 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -666,8 +666,7 @@ void run_surjection_tests(void) { test_input_selection(0); test_input_selection(1); test_input_selection(5); - test_input_selection(100); - test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); test_input_selection_distribution(); test_gen_verify(10, 3); From d6738e890e7178b27f88244ea4ee20f8b53c1552 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 5 Jun 2019 11:15:11 +0200 Subject: [PATCH 062/381] surjection proof: Reject proofs with too many used inputs in reduced mode --- src/modules/surjection/main_impl.h | 5 +++++ src/modules/surjection/surjection_impl.h | 8 +++++--- src/modules/surjection/tests_impl.h | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index e76ebbf9..9614e5f7 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -373,6 +373,11 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return 0; } + /* Reject proofs with too many used inputs in USE_REDUCED_SURJECTION_PROOF_SIZE mode */ + if (n_used_pubkeys > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { + return 0; + } + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 0, NULL) == 0) { return 0; } diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index 1a839ff8..f3652567 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -69,6 +69,9 @@ SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_g secp256k1_ge tmpge; secp256k1_generator_load(&tmpge, &input_tags[i]); secp256k1_ge_neg(&tmpge, &tmpge); + + VERIFY_CHECK(j < SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); + VERIFY_CHECK(j < n_pubkeys); secp256k1_gej_set_ge(&pubkeys[j], &tmpge); secp256k1_generator_load(&tmpge, output_tag); @@ -77,11 +80,10 @@ SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_g *ring_input_index = j; } j++; - if (j > n_pubkeys || j > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { - return 0; - } } } + /* Caller needs to ensure that the number of set bits in used_tags (which we counted in j) equals n_pubkeys. */ + VERIFY_CHECK(j == n_pubkeys); return 1; } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 4885a8e8..ca0b09a0 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -427,6 +427,7 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); CHECK(result == 1); + /* various fail cases */ if (n_inputs > 1) { result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); @@ -441,6 +442,15 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { n_inputs += 1; } + for (i = 0; i < n_inputs; i++) { + /* flip bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); + CHECK(result == 0); + /* reset the bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + } + /* cleanup */ for (i = 0; i < n_inputs + 1; i++) { free(input_blinding_key[i]); From a4410ac779ed047e19d6c6e74fa219c03a4136e2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 25 Jun 2019 08:51:53 +0000 Subject: [PATCH 063/381] Add musig module tests to travis --- .travis.yml | 10 +++++----- contrib/travis.sh | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 49e6a7b7..2fbf8fcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,21 +17,21 @@ compiler: - gcc env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no matrix: - - SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes - - FIELD=64bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes + - SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes + - FIELD=64bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - SCALAR=32bit RECOVERY=yes - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes - SCALAR=64bit - FIELD=64bit RECOVERY=yes - FIELD=64bit ENDOMORPHISM=yes - - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck CTIMETEST= BENCH= - CPPFLAGS=-DDETERMINISTIC diff --git a/contrib/travis.sh b/contrib/travis.sh index 8140dc14..2224851b 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -21,7 +21,8 @@ fi --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ - --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" \ "$EXTRAFLAGS" "$USE_HOST" if [ -n "$BUILD" ] From d9240277653ebde6cd4536a0790025b3ee30fc64 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Jun 2019 18:21:22 +0000 Subject: [PATCH 064/381] Add tweak32 parameter to musig_partial_sig_combine which allows to sign for p2c/taproot commitments --- include/secp256k1_musig.h | 9 ++- src/modules/musig/example.c | 2 +- src/modules/musig/main_impl.h | 21 +++++- src/modules/musig/tests_impl.h | 117 +++++++++++++++++++++++++++++---- 4 files changed, 135 insertions(+), 14 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 657bacab..b24c94ce 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -366,13 +366,20 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif * Out: sig: complete signature (cannot be NULL) * In: partial_sigs: array of partial signatures to combine (cannot be NULL) * n_sigs: number of signatures in the partial_sigs array + * tweak32: if `combined_pk` was tweaked with `ec_pubkey_tweak_add` after + * `musig_pubkey_combine` and before `musig_session_initialize` then + * the same tweak must be provided here in order to get a valid + * signature for the tweaked key. Otherwise `tweak` should be NULL. + * If the tweak is larger than the group order or 0 this function will + * return 0. (can be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, - size_t n_sigs + size_t n_sigs, + const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Converts a partial signature to an adaptor signature by adding a given secret diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 5aebfa20..5a87dc30 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -119,7 +119,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } } } - return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig, partial_sig, N_SIGNERS); + return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig, partial_sig, N_SIGNERS, NULL); } int main(void) { diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 7215f4a5..5acfe7af 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -473,7 +473,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m return 1; } -int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { +int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs, const unsigned char *tweak32) { size_t i; secp256k1_scalar s; secp256k1_ge noncep; @@ -502,6 +502,25 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp secp256k1_scalar_add(&s, &s, &term); } + /* If there is a tweak then add `msghash` times `tweak` to `s`.*/ + if (tweak32 != NULL) { + unsigned char msghash[32]; + secp256k1_scalar e, scalar_tweak; + int overflow = 0; + + if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { + return 0; + } + secp256k1_scalar_set_b32(&e, msghash, NULL); + secp256k1_scalar_set_b32(&scalar_tweak, tweak32, &overflow); + if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) { + /* This mimics the behavior of secp256k1_ec_privkey_tweak_mul regarding + * overflow and tweak32 being 0. */ + return 0; + } + secp256k1_scalar_add(&s, &s, &e); + } + secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); VERIFY_CHECK(secp256k1_fe_is_quad_var(&noncep.y)); secp256k1_fe_normalize(&noncep.x); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 28301fa8..2986921f 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -34,6 +34,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_pubkey combined_pk; unsigned char pk_hash[32]; secp256k1_pubkey pk[2]; + unsigned char tweak[32]; unsigned char sec_adaptor[32]; unsigned char sec_adaptor1[32]; @@ -60,6 +61,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_rand256(sk[1]); secp256k1_rand256(msg); secp256k1_rand256(sec_adaptor); + secp256k1_rand256(tweak); CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); @@ -311,27 +313,36 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing combining and verification */ ecount = 0; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2) == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2, NULL) == 1); CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2, NULL) == 1); CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, NULL, &final_sig, partial_sig_adapted, 2) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, NULL, &final_sig, partial_sig_adapted, 2, tweak) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2, tweak) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, NULL, 2) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, NULL, 2, tweak) == 0); CHECK(ecount == 3); { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig_adapted[0]; partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_tmp, 2) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_tmp, 2, tweak) == 0); } CHECK(ecount == 3); /* Wrong number of partial sigs */ - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 1) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 1, tweak) == 0); + CHECK(ecount == 3); + { + /* Overflowing tweak */ + unsigned char overflowing_tweak[32]; + memset(overflowing_tweak, 0xff, sizeof(overflowing_tweak)); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, overflowing_tweak) == 0); + CHECK(ecount == 3); + } + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, NULL) == 1); CHECK(ecount == 3); CHECK(secp256k1_schnorrsig_verify(vrfy, &final_sig, msg, &combined_pk) == 1); @@ -497,7 +508,7 @@ int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pu CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); } partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]); - sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, &sig, partial_sigs, 2); + sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, &sig, partial_sigs, 2, NULL); if (do_combine != 0) { /* Return 1 if both succeeded */ return partial_verify && sig_combine; @@ -693,7 +704,7 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { * is broadcasted by signer 0 to take B-coins. */ CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], &final_sig_b, partial_sig_b_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], &final_sig_b, partial_sig_b_adapted, 2, NULL) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_b, msg32_b, &combined_pk_b) == 1); /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to @@ -702,7 +713,7 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], &final_sig_a, partial_sig_a, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], &final_sig_a, partial_sig_a, 2, NULL) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_a, msg32_a, &combined_pk_a) == 1); } @@ -739,6 +750,89 @@ void sha256_tag_test(void) { CHECK(memcmp(buf, buf2, 32) == 0); } + +void musig_tweak_test_helper(const secp256k1_pubkey* combined_pubkey, const unsigned char *ec_commit_tweak, const unsigned char *sk0, const unsigned char *sk1, const unsigned char *pk_hash) { + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signers0[2]; + secp256k1_musig_session_signer_data signers1[2]; + secp256k1_pubkey pk[2]; + unsigned char session_id[2][32]; + unsigned char msg[32]; + unsigned char nonce_commitment[2][32]; + secp256k1_pubkey nonce[2]; + const unsigned char *ncs[2]; + secp256k1_musig_partial_signature partial_sig[2]; + secp256k1_schnorrsig final_sig; + + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(msg); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk0) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk1) == 1); + + /* want to show that can both sign for Q and P */ + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pk_hash, 2, 0, sk0) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pk_hash, 2, 1, sk1) == 1); + /* Set nonce commitments */ + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2) == 1); + /* Set nonces */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], &final_sig, partial_sig, 2, ec_commit_tweak)); + CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig, msg, combined_pubkey) == 1); +} + +/* In this test we create a combined public key P and a commitment Q = P + + * hash(P, contract)*G. Then we test that we can sign for both public keys. In + * order to sign for Q we use the tweak32 argument of partial_sig_combine. */ +void musig_tweak_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_pubkey pk[2]; + unsigned char pk_hash[32]; + secp256k1_pubkey P; + unsigned char P_serialized[33]; + size_t compressed_size = 33; + secp256k1_pubkey Q; + + secp256k1_sha256 sha; + unsigned char contract[32]; + unsigned char ec_commit_tweak[32]; + + /* Setup */ + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(contract); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, pk_hash, pk, 2) == 1); + + CHECK(secp256k1_ec_pubkey_serialize(ctx, P_serialized, &compressed_size, &P, SECP256K1_EC_COMPRESSED) == 1); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, P_serialized, 33); + secp256k1_sha256_write(&sha, contract, 32); + secp256k1_sha256_finalize(&sha, ec_commit_tweak); + memcpy(&Q, &P, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &Q, ec_commit_tweak)); + + /* Test signing for P */ + musig_tweak_test_helper(&P, NULL, sk[0], sk[1], pk_hash); + /* Test signing for Q */ + musig_tweak_test_helper(&Q, ec_commit_tweak, sk[0], sk[1], pk_hash); +} + void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); @@ -750,6 +844,7 @@ void run_musig_tests(void) { scriptless_atomic_swap(scratch); } sha256_tag_test(); + musig_tweak_test(scratch); secp256k1_scratch_space_destroy(ctx, scratch); } From 9957307c3fa07b67bf3c45f7cab7f9be104fdf32 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 18 Aug 2019 15:56:44 +0000 Subject: [PATCH 065/381] Fix explanation of H derivation. It doesn't use DER encoding. --- src/modules/rangeproof/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 12cfc80f..54e98009 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -14,7 +14,7 @@ #include "modules/rangeproof/rangeproof_impl.h" /** Alternative generator for secp256k1. - * This is the sha256 of 'g' after DER encoding (without compression), + * This is the sha256 of 'g' after standard encoding (without compression), * which happens to be a point on the curve. More precisely, the generator is * derived by running the following script with the sage mathematics software. From bedff798489d3148df933132d3b185694bb42baf Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 19 Aug 2019 08:29:11 +0000 Subject: [PATCH 066/381] Add cplusplus directive to musig include --- include/secp256k1_musig.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index b24c94ce..7af7543f 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -1,6 +1,10 @@ #ifndef SECP256K1_MUSIG_H #define SECP256K1_MUSIG_H +#ifdef __cplusplus +extern "C" { +#endif + #include /** This module implements a Schnorr-based multi-signature scheme called MuSig @@ -426,4 +430,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_ad int nonce_is_negated ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +#ifdef __cplusplus +} +#endif + #endif From b368a5d1637a78b904f25c54871526c03b769b2f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 19 Aug 2019 08:35:15 +0000 Subject: [PATCH 067/381] Fix ARG_NONNULL macro usage in musig include --- include/secp256k1_musig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 7af7543f..744e8a8d 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -273,7 +273,7 @@ SECP256K1_API int secp256k1_musig_session_combine_nonces( size_t n_signers, int *nonce_is_negated, const secp256k1_pubkey *adaptor -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Sets the message of a session if previously unset * From b74f2dc478702080ac2d22a778a7ece5419fee45 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 19 Aug 2019 14:00:27 +0000 Subject: [PATCH 068/381] Remove mentions of DER in H derivation. --- src/modules/rangeproof/main_impl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 54e98009..c7f921fc 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -20,9 +20,9 @@ import hashlib F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) - G_DER = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' - G2 = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G_DER.decode('hex')).hexdigest(),16))) - print('%x %x' % G2.xy()) + G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' + H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) + print('%x %x' % H.xy()) */ static const secp256k1_generator secp256k1_generator_h_internal = {{ 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, From 4fd0d56e37c83e18192233203138cdf6369a30f1 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 21 Jun 2019 14:12:01 +0000 Subject: [PATCH 069/381] Fix my_index in musig state machine tests --- src/modules/musig/tests_impl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 2986921f..c53ebdf2 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -404,7 +404,7 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256 pks_tmp[0] = pks[0]; CHECK(secp256k1_ec_pubkey_create(ctx, &pks_tmp[1], sk_dummy) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, pk_hash_tmp, pks_tmp, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, pk_hash_tmp, 2, 0, sk_dummy) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, pk_hash_tmp, 2, 1, sk_dummy) == 1); CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); @@ -463,7 +463,7 @@ int musig_state_machine_missing_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey secp256k1_musig_partial_signature partial_sig; int partial_sign, partial_verify; - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); @@ -495,7 +495,7 @@ int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pu secp256k1_schnorrsig sig; int partial_verify, sig_combine; - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); From 96201b4f6e3d4c356fbdfb8b13e549c4cad51f03 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 21 Jun 2019 08:43:18 +0000 Subject: [PATCH 070/381] Require message in musig protocol in an earlier state. In particular, remove the set_msg function and require the message in get_public_nonce at the latest. --- include/secp256k1_musig.h | 29 +++------ src/modules/musig/example.c | 2 +- src/modules/musig/main_impl.h | 31 ++++----- src/modules/musig/musig.md | 5 +- src/modules/musig/tests_impl.h | 115 +++++++++++++++++---------------- 5 files changed, 82 insertions(+), 100 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 744e8a8d..b1c5b912 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -151,9 +151,9 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( * NULL). If a non-unique session_id32 was given then a partial * signature will LEAK THE SECRET KEY. * msg32: the 32-byte message to be signed. Shouldn't be NULL unless you - * require sharing public nonces before the message is known + * require sharing nonce commitments before the message is known * because it reduces nonce misuse resistance. If NULL, must be - * set with `musig_session_set_msg` before signing and verifying. + * set with `musig_session_get_public_nonce`. * combined_pk: the combined public key of all signers (cannot be NULL) * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be * NULL) @@ -190,6 +190,8 @@ SECP256K1_API int secp256k1_musig_session_initialize( * In: commitments: array of 32-byte nonce commitments (cannot be NULL) * n_commitments: the length of commitments and signers array. Must be the total * number of signers participating in the MuSig. + * msg32: the 32-byte message to be signed. Must be NULL if already + * set with `musig_session_initialize` otherwise can not be NULL. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( const secp256k1_context* ctx, @@ -197,7 +199,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, - size_t n_commitments + size_t n_commitments, + const unsigned char *msg32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Initializes a verifier session that can be used for verifying nonce commitments @@ -209,9 +212,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi * Out: session: the session structure to initialize (cannot be NULL) * signers: an array of signers' data to be initialized. Array length must * equal to `n_signers`(cannot be NULL) - * In: msg32: the 32-byte message to be signed If NULL, must be set with - * `musig_session_set_msg` before using the session for verifying - * partial signatures. + * In: msg32: the 32-byte message to be signed (cannot be NULL) * combined_pk: the combined public key of all signers (cannot be NULL) * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) * commitments: array of 32-byte nonce commitments. Array length must equal to @@ -229,7 +230,7 @@ SECP256K1_API int secp256k1_musig_session_initialize_verifier( const unsigned char *pk_hash32, const unsigned char *const *commitments, size_t n_signers -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); +) SECP256K1_ARG_NONNULL(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); /** Checks a signer's public nonce against a commitment to said nonce, and update * data structure if they match @@ -275,20 +276,6 @@ SECP256K1_API int secp256k1_musig_session_combine_nonces( const secp256k1_pubkey *adaptor ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Sets the message of a session if previously unset - * - * Returns 1 if the message was not set yet and is now successfully set - * 0 otherwise - * Args: ctx: pointer to a context object (cannot be NULL) - * session: the session structure to update with the message (cannot be NULL) - * In: msg32: the 32-byte message to be signed (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_set_msg( - const secp256k1_context* ctx, - secp256k1_musig_session *session, - const unsigned char *msg32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - /** Serialize a MuSig partial signature or adaptor signature * * Returns: 1 when the signature could be serialized, 0 otherwise diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 5a87dc30..b4c9a95d 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -77,7 +77,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 /* Communication round 1: Exchange nonce commitments */ for (i = 0; i < N_SIGNERS; i++) { /* Set nonce commitments in the signer data and get the own public nonce */ - if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], &nonce[i], nonce_commitment_ptr, N_SIGNERS)) { + if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], &nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) { return 0; } } diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 5acfe7af..d1123539 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -211,7 +211,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m return 1; } -int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments) { +int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) { secp256k1_sha256 sha; unsigned char nonce_commitments_hash[32]; size_t i; @@ -222,6 +222,10 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 ARG_CHECK(signers != NULL); ARG_CHECK(nonce != NULL); ARG_CHECK(commitments != NULL); + /* If the message was not set during initialization it must be set now. */ + ARG_CHECK(!(!session->msg_is_set && msg32 == NULL)); + /* The message can only be set once. */ + ARG_CHECK(!(session->msg_is_set && msg32 != NULL)); if (!session->has_secret_data || n_commitments != session->n_signers) { return 0; @@ -230,6 +234,10 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 ARG_CHECK(commitments[i] != NULL); } + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + } secp256k1_sha256_initialize(&sha); for (i = 0; i < n_commitments; i++) { memcpy(signers[i].nonce_commitment, commitments[i], 32); @@ -254,6 +262,7 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se VERIFY_CHECK(ctx != NULL); ARG_CHECK(session != NULL); ARG_CHECK(signers != NULL); + ARG_CHECK(msg32 != NULL); ARG_CHECK(combined_pk != NULL); ARG_CHECK(pk_hash32 != NULL); ARG_CHECK(commitments != NULL); @@ -278,11 +287,8 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se memcpy(session->pk_hash, pk_hash32, 32); session->nonce_is_set = 0; - session->msg_is_set = 0; - if (msg32 != NULL) { - memcpy(session->msg, msg32, 32); - session->msg_is_set = 1; - } + session->msg_is_set = 1; + memcpy(session->msg, msg32, 32); session->has_secret_data = 0; session->nonce_commitments_hash_is_set = 0; @@ -373,19 +379,6 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 return 1; } -int secp256k1_musig_session_set_msg(const secp256k1_context* ctx, secp256k1_musig_session *session, const unsigned char *msg32) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(msg32 != NULL); - - if (session->msg_is_set) { - return 0; - } - memcpy(session->msg, msg32, 32); - session->msg_is_set = 1; - return 1; -} - int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) { VERIFY_CHECK(ctx != NULL); ARG_CHECK(out32 != NULL); diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index 015ce91b..112dcc4c 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -91,6 +91,8 @@ signature process, which is also a supported mode) acts as follows. length-32 byte arrays which can be communicated however is communicated. 3. Once all signers nonce commitments have been received, the signer records these commitments with the function `secp256k1_musig_session_get_public_nonce`. + If the signer did not provide a message to `secp256k1_musig_session_initialize`, + a message must be provided now. This function updates in place - the session state `session` - the array of signer data `signers` @@ -111,9 +113,6 @@ signature process, which is also a supported mode) acts as follows. - the array of signer data `signers` It outputs an auxiliary integer `nonce_is_negated` and has an auxiliary input `adaptor`. Both of these may be set to NULL for ordinary signing purposes. - If the signer did not provide a message to `secp256k1_musig_session_initialize`, - a message must be provided now by calling `secp256k1_musig_session_set_msg` which - updates the session state in place. 6. The signer computes a partial signature `s_i` using the function `secp256k1_musig_partial_sign` which takes the session state as input and partial signature as output. diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index c53ebdf2..ce5b37d0 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -136,13 +136,6 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, ones) == 0); CHECK(ecount == 9); - - { - secp256k1_musig_session session_without_msg; - CHECK(secp256k1_musig_session_initialize(sign, &session_without_msg, signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_set_msg(none, &session_without_msg, msg) == 1); - CHECK(secp256k1_musig_session_set_msg(none, &session_without_msg, msg) == 0); - } CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); ncs[0] = nonce_commitment[0]; @@ -153,20 +146,20 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 0); CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, pk_hash, ncs, 2) == 1); - CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, pk_hash, ncs, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, pk_hash, ncs, 2) == 0); CHECK(ecount == 3); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(ecount == 4); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, NULL, 2) == 0); - CHECK(ecount == 4); + CHECK(ecount == 5); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 0) == 0); - CHECK(ecount == 4); + CHECK(ecount == 5); if (SIZE_MAX > UINT32_MAX) { CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, ((size_t) UINT32_MAX) + 2) == 0); } - CHECK(ecount == 4); + CHECK(ecount == 5); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0); @@ -178,7 +171,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_pubkey nonce; /* Can obtain public nonce after commitments have been exchanged; still can't sign */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 0); CHECK(ecount == 0); } @@ -188,22 +181,22 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { { secp256k1_pubkey public_nonce[3]; - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], NULL, &public_nonce[0], ncs, 2) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], NULL, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, NULL, ncs, 2) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, NULL, ncs, 2, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], NULL, 2) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], NULL, 2, NULL) == 0); CHECK(ecount == 4); /* Number of commitments and number of signers are different */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 1) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 1, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer0[0], &public_nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0); @@ -410,8 +403,8 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256 CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); /* Call get_public_nonce with different signers than the signers the session was * initialized with. */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, &nonce, nonce_commitments, 2) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, &nonce, nonce_commitments, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, &nonce, nonce_commitments, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, &nonce, nonce_commitments, 2, NULL) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); @@ -438,7 +431,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combin CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); @@ -451,34 +444,44 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combin return secp256k1_musig_session_combine_nonces(ctx, &session, signers_to_use, 2, NULL, NULL); } -/* Recreates a session with the given session_id, signers, pk, msg etc. parameters - * and tries to sign and verify the other signers partial signature. Both should fail - * if msg is NULL. */ -int musig_state_machine_missing_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { +/* Initializaes a session with the given session_id, signers, pk, msg etc. + * parameters but without a message. Will test that the message must be + * provided with `get_public_nonce`. + */ +void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { + /* Create context for testing ARG_CHECKs by setting an illegal_callback. */ + secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + int ecount = 0; secp256k1_musig_session session; secp256k1_musig_session_signer_data signers[2]; unsigned char nonce_commitment[32]; const unsigned char *ncs[2]; secp256k1_pubkey nonce; secp256k1_musig_partial_signature partial_sig; - int partial_sign, partial_verify; - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pk_hash, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + + /* Trying to get the nonce without providing a message fails. */ + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, &nonce, ncs, 2, NULL) == 0); + CHECK(ecount == 1); + + /* Providing a message should make get_public_nonce succeed. */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, msg) == 1); + /* Trying to set the message again fails. */ + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, &nonce, ncs, 2, msg) == 0); + CHECK(ecount == 2); + + /* Check that it's working */ CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); - partial_sign = secp256k1_musig_partial_sign(ctx, &session, &partial_sig); - partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, &signers[0], partial_sig_other, &pks[0]); - if (msg != NULL) { - /* Return 1 if both succeeded */ - return partial_sign && partial_verify; - } - /* Return 0 if both failed */ - return partial_sign || partial_verify; + CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig)); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); } /* Recreates a session with the given session_id, signers, pk, msg etc. parameters @@ -498,7 +501,7 @@ int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pu CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); @@ -553,16 +556,16 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); /* Changing a nonce commitment is not okay */ ncs[1] = (unsigned char*) "this isn't a nonce commitment..."; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 0); /* Repeating with the same nonce commitments is okay */ ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); /* Get nonce for signer 1 */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); /* Set nonces */ CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); @@ -597,12 +600,12 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, pk_hash, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); CHECK(memcmp(msghash1, msghash2, 32) == 0); CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[1], &pk[1]) == 1); /* Wrong signature */ CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); - /* Can't sign or verify until msg is set */ - CHECK(musig_state_machine_missing_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], sk[1], session_id[1], NULL) == 0); - CHECK(musig_state_machine_missing_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], sk[1], session_id[1], msg) == 1); + /* Can't get the public nonce until msg is set */ + musig_state_machine_late_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); /* Can't verify and combine partial sigs until nonces are combined */ CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); @@ -676,10 +679,10 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { noncommit_b_ptr[1] = noncommit_b[1]; /* Step 2: Exchange nonces */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, &pubnon_a[0], noncommit_a_ptr, 2)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, &pubnon_a[1], noncommit_a_ptr, 2)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, &pubnon_b[0], noncommit_b_ptr, 2)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, &pubnon_b[1], noncommit_b_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, &pubnon_a[0], noncommit_a_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, &pubnon_a[1], noncommit_a_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, &pubnon_b[0], noncommit_b_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, &pubnon_b[1], noncommit_b_ptr, 2, NULL)); CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], &pubnon_a[0])); CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], &pubnon_a[1])); CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], &pubnon_b[0])); @@ -777,8 +780,8 @@ void musig_tweak_test_helper(const secp256k1_pubkey* combined_pubkey, const unsi /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); /* Set nonces */ CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); From fabc8f74e7e4d624d4d2d1aba621b8a75c729e5e Mon Sep 17 00:00:00 2001 From: Jason Davies Date: Wed, 6 May 2020 11:17:29 +0100 Subject: [PATCH 071/381] Fix typo in MuSig documentation. --- src/modules/musig/musig.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index 112dcc4c..ec1f1df5 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -14,7 +14,7 @@ The resulting signatures are valid Schnorr signatures as described in [2]. In MuSig all signers contribute key material to a single signing key, using the equation - P = sum_i µ_i - P_i + P = sum_i µ_i * P_i where `P_i` is the public key of the `i`th signer and `µ_i` is a so-called _MuSig coefficient_ computed according to the following equation From 8b70795b5e6e1e702a42a0836aeede932877ea6d Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 9 Oct 2020 14:16:07 +0000 Subject: [PATCH 072/381] Fix BE platforms by updating endianness macros to match upstream --- src/modules/rangeproof/borromean_impl.h | 4 ++-- src/scalar_4x64_impl.h | 4 ++-- src/scalar_8x32_impl.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 3a82f096..e11db181 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -20,9 +20,9 @@ #include #include -#ifdef WORDS_BIGENDIAN +#if defined(SECP256K1_BIG_ENDIAN) #define BE32(x) (x) -#else +#elif defined(SECP256K1_LITTLE_ENDIAN) #define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #endif diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 294413bd..2cd0f9bc 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -974,9 +974,9 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se a += b; d = ROTL32(d ^ a, 8); \ c += d; b = ROTL32(b ^ c, 7); -#ifdef WORDS_BIGENDIAN +#if defined(SECP256K1_BIG_ENDIAN) #define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#else +#elif defined(SECP256K1_LITTLE_ENDIAN) #define LE32(p) (p) #endif diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index c12b06e6..9afb9e95 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -753,9 +753,9 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se a += b; d = ROTL32(d ^ a, 8); \ c += d; b = ROTL32(b ^ c, 7); -#ifdef WORDS_BIGENDIAN +#if defined(SECP256K1_BIG_ENDIAN) #define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#else +#elif defined(SECP256K1_LITTLE_ENDIAN) #define LE32(p) (p) #endif From bac746c55e72abc1cc1ba1e8e6fabb2fea503cfe Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 14 Oct 2020 14:50:44 +0000 Subject: [PATCH 073/381] (temporarily) disable musig module --- Makefile.am | 6 +++--- configure.ac | 34 +++++++++++++++++----------------- contrib/travis.sh | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index dcc8693f..27a16bb7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -155,9 +155,9 @@ if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif -if ENABLE_MODULE_MUSIG -include src/modules/musig/Makefile.am.include -endif +#if ENABLE_MODULE_MUSIG +#include src/modules/musig/Makefile.am.include +#endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include diff --git a/configure.ac b/configure.ac index ddfc76bd..1107fb20 100644 --- a/configure.ac +++ b/configure.ac @@ -136,10 +136,10 @@ AC_ARG_ENABLE(module_schnorrsig, [enable_module_schnorrsig=$enableval], [enable_module_schnorrsig=no]) -AC_ARG_ENABLE(module_musig, - AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), - [enable_module_musig=$enableval], - [enable_module_musig=no]) +#AC_ARG_ENABLE(module_musig, +# AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), +# [enable_module_musig=$enableval], +# [enable_module_musig=no]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), @@ -468,9 +468,9 @@ if test x"$enable_module_schnorrsig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) fi -if test x"$enable_module_musig" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) -fi +#if test x"$enable_module_musig" = x"yes"; then +# AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) +#fi if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) @@ -514,15 +514,15 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) - AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) +# AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([******]) - if test x"$enable_module_schnorrsig" != x"yes"; then - if test x"$enable_module_musig" = x"yes"; then - AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) - fi - fi +# if test x"$enable_module_schnorrsig" != x"yes"; then +# if test x"$enable_module_musig" = x"yes"; then +# AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) +# fi +# fi if test x"$enable_module_generator" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then @@ -545,9 +545,9 @@ else if test x"$enable_module_schnorrsig" = x"yes"; then AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) fi - if test x"$enable_module_musig" = x"yes"; then - AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) - fi +# if test x"$enable_module_musig" = x"yes"; then +# AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) +# fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -578,7 +578,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) 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_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) diff --git a/contrib/travis.sh b/contrib/travis.sh index fad6bc18..25124822 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -18,7 +18,7 @@ fi --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" \ + --enable-module-schnorrsig="$SCHNORRSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ] From a11250330b24b3dffdf11d2de5d496397b4e4410 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 14 Oct 2020 14:53:21 +0000 Subject: [PATCH 074/381] (actually) remove schnorrsig module --- .gitignore | 1 - Makefile.am | 4 - configure.ac | 15 - contrib/travis.sh | 1 - include/secp256k1.h | 6 - include/secp256k1_schnorrsig.h | 129 ---- src/bench_schnorrsig.c | 129 ---- src/modules/schnorrsig/Makefile.am.include | 8 - src/modules/schnorrsig/main_impl.h | 338 ---------- src/modules/schnorrsig/tests_impl.h | 726 --------------------- src/secp256k1.c | 28 - src/tests.c | 9 - 12 files changed, 1394 deletions(-) delete mode 100644 include/secp256k1_schnorrsig.h delete mode 100644 src/bench_schnorrsig.c delete mode 100644 src/modules/schnorrsig/Makefile.am.include delete mode 100644 src/modules/schnorrsig/main_impl.h delete mode 100644 src/modules/schnorrsig/tests_impl.h diff --git a/.gitignore b/.gitignore index 085bd54d..b95250fc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ bench_ecdh bench_ecmult bench_generator bench_rangeproof -bench_schnorrsig bench_sign bench_verify bench_recover diff --git a/Makefile.am b/Makefile.am index 27a16bb7..99c01035 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,10 +151,6 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif -if ENABLE_MODULE_SCHNORRSIG -include src/modules/schnorrsig/Makefile.am.include -endif - #if ENABLE_MODULE_MUSIG #include src/modules/musig/Makefile.am.include #endif diff --git a/configure.ac b/configure.ac index 1107fb20..6121cc16 100644 --- a/configure.ac +++ b/configure.ac @@ -131,11 +131,6 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) -AC_ARG_ENABLE(module_schnorrsig, - AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), - [enable_module_schnorrsig=$enableval], - [enable_module_schnorrsig=no]) - #AC_ARG_ENABLE(module_musig, # AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), # [enable_module_musig=$enableval], @@ -464,10 +459,6 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi -if test x"$enable_module_schnorrsig" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) -fi - #if test x"$enable_module_musig" = x"yes"; then # AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) #fi @@ -513,7 +504,6 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) - AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) # AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([******]) @@ -542,9 +532,6 @@ else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi - if test x"$enable_module_schnorrsig" = x"yes"; then - AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) - fi # if test x"$enable_module_musig" = x"yes"; then # AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) # fi @@ -577,7 +564,6 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) -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_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) @@ -604,7 +590,6 @@ echo " with benchmarks = $use_benchmark" echo " with coverage = $enable_coverage" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" -echo " module schnorrsig = $enable_module_schnorrsig" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/contrib/travis.sh b/contrib/travis.sh index 25124822..833536d9 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -18,7 +18,6 @@ fi --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ] diff --git a/include/secp256k1.h b/include/secp256k1.h index 8413a401..2178c8e2 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -525,12 +525,6 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; -/** An implementation of the nonce generation function as defined in BIP-schnorr. - * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of - * extra entropy. - */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr; - /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h deleted file mode 100644 index 4c0f263d..00000000 --- a/include/secp256k1_schnorrsig.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef SECP256K1_SCHNORRSIG_H -#define SECP256K1_SCHNORRSIG_H - -#include "secp256k1.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** This module implements a variant of Schnorr signatures compliant with - * BIP-schnorr - * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki). - */ - -/** Opaque data structure that holds a parsed Schnorr signature. - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, or - * comparison, use the `secp256k1_schnorrsig_serialize` and - * `secp256k1_schnorrsig_parse` functions. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_schnorrsig; - -/** Serialize a Schnorr signature. - * - * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: out64: pointer to a 64-byte array to store the serialized signature - * In: sig: pointer to the signature - * - * See secp256k1_schnorrsig_parse for details about the encoding. - */ -SECP256K1_API int secp256k1_schnorrsig_serialize( - const secp256k1_context* ctx, - unsigned char *out64, - const secp256k1_schnorrsig* sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse a Schnorr signature. - * - * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: pointer to a signature object - * In: in64: pointer to the 64-byte signature to be parsed - * - * The signature is serialized in the form R||s, where R is a 32-byte public - * key (x-coordinate only; the y-coordinate is considered to be the unique - * y-coordinate satisfying the curve equation that is a quadratic residue) - * and s is a 32-byte big-endian scalar. - * - * After the call, sig will always be initialized. If parsing failed or the - * encoded numbers are out of range, signature validation with it is - * guaranteed to fail for every message and public key. - */ -SECP256K1_API int secp256k1_schnorrsig_parse( - const secp256k1_context* ctx, - secp256k1_schnorrsig* sig, - const unsigned char *in64 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Create a Schnorr signature. - * - * Returns 1 on success, 0 on failure. - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: sig: pointer to the returned signature (cannot be NULL) - * nonce_is_negated: a pointer to an integer indicates if signing algorithm negated the - * nonce (can be NULL) - * In: msg32: the 32-byte message hash being signed (cannot be NULL) - * seckey: pointer to a 32-byte secret key (cannot be NULL) - * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) - */ -SECP256K1_API int secp256k1_schnorrsig_sign( - const secp256k1_context* ctx, - secp256k1_schnorrsig *sig, - int *nonce_is_negated, - const unsigned char *msg32, - const unsigned char *seckey, - secp256k1_nonce_function noncefp, - void *ndata -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - -/** Verify a Schnorr signature. - * - * Returns: 1: correct signature - * 0: incorrect or unparseable signature - * Args: ctx: a secp256k1 context object, initialized for verification. - * In: sig: the signature being verified (cannot be NULL) - * msg32: the 32-byte message hash being verified (cannot be NULL) - * pubkey: pointer to a public key to verify with (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( - const secp256k1_context* ctx, - const secp256k1_schnorrsig *sig, - const unsigned char *msg32, - const secp256k1_pubkey *pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Verifies a set of Schnorr signatures. - * - * Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0. - * - * Args: ctx: a secp256k1 context object, initialized for verification. - * scratch: scratch space used for the multiexponentiation - * In: sig: array of signatures, or NULL if there are no signatures - * msg32: array of messages, or NULL if there are no signatures - * pk: array of public keys, or NULL if there are no signatures - * n_sigs: number of signatures in above arrays. Must be smaller than - * 2^31 and smaller than half the maximum size_t value. Must be 0 - * if above arrays are NULL. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch( - const secp256k1_context* ctx, - secp256k1_scratch_space *scratch, - const secp256k1_schnorrsig *const *sig, - const unsigned char *const *msg32, - const secp256k1_pubkey *const *pk, - size_t n_sigs -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -#ifdef __cplusplus -} -#endif - -#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c deleted file mode 100644 index a22e3496..00000000 --- a/src/bench_schnorrsig.c +++ /dev/null @@ -1,129 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#include -#include - -#include "include/secp256k1.h" -#include "include/secp256k1_schnorrsig.h" -#include "util.h" -#include "bench.h" - -typedef struct { - secp256k1_context *ctx; - secp256k1_scratch_space *scratch; - int n; - const unsigned char **pk; - const secp256k1_schnorrsig **sigs; - const unsigned char **msgs; -} bench_schnorrsig_data; - -void bench_schnorrsig_sign(void* arg, int iters) { - bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; - int i; - unsigned char sk[32] = "benchmarkexample secrettemplate"; - unsigned char msg[32] = "benchmarkexamplemessagetemplate"; - secp256k1_schnorrsig sig; - - for (i = 0; i < iters; i++) { - msg[0] = i; - msg[1] = i >> 8; - sk[0] = i; - sk[1] = i >> 8; - CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, NULL, msg, sk, NULL, NULL)); - } -} - -void bench_schnorrsig_verify(void* arg, int iters) { - bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; - int i; - - for (i = 0; i < iters; i++) { - secp256k1_pubkey pk; - CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pk, data->pk[i], 33) == 1); - CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk)); - } -} - -void bench_schnorrsig_verify_n(void* arg, int iters) { - bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; - int i, j; - const secp256k1_pubkey **pk = (const secp256k1_pubkey **)malloc(data->n * sizeof(*pk)); - - CHECK(pk != NULL); - for (j = 0; j < iters/data->n; j++) { - for (i = 0; i < data->n; i++) { - secp256k1_pubkey *pk_nonconst = (secp256k1_pubkey *)malloc(sizeof(*pk_nonconst)); - CHECK(secp256k1_ec_pubkey_parse(data->ctx, pk_nonconst, data->pk[i], 33) == 1); - pk[i] = pk_nonconst; - } - CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n)); - for (i = 0; i < data->n; i++) { - free((void *)pk[i]); - } - } - free(pk); -} - -int main(void) { - int i; - bench_schnorrsig_data data; - int iters = get_iters(1000); - - data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); - data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024); - data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); - data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); - data.sigs = (const secp256k1_schnorrsig **)malloc(iters * sizeof(secp256k1_schnorrsig *)); - - for (i = 0; i < iters; i++) { - unsigned char sk[32]; - unsigned char *msg = (unsigned char *)malloc(32); - secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig)); - unsigned char *pk_char = (unsigned char *)malloc(33); - secp256k1_pubkey pk; - size_t pk_len = 33; - msg[0] = sk[0] = i; - msg[1] = sk[1] = i >> 8; - msg[2] = sk[2] = i >> 16; - msg[3] = sk[3] = i >> 24; - memset(&msg[4], 'm', 28); - memset(&sk[4], 's', 28); - - data.pk[i] = pk_char; - data.msgs[i] = msg; - data.sigs[i] = sig; - - CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk)); - CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, NULL, msg, sk, NULL, NULL)); - } - - run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters); - run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters); - for (i = 1; i <= iters; i *= 2) { - char name[64]; - int divisible_iters; - sprintf(name, "schnorrsig_batch_verify_%d", (int) i); - - data.n = i; - divisible_iters = iters - (iters % data.n); - run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, divisible_iters); - } - - for (i = 0; i < iters; i++) { - free((void *)data.pk[i]); - free((void *)data.msgs[i]); - free((void *)data.sigs[i]); - } - free(data.pk); - free(data.msgs); - free(data.sigs); - - secp256k1_scratch_space_destroy(data.ctx, data.scratch); - secp256k1_context_destroy(data.ctx); - return 0; -} diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include deleted file mode 100644 index a82bafe4..00000000 --- a/src/modules/schnorrsig/Makefile.am.include +++ /dev/null @@ -1,8 +0,0 @@ -include_HEADERS += include/secp256k1_schnorrsig.h -noinst_HEADERS += src/modules/schnorrsig/main_impl.h -noinst_HEADERS += src/modules/schnorrsig/tests_impl.h -if USE_BENCHMARK -noinst_PROGRAMS += bench_schnorrsig -bench_schnorrsig_SOURCES = src/bench_schnorrsig.c -bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) -endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h deleted file mode 100644 index b0310ab9..00000000 --- a/src/modules/schnorrsig/main_impl.h +++ /dev/null @@ -1,338 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ -#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ - -#include "include/secp256k1.h" -#include "include/secp256k1_schnorrsig.h" -#include "hash.h" - -int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig) { - (void) ctx; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out64 != NULL); - ARG_CHECK(sig != NULL); - memcpy(out64, sig->data, 64); - return 1; -} - -int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64) { - (void) ctx; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(in64 != NULL); - memcpy(sig->data, in64, 64); - return 1; -} - -int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, int *nonce_is_negated, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { - secp256k1_scalar x; - secp256k1_scalar e; - secp256k1_scalar k; - secp256k1_gej pkj; - secp256k1_gej rj; - secp256k1_ge pk; - secp256k1_ge r; - secp256k1_sha256 sha; - int overflow; - unsigned char buf[33]; - size_t buflen = sizeof(buf); - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(sig != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(seckey != NULL); - - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_bipschnorr; - } - secp256k1_scalar_set_b32(&x, seckey, &overflow); - /* Fail if the secret key is invalid. */ - if (overflow || secp256k1_scalar_is_zero(&x)) { - memset(sig, 0, sizeof(*sig)); - return 0; - } - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); - secp256k1_ge_set_gej(&pk, &pkj); - - if (!noncefp(buf, msg32, seckey, NULL, (void*)ndata, 0)) { - return 0; - } - secp256k1_scalar_set_b32(&k, buf, NULL); - if (secp256k1_scalar_is_zero(&k)) { - return 0; - } - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); - secp256k1_ge_set_gej(&r, &rj); - - if (nonce_is_negated != NULL) { - *nonce_is_negated = 0; - } - if (!secp256k1_fe_is_quad_var(&r.y)) { - secp256k1_scalar_negate(&k, &k); - if (nonce_is_negated != NULL) { - *nonce_is_negated = 1; - } - } - secp256k1_fe_normalize(&r.x); - secp256k1_fe_get_b32(&sig->data[0], &r.x); - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, &sig->data[0], 32); - secp256k1_eckey_pubkey_serialize(&pk, buf, &buflen, 1); - secp256k1_sha256_write(&sha, buf, buflen); - secp256k1_sha256_write(&sha, msg32, 32); - secp256k1_sha256_finalize(&sha, buf); - - secp256k1_scalar_set_b32(&e, buf, NULL); - secp256k1_scalar_mul(&e, &e, &x); - secp256k1_scalar_add(&e, &e, &k); - - secp256k1_scalar_get_b32(&sig->data[32], &e); - secp256k1_scalar_clear(&k); - secp256k1_scalar_clear(&x); - - return 1; -} - -/* Helper function for verification and batch verification. - * Computes R = sG - eP. */ -static int secp256k1_schnorrsig_real_verify(const secp256k1_context* ctx, secp256k1_gej *rj, const secp256k1_scalar *s, const secp256k1_scalar *e, const secp256k1_pubkey *pk) { - secp256k1_scalar nege; - secp256k1_ge pkp; - secp256k1_gej pkj; - - secp256k1_scalar_negate(&nege, e); - - if (!secp256k1_pubkey_load(ctx, &pkp, pk)) { - return 0; - } - secp256k1_gej_set_ge(&pkj, &pkp); - - /* rj = s*G + (-e)*pkj */ - secp256k1_ecmult(&ctx->ecmult_ctx, rj, &pkj, &nege, s); - return 1; -} - -int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk) { - secp256k1_scalar s; - secp256k1_scalar e; - secp256k1_gej rj; - secp256k1_fe rx; - secp256k1_sha256 sha; - unsigned char buf[33]; - size_t buflen = sizeof(buf); - int overflow; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(sig != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(pk != NULL); - - if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { - return 0; - } - - secp256k1_scalar_set_b32(&s, &sig->data[32], &overflow); - if (overflow) { - return 0; - } - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, &sig->data[0], 32); - secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk, SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, buf, buflen); - secp256k1_sha256_write(&sha, msg32, 32); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(&e, buf, NULL); - - if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pk) - || !secp256k1_gej_has_quad_y_var(&rj) /* fails if rj is infinity */ - || !secp256k1_gej_eq_x_var(&rx, &rj)) { - return 0; - } - - return 1; -} - -/* Data that is used by the batch verification ecmult callback */ -typedef struct { - const secp256k1_context *ctx; - /* Seed for the random number generator */ - unsigned char chacha_seed[32]; - /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching - * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and - * the PRNG is called at every odd indexed schnorrsig to fill the cache. */ - secp256k1_scalar randomizer_cache[2]; - /* Signature, message, public key tuples to verify */ - const secp256k1_schnorrsig *const *sig; - const unsigned char *const *msg32; - const secp256k1_pubkey *const *pk; - size_t n_sigs; -} secp256k1_schnorrsig_verify_ecmult_context; - -/* Callback function which is called by ecmult_multi in order to convert the ecmult_context - * consisting of signature, message and public key tuples into scalars and points. */ -static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { - secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data; - - if (idx % 4 == 2) { - /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4 - * consecutive tuples before we need to call the RNG for new randomizers: - * (-randomizer_cache[0], R1) - * (-randomizer_cache[0]*e1, P1) - * (-randomizer_cache[1], R2) - * (-randomizer_cache[1]*e2, P2) */ - secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4); - } - - /* R */ - if (idx % 2 == 0) { - secp256k1_fe rx; - *sc = ecmult_context->randomizer_cache[(idx / 2) % 2]; - if (!secp256k1_fe_set_b32(&rx, &ecmult_context->sig[idx / 2]->data[0])) { - return 0; - } - if (!secp256k1_ge_set_xquad(pt, &rx)) { - return 0; - } - /* eP */ - } else { - unsigned char buf[33]; - size_t buflen = sizeof(buf); - secp256k1_sha256 sha; - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2]->data[0], 32); - secp256k1_ec_pubkey_serialize(ecmult_context->ctx, buf, &buflen, ecmult_context->pk[idx / 2], SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, buf, buflen); - secp256k1_sha256_write(&sha, ecmult_context->msg32[idx / 2], 32); - secp256k1_sha256_finalize(&sha, buf); - - secp256k1_scalar_set_b32(sc, buf, NULL); - secp256k1_scalar_mul(sc, sc, &ecmult_context->randomizer_cache[(idx / 2) % 2]); - - if (!secp256k1_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])) { - return 0; - } - } - return 1; -} - -/** Helper function for batch verification. Hashes signature verification data into the - * randomization seed and initializes ecmult_context. - * - * Returns 1 if the randomizer was successfully initialized. - * - * Args: ctx: a secp256k1 context object - * Out: ecmult_context: context for batch_ecmult_callback - * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a - * seed for the randomizer PRNG - * In: sig: array of signatures, or NULL if there are no signatures - * msg32: array of messages, or NULL if there are no signatures - * pk: array of public keys, or NULL if there are no signatures - * n_sigs: number of signatures in above arrays (must be 0 if they are NULL) - */ -static int secp256k1_schnorrsig_verify_batch_init_randomizer(const secp256k1_context *ctx, secp256k1_schnorrsig_verify_ecmult_context *ecmult_context, secp256k1_sha256 *sha, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { - size_t i; - - if (n_sigs > 0) { - ARG_CHECK(sig != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(pk != NULL); - } - - for (i = 0; i < n_sigs; i++) { - unsigned char buf[33]; - size_t buflen = sizeof(buf); - secp256k1_sha256_write(sha, sig[i]->data, 64); - secp256k1_sha256_write(sha, msg32[i], 32); - secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk[i], SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(sha, buf, buflen); - } - ecmult_context->ctx = ctx; - ecmult_context->sig = sig; - ecmult_context->msg32 = msg32; - ecmult_context->pk = pk; - ecmult_context->n_sigs = n_sigs; - - return 1; -} - -/** Helper function for batch verification. Sums the s part of all signatures multiplied by their - * randomizer. - * - * Returns 1 if s is successfully summed. - * - * In/Out: s: the s part of the input sigs is added to this s argument - * In: chacha_seed: PRNG seed for computing randomizers - * sig: array of signatures, or NULL if there are no signatures - * n_sigs: number of signatures in above array (must be 0 if they are NULL) - */ -static int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *chacha_seed, const secp256k1_schnorrsig *const *sig, size_t n_sigs) { - secp256k1_scalar randomizer_cache[2]; - size_t i; - - secp256k1_scalar_set_int(&randomizer_cache[0], 1); - for (i = 0; i < n_sigs; i++) { - int overflow; - secp256k1_scalar term; - if (i % 2 == 1) { - secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2); - } - - secp256k1_scalar_set_b32(&term, &sig[i]->data[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_mul(&term, &term, &randomizer_cache[i % 2]); - secp256k1_scalar_add(s, s, &term); - } - return 1; -} - -/* schnorrsig batch verification. - * Seeds a random number generator with the inputs and derives a random number ai for every - * signature i. Fails if y-coordinate of any R is not a quadratic residue or if - * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */ -int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_scratch *scratch, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { - secp256k1_schnorrsig_verify_ecmult_context ecmult_context; - secp256k1_sha256 sha; - secp256k1_scalar s; - secp256k1_gej rj; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(scratch != NULL); - /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because - * the number of points given to ecmult_multi is 2*n_sigs. */ - ARG_CHECK(n_sigs <= SIZE_MAX / 2); - /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit - * and 64-bit platforms. */ - ARG_CHECK(n_sigs < ((uint32_t)1 << 31)); - - secp256k1_sha256_initialize(&sha); - if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) { - return 0; - } - secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed); - secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1); - - secp256k1_scalar_clear(&s); - if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) { - return 0; - } - secp256k1_scalar_negate(&s, &s); - - return secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs) - && secp256k1_gej_is_infinity(&rj); -} - -#endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h deleted file mode 100644 index 670b2d1a..00000000 --- a/src/modules/schnorrsig/tests_impl.h +++ /dev/null @@ -1,726 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ -#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ - -#include "secp256k1_schnorrsig.h" - -void test_schnorrsig_serialize(void) { - secp256k1_schnorrsig sig; - unsigned char in[64]; - unsigned char out[64]; - - memset(in, 0x12, 64); - CHECK(secp256k1_schnorrsig_parse(ctx, &sig, in)); - CHECK(secp256k1_schnorrsig_serialize(ctx, out, &sig)); - CHECK(memcmp(in, out, 64) == 0); -} - -void test_schnorrsig_api(secp256k1_scratch_space *scratch) { - unsigned char sk1[32]; - unsigned char sk2[32]; - unsigned char sk3[32]; - unsigned char msg[32]; - unsigned char sig64[64]; - secp256k1_pubkey pk[3]; - secp256k1_schnorrsig sig; - const secp256k1_schnorrsig *sigptr = &sig; - const unsigned char *msgptr = msg; - const secp256k1_pubkey *pkptr = &pk[0]; - int nonce_is_negated; - - /** setup **/ - secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - int ecount; - - secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); - - secp256k1_rand256(sk1); - secp256k1_rand256(sk2); - secp256k1_rand256(sk3); - secp256k1_rand256(msg); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk1) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk2) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[2], sk3) == 1); - - /** main test body **/ - ecount = 0; - CHECK(secp256k1_schnorrsig_sign(none, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, NULL, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, msg, sk1, NULL, NULL) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, NULL, sk1, NULL, NULL) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, NULL, NULL, NULL) == 0); - CHECK(ecount == 5); - - ecount = 0; - CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_serialize(none, NULL, &sig) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_serialize(none, sig64, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_parse(none, &sig, sig64) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_parse(none, NULL, sig64) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); - CHECK(ecount == 4); - - ecount = 0; - CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_verify(sign, &sig, msg, &pk[0]) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &pk[0]) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, NULL, &pk[0]) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, NULL) == 0); - CHECK(ecount == 5); - - ecount = 0; - CHECK(secp256k1_schnorrsig_verify_batch(none, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_verify_batch(sign, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, NULL, NULL, 0) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); - CHECK(ecount == 6); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (uint32_t)1 << 31) == 0); - CHECK(ecount == 7); - - secp256k1_context_destroy(none); - secp256k1_context_destroy(sign); - secp256k1_context_destroy(vrfy); - secp256k1_context_destroy(both); -} - -/* Helper function for schnorrsig_bip_vectors - * Signs the message and checks that it's the same as expected_sig. */ -void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *msg, const unsigned char *expected_sig, const int expected_nonce_is_negated) { - secp256k1_schnorrsig sig; - unsigned char serialized_sig[64]; - secp256k1_pubkey pk; - int nonce_is_negated; - - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &nonce_is_negated, msg, sk, NULL, NULL)); - CHECK(nonce_is_negated == expected_nonce_is_negated); - CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); - CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); - - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); - CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); -} - -/* Helper function for schnorrsig_bip_vectors - * Checks that both verify and verify_batch return the same value as expected. */ -void test_schnorrsig_bip_vectors_check_verify(secp256k1_scratch_space *scratch, const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig_serialized, int expected) { - const unsigned char *msg_arr[1]; - const secp256k1_schnorrsig *sig_arr[1]; - const secp256k1_pubkey *pk_arr[1]; - secp256k1_pubkey pk; - secp256k1_schnorrsig sig; - - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); - CHECK(secp256k1_schnorrsig_parse(ctx, &sig, sig_serialized)); - - sig_arr[0] = &sig; - msg_arr[0] = msg32; - pk_arr[0] = &pk; - - CHECK(expected == secp256k1_schnorrsig_verify(ctx, &sig, msg32, &pk)); - CHECK(expected == secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); -} - -/* Test vectors according to BIP-schnorr - * (https://github.com/sipa/bips/blob/7f6a73e53c8bbcf2d008ea0546f76433e22094a8/bip-schnorr/test-vectors.csv). - */ -void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { - { - /* Test vector 1 */ - const unsigned char sk1[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - const unsigned char pk1[33] = { - 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, - 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, - 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, - 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, - 0x98 - }; - const unsigned char msg1[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const unsigned char sig1[64] = { - 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, - 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, - 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, - 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, - 0x70, 0x31, 0xA9, 0x88, 0x31, 0x85, 0x9D, 0xC3, - 0x4D, 0xFF, 0xEE, 0xDD, 0xA8, 0x68, 0x31, 0x84, - 0x2C, 0xCD, 0x00, 0x79, 0xE1, 0xF9, 0x2A, 0xF1, - 0x77, 0xF7, 0xF2, 0x2C, 0xC1, 0xDC, 0xED, 0x05 - }; - test_schnorrsig_bip_vectors_check_signing(sk1, pk1, msg1, sig1, 1); - test_schnorrsig_bip_vectors_check_verify(scratch, pk1, msg1, sig1, 1); - } - { - /* Test vector 2 */ - const unsigned char sk2[32] = { - 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, - 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, - 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, - 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF - }; - const unsigned char pk2[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg2[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig2[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_signing(sk2, pk2, msg2, sig2, 0); - test_schnorrsig_bip_vectors_check_verify(scratch, pk2, msg2, sig2, 1); - } - { - /* Test vector 3 */ - const unsigned char sk3[32] = { - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC7 - }; - const unsigned char pk3[33] = { - 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, - 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, - 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, - 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, - 0x4B - }; - const unsigned char msg3[32] = { - 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, - 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, - 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, - 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C - }; - const unsigned char sig3[64] = { - 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, - 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, - 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, - 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, - 0x00, 0x88, 0x03, 0x71, 0xD0, 0x17, 0x66, 0x93, - 0x5B, 0x92, 0xD2, 0xAB, 0x4C, 0xD5, 0xC8, 0xA2, - 0xA5, 0x83, 0x7E, 0xC5, 0x7F, 0xED, 0x76, 0x60, - 0x77, 0x3A, 0x05, 0xF0, 0xDE, 0x14, 0x23, 0x80 - }; - test_schnorrsig_bip_vectors_check_signing(sk3, pk3, msg3, sig3, 0); - test_schnorrsig_bip_vectors_check_verify(scratch, pk3, msg3, sig3, 1); - } - { - /* Test vector 4 */ - const unsigned char pk4[33] = { - 0x03, 0xDE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, - 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, - 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, - 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, - 0x34 - }; - const unsigned char msg4[32] = { - 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, - 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, - 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, - 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 - }; - const unsigned char sig4[64] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, - 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, - 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, - 0x02, 0xA8, 0xDC, 0x32, 0xE6, 0x4E, 0x86, 0xA3, - 0x33, 0xF2, 0x0E, 0xF5, 0x6E, 0xAC, 0x9B, 0xA3, - 0x0B, 0x72, 0x46, 0xD6, 0xD2, 0x5E, 0x22, 0xAD, - 0xB8, 0xC6, 0xBE, 0x1A, 0xEB, 0x08, 0xD4, 0x9D - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk4, msg4, sig4, 1); - } - { - /* Test vector 5 */ - const unsigned char pk5[33] = { - 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 - }; - const unsigned char msg5[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const unsigned char sig5[64] = { - 0x52, 0x81, 0x85, 0x79, 0xAC, 0xA5, 0x97, 0x67, - 0xE3, 0x29, 0x1D, 0x91, 0xB7, 0x6B, 0x63, 0x7B, - 0xEF, 0x06, 0x20, 0x83, 0x28, 0x49, 0x92, 0xF2, - 0xD9, 0x5F, 0x56, 0x4C, 0xA6, 0xCB, 0x4E, 0x35, - 0x30, 0xB1, 0xDA, 0x84, 0x9C, 0x8E, 0x83, 0x04, - 0xAD, 0xC0, 0xCF, 0xE8, 0x70, 0x66, 0x03, 0x34, - 0xB3, 0xCF, 0xC1, 0x8E, 0x82, 0x5E, 0xF1, 0xDB, - 0x34, 0xCF, 0xAE, 0x3D, 0xFC, 0x5D, 0x81, 0x87 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk5, msg5, sig5, 1); - } - { - /* Test vector 6 */ - const unsigned char pk6[33] = { - 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, - 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, - 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, - 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, - 0x4B - }; - const unsigned char msg6[32] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - const unsigned char sig6[64] = { - 0x57, 0x0D, 0xD4, 0xCA, 0x83, 0xD4, 0xE6, 0x31, - 0x7B, 0x8E, 0xE6, 0xBA, 0xE8, 0x34, 0x67, 0xA1, - 0xBF, 0x41, 0x9D, 0x07, 0x67, 0x12, 0x2D, 0xE4, - 0x09, 0x39, 0x44, 0x14, 0xB0, 0x50, 0x80, 0xDC, - 0xE9, 0xEE, 0x5F, 0x23, 0x7C, 0xBD, 0x10, 0x8E, - 0xAB, 0xAE, 0x1E, 0x37, 0x75, 0x9A, 0xE4, 0x7F, - 0x8E, 0x42, 0x03, 0xDA, 0x35, 0x32, 0xEB, 0x28, - 0xDB, 0x86, 0x0F, 0x33, 0xD6, 0x2D, 0x49, 0xBD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk6, msg6, sig6, 1); - } - { - /* Test vector 7 */ - const unsigned char pk7[33] = { - 0x03, 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, - 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, - 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, - 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, - 0x34 - }; - secp256k1_pubkey pk7_parsed; - /* No need to check the signature of the test vector as parsing the pubkey already fails */ - CHECK(!secp256k1_ec_pubkey_parse(ctx, &pk7_parsed, pk7, 33)); - } - { - /* Test vector 8 */ - const unsigned char pk8[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg8[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig8[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0xFA, 0x16, 0xAE, 0xE0, 0x66, 0x09, 0x28, 0x0A, - 0x19, 0xB6, 0x7A, 0x24, 0xE1, 0x97, 0x7E, 0x46, - 0x97, 0x71, 0x2B, 0x5F, 0xD2, 0x94, 0x39, 0x14, - 0xEC, 0xD5, 0xF7, 0x30, 0x90, 0x1B, 0x4A, 0xB7 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk8, msg8, sig8, 0); - } - { - /* Test vector 9 */ - const unsigned char pk9[33] = { - 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, - 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, - 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, - 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, - 0x4B - }; - const unsigned char msg9[32] = { - 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, - 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, - 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, - 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C - }; - const unsigned char sig9[64] = { - 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, - 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, - 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, - 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, - 0xD0, 0x92, 0xF9, 0xD8, 0x60, 0xF1, 0x77, 0x6A, - 0x1F, 0x74, 0x12, 0xAD, 0x8A, 0x1E, 0xB5, 0x0D, - 0xAC, 0xCC, 0x22, 0x2B, 0xC8, 0xC0, 0xE2, 0x6B, - 0x20, 0x56, 0xDF, 0x2F, 0x27, 0x3E, 0xFD, 0xEC - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk9, msg9, sig9, 0); - } - { - /* Test vector 10 */ - const unsigned char pk10[33] = { - 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, - 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, - 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, - 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, - 0x98 - }; - const unsigned char msg10[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const unsigned char sig10[64] = { - 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, - 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, - 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, - 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, - 0x8F, 0xCE, 0x56, 0x77, 0xCE, 0x7A, 0x62, 0x3C, - 0xB2, 0x00, 0x11, 0x22, 0x57, 0x97, 0xCE, 0x7A, - 0x8D, 0xE1, 0xDC, 0x6C, 0xCD, 0x4F, 0x75, 0x4A, - 0x47, 0xDA, 0x6C, 0x60, 0x0E, 0x59, 0x54, 0x3C - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk10, msg10, sig10, 0); - } - { - /* Test vector 11 */ - const unsigned char pk11[33] = { - 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg11[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig11[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk11, msg11, sig11, 0); - } - { - /* Test vector 12 */ - const unsigned char pk12[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg12[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig12[64] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9E, 0x9D, 0x01, 0xAF, 0x98, 0x8B, 0x5C, 0xED, - 0xCE, 0x47, 0x22, 0x1B, 0xFA, 0x9B, 0x22, 0x27, - 0x21, 0xF3, 0xFA, 0x40, 0x89, 0x15, 0x44, 0x4A, - 0x4B, 0x48, 0x90, 0x21, 0xDB, 0x55, 0x77, 0x5F - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk12, msg12, sig12, 0); - } - { - /* Test vector 13 */ - const unsigned char pk13[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg13[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig13[64] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xD3, 0x7D, 0xDF, 0x02, 0x54, 0x35, 0x18, 0x36, - 0xD8, 0x4B, 0x1B, 0xD6, 0xA7, 0x95, 0xFD, 0x5D, - 0x52, 0x30, 0x48, 0xF2, 0x98, 0xC4, 0x21, 0x4D, - 0x18, 0x7F, 0xE4, 0x89, 0x29, 0x47, 0xF7, 0x28 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk13, msg13, sig13, 0); - } - { - /* Test vector 14 */ - const unsigned char pk14[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg14[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x14, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig14[64] = { - 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk14, msg14, sig14, 0); - } - { - /* Test vector 15 */ - const unsigned char pk15[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg15[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig15[64] = { - 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, 0xFC, 0x2F, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk15, msg15, sig15, 0); - } - { - /* Test vector 16 */ - const unsigned char pk16[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg16[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig16[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 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 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk16, msg16, sig16, 0); - } -} - -/* Nonce function that returns constant 0 */ -static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - (void) msg32; - (void) key32; - (void) algo16; - (void) data; - (void) counter; - (void) nonce32; - return 0; -} - -/* Nonce function that sets nonce to 0 */ -static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - (void) msg32; - (void) key32; - (void) algo16; - (void) data; - (void) counter; - - memset(nonce32, 0, 32); - return 1; -} - -void test_schnorrsig_sign(void) { - unsigned char sk[32]; - const unsigned char msg[32] = "this is a msg for a schnorrsig.."; - secp256k1_schnorrsig sig; - - memset(sk, 23, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 1); - - /* Overflowing secret key */ - memset(sk, 0xFF, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 0); - memset(sk, 23, sizeof(sk)); - - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_failing, NULL) == 0); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_0, NULL) == 0); -} - -#define N_SIGS 200 -/* Creates N_SIGS valid signatures and verifies them with verify and verify_batch. Then flips some - * bits and checks that verification now fails. */ -void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { - const unsigned char sk[32] = "shhhhhhhh! this key is a secret."; - unsigned char msg[N_SIGS][32]; - secp256k1_schnorrsig sig[N_SIGS]; - size_t i; - const secp256k1_schnorrsig *sig_arr[N_SIGS]; - const unsigned char *msg_arr[N_SIGS]; - const secp256k1_pubkey *pk_arr[N_SIGS]; - secp256k1_pubkey pk; - - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk)); - - CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, NULL, NULL, NULL, 0)); - - for (i = 0; i < N_SIGS; i++) { - secp256k1_rand256(msg[i]); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], NULL, msg[i], sk, NULL, NULL)); - CHECK(secp256k1_schnorrsig_verify(ctx, &sig[i], msg[i], &pk)); - sig_arr[i] = &sig[i]; - msg_arr[i] = msg[i]; - pk_arr[i] = &pk; - } - - CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); - CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 2)); - CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); - CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, N_SIGS)); - - { - /* Flip a few bits in the signature and in the message and check that - * verify and verify_batch fail */ - size_t sig_idx = secp256k1_rand_int(4); - size_t byte_idx = secp256k1_rand_int(32); - unsigned char xorbyte = secp256k1_rand_int(254)+1; - sig[sig_idx].data[byte_idx] ^= xorbyte; - CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); - CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); - sig[sig_idx].data[byte_idx] ^= xorbyte; - - byte_idx = secp256k1_rand_int(32); - sig[sig_idx].data[32+byte_idx] ^= xorbyte; - CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); - CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); - sig[sig_idx].data[32+byte_idx] ^= xorbyte; - - byte_idx = secp256k1_rand_int(32); - msg[sig_idx][byte_idx] ^= xorbyte; - CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); - CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); - msg[sig_idx][byte_idx] ^= xorbyte; - - /* Check that above bitflips have been reversed correctly */ - CHECK(secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); - CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); - } -} -#undef N_SIGS - -void run_schnorrsig_tests(void) { - secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); - - test_schnorrsig_serialize(); - test_schnorrsig_api(scratch); - test_schnorrsig_bip_vectors(scratch); - test_schnorrsig_sign(); - test_schnorrsig_sign_verify(scratch); - - secp256k1_scratch_space_destroy(ctx, scratch); -} - -#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 0c3a09e3..7258d8c8 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -449,29 +449,6 @@ static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *off *offset += len; } -/* This nonce function is described in BIP-schnorr - * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) */ -static int nonce_function_bipschnorr(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - secp256k1_sha256 sha; - (void) counter; - VERIFY_CHECK(counter == 0); - - /* Hash x||msg as per the spec */ - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, key32, 32); - secp256k1_sha256_write(&sha, msg32, 32); - /* Hash in algorithm, which is not in the spec, but may be critical to - * users depending on it to avoid nonce reuse across algorithms. */ - if (algo16 != NULL) { - secp256k1_sha256_write(&sha, algo16, 16); - } - if (data != NULL) { - secp256k1_sha256_write(&sha, data, 32); - } - secp256k1_sha256_finalize(&sha, nonce32); - return 1; -} - static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; unsigned int offset = 0; @@ -502,7 +479,6 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m return 1; } -const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr = nonce_function_bipschnorr; const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; @@ -777,10 +753,6 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "modules/schnorrsig/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 c594dee9..274d26cc 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5504,10 +5504,6 @@ void run_ecdsa_openssl(void) { # include "modules/ecdh/tests_impl.h" #endif -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "modules/schnorrsig/tests_impl.h" -#endif - #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/tests_impl.h" #endif @@ -5824,11 +5820,6 @@ int main(int argc, char **argv) { run_ecdh_tests(); #endif -#ifdef ENABLE_MODULE_SCHNORRSIG - /* Schnorrsig tests */ - run_schnorrsig_tests(); -#endif - #ifdef ENABLE_MODULE_MUSIG run_musig_tests(); #endif From 23900a0d86730f719c52aebba41a1c1cdb9288bd Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 15 Nov 2019 21:38:37 +0000 Subject: [PATCH 075/381] Fix the MuSig module after integrating bip-schnorr updates 1. using xonly_pubkeys in MuSig for input public keys and the combined pk. For that to work we need to store whether the MuSig aggregated point has an even y in the session, may need to negate each signers secret key and may need to negate each signers public key in musig_partial_sig_verify. 2. using a tagged hash for the message hash. 3. use !fe_is_odd in place of fe_is_quad_var --- include/secp256k1_musig.h | 82 ++++--- src/modules/musig/example.c | 31 +-- src/modules/musig/main_impl.h | 149 ++++++------ src/modules/musig/tests_impl.h | 405 ++++++++++++++++----------------- src/secp256k1.c | 23 +- 5 files changed, 356 insertions(+), 334 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index b1c5b912..c5352512 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -1,6 +1,8 @@ #ifndef SECP256K1_MUSIG_H #define SECP256K1_MUSIG_H +#include "secp256k1_extrakeys.h" + #ifdef __cplusplus extern "C" { #endif @@ -8,15 +10,30 @@ extern "C" { #include /** This module implements a Schnorr-based multi-signature scheme called MuSig - * (https://eprint.iacr.org/2018/068.pdf). There's an example C source file in the - * module's directory (src/modules/musig/example.c) that demonstrates how it can be - * used. + * (https://eprint.iacr.org/2018/068.pdf). It is compatible with bip-schnorr. + * There's an example C source file in the module's directory + * (src/modules/musig/example.c) that demonstrates how it can be used. * * The documentation in this include file is for reference and may not be sufficient * for users to begin using the library. A full description of API usage can be found * in src/modules/musig/musig.md */ +/** Data structure containing auxiliary data generated in `pubkey_combine` and + * required for `session_*_initialize`. + * Fields: + * magic: Set during initialization in `pubkey_combine` in order to allow + * detecting an uninitialized object. + * pk_hash: The 32-byte hash of the original public keys + * is_negated: Whether the MuSig-aggregated point was negated when + * converting it to the combined xonly pubkey. + */ +typedef struct { + uint64_t magic; + unsigned char pk_hash[32]; + int is_negated; +} secp256k1_musig_pre_session; + /** Data structure containing data related to a signing session resulting in a single * signature. * @@ -28,14 +45,14 @@ extern "C" { * structure. * * Fields: - * combined_pk: MuSig-computed combined public key + * combined_pk: MuSig-computed combined xonly public key + * pre_session: Auxiliary data created in `pubkey_combine` * n_signers: Number of signers - * pk_hash: The 32-byte hash of the original public keys * combined_nonce: Summed combined public nonce (undefined if `nonce_is_set` is false) * nonce_is_set: Whether the above nonce has been set * nonce_is_negated: If `nonce_is_set`, whether the above nonce was negated after * summing the participants' nonces. Needed to ensure the nonce's y - * coordinate has a quadratic-residue y coordinate + * coordinate is even. * msg: The 32-byte message (hash) to be signed * msg_is_set: Whether the above message has been set * has_secret_data: Whether this session object has a signers' secret data; if this @@ -49,9 +66,9 @@ extern "C" { * nonce_commitments_hash has been set */ typedef struct { - secp256k1_pubkey combined_pk; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; uint32_t n_signers; - unsigned char pk_hash[32]; secp256k1_pubkey combined_nonce; int nonce_is_set; int nonce_is_negated; @@ -119,9 +136,9 @@ typedef struct { * (cannot be NULL) * scratch: scratch space used to compute the combined pubkey by * multiexponentiation. If NULL, an inefficient algorithm is used. - * Out: combined_pk: the MuSig-combined public key (cannot be NULL) - * pk_hash32: if non-NULL, filled with the 32-byte hash of all input public - * keys in order to be used in `musig_session_initialize`. + * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) + * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in + * `musig_session_initialize`. * In: pubkeys: input array of public keys to combine. The order is important; * a different order will result in a different combined public * key (cannot be NULL) @@ -130,9 +147,9 @@ typedef struct { SECP256K1_API int secp256k1_musig_pubkey_combine( const secp256k1_context* ctx, secp256k1_scratch_space *scratch, - secp256k1_pubkey *combined_pk, - unsigned char *pk_hash32, - const secp256k1_pubkey *pubkeys, + secp256k1_xonly_pubkey *combined_pk, + secp256k1_musig_pre_session *pre_session, + const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); @@ -154,9 +171,9 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( * require sharing nonce commitments before the message is known * because it reduces nonce misuse resistance. If NULL, must be * set with `musig_session_get_public_nonce`. - * combined_pk: the combined public key of all signers (cannot be NULL) - * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be - * NULL) + * combined_pk: the combined xonly public key of all signers (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct from + * `musig_pubkey_combine` (cannot be NULL) * n_signers: length of signers array. Number of signers participating in * the MuSig. Must be greater than 0 and at most 2^32 - 1. * my_index: index of this signer in the signers array @@ -169,8 +186,8 @@ SECP256K1_API int secp256k1_musig_session_initialize( unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, - const secp256k1_pubkey *combined_pk, - const unsigned char *pk_hash32, + const secp256k1_xonly_pubkey *combined_pk, + const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey @@ -213,7 +230,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi * signers: an array of signers' data to be initialized. Array length must * equal to `n_signers`(cannot be NULL) * In: msg32: the 32-byte message to be signed (cannot be NULL) - * combined_pk: the combined public key of all signers (cannot be NULL) + * combined_pk: the combined xonly public key of all signers (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct from + * `musig_pubkey_combine` (cannot be NULL) * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) * commitments: array of 32-byte nonce commitments. Array length must equal to * `n_signers` (cannot be NULL) @@ -226,8 +245,8 @@ SECP256K1_API int secp256k1_musig_session_initialize_verifier( secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, - const secp256k1_pubkey *combined_pk, - const unsigned char *pk_hash32, + const secp256k1_xonly_pubkey *combined_pk, + const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, 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) SECP256K1_ARG_NONNULL(7); @@ -343,7 +362,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, - const secp256k1_pubkey *pubkey + const secp256k1_xonly_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Combines partial signatures @@ -354,23 +373,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif * Args: ctx: pointer to a context object (cannot be NULL) * session: initialized session for which the combined nonce has been * computed (cannot be NULL) - * Out: sig: complete signature (cannot be NULL) + * Out: sig64: complete signature (cannot be NULL) * In: partial_sigs: array of partial signatures to combine (cannot be NULL) * n_sigs: number of signatures in the partial_sigs array - * tweak32: if `combined_pk` was tweaked with `ec_pubkey_tweak_add` after - * `musig_pubkey_combine` and before `musig_session_initialize` then - * the same tweak must be provided here in order to get a valid - * signature for the tweaked key. Otherwise `tweak` should be NULL. - * If the tweak is larger than the group order or 0 this function will - * return 0. (can be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( const secp256k1_context* ctx, const secp256k1_musig_session *session, - secp256k1_schnorrsig *sig, + unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, - size_t n_sigs, - const unsigned char *tweak32 + size_t n_sigs ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Converts a partial signature to an adaptor signature by adding a given secret @@ -403,7 +415,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt( * 0: otherwise * Args: ctx: pointer to a context object (cannot be NULL) * Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL) - * In: sig: complete 2-of-2 signature (cannot be NULL) + * In: sig64: complete 2-of-2 signature (cannot be NULL) * partial_sigs: array of partial signatures (cannot be NULL) * n_partial_sigs: number of elements in partial_sigs array * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` @@ -411,7 +423,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( const secp256k1_context* ctx, unsigned char *sec_adaptor32, - const secp256k1_schnorrsig *sig, + const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index b4c9a95d..4670d442 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -18,8 +18,9 @@ /* Number of public keys involved in creating the aggregate signature */ #define N_SIGNERS 3 /* Create a key pair and store it in seckey and pubkey */ -int create_key(const secp256k1_context* ctx, unsigned char* seckey, secp256k1_pubkey* pubkey) { +int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k1_xonly_pubkey *pubkey) { int ret; + secp256k1_keypair keypair; FILE *frand = fopen("/dev/urandom", "r"); if (frand == NULL) { return 0; @@ -32,12 +33,14 @@ int create_key(const secp256k1_context* ctx, unsigned char* seckey, secp256k1_pu /* The probability that this not a valid secret key is approximately 2^-128 */ } while (!secp256k1_ec_seckey_verify(ctx, seckey)); fclose(frand); - ret = secp256k1_ec_pubkey_create(ctx, pubkey, seckey); + ret = secp256k1_keypair_create(ctx, &keypair, seckey); + ret &= secp256k1_keypair_xonly_pub(ctx, pubkey, NULL, &keypair); + return ret; } /* Sign a message hash with the given key pairs and store the result in sig */ -int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_pubkey* pubkeys, const unsigned char* msg32, secp256k1_schnorrsig *sig) { +int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msg32, unsigned char *sig64) { secp256k1_musig_session musig_session[N_SIGNERS]; unsigned char nonce_commitment[N_SIGNERS][32]; const unsigned char *nonce_commitment_ptr[N_SIGNERS]; @@ -49,11 +52,11 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 for (i = 0; i < N_SIGNERS; i++) { FILE *frand; unsigned char session_id32[32]; - unsigned char pk_hash[32]; - secp256k1_pubkey combined_pk; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; /* Create combined pubkey and initialize signer data */ - if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, pk_hash, pubkeys, N_SIGNERS)) { + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pubkeys, N_SIGNERS)) { return 0; } /* Create random session ID. It is absolutely necessary that the session ID @@ -69,7 +72,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } fclose(frand); /* Initialize session */ - if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, pk_hash, N_SIGNERS, i, seckeys[i])) { + if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { return 0; } nonce_commitment_ptr[i] = &nonce_commitment[i][0]; @@ -119,23 +122,23 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } } } - return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig, partial_sig, N_SIGNERS, NULL); + return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig64, partial_sig, N_SIGNERS); } int main(void) { secp256k1_context* ctx; int i; unsigned char seckeys[N_SIGNERS][32]; - secp256k1_pubkey pubkeys[N_SIGNERS]; - secp256k1_pubkey combined_pk; + secp256k1_xonly_pubkey pubkeys[N_SIGNERS]; + secp256k1_xonly_pubkey combined_pk; unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; - secp256k1_schnorrsig sig; + unsigned char sig[64]; /* Create a context for signing and verification */ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); printf("Creating key pairs......"); for (i = 0; i < N_SIGNERS; i++) { - if (!create_key(ctx, seckeys[i], &pubkeys[i])) { + if (!create_keypair(ctx, seckeys[i], &pubkeys[i])) { printf("FAILED\n"); return 1; } @@ -148,13 +151,13 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } printf("ok\n"); printf("Signing message........."); - if (!sign(ctx, seckeys, pubkeys, msg, &sig)) { + if (!sign(ctx, seckeys, pubkeys, msg, sig)) { printf("FAILED\n"); return 1; } printf("ok\n"); printf("Verifying signature....."); - if (!secp256k1_schnorrsig_verify(ctx, &sig, msg, &combined_pk)) { + if (!secp256k1_schnorrsig_verify(ctx, sig, msg, &combined_pk)) { printf("FAILED\n"); return 1; } diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index d1123539..5f9cb0d0 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -7,23 +7,23 @@ #ifndef _SECP256K1_MODULE_MUSIG_MAIN_ #define _SECP256K1_MODULE_MUSIG_MAIN_ +#include #include "include/secp256k1.h" #include "include/secp256k1_musig.h" #include "hash.h" /* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_pubkey *pk, size_t np) { +static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey *pk, size_t np) { secp256k1_sha256 sha; size_t i; secp256k1_sha256_initialize(&sha); for (i = 0; i < np; i++) { - unsigned char ser[33]; - size_t serlen = sizeof(ser); - if (!secp256k1_ec_pubkey_serialize(ctx, ser, &serlen, &pk[i], SECP256K1_EC_COMPRESSED)) { + unsigned char ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, ser, &pk[i])) { return 0; } - secp256k1_sha256_write(&sha, ser, serlen); + secp256k1_sha256_write(&sha, ser, 32); } secp256k1_sha256_finalize(&sha, ell); return 1; @@ -77,14 +77,14 @@ static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char typedef struct { const secp256k1_context *ctx; unsigned char ell[32]; - const secp256k1_pubkey *pks; + const secp256k1_xonly_pubkey *pks; } secp256k1_musig_pubkey_combine_ecmult_data; /* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; secp256k1_musig_coefficient(sc, ctx->ell, idx); - return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); + return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); } @@ -97,10 +97,13 @@ static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *si } } -int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys) { +static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL; + +int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys) { secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; secp256k1_gej pkj; secp256k1_ge pkp; + int is_negated; VERIFY_CHECK(ctx != NULL); ARG_CHECK(combined_pk != NULL); @@ -117,23 +120,26 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat return 0; } secp256k1_ge_set_gej(&pkp, &pkj); - secp256k1_pubkey_save(combined_pk, &pkp); + secp256k1_fe_normalize(&pkp.y); + is_negated = secp256k1_extrakeys_ge_even_y(&pkp); + secp256k1_xonly_pubkey_save(combined_pk, &pkp); - if (pk_hash32 != NULL) { - memcpy(pk_hash32, ecmult_data.ell, 32); + if (pre_session != NULL) { + pre_session->magic = pre_session_magic; + memcpy(pre_session->pk_hash, ecmult_data.ell, 32); + pre_session->is_negated = is_negated; } return 1; } -int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey) { - unsigned char combined_ser[33]; - size_t combined_ser_size = sizeof(combined_ser); +int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { + unsigned char combined_ser[32]; int overflow; secp256k1_scalar secret; secp256k1_scalar mu; secp256k1_sha256 sha; - secp256k1_gej rj; - secp256k1_ge rp; + secp256k1_gej pj; + secp256k1_ge p; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); @@ -142,7 +148,8 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m ARG_CHECK(nonce_commitment32 != NULL); ARG_CHECK(session_id32 != NULL); ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); ARG_CHECK(seckey != NULL); memset(session, 0, sizeof(*session)); @@ -154,7 +161,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m session->msg_is_set = 0; } memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); - memcpy(session->pk_hash, pk_hash32, 32); + session->pre_session = *pre_session; session->nonce_is_set = 0; session->has_secret_data = 1; if (n_signers == 0 || my_index >= n_signers) { @@ -173,7 +180,25 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m secp256k1_scalar_clear(&secret); return 0; } - secp256k1_musig_coefficient(&mu, pk_hash32, (uint32_t) my_index); + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); + /* Compute the signers public key point and determine if the secret needs to + * be negated before signing. If the signer's pubkey is negated XOR the + * MuSig-combined pubkey is negated the secret has to be negated. This can + * be seen by looking at the secret key belonging to `combined_pk`. Let's + * define + * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key + * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function + * that normalizes a point to an even Y by negating if necessary similar to + * secp256k1_extrakeys_ge_even_y. Then we have + * P := |P'| the combined xonly public key. Also, P = x*G where x = + * sum_i(b_i*mu_i*x_i) and b_i = -1 if (P != |P'| XOR P_i != |P_i|) and 1 + * otherwise. */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_fe_normalize(&p.y); + if (secp256k1_fe_is_odd(&p.y) != session->pre_session.is_negated) { + secp256k1_scalar_negate(&secret, &secret); + } secp256k1_scalar_mul(&secret, &secret, &mu); secp256k1_scalar_get_b32(session->seckey, &secret); @@ -183,8 +208,8 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m if (session->msg_is_set) { secp256k1_sha256_write(&sha, msg32, 32); } - secp256k1_ec_pubkey_serialize(ctx, combined_ser, &combined_ser_size, combined_pk, SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, combined_ser, combined_ser_size); + secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk); + secp256k1_sha256_write(&sha, combined_ser, 32); secp256k1_sha256_write(&sha, seckey, 32); secp256k1_sha256_finalize(&sha, session->secnonce); secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow); @@ -194,9 +219,9 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m } /* Compute public nonce and commitment */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &secret); - secp256k1_ge_set_gej(&rp, &rj); - secp256k1_pubkey_save(&session->nonce, &rp); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_pubkey_save(&session->nonce, &p); if (nonce_commitment32 != NULL) { unsigned char commit[33]; @@ -256,7 +281,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 return 1; } -int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, const unsigned char *const *commitments, size_t n_signers) { +int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { size_t i; VERIFY_CHECK(ctx != NULL); @@ -264,7 +289,8 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se ARG_CHECK(signers != NULL); ARG_CHECK(msg32 != NULL); ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); ARG_CHECK(commitments != NULL); /* Check n_signers before checking commitments to allow testing the case where * n_signers is big without allocating the space. */ @@ -279,13 +305,14 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se memset(session, 0, sizeof(*session)); memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + session->pre_session = *pre_session; if (n_signers == 0) { return 0; } session->n_signers = (uint32_t) n_signers; secp256k1_musig_signers_init(signers, session->n_signers); - memcpy(session->pk_hash, pk_hash32, 32); + session->pre_session = *pre_session; session->nonce_is_set = 0; session->msg_is_set = 1; memcpy(session->msg, msg32, 32); @@ -365,7 +392,8 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); } secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); - if (secp256k1_fe_is_quad_var(&combined_noncep.y)) { + secp256k1_fe_normalize(&combined_noncep.y); + if (!secp256k1_fe_is_odd(&combined_noncep.y)) { session->nonce_is_negated = 0; } else { session->nonce_is_negated = 1; @@ -397,21 +425,20 @@ int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp25 /* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { - unsigned char buf[33]; - size_t bufsize = 33; + unsigned char buf[32]; secp256k1_ge rp; secp256k1_sha256 sha; - secp256k1_sha256_initialize(&sha); + secp256k1_schnorrsig_sha256_tagged(&sha); if (!session->nonce_is_set) { return 0; } secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce); secp256k1_fe_get_b32(buf, &rp.x); secp256k1_sha256_write(&sha, buf, 32); - secp256k1_ec_pubkey_serialize(ctx, buf, &bufsize, &session->combined_pk, SECP256K1_EC_COMPRESSED); - VERIFY_CHECK(bufsize == 33); - secp256k1_sha256_write(&sha, buf, bufsize); + + secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk); + secp256k1_sha256_write(&sha, buf, 32); if (!session->msg_is_set) { return 0; } @@ -466,14 +493,14 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m return 1; } -int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs, const unsigned char *tweak32) { +int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { size_t i; secp256k1_scalar s; secp256k1_ge noncep; (void) ctx; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); + ARG_CHECK(sig64 != NULL); ARG_CHECK(partial_sigs != NULL); ARG_CHECK(session != NULL); @@ -495,40 +522,23 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp secp256k1_scalar_add(&s, &s, &term); } - /* If there is a tweak then add `msghash` times `tweak` to `s`.*/ - if (tweak32 != NULL) { - unsigned char msghash[32]; - secp256k1_scalar e, scalar_tweak; - int overflow = 0; - - if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { - return 0; - } - secp256k1_scalar_set_b32(&e, msghash, NULL); - secp256k1_scalar_set_b32(&scalar_tweak, tweak32, &overflow); - if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) { - /* This mimics the behavior of secp256k1_ec_privkey_tweak_mul regarding - * overflow and tweak32 being 0. */ - return 0; - } - secp256k1_scalar_add(&s, &s, &e); - } - secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); - VERIFY_CHECK(secp256k1_fe_is_quad_var(&noncep.y)); + VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); secp256k1_fe_normalize(&noncep.x); - secp256k1_fe_get_b32(&sig->data[0], &noncep.x); - secp256k1_scalar_get_b32(&sig->data[32], &s); + secp256k1_fe_get_b32(&sig64[0], &noncep.x); + secp256k1_scalar_get_b32(&sig64[32], &s); return 1; } -int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_pubkey *pubkey) { +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_xonly_pubkey *pubkey) { unsigned char msghash[32]; secp256k1_scalar s; secp256k1_scalar e; secp256k1_scalar mu; + secp256k1_gej pkj; secp256k1_gej rj; + secp256k1_ge pkp; secp256k1_ge rp; int overflow; @@ -554,16 +564,27 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* Multiplying the messagehash by the musig coefficient is equivalent * to multiplying the signer's public key by the coefficient, except * much easier to do. */ - secp256k1_musig_coefficient(&mu, session->pk_hash, signer->index); + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index); secp256k1_scalar_mul(&e, &e, &mu); if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { return 0; } + /* If the MuSig-combined point is negated, the signers will sign for the + * negation of their individual xonly public key such that the combined + * signature is valid for the MuSig aggregated xonly key. */ + if (session->pre_session.is_negated) { + secp256k1_scalar_negate(&e, &e); + } - if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pubkey)) { + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { return 0; } + secp256k1_gej_set_ge(&pkj, &pkp); + secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); + if (!session->nonce_is_negated) { secp256k1_ge_neg(&rp, &rp); } @@ -603,7 +624,7 @@ int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_mu return 1; } -int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { +int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { secp256k1_scalar t; secp256k1_scalar s; int overflow; @@ -612,10 +633,10 @@ int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigne (void) ctx; VERIFY_CHECK(ctx != NULL); ARG_CHECK(sec_adaptor32 != NULL); - ARG_CHECK(sig != NULL); + ARG_CHECK(sig64 != NULL); ARG_CHECK(partial_sigs != NULL); - secp256k1_scalar_set_b32(&t, &sig->data[32], &overflow); + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); if (overflow) { return 0; } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ce5b37d0..0930e90a 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -9,6 +9,69 @@ #include "secp256k1_musig.h" +int secp256k1_xonly_pubkey_create(secp256k1_xonly_pubkey *pk, const unsigned char *seckey) { + int ret; + secp256k1_keypair keypair; + ret = secp256k1_keypair_create(ctx, &keypair, seckey); + ret &= secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair); + return ret; +} + +/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig combine, sign, verify + * test. */ +void musig_simple_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signer0[2]; + secp256k1_musig_session_signer_data signer1[2]; + unsigned char nonce_commitment[2][32]; + unsigned char msg[32]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + unsigned char session_id[2][32]; + secp256k1_xonly_pubkey pk[2]; + const unsigned char *ncs[2]; + secp256k1_pubkey public_nonce[3]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char final_sig[64]; + + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(msg); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); + + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], &public_nonce[1]) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1); + + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + + CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, &combined_pk) == 1); +} + void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_scratch_space *scratch_small; secp256k1_musig_session session[2]; @@ -19,8 +82,8 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_partial_signature partial_sig[2]; secp256k1_musig_partial_signature partial_sig_adapted[2]; secp256k1_musig_partial_signature partial_sig_overflow; - secp256k1_schnorrsig final_sig; - secp256k1_schnorrsig final_sig_cmp; + unsigned char final_sig[64]; + unsigned char final_sig_cmp[64]; unsigned char buf[32]; unsigned char sk[2][32]; @@ -31,9 +94,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { const unsigned char *ncs[2]; unsigned char msg[32]; unsigned char msghash[32]; - secp256k1_pubkey combined_pk; - unsigned char pk_hash[32]; - secp256k1_pubkey pk[2]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + secp256k1_musig_pre_session pre_session_uninitialized; + secp256k1_xonly_pubkey pk[2]; unsigned char tweak[32]; unsigned char sec_adaptor[32]; @@ -54,6 +118,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); memset(ones, 0xff, 32); + /* Simulate pre_session being uninitialized by setting it to 0s. Actually providing + * an unitialized pre_session object to a initialize_*_session would be undefined + * behavior */ + memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); secp256k1_rand256(session_id[0]); secp256k1_rand256(session_id[1]); @@ -63,104 +131,108 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_rand256(sec_adaptor); secp256k1_rand256(tweak); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); + /** main test body **/ /* Key combination */ ecount = 0; - CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(ecount == 2); /* pubkey_combine does not require a scratch space */ - CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk, 2) == 1); CHECK(ecount == 2); /* A small scratch space works too, but will result in using an ineffecient algorithm */ scratch_small = secp256k1_scratch_space_create(ctx, 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk, 2) == 1); secp256k1_scratch_space_destroy(ctx, scratch_small); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, pk_hash, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk, 2) == 0); CHECK(ecount == 3); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1); CHECK(ecount == 3); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 0) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); /** Session creation **/ ecount = 0; - CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 7); CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); CHECK(ecount == 8); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 0, 0, sk[0]) == 0); - CHECK(ecount == 8); + /* Uninitialized pre_session */ + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); + CHECK(ecount == 9); /* If more than UINT32_MAX fits in a size_t, test that session_initialize * rejects n_signers that high. */ if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); } - CHECK(ecount == 8); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, NULL) == 0); CHECK(ecount == 9); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); + CHECK(ecount == 10); /* secret key overflows */ - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, ones) == 0); - CHECK(ecount == 9); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); + CHECK(ecount == 10); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; ecount = 0; - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(ecount == 0); - CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); CHECK(ecount == 3); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 0) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); CHECK(ecount == 5); if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, ((size_t) UINT32_MAX) + 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); } CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0); CHECK(secp256k1_musig_compute_messagehash(none, msghash, &session[0]) == 0); @@ -306,65 +378,59 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing combining and verification */ ecount = 0; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, NULL) == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2, NULL) == 1); - CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2, NULL) == 1); - CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, NULL, &final_sig, partial_sig_adapted, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, NULL, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); CHECK(ecount == 3); { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig_adapted[0]; partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_tmp, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0); } CHECK(ecount == 3); /* Wrong number of partial sigs */ - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 1, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0); CHECK(ecount == 3); - { - /* Overflowing tweak */ - unsigned char overflowing_tweak[32]; - memset(overflowing_tweak, 0xff, sizeof(overflowing_tweak)); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, overflowing_tweak) == 0); - CHECK(ecount == 3); - } - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify(vrfy, &final_sig, msg, &combined_pk) == 1); + CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1); /** Secret adaptor can be extracted from signature */ ecount = 0; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, nonce_is_negated) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, nonce_is_negated) == 1); CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); - CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, &final_sig, partial_sig, 2, 0) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0); CHECK(ecount == 1); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0); CHECK(ecount == 2); { - secp256k1_schnorrsig final_sig_tmp = final_sig; - memcpy(&final_sig_tmp.data[32], ones, 32); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); + unsigned char final_sig_tmp[64]; + memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp)); + memcpy(&final_sig_tmp[32], ones, 32); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); } CHECK(ecount == 2); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, NULL, 2, 0) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0); CHECK(ecount == 3); { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig[0]; partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); } CHECK(ecount == 3); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 0, 0) == 1); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, 1) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, 1) == 1); /** cleanup **/ memset(&session, 0, sizeof(session)); @@ -380,26 +446,26 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { * ones and return the resulting messagehash. This should not result in a different * messagehash because the public keys of the signers are only used during session * initialization. */ -int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { +int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { secp256k1_musig_session session; secp256k1_musig_session session_tmp; unsigned char nonce_commitment[32]; secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data signers_tmp[2]; unsigned char sk_dummy[32]; - secp256k1_pubkey pks_tmp[2]; - secp256k1_pubkey combined_pk_tmp; - unsigned char pk_hash_tmp[32]; + secp256k1_xonly_pubkey pks_tmp[2]; + secp256k1_xonly_pubkey combined_pk_tmp; + secp256k1_musig_pre_session pre_session_tmp; secp256k1_pubkey nonce; /* Set up signers with different public keys */ secp256k1_rand256(sk_dummy); pks_tmp[0] = pks[0]; - CHECK(secp256k1_ec_pubkey_create(ctx, &pks_tmp[1], sk_dummy) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, pk_hash_tmp, pks_tmp, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, pk_hash_tmp, 2, 1, sk_dummy) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); /* Call get_public_nonce with different signers than the signers the session was * initialized with. */ @@ -417,7 +483,7 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256 * commitments of signers_other do not match the nonce commitments the new session * was initialized with. If do_test is 0, the correct signers are being used and * therefore the function should return 1. */ -int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { +int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { secp256k1_musig_session session; secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data *signers_to_use; @@ -428,7 +494,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combin /* Initialize new signers */ secp256k1_rand256(session_id); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); @@ -448,7 +514,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combin * parameters but without a message. Will test that the message must be * provided with `get_public_nonce`. */ -void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { +void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { /* Create context for testing ARG_CHECKs by setting an illegal_callback. */ secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); int ecount = 0; @@ -460,7 +526,7 @@ void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey * secp256k1_musig_partial_signature partial_sig; secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pk_hash, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; @@ -488,17 +554,17 @@ void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey * * and tries to verify and combine partial sigs. If do_combine is 0, the * combine_nonces step is left out. In that case verify and combine should fail and * this function should return 0. */ -int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { +int musig_state_machine_missing_combine_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { secp256k1_musig_session session; secp256k1_musig_session_signer_data signers[2]; unsigned char nonce_commitment[32]; const unsigned char *ncs[2]; secp256k1_pubkey nonce; secp256k1_musig_partial_signature partial_sigs[2]; - secp256k1_schnorrsig sig; + unsigned char sig[64]; int partial_verify, sig_combine; - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); @@ -511,7 +577,7 @@ int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pu CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); } partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]); - sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, &sig, partial_sigs, 2, NULL); + sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, sig, partial_sigs, 2); if (do_combine != 0) { /* Return 1 if both succeeded */ return partial_verify && sig_combine; @@ -529,9 +595,9 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { unsigned char session_id[2][32]; unsigned char msg[32]; unsigned char sk[2][32]; - secp256k1_pubkey pk[2]; - secp256k1_pubkey combined_pk; - unsigned char pk_hash[32]; + secp256k1_xonly_pubkey pk[2]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; secp256k1_pubkey nonce[2]; const unsigned char *ncs[2]; secp256k1_musig_partial_signature partial_sig[2]; @@ -547,11 +613,11 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { secp256k1_rand256(sk[0]); secp256k1_rand256(sk[1]); secp256k1_rand256(msg); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, pk_hash, pk, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; @@ -583,8 +649,8 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); /* Can't combine nonces from signers of a different session */ - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); /* Partially sign */ CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); @@ -597,7 +663,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { * with different signers (i.e. they diff in public keys). This is because the * public keys of the signers is set in stone when initializing the session. */ CHECK(secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]) == 1); - CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, pk_hash, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); + CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); CHECK(memcmp(msghash1, msghash2, 32) == 0); CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); @@ -605,11 +671,11 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* Wrong signature */ CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); /* Can't get the public nonce until msg is set */ - musig_state_machine_late_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); + musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); /* Can't verify and combine partial sigs until nonces are combined */ - CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); - CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); } } @@ -618,8 +684,8 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { * while the indices 0 and 1 refer to the two signers. Here signer 0 is * sending a-coins to signer 1, while signer 1 is sending b-coins to signer * 0. Signer 0 produces the adaptor signatures. */ - secp256k1_schnorrsig final_sig_a; - secp256k1_schnorrsig final_sig_b; + unsigned char final_sig_a[64]; + unsigned char final_sig_b[64]; secp256k1_musig_partial_signature partial_sig_a[2]; secp256k1_musig_partial_signature partial_sig_b_adapted[2]; secp256k1_musig_partial_signature partial_sig_b[2]; @@ -629,12 +695,12 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { unsigned char seckey_a[2][32]; unsigned char seckey_b[2][32]; - secp256k1_pubkey pk_a[2]; - secp256k1_pubkey pk_b[2]; - unsigned char pk_hash_a[32]; - unsigned char pk_hash_b[32]; - secp256k1_pubkey combined_pk_a; - secp256k1_pubkey combined_pk_b; + secp256k1_xonly_pubkey pk_a[2]; + secp256k1_xonly_pubkey pk_b[2]; + secp256k1_musig_pre_session pre_session_a; + secp256k1_musig_pre_session pre_session_b; + secp256k1_xonly_pubkey combined_pk_a; + secp256k1_xonly_pubkey combined_pk_b; secp256k1_musig_session musig_session_a[2]; secp256k1_musig_session musig_session_b[2]; unsigned char noncommit_a[2][32]; @@ -659,22 +725,22 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { secp256k1_rand256(seckey_b[1]); secp256k1_rand256(sec_adaptor); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[0], seckey_a[0])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[1], seckey_a[1])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[0], seckey_b[0])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[1], seckey_b[1])); + CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0])); + CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1])); + CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0])); + CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1])); CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, pk_hash_a, pk_a, 2)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, pk_hash_b, pk_b, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 0, seckey_a[0])); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 1, seckey_a[1])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); noncommit_a_ptr[0] = noncommit_a[0]; noncommit_a_ptr[1] = noncommit_a[1]; - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 0, seckey_b[0])); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 1, seckey_b[1])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); noncommit_b_ptr[0] = noncommit_b[0]; noncommit_b_ptr[1] = noncommit_b[1]; @@ -707,17 +773,17 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { * is broadcasted by signer 0 to take B-coins. */ CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], &final_sig_b, partial_sig_b_adapted, 2, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_b, msg32_b, &combined_pk_b) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1); /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to * other partial signature, and takes A-coins. */ - CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, &final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], &final_sig_a, partial_sig_a, 2, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_a, msg32_a, &combined_pk_a) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); } /* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the @@ -753,93 +819,13 @@ void sha256_tag_test(void) { CHECK(memcmp(buf, buf2, 32) == 0); } - -void musig_tweak_test_helper(const secp256k1_pubkey* combined_pubkey, const unsigned char *ec_commit_tweak, const unsigned char *sk0, const unsigned char *sk1, const unsigned char *pk_hash) { - secp256k1_musig_session session[2]; - secp256k1_musig_session_signer_data signers0[2]; - secp256k1_musig_session_signer_data signers1[2]; - secp256k1_pubkey pk[2]; - unsigned char session_id[2][32]; - unsigned char msg[32]; - unsigned char nonce_commitment[2][32]; - secp256k1_pubkey nonce[2]; - const unsigned char *ncs[2]; - secp256k1_musig_partial_signature partial_sig[2]; - secp256k1_schnorrsig final_sig; - - secp256k1_rand256(session_id[0]); - secp256k1_rand256(session_id[1]); - secp256k1_rand256(msg); - - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk0) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk1) == 1); - - /* want to show that can both sign for Q and P */ - CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pk_hash, 2, 0, sk0) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pk_hash, 2, 1, sk1) == 1); - /* Set nonce commitments */ - ncs[0] = nonce_commitment[0]; - ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); - /* Set nonces */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], &final_sig, partial_sig, 2, ec_commit_tweak)); - CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig, msg, combined_pubkey) == 1); -} - -/* In this test we create a combined public key P and a commitment Q = P + - * hash(P, contract)*G. Then we test that we can sign for both public keys. In - * order to sign for Q we use the tweak32 argument of partial_sig_combine. */ -void musig_tweak_test(secp256k1_scratch_space *scratch) { - unsigned char sk[2][32]; - secp256k1_pubkey pk[2]; - unsigned char pk_hash[32]; - secp256k1_pubkey P; - unsigned char P_serialized[33]; - size_t compressed_size = 33; - secp256k1_pubkey Q; - - secp256k1_sha256 sha; - unsigned char contract[32]; - unsigned char ec_commit_tweak[32]; - - /* Setup */ - secp256k1_rand256(sk[0]); - secp256k1_rand256(sk[1]); - secp256k1_rand256(contract); - - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, pk_hash, pk, 2) == 1); - - CHECK(secp256k1_ec_pubkey_serialize(ctx, P_serialized, &compressed_size, &P, SECP256K1_EC_COMPRESSED) == 1); - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, P_serialized, 33); - secp256k1_sha256_write(&sha, contract, 32); - secp256k1_sha256_finalize(&sha, ec_commit_tweak); - memcpy(&Q, &P, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &Q, ec_commit_tweak)); - - /* Test signing for P */ - musig_tweak_test_helper(&P, NULL, sk[0], sk[1], pk_hash); - /* Test signing for Q */ - musig_tweak_test_helper(&Q, ec_commit_tweak, sk[0], sk[1], pk_hash); -} - void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + for (i = 0; i < count; i++) { + musig_simple_test(scratch); + } musig_api_tests(scratch); musig_state_machine_tests(scratch); for (i = 0; i < count; i++) { @@ -847,7 +833,6 @@ void run_musig_tests(void) { scriptless_atomic_swap(scratch); } sha256_tag_test(); - musig_tweak_test(scratch); secp256k1_scratch_space_destroy(ctx, scratch); } diff --git a/src/secp256k1.c b/src/secp256k1.c index dee63ac9..fb4283f1 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -768,14 +768,22 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif -#ifdef ENABLE_MODULE_MUSIG -# include "modules/musig/main_impl.h" -#endif - #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_MUSIG +# include "modules/musig/main_impl.h" +#endif + #ifdef ENABLE_MODULE_GENERATOR # include "modules/generator/main_impl.h" #endif @@ -792,10 +800,3 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/surjection/main_impl.h" #endif -#ifdef ENABLE_MODULE_EXTRAKEYS -# include "modules/extrakeys/main_impl.h" -#endif - -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "modules/schnorrsig/main_impl.h" -#endif From 96b9236c425125f348c15b6629b3a73c8a3062f5 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 14 Oct 2020 15:03:26 +0000 Subject: [PATCH 076/381] re-enable musig module --- Makefile.am | 6 +++--- configure.ac | 34 +++++++++++++++++----------------- contrib/travis.sh | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index 715f930f..1cba7a34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,9 +151,9 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif -#if ENABLE_MODULE_MUSIG -#include src/modules/musig/Makefile.am.include -#endif +if ENABLE_MODULE_MUSIG +include src/modules/musig/Makefile.am.include +endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include diff --git a/configure.ac b/configure.ac index 6f177b00..e60583c9 100644 --- a/configure.ac +++ b/configure.ac @@ -131,10 +131,10 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) -#AC_ARG_ENABLE(module_musig, -# AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), -# [enable_module_musig=$enableval], -# [enable_module_musig=no]) +AC_ARG_ENABLE(module_musig, + AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), + [enable_module_musig=$enableval], + [enable_module_musig=no]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), @@ -469,9 +469,9 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi -#if test x"$enable_module_musig" = x"yes"; then -# AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) -#fi +if test x"$enable_module_musig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) +fi if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) @@ -525,17 +525,17 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) -# AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) + AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) -# if test x"$enable_module_schnorrsig" != x"yes"; then -# if test x"$enable_module_musig" = x"yes"; then -# AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) -# fi -# fi + if test x"$enable_module_schnorrsig" != x"yes"; then + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) + fi + fi if test x"$enable_module_generator" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then @@ -555,9 +555,9 @@ else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi -# if test x"$enable_module_musig" = x"yes"; then -# AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) -# fi + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_extrakeys" = x"yes"; then AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.]) fi @@ -593,7 +593,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) -#AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) diff --git a/contrib/travis.sh b/contrib/travis.sh index 25124822..fad6bc18 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -18,7 +18,7 @@ fi --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ] From e0ced690cff035b61763686cb69b7d06571e23e2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 5 Nov 2020 22:07:30 +0000 Subject: [PATCH 077/381] Rename rands64 to testrandi64 This is to make it consistent with upstream changes. --- src/modules/rangeproof/tests_impl.h | 20 ++++++++++---------- src/testrand_impl.h | 2 +- src/tests.c | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index bd5b5b89..7b8a610c 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -280,11 +280,11 @@ static void test_pedersen(void) { } totalv = 0; for (i = 0; i < inputs; i++) { - values[i] = secp256k1_rands64(0, INT64_MAX - totalv); + values[i] = secp256k1_testrandi64(0, INT64_MAX - totalv); totalv += values[i]; } for (i = 0; i < outputs - 1; i++) { - values[i + inputs] = secp256k1_rands64(0, totalv); + values[i + inputs] = secp256k1_testrandi64(0, totalv); totalv -= values[i + inputs]; } values[total - 1] = totalv; @@ -480,7 +480,7 @@ static void test_rangeproof(void) { secp256k1_testrand256(blind); { /*Malleability test.*/ - v = secp256k1_rands64(0, 255); + v = secp256k1_testrandi64(0, 255); CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); @@ -501,19 +501,19 @@ static void test_rangeproof(void) { for (i = 0; i < (size_t) 2*count; i++) { int exp; int min_bits; - v = secp256k1_rands64(0, UINT64_MAX >> (secp256k1_testrand32()&63)); + v = secp256k1_testrandi64(0, UINT64_MAX >> (secp256k1_testrand32()&63)); vmin = 0; if ((v < INT64_MAX) && (secp256k1_testrand32()&1)) { - vmin = secp256k1_rands64(0, v); + vmin = secp256k1_testrandi64(0, v); } secp256k1_testrand256(blind); CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); len = 5134; - exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); + exp = (int)secp256k1_testrandi64(0,18)-(int)secp256k1_testrandi64(0,18); if (exp < 0) { exp = -exp; } - min_bits = (int)secp256k1_rands64(0,64)-(int)secp256k1_rands64(0,64); + min_bits = (int)secp256k1_testrandi64(0,64)-(int)secp256k1_testrandi64(0,64); if (min_bits < 0) { min_bits = -min_bits; } @@ -540,7 +540,7 @@ static void test_rangeproof(void) { len = k; CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } - len = secp256k1_rands64(0, 3072); + len = secp256k1_testrandi64(0, 3072); CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } } @@ -582,11 +582,11 @@ void test_multiple_generators(void) { /* Compute all the values -- can be positive or negative */ total_value = 0; for (i = 0; i < n_outputs; i++) { - value[n_inputs + i] = secp256k1_rands64(0, INT64_MAX - total_value); + value[n_inputs + i] = secp256k1_testrandi64(0, INT64_MAX - total_value); total_value += value[n_inputs + i]; } for (i = 0; i < n_inputs - 1; i++) { - value[i] = secp256k1_rands64(0, total_value); + value[i] = secp256k1_testrandi64(0, total_value); total_value -= value[i]; } value[i] = total_value; diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 725ef9cc..d80cae03 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -109,7 +109,7 @@ static void secp256k1_testrand256_test(unsigned char *b32) { secp256k1_testrand_bytes_test(b32, 32); } -SECP256K1_INLINE static int64_t secp256k1_rands64(uint64_t min, uint64_t max) { +SECP256K1_INLINE static int64_t secp256k1_testrandi64(uint64_t min, uint64_t max) { uint64_t range; uint64_t r; uint64_t clz; diff --git a/src/tests.c b/src/tests.c index 223608eb..33aef6ed 100644 --- a/src/tests.c +++ b/src/tests.c @@ -171,11 +171,11 @@ void run_util_tests(void) { for (i = 0; i < 10; i++) { CHECK(secp256k1_clz64_var((~0ULL) - secp256k1_testrand32()) == 0); r = ((uint64_t)secp256k1_testrand32() << 32) | secp256k1_testrand32(); - r2 = secp256k1_rands64(0, r); + r2 = secp256k1_testrandi64(0, r); CHECK(r2 <= r); - r3 = secp256k1_rands64(r2, r); + r3 = secp256k1_testrandi64(r2, r); CHECK((r3 >= r2) && (r3 <= r)); - r = secp256k1_rands64(0, INT64_MAX); + r = secp256k1_testrandi64(0, INT64_MAX); s = (int64_t)r * (secp256k1_testrand32()&1?-1:1); CHECK(secp256k1_sign_and_abs64(&r2, s) == (s < 0)); CHECK(r2 == r); From 29b4bd85d739cde3191bc2ffa9b71387ed983682 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 21 Nov 2019 12:05:27 +0000 Subject: [PATCH 078/381] musig: simplify state machine by adding explicit round to session struct --- include/secp256k1_musig.h | 27 ++++---- src/modules/musig/main_impl.h | 63 +++++++----------- src/modules/musig/tests_impl.h | 114 ++++++++++++++------------------- 3 files changed, 84 insertions(+), 120 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index c5352512..8d82fb18 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -45,14 +45,10 @@ typedef struct { * structure. * * Fields: - * combined_pk: MuSig-computed combined xonly public key + * round: Current round of the session * pre_session: Auxiliary data created in `pubkey_combine` + * combined_pk: MuSig-computed combined xonly public key * n_signers: Number of signers - * combined_nonce: Summed combined public nonce (undefined if `nonce_is_set` is false) - * nonce_is_set: Whether the above nonce has been set - * nonce_is_negated: If `nonce_is_set`, whether the above nonce was negated after - * summing the participants' nonces. Needed to ensure the nonce's y - * coordinate is even. * msg: The 32-byte message (hash) to be signed * msg_is_set: Whether the above message has been set * has_secret_data: Whether this session object has a signers' secret data; if this @@ -60,18 +56,18 @@ typedef struct { * seckey: If `has_secret_data`, the signer's secret key * secnonce: If `has_secret_data`, the signer's secret nonce * nonce: If `has_secret_data`, the signer's public nonce - * nonce_commitments_hash: If `has_secret_data` and `nonce_commitments_hash_is_set`, - * the hash of all signers' commitments - * nonce_commitments_hash_is_set: If `has_secret_data`, whether the - * nonce_commitments_hash has been set + * nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all + * signers' commitments + * combined_nonce: If round >= 2, the summed combined public nonce + * nonce_is_negated: If round >= 2, whether the above nonce was negated after + * summing the participants' nonces. Needed to ensure the nonce's y + * coordinate is even. */ typedef struct { - secp256k1_xonly_pubkey combined_pk; + int round; secp256k1_musig_pre_session pre_session; + secp256k1_xonly_pubkey combined_pk; uint32_t n_signers; - secp256k1_pubkey combined_nonce; - int nonce_is_set; - int nonce_is_negated; unsigned char msg[32]; int msg_is_set; int has_secret_data; @@ -79,7 +75,8 @@ typedef struct { unsigned char secnonce[32]; secp256k1_pubkey nonce; unsigned char nonce_commitments_hash[32]; - int nonce_commitments_hash_is_set; + secp256k1_pubkey combined_nonce; + int nonce_is_negated; } secp256k1_musig_session; /** Data structure containing data on all signers in a single session. diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 5f9cb0d0..fd180569 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -140,6 +140,8 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m secp256k1_sha256 sha; secp256k1_gej pj; secp256k1_ge p; + unsigned char nonce_ser[33]; + size_t nonce_ser_size = sizeof(nonce_ser); VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); @@ -162,7 +164,6 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m } memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); session->pre_session = *pre_session; - session->nonce_is_set = 0; session->has_secret_data = 1; if (n_signers == 0 || my_index >= n_signers) { return 0; @@ -172,7 +173,6 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m } session->n_signers = (uint32_t) n_signers; secp256k1_musig_signers_init(signers, session->n_signers); - session->nonce_commitments_hash_is_set = 0; /* Compute secret key */ secp256k1_scalar_set_b32(&secret, seckey, &overflow); @@ -223,15 +223,12 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m secp256k1_ge_set_gej(&p, &pj); secp256k1_pubkey_save(&session->nonce, &p); - if (nonce_commitment32 != NULL) { - unsigned char commit[33]; - size_t commit_size = sizeof(commit); - secp256k1_sha256_initialize(&sha); - secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, &session->nonce, SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, commit, commit_size); - secp256k1_sha256_finalize(&sha, nonce_commitment32); - } + secp256k1_sha256_initialize(&sha); + secp256k1_ec_pubkey_serialize(ctx, nonce_ser, &nonce_ser_size, &session->nonce, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, nonce_ser, nonce_ser_size); + secp256k1_sha256_finalize(&sha, nonce_commitment32); + session->round = 0; secp256k1_scalar_clear(&secret); return 1; } @@ -247,6 +244,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 ARG_CHECK(signers != NULL); ARG_CHECK(nonce != NULL); ARG_CHECK(commitments != NULL); + ARG_CHECK(session->round == 0); /* If the message was not set during initialization it must be set now. */ ARG_CHECK(!(!session->msg_is_set && msg32 == NULL)); /* The message can only be set once. */ @@ -269,15 +267,9 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 secp256k1_sha256_write(&sha, commitments[i], 32); } secp256k1_sha256_finalize(&sha, nonce_commitments_hash); - if (session->nonce_commitments_hash_is_set - && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { - /* Abort if get_public_nonce has been called before with a different array of - * commitments. */ - return 0; - } memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); - session->nonce_commitments_hash_is_set = 1; memcpy(nonce, &session->nonce, sizeof(*nonce)); + session->round = 1; return 1; } @@ -313,15 +305,14 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se secp256k1_musig_signers_init(signers, session->n_signers); session->pre_session = *pre_session; - session->nonce_is_set = 0; session->msg_is_set = 1; memcpy(session->msg, msg32, 32); session->has_secret_data = 0; - session->nonce_commitments_hash_is_set = 0; for (i = 0; i < n_signers; i++) { memcpy(signers[i].nonce_commitment, commitments[i], 32); } + session->round = 1; return 1; } @@ -358,6 +349,7 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 VERIFY_CHECK(ctx != NULL); ARG_CHECK(session != NULL); ARG_CHECK(signers != NULL); + ARG_CHECK(session->round == 1); if (n_signers != session->n_signers) { return 0; @@ -373,16 +365,13 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); } secp256k1_sha256_finalize(&sha, nonce_commitments_hash); - /* Either the session is a verifier session or or the nonce_commitments_hash has - * been set in `musig_session_get_public_nonce`. */ - VERIFY_CHECK(!session->has_secret_data || session->nonce_commitments_hash_is_set); + /* If the signers' commitments changed between get_public_nonce and now we + * have to abort because in that case they may have seen our nonce before + * creating their commitment. That can happen if the signer_data given to + * this function is different to the signer_data given to get_public_nonce. + * */ if (session->has_secret_data && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { - /* If the signers' commitments changed between get_public_nonce and now we - * have to abort because in that case they may have seen our nonce before - * creating their commitment. That can happen if the signer_data given to - * this function is different to the signer_data given to get_public_nonce. - * */ return 0; } @@ -403,7 +392,7 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 *nonce_is_negated = session->nonce_is_negated; } secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep); - session->nonce_is_set = 1; + session->round = 2; return 1; } @@ -429,19 +418,15 @@ static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, uns secp256k1_ge rp; secp256k1_sha256 sha; + VERIFY_CHECK(session->round >= 2); + secp256k1_schnorrsig_sha256_tagged(&sha); - if (!session->nonce_is_set) { - return 0; - } secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce); secp256k1_fe_get_b32(buf, &rp.x); secp256k1_sha256_write(&sha, buf, 32); secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk); secp256k1_sha256_write(&sha, buf, 32); - if (!session->msg_is_set) { - return 0; - } secp256k1_sha256_write(&sha, session->msg, 32); secp256k1_sha256_finalize(&sha, msghash); return 1; @@ -456,8 +441,9 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m VERIFY_CHECK(ctx != NULL); ARG_CHECK(partial_sig != NULL); ARG_CHECK(session != NULL); + ARG_CHECK(session->round == 2); - if (!session->nonce_is_set || !session->has_secret_data) { + if (!session->has_secret_data) { return 0; } @@ -503,10 +489,8 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp ARG_CHECK(sig64 != NULL); ARG_CHECK(partial_sigs != NULL); ARG_CHECK(session != NULL); + ARG_CHECK(session->round == 2); - if (!session->nonce_is_set) { - return 0; - } if (n_sigs != session->n_signers) { return 0; } @@ -548,8 +532,9 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 ARG_CHECK(signer != NULL); ARG_CHECK(partial_sig != NULL); ARG_CHECK(pubkey != NULL); + ARG_CHECK(session->round == 2); - if (!session->nonce_is_set || !signer->present) { + if (!signer->present) { return 0; } secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 49880a87..820b8534 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -93,7 +93,6 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { int nonce_is_negated; const unsigned char *ncs[2]; unsigned char msg[32]; - unsigned char msghash[32]; secp256k1_xonly_pubkey combined_pk; secp256k1_musig_pre_session pre_session; secp256k1_musig_pre_session pre_session_uninitialized; @@ -234,37 +233,41 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 5); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); - CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0); - CHECK(secp256k1_musig_compute_messagehash(none, msghash, &session[0]) == 0); - /** Signing step 0 -- exchange nonce commitments */ ecount = 0; { secp256k1_pubkey nonce; + secp256k1_musig_session session_0_tmp; + + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); /* Can obtain public nonce after commitments have been exchanged; still can't sign */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &nonce, ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 0); - CHECK(ecount == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &nonce, ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session_0_tmp, &partial_sig[0]) == 0); + CHECK(ecount == 1); } /** Signing step 1 -- exchange nonces */ ecount = 0; { secp256k1_pubkey public_nonce[3]; + secp256k1_musig_session session_0_tmp; - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], ncs, 2, NULL) == 1); CHECK(ecount == 0); + /* Reset session */ + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], NULL, &public_nonce[0], ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, NULL, ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], NULL, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], NULL, 2, NULL) == 0); CHECK(ecount == 4); /* Number of commitments and number of signers are different */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 1, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], ncs, 1, NULL) == 0); CHECK(ecount == 4); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); @@ -287,17 +290,20 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], &public_nonce[1]) == 1); ecount = 0; - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &nonce_is_negated, &adaptor) == 1); + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &nonce_is_negated, &adaptor) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], NULL, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &nonce_is_negated, &adaptor) == 0); CHECK(ecount == 2); /* Number of signers differs from number during intialization */ - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 1, &nonce_is_negated, &adaptor) == 0); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &nonce_is_negated, &adaptor) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, NULL, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, NULL) == 1); + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &nonce_is_negated, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &nonce_is_negated, &adaptor) == 1); @@ -550,43 +556,8 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); } -/* Recreates a session with the given session_id, signers, pk, msg etc. parameters - * and tries to verify and combine partial sigs. If do_combine is 0, the - * combine_nonces step is left out. In that case verify and combine should fail and - * this function should return 0. */ -int musig_state_machine_missing_combine_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { - secp256k1_musig_session session; - secp256k1_musig_session_signer_data signers[2]; - unsigned char nonce_commitment[32]; - const unsigned char *ncs[2]; - secp256k1_pubkey nonce; - secp256k1_musig_partial_signature partial_sigs[2]; - unsigned char sig[64]; - int partial_verify, sig_combine; - - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); - ncs[0] = nonce_commitment_other; - ncs[1] = nonce_commitment; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); - - partial_sigs[0] = *partial_sig_other; - partial_sigs[1] = *partial_sig; - if (do_combine != 0) { - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); - } - partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]); - sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, sig, partial_sigs, 2); - if (do_combine != 0) { - /* Return 1 if both succeeded */ - return partial_verify && sig_combine; - } - /* Return 0 if both failed */ - return partial_verify || sig_combine; -} - void musig_state_machine_tests(secp256k1_scratch_space *scratch) { + secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_VERIFY); size_t i; secp256k1_musig_session session[2]; secp256k1_musig_session_signer_data signers0[2]; @@ -601,8 +572,13 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { secp256k1_pubkey nonce[2]; const unsigned char *ncs[2]; secp256k1_musig_partial_signature partial_sig[2]; + unsigned char sig[64]; unsigned char msghash1[32]; unsigned char msghash2[32]; + int ecount; + + secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); + ecount = 0; /* Run state machine with the same objects twice to test that it's allowed to * reinitialize session and session_signer_data. */ @@ -618,17 +594,19 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + /* Can't combine nonces unless we're through round 1 already */ + ecount = 0; + CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0); + CHECK(ecount == 1); /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); - /* Changing a nonce commitment is not okay */ - ncs[1] = (unsigned char*) "this isn't a nonce commitment..."; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 0); - /* Repeating with the same nonce commitments is okay */ - ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); + /* Calling the function again is not okay */ + ecount = 0; + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 0); + CHECK(ecount == 1); /* Get nonce for signer 1 */ CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); @@ -654,9 +632,16 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* Partially sign */ CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); - /* Can't verify or sign until nonce is combined */ - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); - CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 0); + /* Can't verify, sign or combine signatures until nonce is combined */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_verify(ctx_tmp, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sign(ctx_tmp, &session[1], &partial_sig[1]) == 0); + CHECK(ecount == 2); + memset(&partial_sig[1], 0, sizeof(partial_sig[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx_tmp, &session[1], sig, partial_sig, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); /* messagehash should be the same as a session whose get_public_nonce was called @@ -672,11 +657,8 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); /* Can't get the public nonce until msg is set */ musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); - - /* Can't verify and combine partial sigs until nonces are combined */ - CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); - CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); } + secp256k1_context_destroy(ctx_tmp); } void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { From ac2d0e669729f44e2b459b1d007d6631b0fb6c40 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 21 Nov 2019 12:36:54 +0000 Subject: [PATCH 079/381] musig: add magic to session to detect if session is uninitalized --- include/secp256k1_musig.h | 5 ++- src/modules/musig/main_impl.h | 10 ++++- src/modules/musig/tests_impl.h | 69 +++++++++++++++++++++------------- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 8d82fb18..942e325c 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -22,7 +22,7 @@ extern "C" { /** Data structure containing auxiliary data generated in `pubkey_combine` and * required for `session_*_initialize`. * Fields: - * magic: Set during initialization in `pubkey_combine` in order to allow + * magic: Set during initialization in `pubkey_combine` to allow * detecting an uninitialized object. * pk_hash: The 32-byte hash of the original public keys * is_negated: Whether the MuSig-aggregated point was negated when @@ -45,6 +45,8 @@ typedef struct { * structure. * * Fields: + * magic: Set in `musig_session_initialize` to allow detecting an + * uninitialized object. * round: Current round of the session * pre_session: Auxiliary data created in `pubkey_combine` * combined_pk: MuSig-computed combined xonly public key @@ -64,6 +66,7 @@ typedef struct { * coordinate is even. */ typedef struct { + uint64_t magic; int round; secp256k1_musig_pre_session pre_session; secp256k1_xonly_pubkey combined_pk; diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index fd180569..8f9ae305 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -87,7 +87,6 @@ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256 return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); } - static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { uint32_t i; for (i = 0; i < n_signers; i++) { @@ -132,6 +131,8 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat return 1; } +static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL; + int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { unsigned char combined_ser[32]; int overflow; @@ -156,6 +157,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m memset(session, 0, sizeof(*session)); + session->magic = session_magic; if (msg32 != NULL) { memcpy(session->msg, msg32, 32); session->msg_is_set = 1; @@ -244,6 +246,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 ARG_CHECK(signers != NULL); ARG_CHECK(nonce != NULL); ARG_CHECK(commitments != NULL); + ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 0); /* If the message was not set during initialization it must be set now. */ ARG_CHECK(!(!session->msg_is_set && msg32 == NULL)); @@ -296,6 +299,7 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se memset(session, 0, sizeof(*session)); + session->magic = session_magic; memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); session->pre_session = *pre_session; if (n_signers == 0) { @@ -349,6 +353,7 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 VERIFY_CHECK(ctx != NULL); ARG_CHECK(session != NULL); ARG_CHECK(signers != NULL); + ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 1); if (n_signers != session->n_signers) { @@ -441,6 +446,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m VERIFY_CHECK(ctx != NULL); ARG_CHECK(partial_sig != NULL); ARG_CHECK(session != NULL); + ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 2); if (!session->has_secret_data) { @@ -489,6 +495,7 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp ARG_CHECK(sig64 != NULL); ARG_CHECK(partial_sigs != NULL); ARG_CHECK(session != NULL); + ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 2); if (n_sigs != session->n_signers) { @@ -532,6 +539,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 ARG_CHECK(signer != NULL); ARG_CHECK(partial_sig != NULL); ARG_CHECK(pubkey != NULL); + ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 2); if (!signer->present) { diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 820b8534..cc15bcec 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -75,6 +75,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_scratch_space *scratch_small; secp256k1_musig_session session[2]; + secp256k1_musig_session session_uninitialized; secp256k1_musig_session verifier_session; secp256k1_musig_session_signer_data signer0[2]; secp256k1_musig_session_signer_data signer1[2]; @@ -117,10 +118,11 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); memset(ones, 0xff, 32); - /* Simulate pre_session being uninitialized by setting it to 0s. Actually providing - * an unitialized pre_session object to a initialize_*_session would be undefined - * behavior */ + /* Simulate structs being uninitialized by setting it to 0s. We don't want + * to produce undefined behavior by actually providing uninitialized + * structs. */ memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); + memset(&session_uninitialized, 0, sizeof(session_uninitialized)); secp256k1_testrand256(session_id[0]); secp256k1_testrand256(session_id[1]); @@ -260,15 +262,18 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, &public_nonce[0], ncs, 2, NULL) == 0); + /* uninitialized session */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, &public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], NULL, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0); CHECK(ecount == 4); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], NULL, 2, NULL) == 0); + CHECK(ecount == 5); /* Number of commitments and number of signers are different */ CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], ncs, 1, NULL) == 0); - CHECK(ecount == 4); + CHECK(ecount == 5); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); @@ -277,12 +282,12 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0); CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); - CHECK(ecount == 4); + CHECK(ecount == 5); CHECK(secp256k1_musig_set_nonce(none, NULL, &public_nonce[0]) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); CHECK(ecount == 6); + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); + CHECK(ecount == 7); CHECK(secp256k1_musig_set_nonce(none, &signer1[0], &public_nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer1[1], &public_nonce[1]) == 1); @@ -295,13 +300,16 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &nonce_is_negated, &adaptor) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &nonce_is_negated, &adaptor) == 0); + /* Uninitialized session */ + CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &nonce_is_negated, &adaptor) == 0); CHECK(ecount == 2); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 3); /* Number of signers differs from number during intialization */ CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &nonce_is_negated, &adaptor) == 0); - CHECK(ecount == 2); + CHECK(ecount == 3); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1); - CHECK(ecount == 2); + CHECK(ecount == 3); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &nonce_is_negated, NULL) == 1); @@ -316,14 +324,17 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 0); CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); + /* Uninitialized session */ + CHECK(secp256k1_musig_partial_sign(none, &session_uninitialized, &partial_sig[0]) == 0); CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); + CHECK(ecount == 3); CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); /* observer can't sign */ CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); - CHECK(ecount == 2); + CHECK(ecount == 3); ecount = 0; CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); @@ -350,14 +361,17 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 2); CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); + /* Unitialized session */ + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session_uninitialized, &signer0[0], &partial_sig[0], &pk[0]) == 0); CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 5); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0); CHECK(ecount == 6); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0); + CHECK(ecount == 7); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1); @@ -365,7 +379,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1); - CHECK(ecount == 6); + CHECK(ecount == 7); /** Adaptor signature verification */ memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); @@ -392,22 +406,25 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); + /* Unitialized session */ + CHECK(secp256k1_musig_partial_sig_combine(none, &session_uninitialized, final_sig, partial_sig_adapted, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); + CHECK(ecount == 4); { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig_adapted[0]; partial_sig_tmp[1] = partial_sig_overflow; CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0); } - CHECK(ecount == 3); + CHECK(ecount == 4); /* Wrong number of partial sigs */ CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0); - CHECK(ecount == 3); + CHECK(ecount == 4); CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); - CHECK(ecount == 3); + CHECK(ecount == 4); CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1); From ebc31f1f9d664fa15e0c4ec8af848d7b9de88375 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 22 Nov 2019 13:58:40 +0000 Subject: [PATCH 080/381] musig: add ARG_CHECKs to functions to help debuggability --- include/secp256k1_musig.h | 18 +++++++----- src/modules/musig/main_impl.h | 51 +++++++++++----------------------- src/modules/musig/tests_impl.h | 34 +++++++++++------------ 3 files changed, 44 insertions(+), 59 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 942e325c..5e0f2ba0 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -142,7 +142,7 @@ typedef struct { * In: pubkeys: input array of public keys to combine. The order is important; * a different order will result in a different combined public * key (cannot be NULL) - * n_pubkeys: length of pubkeys array + * n_pubkeys: length of pubkeys array. Must be greater than 0. */ SECP256K1_API int secp256k1_musig_pubkey_combine( const secp256k1_context* ctx, @@ -176,7 +176,8 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( * `musig_pubkey_combine` (cannot be NULL) * n_signers: length of signers array. Number of signers participating in * the MuSig. Must be greater than 0 and at most 2^32 - 1. - * my_index: index of this signer in the signers array + * my_index: index of this signer in the signers array. Must be less + * than `n_signers`. * seckey: the signer's 32-byte secret key (cannot be NULL) */ SECP256K1_API int secp256k1_musig_session_initialize( @@ -193,7 +194,10 @@ SECP256K1_API int secp256k1_musig_session_initialize( const unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); -/** Gets the signer's public nonce given a list of all signers' data with commitments +/** Gets the signer's public nonce given a list of all signers' data with + * commitments. Called by participating signers after + * `secp256k1_musig_session_initialize` and after all nonce commitments have + * been collected * * Returns: 1: public nonce is written in nonce * 0: signer data is missing commitments or session isn't initialized @@ -204,7 +208,7 @@ SECP256K1_API int secp256k1_musig_session_initialize( * `musig_session_initialize`. Array length must equal to * `n_commitments` (cannot be NULL) * Out: nonce: the nonce (cannot be NULL) - * In: commitments: array of 32-byte nonce commitments (cannot be NULL) + * In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL) * n_commitments: the length of commitments and signers array. Must be the total * number of signers participating in the MuSig. * msg32: the 32-byte message to be signed. Must be NULL if already @@ -234,8 +238,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi * pre_session: pointer to a musig_pre_session struct from * `musig_pubkey_combine` (cannot be NULL) * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) - * commitments: array of 32-byte nonce commitments. Array length must equal to - * `n_signers` (cannot be NULL) + * commitments: array of pointers to 32-byte nonce commitments. Array + * length must equal to `n_signers` (cannot be NULL) * n_signers: length of signers and commitments array. Number of signers * participating in the MuSig. Must be greater than 0 and at most * 2^32 - 1. @@ -369,7 +373,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif * * Returns: 1: all partial signatures have values in range. Does NOT mean the * resulting signature verifies. - * 0: some partial signature had s/r out of range + * 0: some partial signature are missing or had s or r out of range * Args: ctx: pointer to a context object (cannot be NULL) * session: initialized session for which the combined nonce has been * computed (cannot be NULL) diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 8f9ae305..6d602ad9 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -155,6 +155,10 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m ARG_CHECK(pre_session->magic == pre_session_magic); ARG_CHECK(seckey != NULL); + ARG_CHECK(n_signers > 0); + ARG_CHECK(n_signers <= UINT32_MAX); + ARG_CHECK(my_index < n_signers); + memset(session, 0, sizeof(*session)); session->magic = session_magic; @@ -167,12 +171,6 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); session->pre_session = *pre_session; session->has_secret_data = 1; - if (n_signers == 0 || my_index >= n_signers) { - return 0; - } - if (n_signers > UINT32_MAX) { - return 0; - } session->n_signers = (uint32_t) n_signers; secp256k1_musig_signers_init(signers, session->n_signers); @@ -243,19 +241,18 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 VERIFY_CHECK(ctx != NULL); ARG_CHECK(session != NULL); + ARG_CHECK(session->magic == session_magic); ARG_CHECK(signers != NULL); ARG_CHECK(nonce != NULL); ARG_CHECK(commitments != NULL); - ARG_CHECK(session->magic == session_magic); + ARG_CHECK(session->round == 0); /* If the message was not set during initialization it must be set now. */ ARG_CHECK(!(!session->msg_is_set && msg32 == NULL)); /* The message can only be set once. */ ARG_CHECK(!(session->msg_is_set && msg32 != NULL)); - - if (!session->has_secret_data || n_commitments != session->n_signers) { - return 0; - } + ARG_CHECK(session->has_secret_data); + ARG_CHECK(n_commitments == session->n_signers); for (i = 0; i < n_commitments; i++) { ARG_CHECK(commitments[i] != NULL); } @@ -289,9 +286,8 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se ARG_CHECK(commitments != NULL); /* Check n_signers before checking commitments to allow testing the case where * n_signers is big without allocating the space. */ - if (n_signers > UINT32_MAX) { - return 0; - } + ARG_CHECK(n_signers > 0); + ARG_CHECK(n_signers <= UINT32_MAX); for (i = 0; i < n_signers; i++) { ARG_CHECK(commitments[i] != NULL); } @@ -302,9 +298,6 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se session->magic = session_magic; memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); session->pre_session = *pre_session; - if (n_signers == 0) { - return 0; - } session->n_signers = (uint32_t) n_signers; secp256k1_musig_signers_init(signers, session->n_signers); @@ -355,10 +348,8 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 ARG_CHECK(signers != NULL); ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 1); + ARG_CHECK(n_signers == session->n_signers); - if (n_signers != session->n_signers) { - return 0; - } secp256k1_sha256_initialize(&sha); secp256k1_gej_set_infinity(&combined_noncej); for (i = 0; i < n_signers; i++) { @@ -418,7 +409,7 @@ int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp25 } /* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ -static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { +static void secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { unsigned char buf[32]; secp256k1_ge rp; secp256k1_sha256 sha; @@ -434,7 +425,6 @@ static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, uns secp256k1_sha256_write(&sha, buf, 32); secp256k1_sha256_write(&sha, session->msg, 32); secp256k1_sha256_finalize(&sha, msghash); - return 1; } int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) { @@ -448,15 +438,10 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m ARG_CHECK(session != NULL); ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 2); - - if (!session->has_secret_data) { - return 0; - } + ARG_CHECK(session->has_secret_data); /* build message hash */ - if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { - return 0; - } + secp256k1_musig_compute_messagehash(ctx, msghash, session); secp256k1_scalar_set_b32(&e, msghash, NULL); secp256k1_scalar_set_b32(&sk, session->seckey, &overflow); @@ -541,17 +526,13 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 ARG_CHECK(pubkey != NULL); ARG_CHECK(session->magic == session_magic); ARG_CHECK(session->round == 2); + ARG_CHECK(signer->present); - if (!signer->present) { - return 0; - } secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); if (overflow) { return 0; } - if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { - return 0; - } + secp256k1_musig_compute_messagehash(ctx, msghash, session); secp256k1_scalar_set_b32(&e, msghash, NULL); /* Multiplying the messagehash by the musig coefficient is equivalent diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index cc15bcec..19fb0a5e 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -196,18 +196,18 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); CHECK(ecount == 9); CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); - CHECK(ecount == 9); + CHECK(ecount == 10); /* If more than UINT32_MAX fits in a size_t, test that session_initialize * rejects n_signers that high. */ if (SIZE_MAX > UINT32_MAX) { CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); } - CHECK(ecount == 9); + CHECK(ecount == 11); CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); - CHECK(ecount == 10); + CHECK(ecount == 12); /* secret key overflows */ CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); - CHECK(ecount == 10); + CHECK(ecount == 12); CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); @@ -228,11 +228,11 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 5); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); - CHECK(ecount == 5); + CHECK(ecount == 6); if (SIZE_MAX > UINT32_MAX) { CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); } - CHECK(ecount == 5); + CHECK(ecount == 7); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); /** Signing step 0 -- exchange nonce commitments */ @@ -273,7 +273,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 5); /* Number of commitments and number of signers are different */ CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], ncs, 1, NULL) == 0); - CHECK(ecount == 5); + CHECK(ecount == 6); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); @@ -282,12 +282,12 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0); CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); - CHECK(ecount == 5); + CHECK(ecount == 6); CHECK(secp256k1_musig_set_nonce(none, NULL, &public_nonce[0]) == 0); - CHECK(ecount == 6); - CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); CHECK(ecount == 7); + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); + CHECK(ecount == 8); CHECK(secp256k1_musig_set_nonce(none, &signer1[0], &public_nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer1[1], &public_nonce[1]) == 1); @@ -307,9 +307,9 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 3); /* Number of signers differs from number during intialization */ CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &nonce_is_negated, &adaptor) == 0); - CHECK(ecount == 3); + CHECK(ecount == 4); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1); - CHECK(ecount == 3); + CHECK(ecount == 4); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &nonce_is_negated, NULL) == 1); @@ -334,7 +334,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); /* observer can't sign */ CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); - CHECK(ecount == 3); + CHECK(ecount == 4); ecount = 0; CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); @@ -469,7 +469,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { * ones and return the resulting messagehash. This should not result in a different * messagehash because the public keys of the signers are only used during session * initialization. */ -int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { +void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { secp256k1_musig_session session; secp256k1_musig_session session_tmp; unsigned char nonce_commitment[32]; @@ -498,7 +498,7 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256 CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); - return secp256k1_musig_compute_messagehash(ctx, msghash, &session); + secp256k1_musig_compute_messagehash(ctx, msghash, &session); } /* Creates a new session (with a different session id) and tries to use that session @@ -664,8 +664,8 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* messagehash should be the same as a session whose get_public_nonce was called * with different signers (i.e. they diff in public keys). This is because the * public keys of the signers is set in stone when initializing the session. */ - CHECK(secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]) == 1); - CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); + secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]); + musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, &nonce[0], sk[1], session_id[1]); CHECK(memcmp(msghash1, msghash2, 32) == 0); CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); From 2117e7466a2efefeca3bd941d945be0fc7b4d9ff Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 17 Dec 2019 10:10:38 +0000 Subject: [PATCH 081/381] musig: improve variable naming and be consistent with schnorrsig module session_initialize -> session_init msg_is_set -> is_msg_set is_negated -> pk_parity nonce_is_negated -> nonce_parity --- include/secp256k1_musig.h | 63 ++++++++-------- src/modules/musig/example.c | 4 +- src/modules/musig/main_impl.h | 64 ++++++++--------- src/modules/musig/musig.md | 8 +-- src/modules/musig/tests_impl.h | 127 +++++++++++++++++---------------- 5 files changed, 134 insertions(+), 132 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 5e0f2ba0..d580b8df 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -20,18 +20,18 @@ extern "C" { */ /** Data structure containing auxiliary data generated in `pubkey_combine` and - * required for `session_*_initialize`. + * required for `session_*_init`. * Fields: - * magic: Set during initialization in `pubkey_combine` to allow - * detecting an uninitialized object. - * pk_hash: The 32-byte hash of the original public keys - * is_negated: Whether the MuSig-aggregated point was negated when - * converting it to the combined xonly pubkey. + * magic: Set during initialization in `pubkey_combine` to allow + * detecting an uninitialized object. + * pk_hash: The 32-byte hash of the original public keys + * pk_parity: Whether the MuSig-aggregated point was negated when + * converting it to the combined xonly pubkey. */ typedef struct { uint64_t magic; unsigned char pk_hash[32]; - int is_negated; + int pk_parity; } secp256k1_musig_pre_session; /** Data structure containing data related to a signing session resulting in a single @@ -45,14 +45,14 @@ typedef struct { * structure. * * Fields: - * magic: Set in `musig_session_initialize` to allow detecting an + * magic: Set in `musig_session_init` to allow detecting an * uninitialized object. * round: Current round of the session * pre_session: Auxiliary data created in `pubkey_combine` * combined_pk: MuSig-computed combined xonly public key * n_signers: Number of signers * msg: The 32-byte message (hash) to be signed - * msg_is_set: Whether the above message has been set + * is_msg_set: Whether the above message has been set * has_secret_data: Whether this session object has a signers' secret data; if this * is `false`, it may still be used for verification purposes. * seckey: If `has_secret_data`, the signer's secret key @@ -61,9 +61,8 @@ typedef struct { * nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all * signers' commitments * combined_nonce: If round >= 2, the summed combined public nonce - * nonce_is_negated: If round >= 2, whether the above nonce was negated after - * summing the participants' nonces. Needed to ensure the nonce's y - * coordinate is even. + * combined_nonce_parity: If round >= 2, the parity of the Y coordinate of above + * nonce. */ typedef struct { uint64_t magic; @@ -71,23 +70,23 @@ typedef struct { secp256k1_musig_pre_session pre_session; secp256k1_xonly_pubkey combined_pk; uint32_t n_signers; + int is_msg_set; unsigned char msg[32]; - int msg_is_set; int has_secret_data; unsigned char seckey[32]; unsigned char secnonce[32]; secp256k1_pubkey nonce; unsigned char nonce_commitments_hash[32]; secp256k1_pubkey combined_nonce; - int nonce_is_negated; + int combined_nonce_parity; } secp256k1_musig_session; /** Data structure containing data on all signers in a single session. * * The workflow for this structure is as follows: * - * 1. This structure is initialized with `musig_session_initialize` or - * `musig_session_initialize_verifier`, which set the `index` field, and zero out + * 1. This structure is initialized with `musig_session_init` or + * `musig_session_init_verifier`, which set the `index` field, and zero out * all other fields. The public session is initialized with the signers' * nonce_commitments. * @@ -129,7 +128,8 @@ typedef struct { unsigned char data[32]; } secp256k1_musig_partial_signature; -/** Computes a combined public key and the hash of the given public keys +/** Computes a combined public key and the hash of the given public keys. + * Different orders of `pubkeys` result in different `combined_pk`s. * * Returns: 1 if the public keys were successfully combined, 0 otherwise * Args: ctx: pointer to a context object initialized for verification @@ -138,7 +138,7 @@ typedef struct { * multiexponentiation. If NULL, an inefficient algorithm is used. * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in - * `musig_session_initialize`. + * `musig_session_init`. * In: pubkeys: input array of public keys to combine. The order is important; * a different order will result in a different combined public * key (cannot be NULL) @@ -180,7 +180,7 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( * than `n_signers`. * seckey: the signer's 32-byte secret key (cannot be NULL) */ -SECP256K1_API int secp256k1_musig_session_initialize( +SECP256K1_API int secp256k1_musig_session_init( const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, @@ -196,7 +196,7 @@ SECP256K1_API int secp256k1_musig_session_initialize( /** Gets the signer's public nonce given a list of all signers' data with * commitments. Called by participating signers after - * `secp256k1_musig_session_initialize` and after all nonce commitments have + * `secp256k1_musig_session_init` and after all nonce commitments have * been collected * * Returns: 1: public nonce is written in nonce @@ -205,14 +205,14 @@ SECP256K1_API int secp256k1_musig_session_initialize( * Args: ctx: pointer to a context object (cannot be NULL) * session: the signing session to get the nonce from (cannot be NULL) * signers: an array of signers' data initialized with - * `musig_session_initialize`. Array length must equal to + * `musig_session_init`. Array length must equal to * `n_commitments` (cannot be NULL) * Out: nonce: the nonce (cannot be NULL) * In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL) * n_commitments: the length of commitments and signers array. Must be the total * number of signers participating in the MuSig. * msg32: the 32-byte message to be signed. Must be NULL if already - * set with `musig_session_initialize` otherwise can not be NULL. + * set with `musig_session_init` otherwise can not be NULL. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( const secp256k1_context* ctx, @@ -244,7 +244,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi * participating in the MuSig. Must be greater than 0 and at most * 2^32 - 1. */ -SECP256K1_API int secp256k1_musig_session_initialize_verifier( +SECP256K1_API int secp256k1_musig_session_init_verifier( const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, @@ -263,7 +263,7 @@ SECP256K1_API int secp256k1_musig_session_initialize_verifier( * Args: ctx: pointer to a context object (cannot be NULL) * signer: pointer to the signer data to update (cannot be NULL). Must have * been used with `musig_session_get_public_nonce` or initialized - * with `musig_session_initialize_verifier`. + * with `musig_session_init_verifier`. * In: nonce: signer's alleged public nonce (cannot be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( @@ -285,8 +285,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( * (cannot be NULL) * n_signers: the length of the signers array. Must be the total number of * signers participating in the MuSig. - * Out: nonce_is_negated: a pointer to an integer that indicates if the combined - * public nonce had to be negated. + * Out: nonce_parity: if non-NULL, a pointer to an integer that indicates the + * parity of the combined public nonce. Used for adaptor + * signatures. * adaptor: point to add to the combined public nonce. If NULL, nothing is * added to the combined nonce. */ @@ -295,7 +296,7 @@ SECP256K1_API int secp256k1_musig_session_combine_nonces( secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, - int *nonce_is_negated, + int *nonce_parity, const secp256k1_pubkey *adaptor ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); @@ -399,14 +400,14 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combi * In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL) * sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot * be NULL) - * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` + * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces` */ SECP256K1_API int secp256k1_musig_partial_sig_adapt( const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, - int nonce_is_negated + int nonce_parity ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Extracts a secret adaptor from a MuSig, given all parties' partial @@ -422,7 +423,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt( * In: sig64: complete 2-of-2 signature (cannot be NULL) * partial_sigs: array of partial signatures (cannot be NULL) * n_partial_sigs: number of elements in partial_sigs array - * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` + * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces` */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( const secp256k1_context* ctx, @@ -430,7 +431,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_ad const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, - int nonce_is_negated + int nonce_parity ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); #ifdef __cplusplus diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 4670d442..e21dd9b5 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -60,7 +60,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 return 0; } /* Create random session ID. It is absolutely necessary that the session ID - * is unique for every call of secp256k1_musig_session_initialize. Otherwise + * is unique for every call of secp256k1_musig_session_init. Otherwise * it's trivial for an attacker to extract the secret key! */ frand = fopen("/dev/urandom", "r"); if(frand == NULL) { @@ -72,7 +72,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } fclose(frand); /* Initialize session */ - if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { + if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { return 0; } nonce_commitment_ptr[i] = &nonce_commitment[i][0]; diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 6d602ad9..36791566 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -102,7 +102,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; secp256k1_gej pkj; secp256k1_ge pkp; - int is_negated; + int pk_parity; VERIFY_CHECK(ctx != NULL); ARG_CHECK(combined_pk != NULL); @@ -120,20 +120,20 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat } secp256k1_ge_set_gej(&pkp, &pkj); secp256k1_fe_normalize(&pkp.y); - is_negated = secp256k1_extrakeys_ge_even_y(&pkp); + pk_parity = secp256k1_extrakeys_ge_even_y(&pkp); secp256k1_xonly_pubkey_save(combined_pk, &pkp); if (pre_session != NULL) { pre_session->magic = pre_session_magic; memcpy(pre_session->pk_hash, ecmult_data.ell, 32); - pre_session->is_negated = is_negated; + pre_session->pk_parity = pk_parity; } return 1; } static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL; -int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { +int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { unsigned char combined_ser[32]; int overflow; secp256k1_scalar secret; @@ -164,9 +164,9 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m session->magic = session_magic; if (msg32 != NULL) { memcpy(session->msg, msg32, 32); - session->msg_is_set = 1; + session->is_msg_set = 1; } else { - session->msg_is_set = 0; + session->is_msg_set = 0; } memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); session->pre_session = *pre_session; @@ -182,10 +182,10 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m } secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); /* Compute the signers public key point and determine if the secret needs to - * be negated before signing. If the signer's pubkey is negated XOR the - * MuSig-combined pubkey is negated the secret has to be negated. This can - * be seen by looking at the secret key belonging to `combined_pk`. Let's - * define + * be negated before signing. If the signer's pubkey has an odd Y coordinate + * XOR the MuSig-combined pubkey has an odd Y coordinate, the secret has to + * be negated. This can be seen by looking at the secret key belonging to + * `combined_pk`. Let's define * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function * that normalizes a point to an even Y by negating if necessary similar to @@ -196,7 +196,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ge_set_gej(&p, &pj); secp256k1_fe_normalize(&p.y); - if (secp256k1_fe_is_odd(&p.y) != session->pre_session.is_negated) { + if (secp256k1_fe_is_odd(&p.y) != session->pre_session.pk_parity) { secp256k1_scalar_negate(&secret, &secret); } secp256k1_scalar_mul(&secret, &secret, &mu); @@ -205,7 +205,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m /* Compute secret nonce */ secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, session_id32, 32); - if (session->msg_is_set) { + if (session->is_msg_set) { secp256k1_sha256_write(&sha, msg32, 32); } secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk); @@ -248,9 +248,9 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 ARG_CHECK(session->round == 0); /* If the message was not set during initialization it must be set now. */ - ARG_CHECK(!(!session->msg_is_set && msg32 == NULL)); + ARG_CHECK(!(!session->is_msg_set && msg32 == NULL)); /* The message can only be set once. */ - ARG_CHECK(!(session->msg_is_set && msg32 != NULL)); + ARG_CHECK(!(session->is_msg_set && msg32 != NULL)); ARG_CHECK(session->has_secret_data); ARG_CHECK(n_commitments == session->n_signers); for (i = 0; i < n_commitments; i++) { @@ -259,7 +259,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 if (msg32 != NULL) { memcpy(session->msg, msg32, 32); - session->msg_is_set = 1; + session->is_msg_set = 1; } secp256k1_sha256_initialize(&sha); for (i = 0; i < n_commitments; i++) { @@ -273,7 +273,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 return 1; } -int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { +int secp256k1_musig_session_init_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { size_t i; VERIFY_CHECK(ctx != NULL); @@ -302,7 +302,7 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se secp256k1_musig_signers_init(signers, session->n_signers); session->pre_session = *pre_session; - session->msg_is_set = 1; + session->is_msg_set = 1; memcpy(session->msg, msg32, 32); session->has_secret_data = 0; @@ -335,7 +335,7 @@ int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_sess return 1; } -int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_is_negated, const secp256k1_pubkey *adaptor) { +int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_parity, const secp256k1_pubkey *adaptor) { secp256k1_gej combined_noncej; secp256k1_ge combined_noncep; secp256k1_ge noncep; @@ -379,13 +379,13 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); secp256k1_fe_normalize(&combined_noncep.y); if (!secp256k1_fe_is_odd(&combined_noncep.y)) { - session->nonce_is_negated = 0; + session->combined_nonce_parity = 0; } else { - session->nonce_is_negated = 1; + session->combined_nonce_parity = 1; secp256k1_ge_neg(&combined_noncep, &combined_noncep); } - if (nonce_is_negated != NULL) { - *nonce_is_negated = session->nonce_is_negated; + if (nonce_parity != NULL) { + *nonce_parity = session->combined_nonce_parity; } secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep); session->round = 2; @@ -456,7 +456,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m secp256k1_scalar_clear(&k); return 0; } - if (session->nonce_is_negated) { + if (session->combined_nonce_parity) { secp256k1_scalar_negate(&k, &k); } @@ -544,10 +544,10 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { return 0; } - /* If the MuSig-combined point is negated, the signers will sign for the - * negation of their individual xonly public key such that the combined - * signature is valid for the MuSig aggregated xonly key. */ - if (session->pre_session.is_negated) { + /* If the MuSig-combined point has an odd Y coordinate, the signers will + * sign for the negation of their individual xonly public key such that the + * combined signature is valid for the MuSig aggregated xonly key. */ + if (session->pre_session.pk_parity) { secp256k1_scalar_negate(&e, &e); } @@ -559,7 +559,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_gej_set_ge(&pkj, &pkp); secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); - if (!session->nonce_is_negated) { + if (!session->combined_nonce_parity) { secp256k1_ge_neg(&rp, &rp); } secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL); @@ -567,7 +567,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 return secp256k1_gej_is_infinity(&rj); } -int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_is_negated) { +int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_parity) { secp256k1_scalar s; secp256k1_scalar t; int overflow; @@ -588,7 +588,7 @@ int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_mu return 0; } - if (nonce_is_negated) { + if (nonce_parity) { secp256k1_scalar_negate(&t, &t); } @@ -598,7 +598,7 @@ int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_mu return 1; } -int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { +int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_parity) { secp256k1_scalar t; secp256k1_scalar s; int overflow; @@ -625,7 +625,7 @@ int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigne secp256k1_scalar_add(&t, &t, &s); } - if (!nonce_is_negated) { + if (!nonce_parity) { secp256k1_scalar_negate(&t, &t); } secp256k1_scalar_get_b32(sec_adaptor32, &t); diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index ec1f1df5..240e85ca 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -74,7 +74,7 @@ signature process, which is also a supported mode) acts as follows. ### Signing Participant -1. The signer starts the session by calling `secp256k1_musig_session_initialize`. +1. The signer starts the session by calling `secp256k1_musig_session_init`. This function outputs - an initialized session state in the out-pointer `session` - an array of initialized signer data in the out-pointer `signers` @@ -91,7 +91,7 @@ signature process, which is also a supported mode) acts as follows. length-32 byte arrays which can be communicated however is communicated. 3. Once all signers nonce commitments have been received, the signer records these commitments with the function `secp256k1_musig_session_get_public_nonce`. - If the signer did not provide a message to `secp256k1_musig_session_initialize`, + If the signer did not provide a message to `secp256k1_musig_session_init`, a message must be provided now. This function updates in place - the session state `session` @@ -133,8 +133,8 @@ A participant who wants to verify the signing process, i.e. check that nonce com are consistent and partial signatures are correct without contributing a partial signature, may do so using the above instructions except for the following changes: -1. A signing session should be produced using `musig_session_initialize_verifier` - rather than `musig_session_initialize`; this function takes no secret data or +1. A signing session should be produced using `musig_session_init_verifier` + rather than `musig_session_init`; this function takes no secret data or signer index. 2. The participant receives nonce commitments, public nonces and partial signatures, but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce` diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 19fb0a5e..5cdd0822 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -45,8 +45,8 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; @@ -91,7 +91,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { unsigned char ones[32]; unsigned char session_id[2][32]; unsigned char nonce_commitment[2][32]; - int nonce_is_negated; + int combined_nonce_parity; const unsigned char *ncs[2]; unsigned char msg[32]; secp256k1_xonly_pubkey combined_pk; @@ -172,68 +172,68 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Session creation **/ ecount = 0; - CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 7); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); CHECK(ecount == 8); /* Uninitialized pre_session */ - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); CHECK(ecount == 9); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); CHECK(ecount == 10); - /* If more than UINT32_MAX fits in a size_t, test that session_initialize + /* If more than UINT32_MAX fits in a size_t, test that session_init * rejects n_signers that high. */ if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); } CHECK(ecount == 11); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); CHECK(ecount == 12); /* secret key overflows */ - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); CHECK(ecount == 12); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; ecount = 0; - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(ecount == 0); - CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); CHECK(ecount == 6); if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); } CHECK(ecount == 7); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); /** Signing step 0 -- exchange nonce commitments */ ecount = 0; @@ -296,26 +296,26 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { ecount = 0; memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, &adaptor) == 1); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &combined_nonce_parity, &adaptor) == 0); CHECK(ecount == 1); /* Uninitialized session */ - CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &combined_nonce_parity, &adaptor) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &combined_nonce_parity, &adaptor) == 0); CHECK(ecount == 3); /* Number of signers differs from number during intialization */ - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &nonce_is_negated, &adaptor) == 0); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &combined_nonce_parity, &adaptor) == 0); CHECK(ecount == 4); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1); CHECK(ecount == 4); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &nonce_is_negated, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, NULL) == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &nonce_is_negated, &adaptor) == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &combined_nonce_parity, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &combined_nonce_parity, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &combined_nonce_parity, &adaptor) == 1); } /** Signing step 2 -- partial signatures */ @@ -384,16 +384,16 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Adaptor signature verification */ memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); ecount = 0; - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, nonce_is_negated) == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, combined_nonce_parity) == 1); CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0); CHECK(ecount == 1); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, nonce_is_negated) == 0); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, combined_nonce_parity) == 0); CHECK(ecount == 2); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, nonce_is_negated) == 0); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, combined_nonce_parity) == 0); CHECK(ecount == 3); /** Signing combining and verification */ @@ -430,7 +430,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Secret adaptor can be extracted from signature */ ecount = 0; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, nonce_is_negated) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, combined_nonce_parity) == 1); CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0); CHECK(ecount == 1); @@ -440,7 +440,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { unsigned char final_sig_tmp[64]; memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp)); memcpy(&final_sig_tmp[32], ones, 32); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, combined_nonce_parity) == 0); } CHECK(ecount == 2); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0); @@ -449,7 +449,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig[0]; partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, combined_nonce_parity) == 0); } CHECK(ecount == 3); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1); @@ -486,9 +486,9 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 pks_tmp[0] = pks[0]; CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); /* Call get_public_nonce with different signers than the signers the session was * initialized with. */ @@ -517,7 +517,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey * /* Initialize new signers */ secp256k1_testrand256(session_id); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); @@ -549,7 +549,7 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo secp256k1_musig_partial_signature partial_sig; secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; @@ -609,8 +609,8 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); /* Can't combine nonces unless we're through round 1 already */ ecount = 0; CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0); @@ -708,8 +708,8 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { const unsigned char *noncommit_b_ptr[2]; secp256k1_pubkey pubnon_a[2]; secp256k1_pubkey pubnon_b[2]; - int nonce_is_negated_a; - int nonce_is_negated_b; + int combined_nonce_parity_a; + int combined_nonce_parity_b; secp256k1_musig_session_signer_data data_a[2]; secp256k1_musig_session_signer_data data_b[2]; @@ -733,13 +733,13 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); noncommit_a_ptr[0] = noncommit_a[0]; noncommit_a_ptr[1] = noncommit_a[1]; - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); noncommit_b_ptr[0] = noncommit_b[0]; noncommit_b_ptr[1] = noncommit_b[1]; @@ -752,9 +752,9 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], &pubnon_a[1])); CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], &pubnon_b[0])); CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], &pubnon_b[1])); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &nonce_is_negated_a, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &combined_nonce_parity_a, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &nonce_is_negated_b, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &combined_nonce_parity_b, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor)); /* Step 3: Signer 0 produces partial signatures for both chains. */ @@ -770,16 +770,16 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { /* Step 5: Signer 0 adapts its own partial signature and combines it with the * partial signature from signer 1. This results in a complete signature which * is broadcasted by signer 0 to take B-coins. */ - CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, combined_nonce_parity_b)); memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1); /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to * other partial signature, and takes A-coins. */ - CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, combined_nonce_parity_b) == 1); CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ - CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, combined_nonce_parity_a)); CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); @@ -818,6 +818,7 @@ void sha256_tag_test(void) { CHECK(memcmp(buf, buf2, 32) == 0); } + void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); @@ -828,7 +829,7 @@ void run_musig_tests(void) { musig_api_tests(scratch); musig_state_machine_tests(scratch); for (i = 0; i < count; i++) { - /* Run multiple times to ensure that the nonce is negated in some tests */ + /* Run multiple times to ensure that the nonce has different y parities */ scriptless_atomic_swap(scratch); } sha256_tag_test(); From 73792e4a27a634b6ae134a009d4ba14e9c1af8a4 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Dec 2019 20:04:31 +0000 Subject: [PATCH 082/381] musig: represent a combined_nonce as an xonly_pubkey --- include/secp256k1_musig.h | 2 +- src/modules/musig/main_impl.h | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index d580b8df..f3e4d9ee 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -77,7 +77,7 @@ typedef struct { unsigned char secnonce[32]; secp256k1_pubkey nonce; unsigned char nonce_commitments_hash[32]; - secp256k1_pubkey combined_nonce; + secp256k1_xonly_pubkey combined_nonce; int combined_nonce_parity; } secp256k1_musig_session; diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 36791566..5353ebbd 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -376,18 +376,15 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 secp256k1_pubkey_load(ctx, &noncep, adaptor); secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); } + + /* Negate nonce if Y coordinate is not square */ secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); - secp256k1_fe_normalize(&combined_noncep.y); - if (!secp256k1_fe_is_odd(&combined_noncep.y)) { - session->combined_nonce_parity = 0; - } else { - session->combined_nonce_parity = 1; - secp256k1_ge_neg(&combined_noncep, &combined_noncep); - } + secp256k1_fe_normalize_var(&combined_noncep.y); + session->combined_nonce_parity = secp256k1_extrakeys_ge_even_y(&combined_noncep); if (nonce_parity != NULL) { *nonce_parity = session->combined_nonce_parity; } - secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep); + secp256k1_xonly_pubkey_save(&session->combined_nonce, &combined_noncep); session->round = 2; return 1; } @@ -417,7 +414,7 @@ static void secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, un VERIFY_CHECK(session->round >= 2); secp256k1_schnorrsig_sha256_tagged(&sha); - secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce); + secp256k1_xonly_pubkey_load(ctx, &rp, &session->combined_nonce); secp256k1_fe_get_b32(buf, &rp.x); secp256k1_sha256_write(&sha, buf, 32); @@ -498,7 +495,7 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp secp256k1_scalar_add(&s, &s, &term); } - secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); + secp256k1_xonly_pubkey_load(ctx, &noncep, &session->combined_nonce); VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); secp256k1_fe_normalize(&noncep.x); secp256k1_fe_get_b32(&sig64[0], &noncep.x); From 62f0b2d867a86971d05f3070c08345b78f7ac34f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Dec 2019 21:03:26 +0000 Subject: [PATCH 083/381] musig: make musig partial nonces byte arrays instead of "pubkeys" --- include/secp256k1_musig.h | 10 ++- src/modules/musig/example.c | 6 +- src/modules/musig/main_impl.h | 19 +++-- src/modules/musig/tests_impl.h | 133 ++++++++++++++++----------------- 4 files changed, 87 insertions(+), 81 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index f3e4d9ee..c3e225e8 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -207,7 +207,9 @@ SECP256K1_API int secp256k1_musig_session_init( * signers: an array of signers' data initialized with * `musig_session_init`. Array length must equal to * `n_commitments` (cannot be NULL) - * Out: nonce: the nonce (cannot be NULL) + * Out: nonce33: filled with a 33-byte public nonce which is supposed to be + * sent to the other signers and then used in `musig_set nonce` + * (cannot be NULL) * In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL) * n_commitments: the length of commitments and signers array. Must be the total * number of signers participating in the MuSig. @@ -218,7 +220,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, - secp256k1_pubkey *nonce, + unsigned char *nonce33, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32 @@ -264,12 +266,12 @@ SECP256K1_API int secp256k1_musig_session_init_verifier( * signer: pointer to the signer data to update (cannot be NULL). Must have * been used with `musig_session_get_public_nonce` or initialized * with `musig_session_init_verifier`. - * In: nonce: signer's alleged public nonce (cannot be NULL) + * In: nonce33: signer's alleged public nonce (cannot be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, - const secp256k1_pubkey *nonce + const unsigned char *nonce33 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Updates a session with the combined public nonce of all signers. The combined diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index e21dd9b5..43d982be 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -45,7 +45,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 unsigned char nonce_commitment[N_SIGNERS][32]; const unsigned char *nonce_commitment_ptr[N_SIGNERS]; secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; - secp256k1_pubkey nonce[N_SIGNERS]; + unsigned char nonce[N_SIGNERS][33]; int i, j; secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; @@ -80,14 +80,14 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 /* Communication round 1: Exchange nonce commitments */ for (i = 0; i < N_SIGNERS; i++) { /* Set nonce commitments in the signer data and get the own public nonce */ - if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], &nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) { + if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) { return 0; } } /* Communication round 2: Exchange nonces */ for (i = 0; i < N_SIGNERS; i++) { for (j = 0; j < N_SIGNERS; j++) { - if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], &nonce[j])) { + if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], nonce[j])) { /* Signer j's nonce does not match the nonce commitment. In this case * abort the protocol. If you make another attempt at finishing the * protocol, create a new session (with a fresh session ID!). */ diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 5353ebbd..99f054cc 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -233,10 +233,12 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s return 1; } -int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) { +int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) { secp256k1_sha256 sha; unsigned char nonce_commitments_hash[32]; size_t i; + unsigned char nonce_ser[33]; + size_t nonce_ser_size = sizeof(nonce_ser); (void) ctx; VERIFY_CHECK(ctx != NULL); @@ -268,7 +270,9 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 } secp256k1_sha256_finalize(&sha, nonce_commitments_hash); memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); - memcpy(nonce, &session->nonce, sizeof(*nonce)); + + secp256k1_ec_pubkey_serialize(ctx, nonce_ser, &nonce_ser_size, &session->nonce, SECP256K1_EC_COMPRESSED); + memcpy(nonce, &nonce_ser, nonce_ser_size); session->round = 1; return 1; } @@ -313,24 +317,25 @@ int secp256k1_musig_session_init_verifier(const secp256k1_context* ctx, secp256k return 1; } -int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const secp256k1_pubkey *nonce) { - unsigned char commit[33]; - size_t commit_size = sizeof(commit); +int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const unsigned char *nonce) { secp256k1_sha256 sha; + unsigned char commit[32]; VERIFY_CHECK(ctx != NULL); ARG_CHECK(signer != NULL); ARG_CHECK(nonce != NULL); secp256k1_sha256_initialize(&sha); - secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, nonce, SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, commit, commit_size); + secp256k1_sha256_write(&sha, nonce, 33); secp256k1_sha256_finalize(&sha, commit); if (memcmp(commit, signer->nonce_commitment, 32) != 0) { return 0; } memcpy(&signer->nonce, nonce, sizeof(*nonce)); + if (!secp256k1_ec_pubkey_parse(ctx, &signer->nonce, nonce, 33)) { + return 0; + } signer->present = 1; return 1; } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 5cdd0822..0ea712b4 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -31,7 +31,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { unsigned char session_id[2][32]; secp256k1_xonly_pubkey pk[2]; const unsigned char *ncs[2]; - secp256k1_pubkey public_nonce[3]; + unsigned char public_nonce[3][33]; secp256k1_musig_partial_signature partial_sig[2]; unsigned char final_sig[64]; @@ -51,13 +51,13 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], &public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], &public_nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], &public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], public_nonce[1]) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1); @@ -238,13 +238,13 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing step 0 -- exchange nonce commitments */ ecount = 0; { - secp256k1_pubkey nonce; + unsigned char nonce[33]; secp256k1_musig_session session_0_tmp; memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); /* Can obtain public nonce after commitments have been exchanged; still can't sign */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &nonce, ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_partial_sign(none, &session_0_tmp, &partial_sig[0]) == 0); CHECK(ecount == 1); } @@ -252,47 +252,47 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing step 1 -- exchange nonces */ ecount = 0; { - secp256k1_pubkey public_nonce[3]; + unsigned char public_nonce[3][33]; secp256k1_musig_session session_0_tmp; memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 2, NULL) == 1); CHECK(ecount == 0); /* Reset session */ memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 1); /* uninitialized session */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, &public_nonce[0], ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, &public_nonce[0], ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, public_nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 3); CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], NULL, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], NULL, 2, NULL) == 0); CHECK(ecount == 5); /* Number of commitments and number of signers are different */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, &public_nonce[0], ncs, 1, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 1, NULL) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer0[0], &public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0); - CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[0]) == 0); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1); CHECK(ecount == 6); - CHECK(secp256k1_musig_set_nonce(none, NULL, &public_nonce[0]) == 0); + CHECK(secp256k1_musig_set_nonce(none, NULL, public_nonce[0]) == 0); CHECK(ecount == 7); CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); CHECK(ecount == 8); - CHECK(secp256k1_musig_set_nonce(none, &signer1[0], &public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer1[1], &public_nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], &public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer1[1], public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], public_nonce[1]) == 1); ecount = 0; memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); @@ -469,7 +469,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { * ones and return the resulting messagehash. This should not result in a different * messagehash because the public keys of the signers are only used during session * initialization. */ -void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { +void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id) { secp256k1_musig_session session; secp256k1_musig_session session_tmp; unsigned char nonce_commitment[32]; @@ -479,7 +479,7 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 secp256k1_xonly_pubkey pks_tmp[2]; secp256k1_xonly_pubkey combined_pk_tmp; secp256k1_musig_pre_session pre_session_tmp; - secp256k1_pubkey nonce; + unsigned char nonce[33]; /* Set up signers with different public keys */ secp256k1_testrand256(sk_dummy); @@ -492,10 +492,10 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); /* Call get_public_nonce with different signers than the signers the session was * initialized with. */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, &nonce, nonce_commitments, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, &nonce, nonce_commitments, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, nonce, nonce_commitments, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, nonce, nonce_commitments, 2, NULL) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); secp256k1_musig_compute_messagehash(ctx, msghash, &session); @@ -506,13 +506,13 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 * commitments of signers_other do not match the nonce commitments the new session * was initialized with. If do_test is 0, the correct signers are being used and * therefore the function should return 1. */ -int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { +int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { secp256k1_musig_session session; secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data *signers_to_use; unsigned char nonce_commitment[32]; unsigned char session_id[32]; - secp256k1_pubkey nonce; + unsigned char nonce[33]; const unsigned char *ncs[2]; /* Initialize new signers */ @@ -520,10 +520,10 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey * CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL); if (do_test) { signers_to_use = signers_other; @@ -537,7 +537,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey * * parameters but without a message. Will test that the message must be * provided with `get_public_nonce`. */ -void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { +void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { /* Create context for testing ARG_CHECKs by setting an illegal_callback. */ secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); int ecount = 0; @@ -545,7 +545,7 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo secp256k1_musig_session_signer_data signers[2]; unsigned char nonce_commitment[32]; const unsigned char *ncs[2]; - secp256k1_pubkey nonce; + unsigned char nonce[33]; secp256k1_musig_partial_signature partial_sig; secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); @@ -555,19 +555,19 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo /* Trying to get the nonce without providing a message fails. */ CHECK(ecount == 0); - CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, &nonce, ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, NULL) == 0); CHECK(ecount == 1); /* Providing a message should make get_public_nonce succeed. */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, msg) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, msg) == 1); /* Trying to set the message again fails. */ CHECK(ecount == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, &nonce, ncs, 2, msg) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, msg) == 0); CHECK(ecount == 2); /* Check that it's working */ CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig)); CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); @@ -586,7 +586,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { secp256k1_xonly_pubkey pk[2]; secp256k1_xonly_pubkey combined_pk; secp256k1_musig_pre_session pre_session; - secp256k1_pubkey nonce[2]; + unsigned char nonce[2][33]; const unsigned char *ncs[2]; secp256k1_musig_partial_signature partial_sig[2]; unsigned char sig[64]; @@ -619,33 +619,33 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1); /* Calling the function again is not okay */ ecount = 0; - CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 0); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, nonce[0], ncs, 2, NULL) == 0); CHECK(ecount == 1); /* Get nonce for signer 1 */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1); /* Set nonces */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1); /* Can't set nonce that doesn't match nonce commitment */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[0]) == 0); + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[0]) == 0); /* Set correct nonce */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1); /* Combine nonces */ CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); /* Not everyone is present from signer 1's view */ CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0); /* Make everyone present */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1); /* Can't combine nonces from signers of a different session */ - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 1) == 0); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 0) == 1); /* Partially sign */ CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); @@ -665,7 +665,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { * with different signers (i.e. they diff in public keys). This is because the * public keys of the signers is set in stone when initializing the session. */ secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]); - musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, &nonce[0], sk[1], session_id[1]); + musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, nonce[0], sk[1], session_id[1]); CHECK(memcmp(msghash1, msghash2, 32) == 0); CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); @@ -673,7 +673,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* Wrong signature */ CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); /* Can't get the public nonce until msg is set */ - musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); + musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], nonce[0], sk[1], session_id[1], msg); } secp256k1_context_destroy(ctx_tmp); } @@ -706,8 +706,8 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { unsigned char noncommit_b[2][32]; const unsigned char *noncommit_a_ptr[2]; const unsigned char *noncommit_b_ptr[2]; - secp256k1_pubkey pubnon_a[2]; - secp256k1_pubkey pubnon_b[2]; + unsigned char pubnon_a[2][33]; + unsigned char pubnon_b[2][33]; int combined_nonce_parity_a; int combined_nonce_parity_b; secp256k1_musig_session_signer_data data_a[2]; @@ -744,14 +744,14 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { noncommit_b_ptr[1] = noncommit_b[1]; /* Step 2: Exchange nonces */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, &pubnon_a[0], noncommit_a_ptr, 2, NULL)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, &pubnon_a[1], noncommit_a_ptr, 2, NULL)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, &pubnon_b[0], noncommit_b_ptr, 2, NULL)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, &pubnon_b[1], noncommit_b_ptr, 2, NULL)); - CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], &pubnon_a[0])); - CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], &pubnon_a[1])); - CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], &pubnon_b[0])); - CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], &pubnon_b[1])); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, pubnon_a[0], noncommit_a_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, pubnon_a[1], noncommit_a_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, pubnon_b[0], noncommit_b_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, pubnon_b[1], noncommit_b_ptr, 2, NULL)); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], pubnon_a[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], pubnon_a[1])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], pubnon_b[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], pubnon_b[1])); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &combined_nonce_parity_a, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &combined_nonce_parity_b, &pub_adaptor)); @@ -818,7 +818,6 @@ void sha256_tag_test(void) { CHECK(memcmp(buf, buf2, 32) == 0); } - void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); From 5b4eb18ec5e8ccd9b6a5b5f23fe47e72481b7349 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Dec 2019 21:45:02 +0000 Subject: [PATCH 084/381] musig: shorten partial nonce byte array from 33 to 32 bytes --- include/secp256k1_musig.h | 13 +++++++------ src/modules/musig/example.c | 2 +- src/modules/musig/main_impl.h | 22 ++++++++++++---------- src/modules/musig/tests_impl.h | 18 +++++++++--------- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index c3e225e8..52b78ef0 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -75,7 +75,8 @@ typedef struct { int has_secret_data; unsigned char seckey[32]; unsigned char secnonce[32]; - secp256k1_pubkey nonce; + secp256k1_xonly_pubkey nonce; + int partial_nonce_parity; unsigned char nonce_commitments_hash[32]; secp256k1_xonly_pubkey combined_nonce; int combined_nonce_parity; @@ -111,7 +112,7 @@ typedef struct { typedef struct { int present; uint32_t index; - secp256k1_pubkey nonce; + secp256k1_xonly_pubkey nonce; unsigned char nonce_commitment[32]; } secp256k1_musig_session_signer_data; @@ -207,7 +208,7 @@ SECP256K1_API int secp256k1_musig_session_init( * signers: an array of signers' data initialized with * `musig_session_init`. Array length must equal to * `n_commitments` (cannot be NULL) - * Out: nonce33: filled with a 33-byte public nonce which is supposed to be + * Out: nonce32: filled with a 32-byte public nonce which is supposed to be * sent to the other signers and then used in `musig_set nonce` * (cannot be NULL) * In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL) @@ -220,7 +221,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, - unsigned char *nonce33, + unsigned char *nonce32, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32 @@ -266,12 +267,12 @@ SECP256K1_API int secp256k1_musig_session_init_verifier( * signer: pointer to the signer data to update (cannot be NULL). Must have * been used with `musig_session_get_public_nonce` or initialized * with `musig_session_init_verifier`. - * In: nonce33: signer's alleged public nonce (cannot be NULL) + * In: nonce32: signer's alleged public nonce (cannot be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, - const unsigned char *nonce33 + const unsigned char *nonce32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Updates a session with the combined public nonce of all signers. The combined diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 43d982be..2c5b7006 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -45,7 +45,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 unsigned char nonce_commitment[N_SIGNERS][32]; const unsigned char *nonce_commitment_ptr[N_SIGNERS]; secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; - unsigned char nonce[N_SIGNERS][33]; + unsigned char nonce[N_SIGNERS][32]; int i, j; secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 99f054cc..7f750945 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -141,7 +141,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s secp256k1_sha256 sha; secp256k1_gej pj; secp256k1_ge p; - unsigned char nonce_ser[33]; + unsigned char nonce_ser[32]; size_t nonce_ser_size = sizeof(nonce_ser); VERIFY_CHECK(ctx != NULL); @@ -221,10 +221,12 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s /* Compute public nonce and commitment */ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ge_set_gej(&p, &pj); - secp256k1_pubkey_save(&session->nonce, &p); + secp256k1_fe_normalize_var(&p.y); + session->partial_nonce_parity = secp256k1_extrakeys_ge_even_y(&p); + secp256k1_xonly_pubkey_save(&session->nonce, &p); secp256k1_sha256_initialize(&sha); - secp256k1_ec_pubkey_serialize(ctx, nonce_ser, &nonce_ser_size, &session->nonce, SECP256K1_EC_COMPRESSED); + secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce); secp256k1_sha256_write(&sha, nonce_ser, nonce_ser_size); secp256k1_sha256_finalize(&sha, nonce_commitment32); @@ -237,7 +239,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 secp256k1_sha256 sha; unsigned char nonce_commitments_hash[32]; size_t i; - unsigned char nonce_ser[33]; + unsigned char nonce_ser[32]; size_t nonce_ser_size = sizeof(nonce_ser); (void) ctx; @@ -271,7 +273,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 secp256k1_sha256_finalize(&sha, nonce_commitments_hash); memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); - secp256k1_ec_pubkey_serialize(ctx, nonce_ser, &nonce_ser_size, &session->nonce, SECP256K1_EC_COMPRESSED); + secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce); memcpy(nonce, &nonce_ser, nonce_ser_size); session->round = 1; return 1; @@ -326,14 +328,14 @@ int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_sess ARG_CHECK(nonce != NULL); secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, nonce, 33); + secp256k1_sha256_write(&sha, nonce, 32); secp256k1_sha256_finalize(&sha, commit); if (memcmp(commit, signer->nonce_commitment, 32) != 0) { return 0; } memcpy(&signer->nonce, nonce, sizeof(*nonce)); - if (!secp256k1_ec_pubkey_parse(ctx, &signer->nonce, nonce, 33)) { + if (!secp256k1_xonly_pubkey_parse(ctx, &signer->nonce, nonce)) { return 0; } signer->present = 1; @@ -362,7 +364,7 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 return 0; } secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32); - secp256k1_pubkey_load(ctx, &noncep, &signers[i].nonce); + secp256k1_xonly_pubkey_load(ctx, &noncep, &signers[i].nonce); secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); } secp256k1_sha256_finalize(&sha, nonce_commitments_hash); @@ -458,7 +460,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m secp256k1_scalar_clear(&k); return 0; } - if (session->combined_nonce_parity) { + if (session->partial_nonce_parity != session->combined_nonce_parity) { secp256k1_scalar_negate(&k, &k); } @@ -543,7 +545,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index); secp256k1_scalar_mul(&e, &e, &mu); - if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { + if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { return 0; } /* If the MuSig-combined point has an odd Y coordinate, the signers will diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 0ea712b4..18f3b2ef 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -31,7 +31,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { unsigned char session_id[2][32]; secp256k1_xonly_pubkey pk[2]; const unsigned char *ncs[2]; - unsigned char public_nonce[3][33]; + unsigned char public_nonce[3][32]; secp256k1_musig_partial_signature partial_sig[2]; unsigned char final_sig[64]; @@ -238,7 +238,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing step 0 -- exchange nonce commitments */ ecount = 0; { - unsigned char nonce[33]; + unsigned char nonce[32]; secp256k1_musig_session session_0_tmp; memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); @@ -252,7 +252,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing step 1 -- exchange nonces */ ecount = 0; { - unsigned char public_nonce[3][33]; + unsigned char public_nonce[3][32]; secp256k1_musig_session session_0_tmp; memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); @@ -479,7 +479,7 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 secp256k1_xonly_pubkey pks_tmp[2]; secp256k1_xonly_pubkey combined_pk_tmp; secp256k1_musig_pre_session pre_session_tmp; - unsigned char nonce[33]; + unsigned char nonce[32]; /* Set up signers with different public keys */ secp256k1_testrand256(sk_dummy); @@ -512,7 +512,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey * secp256k1_musig_session_signer_data *signers_to_use; unsigned char nonce_commitment[32]; unsigned char session_id[32]; - unsigned char nonce[33]; + unsigned char nonce[32]; const unsigned char *ncs[2]; /* Initialize new signers */ @@ -545,7 +545,7 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo secp256k1_musig_session_signer_data signers[2]; unsigned char nonce_commitment[32]; const unsigned char *ncs[2]; - unsigned char nonce[33]; + unsigned char nonce[32]; secp256k1_musig_partial_signature partial_sig; secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); @@ -586,7 +586,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { secp256k1_xonly_pubkey pk[2]; secp256k1_xonly_pubkey combined_pk; secp256k1_musig_pre_session pre_session; - unsigned char nonce[2][33]; + unsigned char nonce[2][32]; const unsigned char *ncs[2]; secp256k1_musig_partial_signature partial_sig[2]; unsigned char sig[64]; @@ -706,8 +706,8 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { unsigned char noncommit_b[2][32]; const unsigned char *noncommit_a_ptr[2]; const unsigned char *noncommit_b_ptr[2]; - unsigned char pubnon_a[2][33]; - unsigned char pubnon_b[2][33]; + unsigned char pubnon_a[2][32]; + unsigned char pubnon_b[2][32]; int combined_nonce_parity_a; int combined_nonce_parity_b; secp256k1_musig_session_signer_data data_a[2]; From 38a8b209913ab6ab3b2bac5e13a2c0a4f1be282e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Dec 2019 21:47:35 +0000 Subject: [PATCH 085/381] musig: fix memory leak in musig test --- src/modules/musig/tests_impl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 18f3b2ef..bb6ef3ae 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -571,6 +571,7 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig)); CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); + secp256k1_context_destroy(ctx_tmp); } void musig_state_machine_tests(secp256k1_scratch_space *scratch) { From 4d20713425e22553edd59f403e223f1d8bc37e44 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 24 Nov 2020 10:43:28 +1100 Subject: [PATCH 086/381] Remove unused context initializer functions Fixes #15. --- include/secp256k1_rangeproof.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 22cc53eb..74671061 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -55,9 +55,6 @@ SECP256K1_API int secp256k1_pedersen_commitment_serialize( const secp256k1_pedersen_commitment* commit ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Initialize a context for usage with Pedersen commitments. */ -void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); - /** Generate a pedersen commitment. * Returns 1: Commitment successfully created. * 0: Error. The blinding factor is larger than the group order @@ -161,9 +158,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generato size_t n_inputs ); -/** Initialize a context for usage with Pedersen commitments. */ -void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); - /** Verify a proof that a committed value is within a range. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. * 0: Proof failed or other error. From 4721bec0effb372457413dce91f8f6ba3a4239c9 Mon Sep 17 00:00:00 2001 From: Jon Griffiths Date: Fri, 27 Nov 2020 13:40:50 +1300 Subject: [PATCH 087/381] Update renamed decl missed in e0ced690cff035b61763686cb69b7d06571e23e2 --- src/testrand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testrand.h b/src/testrand.h index 9301663e..6b0cbc05 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -36,7 +36,7 @@ static void secp256k1_testrand256_test(unsigned char *b32); static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len); /** Generate a pseudorandom 64-bit integer in the range min..max, inclusive. */ -static int64_t secp256k1_rands64(uint64_t min, uint64_t max); +static int64_t secp256k1_testrandi64(uint64_t min, uint64_t max); /** Flip a single random bit in a byte array */ static void secp256k1_testrand_flip(unsigned char *b, size_t len); From b9d91b3ecbc9b5fd3797294d51f935360efbec00 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 10 Nov 2020 22:33:47 +0000 Subject: [PATCH 088/381] musig: add pubkey_tweak_add function to allow taproot tweaking --- include/secp256k1_musig.h | 50 ++++++++++++- src/modules/musig/main_impl.h | 84 ++++++++++++++++++--- src/modules/musig/tests_impl.h | 131 ++++++++++++++++++++++++++++++++- 3 files changed, 250 insertions(+), 15 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 52b78ef0..46fe6c28 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -27,11 +27,18 @@ extern "C" { * pk_hash: The 32-byte hash of the original public keys * pk_parity: Whether the MuSig-aggregated point was negated when * converting it to the combined xonly pubkey. + * is_tweaked: Whether the combined pubkey was tweaked + * tweak: If is_tweaked, array with the 32-byte tweak + * internal_key_parity: If is_tweaked, the parity of the combined pubkey + * before tweaking */ typedef struct { uint64_t magic; unsigned char pk_hash[32]; int pk_parity; + int is_tweaked; + unsigned char tweak[32]; + int internal_key_parity; } secp256k1_musig_pre_session; /** Data structure containing data related to a signing session resulting in a single @@ -139,7 +146,7 @@ typedef struct { * multiexponentiation. If NULL, an inefficient algorithm is used. * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in - * `musig_session_init`. + * `musig_session_init` or `musig_pubkey_tweak_add`. * In: pubkeys: input array of public keys to combine. The order is important; * a different order will result in a different combined public * key (cannot be NULL) @@ -154,6 +161,42 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); +/** Tweak an x-only public key by adding the generator multiplied with tweak32 + * to it. The resulting output_pubkey with the given internal_pubkey and tweak + * passes `secp256k1_xonly_pubkey_tweak_test`. + * + * This function is only useful before initializing a signing session. If you + * are only computing a public key, but not intending to create a signature for + * it, you can just use `secp256k1_xonly_pubkey_tweak_add`. Can only be called + * once with a given pre_session. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * pre_session: pointer to a `musig_pre_session` struct initialized in + * `musig_pubkey_combine` (cannot be NULL) + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0 (cannot + * be NULL) + * In: internal_pubkey: pointer to the `combined_pk` from + * `musig_pubkey_combine` to which the tweak is applied. + * (cannot be NULL). + * tweak32: pointer to a 32-byte tweak. If the tweak is invalid + * according to secp256k1_ec_seckey_verify, this function + * returns 0. For uniformly random 32-byte arrays the + * chance of being invalid is negligible (around 1 in + * 2^128) (cannot be NULL). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_musig_pre_session *pre_session, + secp256k1_pubkey *output_pubkey, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + /** Initializes a signing session for a signer * * Returns: 1: session is successfully initialized @@ -173,8 +216,9 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( * because it reduces nonce misuse resistance. If NULL, must be * set with `musig_session_get_public_nonce`. * combined_pk: the combined xonly public key of all signers (cannot be NULL) - * pre_session: pointer to a musig_pre_session struct from - * `musig_pubkey_combine` (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct after initializing + * it with `musig_pubkey_combine` and optionally provided to + * `musig_pubkey_tweak_add` (cannot be NULL). * n_signers: length of signers array. Number of signers participating in * the MuSig. Must be greater than 0 and at most 2^32 - 1. * my_index: index of this signer in the signers array. Must be less diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 7f750945..d397eab3 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -127,10 +127,36 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat pre_session->magic = pre_session_magic; memcpy(pre_session->pk_hash, ecmult_data.ell, 32); pre_session->pk_parity = pk_parity; + pre_session->is_tweaked = 0; } return 1; } +int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_ge pk; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); + /* This function can only be called once because otherwise signing would not + * succeed */ + ARG_CHECK(pre_session->is_tweaked == 0); + + pre_session->internal_key_parity = pre_session->pk_parity; + if(!secp256k1_xonly_pubkey_tweak_add(ctx, output_pubkey, internal_pubkey, tweak32)) { + return 0; + } + + memcpy(pre_session->tweak, tweak32, 32); + pre_session->is_tweaked = 1; + + if (!secp256k1_pubkey_load(ctx, &pk, output_pubkey)) { + return 0; + } + pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk); + return 1; +} + static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL; int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { @@ -181,22 +207,33 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s return 0; } secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); - /* Compute the signers public key point and determine if the secret needs to - * be negated before signing. If the signer's pubkey has an odd Y coordinate - * XOR the MuSig-combined pubkey has an odd Y coordinate, the secret has to - * be negated. This can be seen by looking at the secret key belonging to - * `combined_pk`. Let's define + /* Compute the signer's public key point and determine if the secret is + * negated before signing. That happens if if the signer's pubkey has an odd + * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR + * (if tweaked) the internal key has an odd Y coordinate. + * + * This can be seen by looking at the secret key belonging to `combined_pk`. + * Let's define * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function * that normalizes a point to an even Y by negating if necessary similar to * secp256k1_extrakeys_ge_even_y. Then we have - * P := |P'| the combined xonly public key. Also, P = x*G where x = - * sum_i(b_i*mu_i*x_i) and b_i = -1 if (P != |P'| XOR P_i != |P_i|) and 1 - * otherwise. */ + * P := |P'| + t*G where t is the tweak. + * And the combined xonly public key is + * |P| = x*G + * where x = sum_i(b_i*mu_i*x_i) + b'*t + * b' = -1 if P != |P|, 1 otherwise + * b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1 + * otherwise. + */ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ge_set_gej(&p, &pj); secp256k1_fe_normalize(&p.y); - if (secp256k1_fe_is_odd(&p.y) != session->pre_session.pk_parity) { + if((secp256k1_fe_is_odd(&p.y) + + session->pre_session.pk_parity + + (session->pre_session.is_tweaked + && session->pre_session.internal_key_parity)) + % 2 == 1) { secp256k1_scalar_negate(&secret, &secret); } secp256k1_scalar_mul(&secret, &secret, &mu); @@ -502,6 +539,26 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp secp256k1_scalar_add(&s, &s, &term); } + /* If there is a tweak then add (or subtract) `msghash` times `tweak` to `s`.*/ + if (session->pre_session.is_tweaked) { + unsigned char msghash[32]; + secp256k1_scalar e, scalar_tweak; + int overflow = 0; + + secp256k1_musig_compute_messagehash(ctx, msghash, session); + secp256k1_scalar_set_b32(&e, msghash, NULL); + secp256k1_scalar_set_b32(&scalar_tweak, session->pre_session.tweak, &overflow); + if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) { + /* This mimics the behavior of secp256k1_ec_seckey_tweak_mul regarding + * overflow and tweak being 0. */ + return 0; + } + if (session->pre_session.pk_parity) { + secp256k1_scalar_negate(&e, &e); + } + secp256k1_scalar_add(&s, &s, &e); + } + secp256k1_xonly_pubkey_load(ctx, &noncep, &session->combined_nonce); VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); secp256k1_fe_normalize(&noncep.x); @@ -548,10 +605,15 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { return 0; } + /* If the MuSig-combined point has an odd Y coordinate, the signers will * sign for the negation of their individual xonly public key such that the - * combined signature is valid for the MuSig aggregated xonly key. */ - if (session->pre_session.pk_parity) { + * combined signature is valid for the MuSig aggregated xonly key. If the + * MuSig-combined point was tweaked then `e` is negated if the combined key + * has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/ + if (session->pre_session.pk_parity + != (session->pre_session.is_tweaked + && session->pre_session.internal_key_parity)) { secp256k1_scalar_negate(&e, &e); } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index bb6ef3ae..22a5d05b 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -170,6 +170,42 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + /** Tweaking */ + ecount = 0; + { + secp256k1_xonly_pubkey tmp_internal_pk = combined_pk; + secp256k1_pubkey tmp_output_pk; + secp256k1_musig_pre_session tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); + /* Reset pre_session */ + tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); + CHECK(ecount == 2); + tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 3); + /* Uninitialized pre_session */ + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &pre_session_uninitialized, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 4); + /* Using the same pre_session twice does not work */ + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 5); + tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, NULL, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, NULL, tweak) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, NULL) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, ones) == 0); + CHECK(ecount == 8); + } + /** Session creation **/ ecount = 0; CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); @@ -819,6 +855,97 @@ void sha256_tag_test(void) { CHECK(memcmp(buf, buf2, 32) == 0); } +/* Attempts to create a signature for the combined public key using given secret + * keys and pre_session. */ +void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_pre_session *pre_session) { + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signers0[2]; + secp256k1_musig_session_signer_data signers1[2]; + secp256k1_xonly_pubkey pk[2]; + unsigned char session_id[2][32]; + unsigned char msg[32]; + unsigned char nonce_commitment[2][32]; + unsigned char nonce[2][32]; + const unsigned char *ncs[2]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char final_sig[64]; + + secp256k1_testrand256(session_id[0]); + secp256k1_testrand256(session_id[1]); + secp256k1_testrand256(msg); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1); + + CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, 0, sk0) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, 1, sk1) == 1); + /* Set nonce commitments */ + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1); + /* Set nonces */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2)); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, combined_pubkey) == 1); +} + +/* In this test we create a combined public key P and a commitment Q = P + + * hash(P, contract)*G. Then we test that we can sign for both public keys. In + * order to sign for Q we use the tweak32 argument of partial_sig_combine. */ +void musig_tweak_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_xonly_pubkey pk[2]; + secp256k1_musig_pre_session pre_session_P; + secp256k1_musig_pre_session pre_session_Q; + secp256k1_xonly_pubkey P; + unsigned char P_serialized[32]; + secp256k1_pubkey Q; + int Q_parity; + secp256k1_xonly_pubkey Q_xonly; + unsigned char Q_serialized[32]; + + secp256k1_sha256 sha; + unsigned char contract[32]; + unsigned char ec_commit_tweak[32]; + + /* Setup */ + secp256k1_testrand256(sk[0]); + secp256k1_testrand256(sk[1]); + secp256k1_testrand256(contract); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk, 2) == 1); + + CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, P_serialized, 32); + secp256k1_sha256_write(&sha, contract, 32); + secp256k1_sha256_finalize(&sha, ec_commit_tweak); + pre_session_Q = pre_session_P; + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &pre_session_Q, &Q, &P, ec_commit_tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &Q_xonly, &Q_parity, &Q)); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, Q_serialized, &Q_xonly)); + /* Check that musig_pubkey_tweak_add produces same result as + * xonly_pubkey_tweak_add. */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, Q_serialized, Q_parity, &P, ec_commit_tweak) == 1); + + /* Test signing for P */ + musig_tweak_test_helper(&P, sk[0], sk[1], &pre_session_P); + /* Test signing for Q */ + musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q); +} + void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); @@ -829,8 +956,10 @@ void run_musig_tests(void) { musig_api_tests(scratch); musig_state_machine_tests(scratch); for (i = 0; i < count; i++) { - /* Run multiple times to ensure that the nonce has different y parities */ + /* Run multiple times to ensure that pk and nonce have different y + * parities */ scriptless_atomic_swap(scratch); + musig_tweak_test(scratch); } sha256_tag_test(); From 3fb4d6db9ccb0546691e51c9a6e3d2e3fa7ba086 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 13 Nov 2020 15:50:48 +0000 Subject: [PATCH 089/381] travis: run musig test whenever schnorrsig tests are run Previously the musig module was not tested under valgrind and not with sanitizers. --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2ace5be9..9b7fe6f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,9 +22,9 @@ env: - WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int64 RECOVERY=yes - - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 - - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 ASM=x86_64 - BIGNUM=no @@ -33,10 +33,10 @@ env: - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no - CPPFLAGS=-DDETERMINISTIC - CFLAGS=-O0 CTIMETEST=no - - CFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST=no + - CFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST=no - ECMULTGENPRECISION=2 - ECMULTGENPRECISION=8 - - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD= + - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD= matrix: fast_finish: true include: @@ -84,7 +84,7 @@ matrix: - libc6-dbg:i386 # S390x build (big endian system) - compiler: gcc - env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST= + env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST= arch: s390x # We use this to install macOS dependencies instead of the built in `homebrew` plugin, From 29f9a7dc62f5158c30366efbce073680314851dc Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 16 Nov 2020 16:05:24 +0000 Subject: [PATCH 090/381] reduce test rounds for rangeproof and surjectionproof --- src/modules/rangeproof/tests_impl.h | 10 +++++----- src/modules/surjection/tests_impl.h | 5 +---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 7b8a610c..3b71ad6e 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -498,7 +498,7 @@ static void test_rangeproof(void) { CHECK(maxv >= v); } memcpy(&commit2, &commit, sizeof(commit)); - for (i = 0; i < (size_t) 2*count; i++) { + for (i = 0; i < (size_t) count; i++) { int exp; int min_bits; v = secp256k1_testrandi64(0, UINT64_MAX >> (secp256k1_testrand32()&63)); @@ -532,11 +532,11 @@ static void test_rangeproof(void) { CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); memcpy(&commit2, &commit, sizeof(commit)); } - for (j = 0; j < 5; j++) { + for (j = 0; j < 3; j++) { for (i = 0; i < 96; i++) { secp256k1_testrand256(&proof[i * 32]); } - for (k = 0; k < 128; k++) { + for (k = 0; k < 128; k += 3) { len = k; CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } @@ -696,10 +696,10 @@ void run_rangeproof_tests(void) { test_api(); test_rangeproof_fixed_vectors(); test_pedersen_commitment_fixed_vector(); - for (i = 0; i < 10*count; i++) { + for (i = 0; i < count / 2 + 1; i++) { test_pedersen(); } - for (i = 0; i < 10*count; i++) { + for (i = 0; i < count / 2 + 1; i++) { test_borromean(); } test_rangeproof(); diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index e90d2aa0..95d1607b 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -667,10 +667,7 @@ void test_fixed_vectors(void) { } void run_surjection_tests(void) { - int i; - for (i = 0; i < count; i++) { - test_surjectionproof_api(); - } + test_surjectionproof_api(); test_fixed_vectors(); test_input_selection(0); From 826bd04b43f823813c633449223595031d5c31f7 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 5 Dec 2020 22:40:54 +0000 Subject: [PATCH 091/381] add eccommit functionality Co-authored-by: Marko Bencun Co-authored-by: Jonas Nick --- Makefile.am | 2 + src/eccommit.h | 28 +++++++++++++ src/eccommit_impl.h | 73 ++++++++++++++++++++++++++++++++++ src/modules/musig/example.c | 2 +- src/secp256k1.c | 1 + src/tests.c | 78 +++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/eccommit.h create mode 100644 src/eccommit_impl.h diff --git a/Makefile.am b/Makefile.am index 1cba7a34..7309fa4a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,6 +16,8 @@ noinst_HEADERS += src/group.h noinst_HEADERS += src/group_impl.h noinst_HEADERS += src/num_gmp.h noinst_HEADERS += src/num_gmp_impl.h +noinst_HEADERS += src/eccommit.h +noinst_HEADERS += src/eccommit_impl.h noinst_HEADERS += src/ecdsa.h noinst_HEADERS += src/ecdsa_impl.h noinst_HEADERS += src/eckey.h diff --git a/src/eccommit.h b/src/eccommit.h new file mode 100644 index 00000000..6bb11039 --- /dev/null +++ b/src/eccommit.h @@ -0,0 +1,28 @@ +/********************************************************************** + * Copyright (c) 2020 The libsecp256k1-zkp Developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECCOMMIT_H +#define SECP256K1_ECCOMMIT_H + +/** Helper function to add a 32-byte value to a scalar */ +static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak); +/** Helper function to add a 32-byte value, times G, to an EC point */ +static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak); + +/** Serializes elem as a 33 byte array. This is non-constant time with respect to + * whether pubp is the point at infinity. Thus, you may need to declassify + * pubp->infinity before calling this function. */ +static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33); +/** Compute an ec commitment tweak as hash(pubkey, data). */ +static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); +/** Compute an ec commitment as pubkey + hash(pubkey, data)*G. */ +static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); +/** Compute a secret key commitment as seckey + hash(pubkey, data). */ +static int secp256k1_ec_commit_seckey(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); +/** Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */ +static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size); + +#endif /* SECP256K1_ECCOMMIT_H */ diff --git a/src/eccommit_impl.h b/src/eccommit_impl.h new file mode 100644 index 00000000..641c07d2 --- /dev/null +++ b/src/eccommit_impl.h @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (c) 2020 The libsecp256k1 Developers * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "eckey.h" +#include "hash.h" + +/* from secp256k1.c */ +static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak); +static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *pubp, const unsigned char *tweak); + +static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33) { + if (secp256k1_ge_is_infinity(pubp)) { + return 0; + } + secp256k1_fe_normalize(&pubp->x); + secp256k1_fe_normalize(&pubp->y); + secp256k1_fe_get_b32(&buf33[1], &pubp->x); + buf33[0] = secp256k1_fe_is_odd(&pubp->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; + return 1; +} + +/* Compute an ec commitment tweak as hash(pubp, data). */ +static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) +{ + unsigned char rbuf[33]; + + if (!secp256k1_ec_commit_pubkey_serialize_const(pubp, rbuf)) { + return 0; + } + secp256k1_sha256_write(sha, rbuf, sizeof(rbuf)); + secp256k1_sha256_write(sha, data, data_size); + secp256k1_sha256_finalize(sha, tweak32); + return 1; +} + +/* Compute an ec commitment as pubp + hash(pubp, data)*G. */ +static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { + unsigned char tweak[32]; + + *commitp = *pubp; + return secp256k1_ec_commit_tweak(tweak, commitp, sha, data, data_size) + && secp256k1_ec_pubkey_tweak_add_helper(ecmult_ctx, commitp, tweak); +} + +/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey + + * hash(pubp, data). */ +static int secp256k1_ec_commit_seckey(secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { + unsigned char tweak[32]; + return secp256k1_ec_commit_tweak(tweak, pubp, sha, data, data_size) + && secp256k1_ec_seckey_tweak_add_helper(seckey, tweak); +} + +/* Verify an ec commitment as pubp + hash(pubp, data)*G ?= commitment. */ +static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) { + secp256k1_gej pj; + secp256k1_ge p; + + if (!secp256k1_ec_commit(ecmult_ctx, &p, pubp, sha, data, data_size)) { + return 0; + } + + /* Return p == commitp */ + secp256k1_ge_neg(&p, &p); + secp256k1_gej_set_ge(&pj, &p); + secp256k1_gej_add_ge_var(&pj, &pj, commitp, NULL); + return secp256k1_gej_is_infinity(&pj); +} + diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 2c5b7006..fa3f5833 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -107,7 +107,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 for (i = 0; i < N_SIGNERS; i++) { for (j = 0; j < N_SIGNERS; j++) { /* To check whether signing was successful, it suffices to either verify - * the the combined signature with the combined public key using + * the combined signature with the combined public key using * secp256k1_schnorrsig_verify, or verify all partial signatures of all * signers individually. Verifying the combined signature is cheaper but * verifying the individual partial signatures has the advantage that it diff --git a/src/secp256k1.c b/src/secp256k1.c index 42309a03..0c7a2575 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -13,6 +13,7 @@ #include "field_impl.h" #include "scalar_impl.h" #include "group_impl.h" +#include "eccommit_impl.h" #include "ecmult_impl.h" #include "ecmult_const_impl.h" #include "ecmult_gen_impl.h" diff --git a/src/tests.c b/src/tests.c index 33aef6ed..bd6f7fd1 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2609,6 +2609,83 @@ void run_ec_combine(void) { } } +void test_ec_commit(void) { + secp256k1_scalar seckey_s; + secp256k1_ge pubkey; + secp256k1_gej pubkeyj; + secp256k1_ge commitment; + unsigned char data[32]; + secp256k1_sha256 sha; + + /* Create random keypair and data */ + random_scalar_order_test(&seckey_s); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey_s); + secp256k1_ge_set_gej(&pubkey, &pubkeyj); + secp256k1_testrand256_test(data); + + /* Commit to data and verify */ + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 1); + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 1); + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_seckey(&seckey_s, &pubkey, &sha, data, 32) == 1); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey_s); + ge_equals_gej(&commitment, &pubkeyj); + + /* Check that verification fails with different data */ + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 31) == 0); + + /* Check that commmitting fails when the inner pubkey is the point at + * infinity */ + secp256k1_sha256_initialize(&sha); + secp256k1_ge_set_infinity(&pubkey); + CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 0); + secp256k1_scalar_set_int(&seckey_s, 0); + CHECK(secp256k1_ec_commit_seckey(&seckey_s, &pubkey, &sha, data, 32) == 0); + CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 0); +} + +void test_ec_commit_api(void) { + unsigned char seckey[32]; + secp256k1_scalar seckey_s; + secp256k1_ge pubkey; + secp256k1_gej pubkeyj; + secp256k1_ge commitment; + unsigned char data[32]; + secp256k1_sha256 sha; + + memset(data, 23, sizeof(data)); + + /* Create random keypair */ + random_scalar_order_test(&seckey_s); + secp256k1_scalar_get_b32(seckey, &seckey_s); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey_s); + secp256k1_ge_set_gej(&pubkey, &pubkeyj); + + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 1) == 1); + /* The same pubkey can be both input and output of the function */ + { + secp256k1_ge pubkey_tmp = pubkey; + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &pubkey_tmp, &pubkey_tmp, &sha, data, 1) == 1); + ge_equals_ge(&commitment, &pubkey_tmp); + } + + secp256k1_sha256_initialize(&sha); + CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 1) == 1); +} + +void run_ec_commit(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_commit(); + } + test_ec_commit_api(); +} + void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; @@ -5858,6 +5935,7 @@ int main(int argc, char **argv) { run_ecmult_const_tests(); run_ecmult_multi_tests(); run_ec_combine(); + run_ec_commit(); /* endomorphism tests */ run_endomorphism_tests(); From 8e46cac5b31c3a3127d33d46466c29e97545cf16 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 5 Dec 2020 23:18:54 +0000 Subject: [PATCH 092/381] ecdsa-s2c: block in module Co-authored-by: Marko Bencun Co-authored-by: Jonas Nick --- .travis.yml | 9 +-- Makefile.am | 5 ++ configure.ac | 15 +++++ contrib/travis.sh | 1 + include/secp256k1_ecdsa_s2c.h | 58 +++++++++++++++++ src/modules/ecdsa_s2c/Makefile.am.include | 3 + src/modules/ecdsa_s2c/main_impl.h | 28 +++++++++ src/modules/ecdsa_s2c/tests_impl.h | 76 +++++++++++++++++++++++ src/secp256k1.c | 4 ++ src/tests.c | 9 +++ 10 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 include/secp256k1_ecdsa_s2c.h create mode 100644 src/modules/ecdsa_s2c/Makefile.am.include create mode 100755 src/modules/ecdsa_s2c/main_impl.h create mode 100644 src/modules/ecdsa_s2c/tests_impl.h diff --git a/.travis.yml b/.travis.yml index 9b7fe6f3..6826614e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,18 +17,19 @@ compiler: - gcc env: global: - - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no + - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no ECDSA_S2C=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no matrix: - WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int64 RECOVERY=yes - - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 - - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 ASM=x86_64 - BIGNUM=no - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no - CPPFLAGS=-DDETERMINISTIC diff --git a/Makefile.am b/Makefile.am index 7309fa4a..434360f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -184,3 +184,8 @@ endif if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif + +if ENABLE_MODULE_ECDSA_S2C +include src/modules/ecdsa_s2c/Makefile.am.include +endif + diff --git a/configure.ac b/configure.ac index f4a341bc..27b245eb 100644 --- a/configure.ac +++ b/configure.ac @@ -161,6 +161,11 @@ AC_ARG_ENABLE(module_schnorrsig, [enable_module_schnorrsig=$enableval], [enable_module_schnorrsig=no]) +AC_ARG_ENABLE(module_ecdsa_s2c, + AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), + [enable_module_ecdsa_s2c=$enableval], + [enable_module_ecdsa_s2c=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -509,6 +514,10 @@ if test x"$enable_module_extrakeys" = x"yes"; then AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) fi +if test x"$enable_module_ecdsa_s2c" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDSA_S2C, 1, [Define this symbol to enable the ECDSA sign-to-contract module]) +fi + if test x"$use_external_asm" = x"yes"; then AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) fi @@ -532,6 +541,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) + AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_s2c]) AC_MSG_NOTICE([******]) @@ -565,6 +575,9 @@ else if test x"$enable_module_schnorrsig" = x"yes"; then AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_ecdsa_s2c" = x"yes"; then + AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -601,6 +614,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = 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_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) @@ -625,6 +639,7 @@ 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 ecdsa-s2c = $enable_module_ecdsa_s2c" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/contrib/travis.sh b/contrib/travis.sh index c667c151..fb3e4d0c 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -17,6 +17,7 @@ fi --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ecdsa-s2c="$ECDSA_S2C" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG"\ --with-valgrind="$WITH_VALGRIND" \ diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h new file mode 100644 index 00000000..7f54e71f --- /dev/null +++ b/include/secp256k1_ecdsa_s2c.h @@ -0,0 +1,58 @@ +#ifndef SECP256K1_ECDSA_S2C_H +#define SECP256K1_ECDSA_S2C_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Data structure that holds a sign-to-contract ("s2c") opening information. + * Sign-to-contract allows a signer to commit to some data as part of a signature. It + * can be used as an Out-argument in certain signing functions. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_ecdsa_s2c_opening_serialize and secp256k1_ecdsa_s2c_opening_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_s2c_opening; + +/** Parse a sign-to-contract opening. + * + * Returns: 1 if the opening could be parsed + * 0 if the opening could not be parsed + * Args: ctx: a secp256k1 context object. + * Out: opening: pointer to an opening object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is unspecified. + * In: input33: pointer to 33-byte array with a serialized opening + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_parse( + const secp256k1_context* ctx, + secp256k1_ecdsa_s2c_opening* opening, + const unsigned char* input33 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a sign-to-contract opening into a byte sequence. + * + * Returns: 1 if the opening was successfully serialized. + * 0 if the opening could not be serialized + * Args: ctx: a secp256k1 context object + * Out: output33: pointer to a 33-byte array to place the serialized opening in + * In: opening: a pointer to an initialized `secp256k1_ecdsa_s2c_opening` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_serialize( + const secp256k1_context* ctx, + unsigned char* output33, + const secp256k1_ecdsa_s2c_opening* opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDSA_S2C_H */ diff --git a/src/modules/ecdsa_s2c/Makefile.am.include b/src/modules/ecdsa_s2c/Makefile.am.include new file mode 100644 index 00000000..b4939a91 --- /dev/null +++ b/src/modules/ecdsa_s2c/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_ecdsa_s2c.h +noinst_HEADERS += src/modules/ecdsa_s2c/main_impl.h +noinst_HEADERS += src/modules/ecdsa_s2c/tests_impl.h diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h new file mode 100755 index 00000000..cf152359 --- /dev/null +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -0,0 +1,28 @@ +/********************************************************************** + * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_S2C_MAIN_H +#define SECP256K1_MODULE_ECDSA_S2C_MAIN_H + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdsa_s2c.h" + +int secp256k1_ecdsa_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* input33) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(input33 != NULL); + return secp256k1_ec_pubkey_parse(ctx, (secp256k1_pubkey*) opening, input33, 33); +} + +int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char* output33, const secp256k1_ecdsa_s2c_opening* opening) { + size_t out_len = 33; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output33 != NULL); + ARG_CHECK(opening != NULL); + return secp256k1_ec_pubkey_serialize(ctx, output33, &out_len, (const secp256k1_pubkey*) opening, SECP256K1_EC_COMPRESSED); +} + +#endif /* SECP256K1_ECDSA_S2C_MAIN_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h new file mode 100644 index 00000000..cd7e18f6 --- /dev/null +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -0,0 +1,76 @@ +/********************************************************************** + * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_S2C_TESTS_H +#define SECP256K1_MODULE_ECDSA_S2C_TESTS_H + +#include "include/secp256k1_ecdsa_s2c.h" + +void run_s2c_opening_test(void) { + int i = 0; + unsigned char output[33]; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + unsigned char input[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + secp256k1_ecdsa_s2c_opening opening; + int32_t ecount = 0; + + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + + /* First parsing, then serializing works */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1); + CHECK(ecount == 0); + + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, NULL, input) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1); + + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, NULL, &opening) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, NULL) == 0); + + CHECK(ecount == 4); + /* Invalid pubkey makes parsing fail */ + input[0] = 0; /* bad oddness bit */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 0); + input[0] = 2; + input[31] = 1; /* point not on the curve */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 0); + CHECK(ecount == 4); /* neither of the above are API errors */ + + /* Try parsing and serializing a bunch of openings */ + for (i = 0; i < count; i++) { + /* This is expected to fail in about 50% of iterations because the + * points' x-coordinates are uniformly random */ + if (secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1) { + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1); + CHECK(memcmp(output, input, sizeof(output)) == 0); + } + secp256k1_testrand256(&input[1]); + /* Set pubkey oddness tag to first bit of input[1] */ + input[0] = (input[1] & 1) + 2; + i++; + } + + secp256k1_context_destroy(none); +} + + +static void run_ecdsa_s2c_tests(void) { + run_s2c_opening_test(); +} + +#endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index 0c7a2575..a48a7371 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -786,6 +786,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/schnorrsig/main_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "modules/ecdsa_s2c/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 bd6f7fd1..d0dc061a 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5697,6 +5697,10 @@ void run_ecdsa_openssl(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "modules/ecdsa_s2c/tests_impl.h" +#endif + void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5998,6 +6002,11 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_ECDSA_S2C + /* ECDSA sign to contract */ + run_ecdsa_s2c_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); From 290dee566e14efa852d4e5437546f6a8ff8bfa1a Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 5 Dec 2020 23:34:14 +0000 Subject: [PATCH 093/381] ecdsa-s2c: add actual sign-to-contract functionality Co-authored-by: Marko Bencun Co-authored-by: Jonas Nick --- include/secp256k1_ecdsa_s2c.h | 37 ++++++ src/modules/ecdsa_s2c/main_impl.h | 112 ++++++++++++++++++ src/modules/ecdsa_s2c/tests_impl.h | 184 ++++++++++++++++++++++++++++- src/modules/recovery/main_impl.h | 2 +- src/secp256k1.c | 45 ++++++- 5 files changed, 376 insertions(+), 4 deletions(-) diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h index 7f54e71f..b28003f0 100644 --- a/include/secp256k1_ecdsa_s2c.h +++ b/include/secp256k1_ecdsa_s2c.h @@ -51,6 +51,43 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_seria const secp256k1_ecdsa_s2c_opening* opening ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Same as secp256k1_ecdsa_sign, but s2c_data32 is committed to inside the nonce + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * s2c_opening: if non-NULL, pointer to an secp256k1_ecdsa_s2c_opening structure to populate + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * s2c_data32: pointer to a 32-byte data to commit to in the nonce (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_s2c_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + secp256k1_ecdsa_s2c_opening* s2c_opening, + const unsigned char* msg32, + const unsigned char* seckey, + const unsigned char* s2c_data32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +/** Verify a sign-to-contract commitment. + * + * Returns: 1: the signature contains a commitment to data32 (though it does + * not necessarily need to be a valid siganture!) + * 0: incorrect opening + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature containing the sign-to-contract commitment (cannot be NULL) + * data32: the 32-byte data that was committed to (cannot be NULL) + * opening: pointer to the opening created during signing (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *data32, + const secp256k1_ecdsa_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index cf152359..7246ddc7 100755 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -10,6 +10,14 @@ #include "include/secp256k1.h" #include "include/secp256k1_ecdsa_s2c.h" +static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) { + secp256k1_pubkey_save((secp256k1_pubkey*) opening, ge); +} + +static int secp256k1_ecdsa_s2c_opening_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_ecdsa_s2c_opening* opening) { + return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey*) opening); +} + int secp256k1_ecdsa_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* input33) { VERIFY_CHECK(ctx != NULL); ARG_CHECK(opening != NULL); @@ -25,4 +33,108 @@ int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned return secp256k1_ec_pubkey_serialize(ctx, output33, &out_len, (const secp256k1_pubkey*) opening, SECP256K1_EC_COMPRESSED); } +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("s2c/ecdsa/point")||SHA256("s2c/ecdsa/point"). */ +static void secp256k1_s2c_ecdsa_point_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xa9b21c7bul; + sha->s[1] = 0x358c3e3eul; + sha->s[2] = 0x0b6863d1ul; + sha->s[3] = 0xc62b2035ul; + sha->s[4] = 0xb44b40ceul; + sha->s[5] = 0x254a8912ul; + sha->s[6] = 0x0f85d0d4ul; + sha->s[7] = 0x8a5bf91cul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("s2c/ecdsa/data")||SHA256("s2c/ecdsa/data"). */ +static void secp256k1_s2c_ecdsa_data_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xfeefd675ul; + sha->s[1] = 0x73166c99ul; + sha->s[2] = 0xe2309cb8ul; + sha->s[3] = 0x6d458113ul; + sha->s[4] = 0x01d3a512ul; + sha->s[5] = 0x00e18112ul; + sha->s[6] = 0x37ee0874ul; + sha->s[7] = 0x421fc55ful; + + sha->bytes = 64; +} + +int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* signature, secp256k1_ecdsa_s2c_opening* s2c_opening, const unsigned char + *msg32, const unsigned char *seckey, const unsigned char* s2c_data32) { + secp256k1_scalar r, s; + int ret; + unsigned char ndata[32]; + secp256k1_sha256 s2c_sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(s2c_data32 != NULL); + + /* Provide `s2c_data32` to the nonce function as additional data to + * derive the nonce. It is first hashed because it should be possible + * to derive nonces even if only a SHA256 commitment to the data is + * known. This is important in the ECDSA anti-klepto protocol. */ + secp256k1_s2c_ecdsa_data_sha256_tagged(&s2c_sha); + secp256k1_sha256_write(&s2c_sha, s2c_data32, 32); + secp256k1_sha256_finalize(&s2c_sha, ndata); + + secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha); + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, &s2c_sha, s2c_opening, s2c_data32, msg32, seckey, NULL, ndata); + secp256k1_scalar_cmov(&r, &secp256k1_scalar_zero, !ret); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_zero, !ret); + secp256k1_ecdsa_signature_save(signature, &r, &s); + return ret; +} + +int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp256k1_ecdsa_signature* sig, const unsigned char* data32, const secp256k1_ecdsa_s2c_opening* opening) { + secp256k1_ge commitment_ge; + secp256k1_ge original_pubnonce_ge; + unsigned char x_bytes[32]; + secp256k1_scalar sigr, sigs, x_scalar; + secp256k1_sha256 s2c_sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(data32 != NULL); + ARG_CHECK(opening != NULL); + + if (!secp256k1_ecdsa_s2c_opening_load(ctx, &original_pubnonce_ge, opening)) { + return 0; + } + secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha); + if (!secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment_ge, &original_pubnonce_ge, &s2c_sha, data32, 32)) { + return 0; + } + + /* Check that sig_r == commitment_x (mod n) + * sig_r is the x coordinate of R represented by a scalar. + * commitment_x is the x coordinate of the commitment (field element). + * + * Note that we are only checking the x-coordinate -- this is because the y-coordinate + * is not part of the ECDSA signature (and therefore not part of the commitment!) + */ + secp256k1_ecdsa_signature_load(ctx, &sigr, &sigs, sig); + + secp256k1_fe_normalize(&commitment_ge.x); + secp256k1_fe_get_b32(x_bytes, &commitment_ge.x); + /* Do not check overflow; overflowing a scalar does not affect whether + * or not the R value is a cryptographic commitment, only whether it + * is a valid R value for an ECDSA signature. If users care about that + * they should use `ecdsa_verify` or `anti_klepto_host_verify`. In other + * words, this check would be (at best) unnecessary, and (at worst) + * insufficient. */ + secp256k1_scalar_set_b32(&x_scalar, x_bytes, NULL); + return secp256k1_scalar_eq(&sigr, &x_scalar); +} + #endif /* SECP256K1_ECDSA_S2C_MAIN_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index cd7e18f6..6e8dae8f 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -9,6 +9,27 @@ #include "include/secp256k1_ecdsa_s2c.h" +static void test_ecdsa_s2c_tagged_hash(void) { + unsigned char tag_data[14] = "s2c/ecdsa/data"; + unsigned char tag_point[15] = "s2c/ecdsa/point"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char output[32]; + unsigned char output_optimized[32]; + + secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data)); + secp256k1_s2c_ecdsa_data_sha256_tagged(&sha_optimized); + secp256k1_sha256_finalize(&sha, output); + secp256k1_sha256_finalize(&sha_optimized, output_optimized); + CHECK(secp256k1_memcmp_var(output, output_optimized, 32) == 0); + + secp256k1_sha256_initialize_tagged(&sha, tag_point, sizeof(tag_point)); + secp256k1_s2c_ecdsa_point_sha256_tagged(&sha_optimized); + secp256k1_sha256_finalize(&sha, output); + secp256k1_sha256_finalize(&sha_optimized, output_optimized); + CHECK(secp256k1_memcmp_var(output, output_optimized, 32) == 0); +} + void run_s2c_opening_test(void) { int i = 0; unsigned char output[33]; @@ -62,15 +83,176 @@ void run_s2c_opening_test(void) { secp256k1_testrand256(&input[1]); /* Set pubkey oddness tag to first bit of input[1] */ input[0] = (input[1] & 1) + 2; - i++; } secp256k1_context_destroy(none); } +static void test_ecdsa_s2c_api(void) { + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + secp256k1_ecdsa_s2c_opening s2c_opening; + secp256k1_ecdsa_signature sig; + const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"; + const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss"; + const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd"; + secp256k1_pubkey pk; + + int32_t ecount; + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sec)); + + ecount = 0; + CHECK(secp256k1_ecdsa_s2c_sign(both, NULL, &s2c_opening, msg, sec, s2c_data) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, NULL, msg, sec, s2c_data) == 1); + CHECK(ecount == 1); /* NULL opening is not an API error */ + CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, &s2c_opening, NULL, sec, s2c_data) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, &s2c_opening, msg, NULL, s2c_data) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, &s2c_opening, msg, sec, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_s2c_sign(none, &sig, &s2c_opening, msg, sec, s2c_data) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_s2c_sign(vrfy, &sig, &s2c_opening, msg, sec, s2c_data) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, &s2c_opening, msg, sec, s2c_data) == 1); + CHECK(ecount == 6); + + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pk) == 1); + + ecount = 0; + CHECK(secp256k1_ecdsa_s2c_verify_commit(both, NULL, s2c_data, &s2c_opening) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(both, &sig, NULL, &s2c_opening) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_s2c_verify_commit(both, &sig, s2c_data, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_s2c_verify_commit(none, &sig, s2c_data, &s2c_opening) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_s2c_verify_commit(sign, &sig, s2c_data, &s2c_opening) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, sec, &s2c_opening) == 0); + CHECK(ecount == 5); /* wrong data is not an API error */ + + /* Signing with NULL s2c_opening gives the same result */ + CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, NULL, msg, sec, s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1); + + secp256k1_context_destroy(both); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(none); +} + +/* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */ +typedef struct { + unsigned char s2c_data[32]; + unsigned char expected_s2c_opening[33]; +} ecdsa_s2c_test; + +static ecdsa_s2c_test ecdsa_s2c_tests[] = { + { + "\x1b\xf6\xfb\x42\xf4\x1e\xb8\x76\xc4\xd7\xaa\x0d\x67\x24\x2b\x00\xba\xab\x99\xdc\x20\x84\x49\x3e\x4e\x63\x27\x7f\xa1\xf7\x7f\x22", + "\x03\xf0\x30\xde\xf3\x18\x8c\x0f\x56\xfc\xea\x87\x43\x5b\x30\x76\x43\xf4\x5d\xaf\xe2\x2c\xbc\x82\xfd\x56\x03\x4f\xae\x97\x41\x7d\x3a", + }, + { + "\x35\x19\x9a\x8f\xbf\x84\xad\x6e\xf6\x9a\x18\x4c\x1b\x19\x28\x5b\xef\xbe\x06\xe6\x0b\x62\x64\xe6\xd3\x73\x89\x3f\x68\x55\xe2\x4a", + "\x03\x90\x17\x17\xce\x7c\x74\x84\xa2\xce\x1b\x7d\xc7\x40\x3b\x14\xe0\x35\x49\x71\x39\x3e\xc0\x92\xa7\xf3\xe0\xc8\xe4\xe2\xd2\x63\x9d", + }, +}; + +static void test_ecdsa_s2c_fixed_vectors(void) { + const unsigned char privkey[32] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + }; + const unsigned char message[32] = { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + }; + size_t i; + + for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) { + secp256k1_ecdsa_s2c_opening s2c_opening; + unsigned char opening_ser[33]; + const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; + secp256k1_ecdsa_signature signature; + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, opening_ser, &s2c_opening) == 1); + CHECK(memcmp(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0); + } +} + +static void test_ecdsa_s2c_sign_verify(void) { + unsigned char privkey[32]; + secp256k1_pubkey pubkey; + unsigned char message[32]; + unsigned char noncedata[32]; + unsigned char s2c_data[32]; + unsigned char s2c_data2[32]; + secp256k1_ecdsa_signature signature; + secp256k1_ecdsa_s2c_opening s2c_opening; + + /* Generate a random key, message, noncedata and s2c_data. */ + { + secp256k1_scalar key; + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + secp256k1_testrand256_test(message); + secp256k1_testrand256_test(noncedata); + secp256k1_testrand256_test(s2c_data); + secp256k1_testrand256_test(s2c_data2); + } + + { /* invalid privkeys */ + unsigned char zero_privkey[32] = {0}; + unsigned char overflow_privkey[32] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, NULL, message, zero_privkey, s2c_data) == 0); + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, NULL, message, overflow_privkey, s2c_data) == 0); + } + /* Check that the sign-to-contract signature is valid, with s2c_data. Also check the commitment. */ + { + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, s2c_data) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature, message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, s2c_data, &s2c_opening) == 1); + } + /* Check that an invalid commitment does not verify */ + { + unsigned char sigbytes[64]; + size_t i; + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, s2c_data) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature, message, &pubkey) == 1); + + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, sigbytes, &signature) == 1); + for(i = 0; i < 32; i++) { + /* change one byte */ + sigbytes[i] = (((int)sigbytes[i]) + 1) % 256; + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, s2c_data, &s2c_opening) == 0); + /* revert */ + sigbytes[i] = (((int)sigbytes[i]) + 255) % 256; + } + } +} static void run_ecdsa_s2c_tests(void) { run_s2c_opening_test(); + test_ecdsa_s2c_tagged_hash(); + test_ecdsa_s2c_api(); + test_ecdsa_s2c_fixed_vectors(); + test_ecdsa_s2c_sign_verify(); } #endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/modules/recovery/main_impl.h b/src/modules/recovery/main_impl.h index e2576aa9..4a225dcb 100644 --- a/src/modules/recovery/main_impl.h +++ b/src/modules/recovery/main_impl.h @@ -129,7 +129,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd ARG_CHECK(signature != NULL); ARG_CHECK(seckey != NULL); - ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata); + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, NULL, NULL, NULL, msg32, seckey, noncefp, noncedata); secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); return ret; } diff --git a/src/secp256k1.c b/src/secp256k1.c index a48a7371..68390303 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -37,6 +37,18 @@ # include "modules/rangeproof/rangeproof.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "include/secp256k1_ecdsa_s2c.h" +static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge); +#else +typedef void secp256k1_ecdsa_s2c_opening; +static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) { + (void) opening; + (void) ge; + VERIFY_CHECK(0); +} +#endif + #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ @@ -488,7 +500,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; -static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { +static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, secp256k1_sha256* s2c_sha, secp256k1_ecdsa_s2c_opening *s2c_opening, const unsigned char* s2c_data32, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { secp256k1_scalar sec, non, msg; int ret = 0; int is_sec_valid; @@ -503,6 +515,11 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc if (noncefp == NULL) { noncefp = secp256k1_nonce_function_default; } + /* sign-to-contract commitments only work with the default nonce function, + * because we need to ensure that s2c_data is actually hashed into the nonce and + * not just ignored. Otherwise an attacker can exfiltrate the secret key by + * signing the same message thrice with different commitments. */ + VERIFY_CHECK(s2c_data32 == NULL || noncefp == secp256k1_nonce_function_default); /* Fail if the secret key is invalid. */ is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey); @@ -518,6 +535,30 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */ secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); if (is_nonce_valid) { + if (s2c_data32 != NULL) { + secp256k1_gej nonce_pj; + secp256k1_ge nonce_p; + + /* Compute original nonce commitment/pubkey */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_pj, &non); + secp256k1_ge_set_gej(&nonce_p, &nonce_pj); + if (s2c_opening != NULL) { + secp256k1_ecdsa_s2c_opening_save(s2c_opening, &nonce_p); + } + + /* Because the nonce is valid, the nonce point isn't the point + * at infinity and we can declassify that information to be able to + * serialize the point. */ + secp256k1_declassify(ctx, &nonce_p.infinity, sizeof(nonce_p.infinity)); + + /* Tweak nonce with s2c commitment. */ + ret = secp256k1_ec_commit_seckey(&non, &nonce_p, s2c_sha, s2c_data32, 32); + secp256k1_declassify(ctx, &ret, sizeof(ret)); /* may be secret that the tweak falied, but happens with negligible probability */ + if (!ret) { + break; + } + } + ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid); /* The final signature is no longer a secret, nor is the fact that we were successful or not. */ secp256k1_declassify(ctx, &ret, sizeof(ret)); @@ -553,7 +594,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature ARG_CHECK(signature != NULL); ARG_CHECK(seckey != NULL); - ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msg32, seckey, noncefp, noncedata); + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, NULL, NULL, NULL, msg32, seckey, noncefp, noncedata); secp256k1_ecdsa_signature_save(signature, &r, &s); return ret; } From 396b558273ce88969d4b0abc86e003f7557224f7 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 6 Dec 2020 16:31:42 +0000 Subject: [PATCH 094/381] ecdsa-s2c: add anti-klepto protocol Co-authored-by: Marko Bencun Co-authored-by: Jonas Nick --- include/secp256k1_ecdsa_s2c.h | 139 +++++++++++++++++++++++++ src/modules/ecdsa_s2c/main_impl.h | 58 +++++++++++ src/modules/ecdsa_s2c/tests_impl.h | 158 +++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+) diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h index b28003f0..482b0c13 100644 --- a/include/secp256k1_ecdsa_s2c.h +++ b/include/secp256k1_ecdsa_s2c.h @@ -3,6 +3,14 @@ #include "secp256k1.h" +/** This module implements the sign-to-contract scheme for ECDSA signatures, as + * well as the "ECDSA Anti-Klepto Protocol" that is based on sign-to-contract + * and is specified further down. The sign-to-contract scheme allows creating a + * signature that also commits to some data. This works by offsetting the public + * nonce point of the signature R by hash(R, data)*G where G is the secp256k1 + * group generator. + */ + #ifdef __cplusplus extern "C" { #endif @@ -88,6 +96,137 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit const secp256k1_ecdsa_s2c_opening *opening ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** ECDSA Anti-Klepto Protocol + * + * The ecdsa_anti_klepto_* functions can be used to prevent a signing device from + * exfiltrating the secret signing keys through biased signature nonces. The general + * idea is that a host provides additional randomness to the signing device client + * and the client commits to the randomness in the nonce using sign-to-contract. + * + * The following scheme is described by Stepan Snigirev here: + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017655.html + * and by Pieter Wuille (as "Scheme 6") here: + * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017667.html + * + * In order to ensure the host cannot trick the signing device into revealing its + * keys, or the signing device to bias the nonce despite the host's contributions, + * the host and client must engage in a commit-reveal protocol as follows: + * 1. The host draws randomness `rho` and computes a sha256 commitment to it using + * `secp256k1_ecdsa_anti_klepto_host_commit`. It sends this to the signing device. + * 2. The signing device computes a public nonce `R` using the host's commitment + * as auxiliary randomness, using `secp256k1_ecdsa_anti_klepto_signer_commit`. + * The signing device sends the resulting `R` to the host as a s2c_opening. + * + * If, at any point from this step onward, the hardware device fails, it is + * okay to restart the protocol using **exactly the same `rho`** and checking + * that the hardware device proposes **exactly the same** `R`. Otherwise, the + * hardware device may be selectively aborting and thereby biasing the set of + * nonces that are used in actual signatures. + * + * It takes many (>100) such aborts before there is a plausible attack, given + * current knowledge in 2020. However such aborts accumulate even across a total + * replacement of all relevant devices (but not across replacement of the actual + * signing keys with new independently random ones). + * + * In case the hardware device cannot be made to sign with the given `rho`, `R` + * pair, wallet authors should alert the user and present a very scary message + * implying that if this happens more than even a few times, say 20 or more times + * EVER, they should change hardware vendors and perhaps sweep their coins. + * + * 3. The host replies with `rho` generated in step 1. + * 4. The device signs with `secp256k1_anti_klepto_sign`, using `rho` as `host_data32`, + * and sends the signature to the host. + * 5. The host verifies that the signature's public nonce matches the opening from + * step 2 and its original randomness `rho`, using `secp256k1_anti_klepto_host_verify`. + * + * Rationale: + * - The reason for having a host commitment is to allow the signing device to + * deterministically derive a unique nonce even if the host restarts the protocol + * using the same message and keys. Otherwise the signer might reuse the original + * nonce in two iterations of the protocol with different `rho`, which leaks the + * the secret key. + * - The signer does not need to check that the host commitment matches the host's + * claimed `rho`. Instead it re-derives the commitment (and its original `R`) from + * the provided `rho`. If this differs from the original commitment, the result + * will be an invalid `s2c_opening`, but since `R` was unique there is no risk to + * the signer's secret keys. Because of this, the signing device does not need to + * maintain any state about the progress of the protocol. + */ + +/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Klepto Protocol. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL) + * In: rand32: the 32-byte randomness to commit to (cannot be NULL). It must come from + * a cryptographically secure RNG. As per the protocol, this value must not + * be revealed to the client until after the host has received the client + * commitment. + */ +SECP256K1_API int secp256k1_ecdsa_anti_klepto_host_commit( + const secp256k1_context* ctx, + unsigned char* rand_commitment32, + const unsigned char* rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute signer's original nonce. Part of the ECDSA Anti-Klepto Protocol. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: s2c_opening: pointer to an s2c_opening where the signer's public nonce will be + * placed. (cannot be NULL) + * In: msg32: the 32-byte message hash to be signed (cannot be NULL) + * seckey32: the 32-byte secret key used for signing (cannot be NULL) + * rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit( + const secp256k1_context* ctx, + secp256k1_ecdsa_s2c_opening* s2c_opening, + const unsigned char* msg32, + const unsigned char* seckey32, + const unsigned char* rand_commitment32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Same as secp256k1_ecdsa_sign, but commits to host randomness in the nonce. Part of the + * ECDSA Anti-Klepto Protocol. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * host_data32: pointer to 32-byte host-provided randomness (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char* msg32, + const unsigned char* seckey, + const unsigned char* host_data32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a signature was correctly constructed using the ECDSA Anti-Klepto Protocol. + * + * Returns: 1: the signature is valid and contains a commitment to host_data32 + * 0: incorrect opening + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature produced by the signer (cannot be NULL) + * msghash32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to the signer's public key (cannot be NULL) + * host_data32: the 32-byte data provided by the host (cannot be NULL) + * opening: the s2c opening provided by the signer (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_host_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey, + const unsigned char *host_data32, + const secp256k1_ecdsa_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 7246ddc7..093bab85 100755 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -137,4 +137,62 @@ int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp25 return secp256k1_scalar_eq(&sigr, &x_scalar); } +/*** anti-klepto ***/ +int secp256k1_ecdsa_anti_klepto_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) { + secp256k1_sha256 sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(rand_commitment32 != NULL); + ARG_CHECK(rand32 != NULL); + + secp256k1_s2c_ecdsa_data_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, rand32, 32); + secp256k1_sha256_finalize(&sha, rand_commitment32); + return 1; +} + +int secp256k1_ecdsa_anti_klepto_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) { + unsigned char nonce32[32]; + secp256k1_scalar k; + secp256k1_gej rj; + secp256k1_ge r; + unsigned int count = 0; + int is_nonce_valid = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(opening != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(rand_commitment32 != NULL); + + memset(nonce32, 0, 32); + while (!is_nonce_valid) { + /* cast to void* removes const qualifier, but secp256k1_nonce_function_default does not modify it */ + if (!secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, (void*)rand_commitment32, count)) { + secp256k1_callback_call(&ctx->error_callback, "(cryptographically unreachable) generated bad nonce"); + } + is_nonce_valid = secp256k1_scalar_set_b32_seckey(&k, nonce32); + /* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */ + secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); + count++; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + secp256k1_ecdsa_s2c_opening_save(opening, &r); + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&k); + return 1; +} + +int secp256k1_anti_klepto_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) { + return secp256k1_ecdsa_s2c_sign(ctx, sig, NULL, msg32, seckey, host_data32); +} + +int secp256k1_anti_klepto_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) { + return secp256k1_ecdsa_s2c_verify_commit(ctx, sig, host_data32, opening) && + secp256k1_ecdsa_verify(ctx, sig, msg32, pubkey); +} + #endif /* SECP256K1_ECDSA_S2C_MAIN_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index 6e8dae8f..c76e6b11 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -99,6 +99,8 @@ static void test_ecdsa_s2c_api(void) { const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"; const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss"; const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd"; + const unsigned char hostrand[32] = "hrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhr"; + unsigned char hostrand_commitment[32]; secp256k1_pubkey pk; int32_t ecount; @@ -148,6 +150,65 @@ static void test_ecdsa_s2c_api(void) { CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, NULL, msg, sec, s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1); + /* anti-klepto */ + ecount = 0; + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, NULL, hostrand) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, hostrand) == 1); + CHECK(ecount == 2); + + ecount = 0; + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, NULL, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, NULL, sec, hostrand_commitment) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, NULL, hostrand_commitment) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, sec, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(none, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(vrfy, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(sign, &s2c_opening, msg, sec, hostrand_commitment) == 1); + CHECK(ecount == 6); + + ecount = 0; + CHECK(secp256k1_anti_klepto_sign(both, NULL, msg, sec, hostrand) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_anti_klepto_sign(both, &sig, NULL, sec, hostrand) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, NULL, hostrand) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_anti_klepto_sign(none, &sig, msg, sec, hostrand) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_anti_klepto_sign(vrfy, &sig, msg, sec, hostrand) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, hostrand) == 1); + CHECK(ecount == 6); + + ecount = 0; + CHECK(secp256k1_anti_klepto_host_verify(both, NULL, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, NULL, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, NULL, hostrand, &s2c_opening) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, NULL, &s2c_opening) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, hostrand, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_anti_klepto_host_verify(none, &sig, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_anti_klepto_host_verify(sign, &sig, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_anti_klepto_host_verify(vrfy, &sig, msg, &pk, hostrand, &s2c_opening) == 1); + CHECK(ecount == 7); + secp256k1_context_destroy(both); secp256k1_context_destroy(vrfy); secp256k1_context_destroy(sign); @@ -156,18 +217,24 @@ static void test_ecdsa_s2c_api(void) { /* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */ typedef struct { + /* Data to commit to */ unsigned char s2c_data[32]; + /* Original nonce */ unsigned char expected_s2c_opening[33]; + /* Original nonce (anti-klepto protocol, which mixes in host randomness) */ + unsigned char expected_s2c_klepto_opening[33]; } ecdsa_s2c_test; static ecdsa_s2c_test ecdsa_s2c_tests[] = { { "\x1b\xf6\xfb\x42\xf4\x1e\xb8\x76\xc4\xd7\xaa\x0d\x67\x24\x2b\x00\xba\xab\x99\xdc\x20\x84\x49\x3e\x4e\x63\x27\x7f\xa1\xf7\x7f\x22", "\x03\xf0\x30\xde\xf3\x18\x8c\x0f\x56\xfc\xea\x87\x43\x5b\x30\x76\x43\xf4\x5d\xaf\xe2\x2c\xbc\x82\xfd\x56\x03\x4f\xae\x97\x41\x7d\x3a", + "\x02\xdf\x63\x75\x5d\x1f\x32\x92\xbf\xfe\xd8\x29\x86\xb1\x06\x49\x7c\x93\xb1\xf8\xbd\xc0\x45\x4b\x6b\x0b\x0a\x47\x79\xc0\xef\x71\x88", }, { "\x35\x19\x9a\x8f\xbf\x84\xad\x6e\xf6\x9a\x18\x4c\x1b\x19\x28\x5b\xef\xbe\x06\xe6\x0b\x62\x64\xe6\xd3\x73\x89\x3f\x68\x55\xe2\x4a", "\x03\x90\x17\x17\xce\x7c\x74\x84\xa2\xce\x1b\x7d\xc7\x40\x3b\x14\xe0\x35\x49\x71\x39\x3e\xc0\x92\xa7\xf3\xe0\xc8\xe4\xe2\xd2\x63\x9d", + "\x02\xc0\x4a\xc7\xf7\x71\xe8\xeb\xdb\xf3\x15\xff\x5e\x58\xb7\xfe\x95\x16\x10\x21\x03\x50\x00\x66\x17\x2c\x4f\xac\x5b\x20\xf9\xe0\xea", }, }; @@ -190,6 +257,7 @@ static void test_ecdsa_s2c_fixed_vectors(void) { CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, opening_ser, &s2c_opening) == 1); CHECK(memcmp(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0); + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, test->s2c_data, &s2c_opening) == 1); } } @@ -247,12 +315,102 @@ static void test_ecdsa_s2c_sign_verify(void) { } } +static void test_ecdsa_anti_klepto_signer_commit(void) { + size_t i; + unsigned char privkey[32] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + }; + unsigned char message[32] = { + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + }; + /* Check that original pubnonce is derived from s2c_data */ + for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) { + secp256k1_ecdsa_s2c_opening s2c_opening; + unsigned char buf[33]; + const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, message, privkey, test->s2c_data) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, buf, &s2c_opening) == 1); + CHECK(memcmp(test->expected_s2c_klepto_opening, buf, sizeof(buf)) == 0); + } +} + +/* This tests the full ECDSA Anti-Klepto Protocol */ +static void test_ecdsa_anti_klepto(void) { + unsigned char signer_privkey[32]; + unsigned char host_msg[32]; + unsigned char host_commitment[32]; + unsigned char host_nonce_contribution[32]; + secp256k1_pubkey signer_pubkey; + secp256k1_ecdsa_signature signature; + secp256k1_ecdsa_s2c_opening s2c_opening; + + /* Generate a random key, message. */ + { + secp256k1_scalar key; + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(signer_privkey, &key); + CHECK(secp256k1_ec_pubkey_create(ctx, &signer_pubkey, signer_privkey) == 1); + secp256k1_testrand256_test(host_msg); + secp256k1_testrand256_test(host_nonce_contribution); + } + + /* Protocol step 1. */ + CHECK(secp256k1_ecdsa_anti_klepto_host_commit(ctx, host_commitment, host_nonce_contribution) == 1); + /* Protocol step 2. */ + CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, host_msg, signer_privkey, host_commitment) == 1); + /* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */ + /* Protocol step 4. */ + CHECK(secp256k1_anti_klepto_sign(ctx, &signature, host_msg, signer_privkey, host_nonce_contribution) == 1); + /* Protocol step 5. */ + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + /* Protocol step 5 (explicitly) */ + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); + + { /* host_verify: commitment does not match */ + unsigned char sigbytes[64]; + size_t i; + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, sigbytes, &signature) == 1); + for(i = 0; i < 32; i++) { + /* change one byte */ + sigbytes[i] += 1; + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1); + CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + /* revert */ + sigbytes[i] -= 1; + } + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1); + } + { /* host_verify: message does not match */ + unsigned char bad_msg[32]; + secp256k1_testrand256_test(bad_msg); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, bad_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + } + { /* s2c_sign: host provided data that didn't match commitment */ + secp256k1_ecdsa_s2c_opening orig_opening = s2c_opening; + unsigned char bad_nonce_contribution[32] = { 1, 2, 3, 4 }; + CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, host_msg, signer_privkey, bad_nonce_contribution) == 1); + /* good signature but the opening (original public nonce does not match the original */ + CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1); + CHECK(memcmp(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0); + } +} + static void run_ecdsa_s2c_tests(void) { run_s2c_opening_test(); test_ecdsa_s2c_tagged_hash(); test_ecdsa_s2c_api(); test_ecdsa_s2c_fixed_vectors(); test_ecdsa_s2c_sign_verify(); + + test_ecdsa_anti_klepto_signer_commit(); + test_ecdsa_anti_klepto(); } #endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ From 47efb5e39a1bf6330bd3bf6bc4b4416c5ca11878 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 21 Dec 2020 20:27:14 +0000 Subject: [PATCH 095/381] ecdsa-s2c: add ctime tests --- src/valgrind_ctime_test.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 3169e365..9750d185 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -25,6 +25,10 @@ #include "include/secp256k1_schnorrsig.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +#include "include/secp256k1_ecdsa_s2c.h" +#endif + int main(void) { secp256k1_context* ctx; secp256k1_ecdsa_signature signature; @@ -152,6 +156,31 @@ int main(void) { CHECK(ret == 1); #endif +#ifdef ENABLE_MODULE_ECDSA_S2C + { + unsigned char s2c_data[32] = {0}; + unsigned char s2c_data_comm[32] = {0}; + secp256k1_ecdsa_s2c_opening s2c_opening; + + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32); + ret = secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, msg, key, s2c_data); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32); + ret = secp256k1_ecdsa_anti_klepto_host_commit(ctx, s2c_data_comm, s2c_data); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32); + ret = secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, msg, key, s2c_data); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + } +#endif + secp256k1_context_destroy(ctx); return 0; } From 41d6963bc1c99081e1a0cebbd1e97a77b6d02445 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 5 Jan 2021 13:41:32 +0000 Subject: [PATCH 096/381] rangeproof: clarify rewind outlen argument --- include/secp256k1_rangeproof.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 74671061..d4f35de7 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -197,7 +197,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment * value_out: pointer to an unsigned int64 which has the exact value of the commitment. * message_out: pointer to a 4096 byte character array to receive message data from the proof author. - * outlen: length of message data written to message_out. + * outlen: length of message data written to message_out. This is generally not equal to the + * msg_len used by the signer. However, for all i with msg_len <= i < outlen, it is + * guaranteed that message_out[i] == 0. * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ From 96c83a83dcf742bc175c079188b06dfd0622406c Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Tue, 5 Jan 2021 16:09:04 -0800 Subject: [PATCH 097/381] Remove repeated schnorr flag from travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6826614e..df4136ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ compiler: - gcc env: global: - - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no ECDSA_S2C=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no + - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no ECDSA_S2C=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no matrix: - WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes From 7eeacd7725fa8c895c2f58850b151e66199137cf Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 27 Sep 2020 20:17:29 +0000 Subject: [PATCH 098/381] Add contrib/sync-upstream.sh script to automate merging upstream PRs --- .gitignore | 1 + contrib/sync-upstream.sh | 111 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100755 contrib/sync-upstream.sh diff --git a/.gitignore b/.gitignore index 5b2e4ac6..d824b57b 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ build-aux/compile build-aux/test-driver src/stamp-h1 libsecp256k1.pc +contrib/gh-pr-create.sh diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh new file mode 100755 index 00000000..f2a6f994 --- /dev/null +++ b/contrib/sync-upstream.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +set -eou pipefail + +help() { + echo "$0 range [end]" + echo " merges every merge commit missing merge commit." + echo " If the optional [end] commit is provided, only merges up to [end]." + echo + echo "$0 select ... " + echo " merges every selected merge commit" + echo + echo "This tool creates a branch and a script that can be executed to create the" + echo "PR automatically. The script requires the github-cli tool (aka gh)." + echo "" + echo "Tip: \`git log --oneline upstream/master --merges\` shows merge commits." + exit 1 +} + +if [ "$#" -lt 1 ]; then + help +fi + +REMOTE=upstream +REMOTE_BRANCH=$REMOTE/master +# Makes sure you have a remote "upstream" that is up-to-date +setup() { + ret=0 + git fetch $REMOTE &> /dev/null || ret=$? + if [ ${ret} == 0 ]; then + return + fi + echo "Adding remote \"$REMOTE\" with URL git@github.com:bitcoin-core/secp256k1.git. Continue with y" + read -r yn + case $yn in + [Yy]* ) ;; + * ) exit 1;; + esac + git remote add $REMOTE git@github.com:bitcoin-core/secp256k1.git &> /dev/null + git fetch $REMOTE &> /dev/null +} + +range() { + RANGESTART_COMMIT=$(git merge-base $REMOTE_BRANCH master) + RANGEEND_COMMIT=$REMOTE_BRANCH + if [ "$#" = 1 ]; then + RANGEEND_COMMIT=$1 + fi + + COMMITS=$(git --no-pager log --oneline "$REMOTE_BRANCH" --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") + COMMITS=$(echo "$COMMITS" | tac | awk '{ print $1 }' ORS=' ') + echo "Merging $COMMITS. Continue with y" + read -r yn + case $yn in + [Yy]* ) ;; + * ) exit 1;; + esac +} + +case $1 in + range) + shift + setup + range "$@" + ;; + select) + shift + setup + COMMITS=$* + ;; + help) + help + ;; + *) + help +esac + +TITLE="Upstream PRs " +BODY="" +for COMMIT in $COMMITS +do + PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\).*'/'\1'/) + TITLE="$TITLE #$PRNUM" + BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\)'/'[upstream PR #\1]'/)") +done + +BODY=$(printf "%s\n\n%s" "$BODY" "This PR was automatically created with \\\`$0 $*\\\`.") + +echo "-----------------------------------" +echo "$TITLE" +echo "-----------------------------------" +echo "$BODY" +echo "-----------------------------------" +# Create branch from PR commit and create PR +git checkout master +git pull +git checkout -b temp-merge-"$PRNUM" + +BASEDIR=$(dirname "$0") +FNAME=$BASEDIR/gh-pr-create.sh +cat < "$FNAME" +#!/bin/sh +gh pr create -t "$TITLE" -b "$BODY" --web +# Remove temporary branch +git checkout master +git branch -D temp-merge-"$PRNUM" +EOT +chmod +x "$FNAME" +echo Run "$FNAME" after solving the merge conflicts + +git merge --no-edit -m "Merge commits '$COMMITS' into temp-merge-$PRNUM" $COMMITS From e354c5751d670716791778d86c1dcd599f2e1a9c Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 9 Feb 2021 22:46:03 +0000 Subject: [PATCH 099/381] ecdsa_s2c: rename anti-klepto to anti-exfil --- include/secp256k1_ecdsa_s2c.h | 30 +++++----- src/modules/ecdsa_s2c/main_impl.h | 14 ++--- src/modules/ecdsa_s2c/tests_impl.h | 88 +++++++++++++++--------------- src/valgrind_ctime_test.c | 4 +- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h index 482b0c13..e5920acd 100644 --- a/include/secp256k1_ecdsa_s2c.h +++ b/include/secp256k1_ecdsa_s2c.h @@ -4,7 +4,7 @@ #include "secp256k1.h" /** This module implements the sign-to-contract scheme for ECDSA signatures, as - * well as the "ECDSA Anti-Klepto Protocol" that is based on sign-to-contract + * well as the "ECDSA Anti-Exfil Protocol" that is based on sign-to-contract * and is specified further down. The sign-to-contract scheme allows creating a * signature that also commits to some data. This works by offsetting the public * nonce point of the signature R by hash(R, data)*G where G is the secp256k1 @@ -97,9 +97,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -/** ECDSA Anti-Klepto Protocol +/** ECDSA Anti-Exfil Protocol * - * The ecdsa_anti_klepto_* functions can be used to prevent a signing device from + * The ecdsa_anti_exfil_* functions can be used to prevent a signing device from * exfiltrating the secret signing keys through biased signature nonces. The general * idea is that a host provides additional randomness to the signing device client * and the client commits to the randomness in the nonce using sign-to-contract. @@ -113,9 +113,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit * keys, or the signing device to bias the nonce despite the host's contributions, * the host and client must engage in a commit-reveal protocol as follows: * 1. The host draws randomness `rho` and computes a sha256 commitment to it using - * `secp256k1_ecdsa_anti_klepto_host_commit`. It sends this to the signing device. + * `secp256k1_ecdsa_anti_exfil_host_commit`. It sends this to the signing device. * 2. The signing device computes a public nonce `R` using the host's commitment - * as auxiliary randomness, using `secp256k1_ecdsa_anti_klepto_signer_commit`. + * as auxiliary randomness, using `secp256k1_ecdsa_anti_exfil_signer_commit`. * The signing device sends the resulting `R` to the host as a s2c_opening. * * If, at any point from this step onward, the hardware device fails, it is @@ -135,10 +135,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit * EVER, they should change hardware vendors and perhaps sweep their coins. * * 3. The host replies with `rho` generated in step 1. - * 4. The device signs with `secp256k1_anti_klepto_sign`, using `rho` as `host_data32`, + * 4. The device signs with `secp256k1_anti_exfil_sign`, using `rho` as `host_data32`, * and sends the signature to the host. * 5. The host verifies that the signature's public nonce matches the opening from - * step 2 and its original randomness `rho`, using `secp256k1_anti_klepto_host_verify`. + * step 2 and its original randomness `rho`, using `secp256k1_anti_exfil_host_verify`. * * Rationale: * - The reason for having a host commitment is to allow the signing device to @@ -154,7 +154,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit * maintain any state about the progress of the protocol. */ -/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Klepto Protocol. +/** Create the initial host commitment to `rho`. Part of the ECDSA Anti-Exfil Protocol. * * Returns 1 on success, 0 on failure. * Args: ctx: pointer to a context object (cannot be NULL) @@ -164,13 +164,13 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_verify_commit * be revealed to the client until after the host has received the client * commitment. */ -SECP256K1_API int secp256k1_ecdsa_anti_klepto_host_commit( +SECP256K1_API int secp256k1_ecdsa_anti_exfil_host_commit( const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Compute signer's original nonce. Part of the ECDSA Anti-Klepto Protocol. +/** Compute signer's original nonce. Part of the ECDSA Anti-Exfil Protocol. * * Returns 1 on success, 0 on failure. * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) @@ -180,7 +180,7 @@ SECP256K1_API int secp256k1_ecdsa_anti_klepto_host_commit( * seckey32: the 32-byte secret key used for signing (cannot be NULL) * rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL) */ -SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit( +SECP256K1_API int secp256k1_ecdsa_anti_exfil_signer_commit( const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* s2c_opening, const unsigned char* msg32, @@ -189,7 +189,7 @@ SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit( ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Same as secp256k1_ecdsa_sign, but commits to host randomness in the nonce. Part of the - * ECDSA Anti-Klepto Protocol. + * ECDSA Anti-Exfil Protocol. * * Returns: 1: signature created * 0: the nonce generation function failed, or the private key was invalid. @@ -199,7 +199,7 @@ SECP256K1_API int secp256k1_ecdsa_anti_klepto_signer_commit( * seckey: pointer to a 32-byte secret key (cannot be NULL) * host_data32: pointer to 32-byte host-provided randomness (cannot be NULL) */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_exfil_sign( const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, @@ -207,7 +207,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign( const unsigned char* host_data32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); -/** Verify a signature was correctly constructed using the ECDSA Anti-Klepto Protocol. +/** Verify a signature was correctly constructed using the ECDSA Anti-Exfil Protocol. * * Returns: 1: the signature is valid and contains a commitment to host_data32 * 0: incorrect opening @@ -218,7 +218,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_sign( * host_data32: the 32-byte data provided by the host (cannot be NULL) * opening: the s2c opening provided by the signer (cannot be NULL) */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_klepto_host_verify( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_exfil_host_verify( const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 093bab85..aae8a0eb 100755 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -82,7 +82,7 @@ int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signa /* Provide `s2c_data32` to the nonce function as additional data to * derive the nonce. It is first hashed because it should be possible * to derive nonces even if only a SHA256 commitment to the data is - * known. This is important in the ECDSA anti-klepto protocol. */ + * known. This is important in the ECDSA anti-exfil protocol. */ secp256k1_s2c_ecdsa_data_sha256_tagged(&s2c_sha); secp256k1_sha256_write(&s2c_sha, s2c_data32, 32); secp256k1_sha256_finalize(&s2c_sha, ndata); @@ -130,15 +130,15 @@ int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp25 /* Do not check overflow; overflowing a scalar does not affect whether * or not the R value is a cryptographic commitment, only whether it * is a valid R value for an ECDSA signature. If users care about that - * they should use `ecdsa_verify` or `anti_klepto_host_verify`. In other + * they should use `ecdsa_verify` or `anti_exfil_host_verify`. In other * words, this check would be (at best) unnecessary, and (at worst) * insufficient. */ secp256k1_scalar_set_b32(&x_scalar, x_bytes, NULL); return secp256k1_scalar_eq(&sigr, &x_scalar); } -/*** anti-klepto ***/ -int secp256k1_ecdsa_anti_klepto_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) { +/*** anti-exfil ***/ +int secp256k1_ecdsa_anti_exfil_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) { secp256k1_sha256 sha; VERIFY_CHECK(ctx != NULL); @@ -151,7 +151,7 @@ int secp256k1_ecdsa_anti_klepto_host_commit(const secp256k1_context* ctx, unsign return 1; } -int secp256k1_ecdsa_anti_klepto_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) { +int secp256k1_ecdsa_anti_exfil_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) { unsigned char nonce32[32]; secp256k1_scalar k; secp256k1_gej rj; @@ -186,11 +186,11 @@ int secp256k1_ecdsa_anti_klepto_signer_commit(const secp256k1_context* ctx, secp return 1; } -int secp256k1_anti_klepto_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) { +int secp256k1_anti_exfil_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) { return secp256k1_ecdsa_s2c_sign(ctx, sig, NULL, msg32, seckey, host_data32); } -int secp256k1_anti_klepto_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) { +int secp256k1_anti_exfil_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) { return secp256k1_ecdsa_s2c_verify_commit(ctx, sig, host_data32, opening) && secp256k1_ecdsa_verify(ctx, sig, msg32, pubkey); } diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index c76e6b11..7ca1a3a6 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -150,63 +150,63 @@ static void test_ecdsa_s2c_api(void) { CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, NULL, msg, sec, s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1); - /* anti-klepto */ + /* anti-exfil */ ecount = 0; - CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, NULL, hostrand) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_host_commit(none, NULL, hostrand) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, NULL) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_host_commit(none, hostrand_commitment, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, hostrand) == 1); + CHECK(secp256k1_ecdsa_anti_exfil_host_commit(none, hostrand_commitment, hostrand) == 1); CHECK(ecount == 2); ecount = 0; - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, NULL, msg, sec, hostrand_commitment) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(both, NULL, msg, sec, hostrand_commitment) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, NULL, sec, hostrand_commitment) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(both, &s2c_opening, NULL, sec, hostrand_commitment) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, NULL, hostrand_commitment) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(both, &s2c_opening, msg, NULL, hostrand_commitment) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, sec, NULL) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(both, &s2c_opening, msg, sec, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(none, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(none, &s2c_opening, msg, sec, hostrand_commitment) == 0); CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(vrfy, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(vrfy, &s2c_opening, msg, sec, hostrand_commitment) == 0); CHECK(ecount == 6); - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(sign, &s2c_opening, msg, sec, hostrand_commitment) == 1); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(sign, &s2c_opening, msg, sec, hostrand_commitment) == 1); CHECK(ecount == 6); ecount = 0; - CHECK(secp256k1_anti_klepto_sign(both, NULL, msg, sec, hostrand) == 0); + CHECK(secp256k1_anti_exfil_sign(both, NULL, msg, sec, hostrand) == 0); CHECK(ecount == 1); - CHECK(secp256k1_anti_klepto_sign(both, &sig, NULL, sec, hostrand) == 0); + CHECK(secp256k1_anti_exfil_sign(both, &sig, NULL, sec, hostrand) == 0); CHECK(ecount == 2); - CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, NULL, hostrand) == 0); + CHECK(secp256k1_anti_exfil_sign(both, &sig, msg, NULL, hostrand) == 0); CHECK(ecount == 3); - CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, NULL) == 0); + CHECK(secp256k1_anti_exfil_sign(both, &sig, msg, sec, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_anti_klepto_sign(none, &sig, msg, sec, hostrand) == 0); + CHECK(secp256k1_anti_exfil_sign(none, &sig, msg, sec, hostrand) == 0); CHECK(ecount == 5); - CHECK(secp256k1_anti_klepto_sign(vrfy, &sig, msg, sec, hostrand) == 0); + CHECK(secp256k1_anti_exfil_sign(vrfy, &sig, msg, sec, hostrand) == 0); CHECK(ecount == 6); - CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, hostrand) == 1); + CHECK(secp256k1_anti_exfil_sign(both, &sig, msg, sec, hostrand) == 1); CHECK(ecount == 6); ecount = 0; - CHECK(secp256k1_anti_klepto_host_verify(both, NULL, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(both, NULL, msg, &pk, hostrand, &s2c_opening) == 0); CHECK(ecount == 1); - CHECK(secp256k1_anti_klepto_host_verify(both, &sig, NULL, &pk, hostrand, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(both, &sig, NULL, &pk, hostrand, &s2c_opening) == 0); CHECK(ecount == 2); - CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, NULL, hostrand, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(both, &sig, msg, NULL, hostrand, &s2c_opening) == 0); CHECK(ecount == 3); - CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, NULL, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(both, &sig, msg, &pk, NULL, &s2c_opening) == 0); CHECK(ecount == 4); - CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, hostrand, NULL) == 0); + CHECK(secp256k1_anti_exfil_host_verify(both, &sig, msg, &pk, hostrand, NULL) == 0); CHECK(ecount == 5); - CHECK(secp256k1_anti_klepto_host_verify(none, &sig, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(none, &sig, msg, &pk, hostrand, &s2c_opening) == 0); CHECK(ecount == 6); - CHECK(secp256k1_anti_klepto_host_verify(sign, &sig, msg, &pk, hostrand, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(sign, &sig, msg, &pk, hostrand, &s2c_opening) == 0); CHECK(ecount == 7); - CHECK(secp256k1_anti_klepto_host_verify(vrfy, &sig, msg, &pk, hostrand, &s2c_opening) == 1); + CHECK(secp256k1_anti_exfil_host_verify(vrfy, &sig, msg, &pk, hostrand, &s2c_opening) == 1); CHECK(ecount == 7); secp256k1_context_destroy(both); @@ -221,8 +221,8 @@ typedef struct { unsigned char s2c_data[32]; /* Original nonce */ unsigned char expected_s2c_opening[33]; - /* Original nonce (anti-klepto protocol, which mixes in host randomness) */ - unsigned char expected_s2c_klepto_opening[33]; + /* Original nonce (anti-exfil protocol, which mixes in host randomness) */ + unsigned char expected_s2c_exfil_opening[33]; } ecdsa_s2c_test; static ecdsa_s2c_test ecdsa_s2c_tests[] = { @@ -315,7 +315,7 @@ static void test_ecdsa_s2c_sign_verify(void) { } } -static void test_ecdsa_anti_klepto_signer_commit(void) { +static void test_ecdsa_anti_exfil_signer_commit(void) { size_t i; unsigned char privkey[32] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, @@ -330,14 +330,14 @@ static void test_ecdsa_anti_klepto_signer_commit(void) { secp256k1_ecdsa_s2c_opening s2c_opening; unsigned char buf[33]; const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, message, privkey, test->s2c_data) == 1); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(ctx, &s2c_opening, message, privkey, test->s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, buf, &s2c_opening) == 1); - CHECK(memcmp(test->expected_s2c_klepto_opening, buf, sizeof(buf)) == 0); + CHECK(memcmp(test->expected_s2c_exfil_opening, buf, sizeof(buf)) == 0); } } -/* This tests the full ECDSA Anti-Klepto Protocol */ -static void test_ecdsa_anti_klepto(void) { +/* This tests the full ECDSA Anti-Exfil Protocol */ +static void test_ecdsa_anti_exfil(void) { unsigned char signer_privkey[32]; unsigned char host_msg[32]; unsigned char host_commitment[32]; @@ -357,14 +357,14 @@ static void test_ecdsa_anti_klepto(void) { } /* Protocol step 1. */ - CHECK(secp256k1_ecdsa_anti_klepto_host_commit(ctx, host_commitment, host_nonce_contribution) == 1); + CHECK(secp256k1_ecdsa_anti_exfil_host_commit(ctx, host_commitment, host_nonce_contribution) == 1); /* Protocol step 2. */ - CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, host_msg, signer_privkey, host_commitment) == 1); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(ctx, &s2c_opening, host_msg, signer_privkey, host_commitment) == 1); /* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */ /* Protocol step 4. */ - CHECK(secp256k1_anti_klepto_sign(ctx, &signature, host_msg, signer_privkey, host_nonce_contribution) == 1); + CHECK(secp256k1_anti_exfil_sign(ctx, &signature, host_msg, signer_privkey, host_nonce_contribution) == 1); /* Protocol step 5. */ - CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); /* Protocol step 5 (explicitly) */ CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 1); CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); @@ -378,7 +378,7 @@ static void test_ecdsa_anti_klepto(void) { sigbytes[i] += 1; CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1); CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 0); - CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); /* revert */ sigbytes[i] -= 1; } @@ -387,8 +387,8 @@ static void test_ecdsa_anti_klepto(void) { { /* host_verify: message does not match */ unsigned char bad_msg[32]; secp256k1_testrand256_test(bad_msg); - CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); - CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, bad_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, bad_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); } { /* s2c_sign: host provided data that didn't match commitment */ secp256k1_ecdsa_s2c_opening orig_opening = s2c_opening; @@ -396,8 +396,8 @@ static void test_ecdsa_anti_klepto(void) { CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, host_msg, signer_privkey, bad_nonce_contribution) == 1); /* good signature but the opening (original public nonce does not match the original */ CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); - CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); - CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1); + CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); + CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1); CHECK(memcmp(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0); } } @@ -409,8 +409,8 @@ static void run_ecdsa_s2c_tests(void) { test_ecdsa_s2c_fixed_vectors(); test_ecdsa_s2c_sign_verify(); - test_ecdsa_anti_klepto_signer_commit(); - test_ecdsa_anti_klepto(); + test_ecdsa_anti_exfil_signer_commit(); + test_ecdsa_anti_exfil(); } #endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 9750d185..4dba9b03 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -169,13 +169,13 @@ int main(void) { CHECK(ret == 1); VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32); - ret = secp256k1_ecdsa_anti_klepto_host_commit(ctx, s2c_data_comm, s2c_data); + ret = secp256k1_ecdsa_anti_exfil_host_commit(ctx, s2c_data_comm, s2c_data); VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret == 1); VALGRIND_MAKE_MEM_UNDEFINED(key, 32); VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32); - ret = secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, msg, key, s2c_data); + ret = secp256k1_ecdsa_anti_exfil_signer_commit(ctx, &s2c_opening, msg, key, s2c_data); VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret == 1); } From 649bf201d85c233efa7e7689e34d03187f23dc08 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 10 Mar 2021 13:20:01 +0000 Subject: [PATCH 100/381] musig: fix tests for 32-bit --- src/modules/musig/tests_impl.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 22a5d05b..edd43eac 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -237,8 +237,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { * rejects n_signers that high. */ if (SIZE_MAX > UINT32_MAX) { CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + CHECK(ecount == 11); + } else { + ecount = 11; } - CHECK(ecount == 11); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); CHECK(ecount == 12); /* secret key overflows */ @@ -267,8 +269,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 6); if (SIZE_MAX > UINT32_MAX) { CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); + CHECK(ecount == 7); + } else { + ecount = 7; } - CHECK(ecount == 7); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); /** Signing step 0 -- exchange nonce commitments */ From 79d4c3ac681aae732fb0a1551c0281b17f517d02 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 10 Mar 2021 13:28:16 +0000 Subject: [PATCH 101/381] whitelist: add SECP_INCLUDES to bench_whitelist CPPFLAGS This will fix the following compile error on macOS In file included from src/num.h:17, from src/num_impl.h:14, from src/bench_whitelist.c:14: src/num_gmp.h:10:10: fatal error: gmp.h: No such file or directory --- src/modules/whitelist/Makefile.am.include | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include index 0dc5a64d..f43e3e4b 100644 --- a/src/modules/whitelist/Makefile.am.include +++ b/src/modules/whitelist/Makefile.am.include @@ -5,6 +5,7 @@ noinst_HEADERS += src/modules/whitelist/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_whitelist bench_whitelist_SOURCES = src/bench_whitelist.c +bench_whitelist_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) bench_whitelist_LDADD = libsecp256k1.la $(SECP_LIBS) bench_generator_LDFLAGS = -static endif From 38f1e777d4958a7bbc68d9a6e5555e626aaedf34 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 10 Mar 2021 15:07:07 +0100 Subject: [PATCH 102/381] sync-upstream: Create proper links to upstream PRs --- contrib/sync-upstream.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index f2a6f994..24606a39 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -75,14 +75,16 @@ case $1 in help esac -TITLE="Upstream PRs " +TITLE="Upstream PRs" BODY="" for COMMIT in $COMMITS do PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\).*'/'\1'/) - TITLE="$TITLE #$PRNUM" - BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\)'/'[upstream PR #\1]'/)") + TITLE="$TITLE $PRNUM," + BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\)'/'[bitcoin-core\/secp256k1#\1]'/)") done +# Remove trailing "," +TITLE=${TITLE%?} BODY=$(printf "%s\n\n%s" "$BODY" "This PR was automatically created with \\\`$0 $*\\\`.") From 136ed8f84d9c8ab852698375deccf85090e21913 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 10 Mar 2021 16:02:32 +0100 Subject: [PATCH 103/381] sync-upstream: Fix output of command to reproduce --- contrib/sync-upstream.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 24606a39..c9f47d2e 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -4,7 +4,7 @@ set -eou pipefail help() { echo "$0 range [end]" - echo " merges every merge commit missing merge commit." + echo " merges every merge commit present in upstream and missing locally." echo " If the optional [end] commit is provided, only merges up to [end]." echo echo "$0 select ... " @@ -42,7 +42,7 @@ setup() { range() { RANGESTART_COMMIT=$(git merge-base $REMOTE_BRANCH master) - RANGEEND_COMMIT=$REMOTE_BRANCH + RANGEEND_COMMIT=$(git rev-parse $REMOTE_BRANCH) if [ "$#" = 1 ]; then RANGEEND_COMMIT=$1 fi @@ -62,11 +62,13 @@ case $1 in shift setup range "$@" + REPRODUCE_COMMAND="$0 range $RANGEEND_COMMIT" ;; select) shift setup COMMITS=$* + REPRODUCE_COMMAND="$0 $@" ;; help) help @@ -86,7 +88,7 @@ done # Remove trailing "," TITLE=${TITLE%?} -BODY=$(printf "%s\n\n%s" "$BODY" "This PR was automatically created with \\\`$0 $*\\\`.") +BODY=$(printf "%s\n\n%s" "$BODY" "This PR can be recreated with \`$REPRODUCE_COMMAND\`.") echo "-----------------------------------" echo "$TITLE" From 4091e619248b4723a2a4ed5dd7289628ee0320a5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 10 Mar 2021 21:02:19 +0000 Subject: [PATCH 104/381] cirrus: increase timeout for macOS tasks --- .cirrus.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index d2e40713..151227da 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -149,6 +149,8 @@ task: name: "x86_64: macOS Catalina" macos_instance: image: catalina-base + # As of d4ca81f48e tasks with valgrind enabled take about 60 minutes + timeout_in: 90m env: HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 From 654cd633f509db3100ce99acd84f47db594ff9a6 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Thu, 4 Mar 2021 23:38:48 -0800 Subject: [PATCH 105/381] ecdsa_adaptor: initialize project This commit adds the foundational configuration and building scripts and an initial structure for the project. --- Makefile.am | 3 +++ README.md | 1 + configure.ac | 15 +++++++++++++++ include/secp256k1_ecdsa_adaptor.h | 19 +++++++++++++++++++ src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/main_impl.h | 12 ++++++++++++ src/secp256k1.c | 4 ++++ 7 files changed, 55 insertions(+) create mode 100644 include/secp256k1_ecdsa_adaptor.h create mode 100644 src/modules/ecdsa_adaptor/Makefile.am.include create mode 100644 src/modules/ecdsa_adaptor/main_impl.h diff --git a/Makefile.am b/Makefile.am index 434360f8..796be6e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -189,3 +189,6 @@ if ENABLE_MODULE_ECDSA_S2C include src/modules/ecdsa_s2c/Makefile.am.include endif +if ENABLE_MODULE_ECDSA_ADAPTOR +include src/modules/ecdsa_adaptor/Makefile.am.include +endif diff --git a/README.md b/README.md index 9918678e..43dc4238 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Features: * Suitable for embedded systems. * Optional module for public key recovery. * Optional module for ECDH key exchange. +* Optional module for ECDSA adaptor signatures (experimental). Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable. diff --git a/configure.ac b/configure.ac index 64942d1f..17e52078 100644 --- a/configure.ac +++ b/configure.ac @@ -180,6 +180,11 @@ AC_ARG_ENABLE(module_ecdsa_s2c, [enable_module_ecdsa_s2c=$enableval], [enable_module_ecdsa_s2c=no]) +AC_ARG_ENABLE(module_ecdsa-adaptor, + AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]), + [enable_module_ecdsa_adaptor=$enableval], + [enable_module_ecdsa_adaptor=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -580,6 +585,10 @@ if test x"$use_reduced_surjection_proof_size" = x"yes"; then AC_DEFINE(USE_REDUCED_SURJECTION_PROOF_SIZE, 1, [Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, disabling parsing and verification]) fi +if test x"$enable_module_ecdsa_adaptor" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDSA_ADAPTOR, 1, [Define this symbol to enable the ECDSA adaptor module]) +fi + ### ### Check for --enable-experimental if necessary ### @@ -596,6 +605,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_s2c]) + AC_MSG_NOTICE([Building ECDSA adaptor signatures module: $enable_module_ecdsa_adaptor]) AC_MSG_NOTICE([******]) @@ -632,6 +642,9 @@ else if test x"$enable_module_ecdsa_s2c" = x"yes"; then AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_ecdsa_adaptor" = x"yes"; then + AC_MSG_ERROR([ecdsa adaptor signatures module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -673,6 +686,7 @@ AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x" 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_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) @@ -698,6 +712,7 @@ echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" +echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h new file mode 100644 index 00000000..60da16a4 --- /dev/null +++ b/include/secp256k1_ecdsa_adaptor.h @@ -0,0 +1,19 @@ +#ifndef SECP256K1_ECDSA_ADAPTOR_H +#define SECP256K1_ECDSA_ADAPTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements single signer ECDSA adaptor signatures following + * "One-Time Verifiably Encrypted Signatures A.K.A. Adaptor Signatures" by + * Lloyd Fournier + * (https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-November/002316.html + * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDSA_ADAPTOR_H */ diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include new file mode 100644 index 00000000..17766fed --- /dev/null +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -0,0 +1 @@ +include_HEADERS += include/secp256k1_ecdsa_adaptor.h diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h new file mode 100644 index 00000000..79b98fee --- /dev/null +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -0,0 +1,12 @@ +/********************************************************************** + * Copyright (c) 2020-2021 Jonas Nick, Jesse Posner * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H +#define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H + +#include "include/secp256k1_ecdsa_adaptor.h" + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 0ccbaf2e..32cc3e12 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -831,6 +831,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdsa_s2c/main_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +# include "modules/ecdsa_adaptor/main_impl.h" +#endif + #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/main_impl.h" #endif From d8f336564fe1255752c7e454d998beaa25f945c1 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 00:00:13 -0800 Subject: [PATCH 106/381] ecdsa_adaptor: add nonce function and tags This commit adds a nonce function that will be used by default for ECDSA adaptor signatures. This nonce function is similar to secp256k1_nonce_function_hardened except it uses the compressed 33-byte encoding for the pubkey argument. We need 33 bytes instead of 32 because, unlike with BIP-340, an ECDSA X-coordinate alone is not sufficient to disambiguate the Y-coordinate. --- include/secp256k1_ecdsa_adaptor.h | 34 ++++++++ src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/main_impl.h | 78 +++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index 60da16a4..7bc43277 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -12,6 +12,40 @@ extern "C" { * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). */ +/** A pointer to a function to deterministically generate a nonce. + * + * Same as secp256k1_nonce_function_hardened with the exception of using the + * compressed 33-byte encoding for the pubkey argument. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to + * return an error. + * Out: nonce32: pointer to a 32-byte array to be filled by the function + * In: msg32: the 32-byte message hash being verified + * key32: pointer to a 32-byte secret key + * pk33: the 33-byte serialized pubkey corresponding to key32 + * algo: pointer to an array describing the signature algorithm + * algolen: the length of the algo array + * data: arbitrary data pointer that is passed through + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the key, the pubkey, the algorithm description, and data. + */ +typedef int (*secp256k1_nonce_function_hardened_ecdsa_adaptor)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *pk33, + const unsigned char *algo, + size_t algolen, + void *data +); + +/** A modified BIP-340 nonce generation function. If a data pointer is passed, it is + * assumed to be a pointer to 32 bytes of auxiliary random data as defined in BIP-340. + * The hash will be tagged with algo after removing all terminating null bytes. + */ +SECP256K1_API extern const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor; + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include index 17766fed..31c881a3 100644 --- a/src/modules/ecdsa_adaptor/Makefile.am.include +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -1 +1,2 @@ include_HEADERS += include/secp256k1_ecdsa_adaptor.h +noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 79b98fee..e7426f59 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -9,4 +9,82 @@ #include "include/secp256k1_ecdsa_adaptor.h" +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ +static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x791dae43ul; + sha->s[1] = 0xe52d3b44ul; + sha->s[2] = 0x37f9edeaul; + sha->s[3] = 0x9bfd2ab1ul; + sha->s[4] = 0xcfb0f44dul; + sha->s[5] = 0xccf1d880ul; + sha->s[6] = 0xd18f2c13ul; + sha->s[7] = 0xa37b9024ul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("ECDSAadaptor/aux")||SHA256("ECDSAadaptor/aux"). */ +static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xd14c7bd9ul; + sha->s[1] = 0x095d35e6ul; + sha->s[2] = 0xb8490a88ul; + sha->s[3] = 0xfb00ef74ul; + sha->s[4] = 0x0baa488ful; + sha->s[5] = 0x69366693ul; + sha->s[6] = 0x1c81c5baul; + sha->s[7] = 0xc33b296aul; + + sha->bytes = 64; +} + +/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ +static const unsigned char ecdsa_adaptor_algo[16] = "ECDSAadaptor/non"; + +/* Modified BIP-340 nonce function */ +static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *pk33, const unsigned char *algo, size_t algolen, void *data) { + secp256k1_sha256 sha; + unsigned char masked_key[32]; + int i; + + if (algo == NULL) { + return 0; + } + + if (data != NULL) { + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha); + secp256k1_sha256_write(&sha, data, 32); + secp256k1_sha256_finalize(&sha, masked_key); + for (i = 0; i < 32; i++) { + masked_key[i] ^= key32[i]; + } + } + + /* Tag the hash with algo which is important to avoid nonce reuse across + * algorithims. An optimized tagging implementation is used if the default + * tag is provided. */ + if (algolen == sizeof(ecdsa_adaptor_algo) + && secp256k1_memcmp_var(algo, ecdsa_adaptor_algo, algolen) == 0) { + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha); + } else { + secp256k1_sha256_initialize_tagged(&sha, algo, algolen); + } + + /* Hash (masked-)key||pk||msg using the tagged hash as per BIP-340 */ + if (data != NULL) { + secp256k1_sha256_write(&sha, masked_key, 32); + } else { + secp256k1_sha256_write(&sha, key32, 32); + } + secp256k1_sha256_write(&sha, pk33, 33); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + +const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor = nonce_function_ecdsa_adaptor; + #endif From b508e5dd9b1f6f4f9e552056a1fe898fffc0a450 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 00:04:03 -0800 Subject: [PATCH 107/381] ecdsa_adaptor: add support for proof of discrete logarithm equality This commit adds proving and verification functions for discrete logarithm equality. From the spec (https://github.com/discreetlogcontracts/dlcspecs/pull/114): "As part of the ECDSA adaptor signature a proof of discrete logarithm equality must be provided. This is a proof that the discrete logarithm of some X to the standard base G is the same as the discrete logarithm of some Z to the base Y. This proof can be constructed by using equality composition on two Sigma protocols proving knowledge of the discrete logarithm between both pairs of points. In other words the prover proves knowledge of a such that X = a * G and b such that Z = b * Y and that a = b. We make the resulting Sigma protocol non-interactive by applying the Fiat-Shamir transformation with SHA256 as the challenge hash." --- src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/dleq_impl.h | 158 ++++++++++++++++++ src/modules/ecdsa_adaptor/main_impl.h | 4 + 3 files changed, 163 insertions(+) create mode 100644 src/modules/ecdsa_adaptor/dleq_impl.h diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include index 31c881a3..d48d028c 100644 --- a/src/modules/ecdsa_adaptor/Makefile.am.include +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -1,2 +1,3 @@ include_HEADERS += include/secp256k1_ecdsa_adaptor.h noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h +noinst_HEADERS += src/modules/ecdsa_adaptor/dleq_impl.h diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h new file mode 100644 index 00000000..da764ee7 --- /dev/null +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -0,0 +1,158 @@ +#ifndef SECP256K1_DLEQ_IMPL_H +#define SECP256K1_DLEQ_IMPL_H + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("DLEQ")||SHA256("DLEQ"). */ +static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x8cc4beacul; + sha->s[1] = 0x2e011f3ful; + sha->s[2] = 0x355c75fbul; + sha->s[3] = 0x3ba6a2c5ul; + sha->s[4] = 0xe96f3aeful; + sha->s[5] = 0x180530fdul; + sha->s[6] = 0x94582499ul; + sha->s[7] = 0x577fd564ul; + + sha->bytes = 64; +} + +/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ +static const unsigned char dleq_algo[4] = "DLEQ"; + +static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { + unsigned char buf[33]; + size_t size = 33; + + if (!secp256k1_eckey_pubkey_serialize(p, buf, &size, 1)) { + return 0; + } + + secp256k1_sha256_write(sha, buf, size); + return 1; +} + +static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, const unsigned char *gen2_33, const unsigned char *p1_33, const unsigned char *p2_33, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_sha256 sha; + unsigned char buf[32]; + unsigned char nonce[32]; + size_t size = 33; + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_ecdsa_adaptor; + } + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, p1_33, size); + secp256k1_sha256_write(&sha, p2_33, size); + secp256k1_sha256_finalize(&sha, buf); + + if (!noncefp(nonce, buf, sk32, gen2_33, dleq_algo, sizeof(dleq_algo), ndata)) { + return 0; + } + secp256k1_scalar_set_b32(k, nonce, NULL); + if (secp256k1_scalar_is_zero(k)) { + return 0; + } + + return 1; +} + +/* Generates a challenge as defined in the DLC Specification at + * https://github.com/discreetlogcontracts/dlcspecs */ +static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *gen2, secp256k1_ge *r1, secp256k1_ge *r2, secp256k1_ge *p1, secp256k1_ge *p2) { + unsigned char buf[32]; + secp256k1_sha256 sha; + + secp256k1_nonce_function_dleq_sha256_tagged(&sha); + secp256k1_dleq_hash_point(&sha, p1); + secp256k1_dleq_hash_point(&sha, gen2); + secp256k1_dleq_hash_point(&sha, p2); + secp256k1_dleq_hash_point(&sha, r1); + secp256k1_dleq_hash_point(&sha, r2); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(e, buf, NULL); +} + +/* P1 = x*G, P2 = x*Y */ +static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *p1, secp256k1_ge *p2, const secp256k1_scalar *sk, const secp256k1_ge *gen2) { + secp256k1_gej p1j, p2j; + + secp256k1_ecmult_gen(ecmult_gen_ctx, &p1j, sk); + secp256k1_ge_set_gej(p1, &p1j); + secp256k1_ecmult_const(&p2j, gen2, sk, 256); + secp256k1_ge_set_gej(p2, &p2j); +} + +/* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the + * same as the discrete logarithm of P2 to the base Y */ +static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *gen2, secp256k1_ge *p1, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_ge r1, r2; + secp256k1_scalar k = { 0 }; + unsigned char sk32[32]; + unsigned char gen2_33[33]; + unsigned char p1_33[33]; + unsigned char p2_33[33]; + int ret = 1; + size_t pubkey_size = 33; + + secp256k1_scalar_get_b32(sk32, sk); + if (!secp256k1_eckey_pubkey_serialize(gen2, gen2_33, &pubkey_size, 1)) { + return 0; + } + if (!secp256k1_eckey_pubkey_serialize(p1, p1_33, &pubkey_size, 1)) { + return 0; + } + if (!secp256k1_eckey_pubkey_serialize(p2, p2_33, &pubkey_size, 1)) { + return 0; + } + + ret &= secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, noncefp, ndata); + /* R1 = k*G, R2 = k*Y */ + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &r1, &r2, &k, gen2); + /* 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(p1, gen2, p2, r1, r2) */ + /* s = k + e * sk */ + secp256k1_dleq_challenge(e, gen2, &r1, &r2, p1, p2); + secp256k1_scalar_mul(s, e, sk); + secp256k1_scalar_add(s, s, &k); + + secp256k1_scalar_clear(&k); + return ret; +} + +static int secp256k1_dleq_verify(const secp256k1_ecmult_context *ecmult_ctx, const secp256k1_scalar *s, const secp256k1_scalar *e, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2) { + secp256k1_scalar e_neg; + secp256k1_scalar e_expected; + secp256k1_gej gen2j; + secp256k1_gej p1j, p2j; + secp256k1_gej r1j, r2j; + secp256k1_ge r1, r2; + secp256k1_gej tmpj; + + secp256k1_gej_set_ge(&p1j, p1); + secp256k1_gej_set_ge(&p2j, p2); + + secp256k1_scalar_negate(&e_neg, e); + /* R1 = s*G - e*P1 */ + secp256k1_ecmult(ecmult_ctx, &r1j, &p1j, &e_neg, s); + /* R2 = s*gen2 - e*P2 */ + secp256k1_ecmult(ecmult_ctx, &tmpj, &p2j, &e_neg, &secp256k1_scalar_zero); + secp256k1_gej_set_ge(&gen2j, gen2); + secp256k1_ecmult(ecmult_ctx, &r2j, &gen2j, s, &secp256k1_scalar_zero); + secp256k1_gej_add_var(&r2j, &r2j, &tmpj, NULL); + + secp256k1_ge_set_gej(&r1, &r1j); + secp256k1_ge_set_gej(&r2, &r2j); + secp256k1_dleq_challenge(&e_expected, gen2, &r1, &r2, p1, p2); + + secp256k1_scalar_add(&e_expected, &e_expected, &e_neg); + return secp256k1_scalar_is_zero(&e_expected); +} + +#endif diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index e7426f59..005baf0e 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H #include "include/secp256k1_ecdsa_adaptor.h" +#include "modules/ecdsa_adaptor/dleq_impl.h" /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ @@ -69,6 +70,9 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c if (algolen == sizeof(ecdsa_adaptor_algo) && secp256k1_memcmp_var(algo, ecdsa_adaptor_algo, algolen) == 0) { secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha); + } else if (algolen == sizeof(dleq_algo) + && secp256k1_memcmp_var(algo, dleq_algo, algolen) == 0) { + secp256k1_nonce_function_dleq_sha256_tagged(&sha); } else { secp256k1_sha256_initialize_tagged(&sha, algo, algolen); } From cc82ad5ab743c6c74793d1e5cd5cee6f60175a53 Mon Sep 17 00:00:00 2001 From: Sanket Kanjalkar Date: Wed, 24 Mar 2021 01:44:15 -0700 Subject: [PATCH 108/381] Make function argument name consistent with doc --- include/secp256k1_generator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index 5b5ee647..cb55af91 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -82,7 +82,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blinded( const secp256k1_context* ctx, secp256k1_generator* gen, - const unsigned char *key32, + const unsigned char *seed32, const unsigned char *blind32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); From 6955af5ca8930aa674e5fdbc4343e722b25e0ca8 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 00:10:05 -0800 Subject: [PATCH 109/381] ecdsa_adaptor: add ECDSA adaptor signature APIs This commit adds the ECDSA adaptor signature APIs: - Encrypted Signing Creates an adaptor signature, which includes a proof to verify the adaptor signature. - Encryption Verification Verifies that the adaptor decryption key can be extracted from the adaptor signature and the completed ECDSA signature. - Signature Decryption Derives an ECDSA signature from an adaptor signature and an adaptor decryption key. - Key Recovery Extracts the adaptor decryption key from the complete signature and the adaptor signature. --- include/secp256k1_ecdsa_adaptor.h | 111 ++++++++++- src/modules/ecdsa_adaptor/main_impl.h | 271 ++++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index 7bc43277..64784a4f 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -10,7 +10,22 @@ extern "C" { * Lloyd Fournier * (https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-November/002316.html * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). -*/ + * + * WARNING! DANGER AHEAD! + * As mentioned in Lloyd Fournier's paper, the adaptor signature leaks the + * Elliptic-curve Diffie–Hellman (ECDH) key between the signing key and the + * encryption key. This is not a problem for ECDSA adaptor signatures + * themselves, but may result in a complete loss of security when they are + * composed with other schemes. More specifically, let us refer to the + * signer's public key as X = x*G, and to the encryption key as Y = y*G. + * Given X, Y and the adaptor signature, it is trivial to compute Y^x = X^y. + * + * A defense is to not reuse the signing key of ECDSA adaptor signatures in + * protocols that rely on the hardness of the CDH problem, e.g., Diffie-Hellman + * key exchange and ElGamal encryption. In general, it is a well-established + * cryptographic practice to seperate keys for different purposes whenever + * possible. + */ /** A pointer to a function to deterministically generate a nonce. * @@ -46,6 +61,100 @@ typedef int (*secp256k1_nonce_function_hardened_ecdsa_adaptor)( */ SECP256K1_API extern const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor; +/** Encrypted Signing + * + * Creates an adaptor signature, which includes a proof to verify the adaptor + * signature. + * WARNING: Make sure you have read and understood the WARNING at the top of + * this file and applied the suggested countermeasures. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object, initialized for signing + * Out: adaptor_sig162: pointer to 162 byte to store the returned signature + * In: seckey32: pointer to 32 byte secret key that will be used for + * signing + * enckey: pointer to the encryption public key + * msg32: pointer to the 32-byte message hash to sign + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_ecdsa_adaptor is used + * ndata: pointer to arbitrary data used by the nonce generation + * function (can be NULL). If it is non-NULL and + * secp256k1_nonce_function_ecdsa_adaptor is used, then + * ndata must be a pointer to 32-byte auxiliary randomness + * as per BIP-340. + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_encrypt( + const secp256k1_context* ctx, + unsigned char *adaptor_sig162, + unsigned char *seckey32, + const secp256k1_pubkey *enckey, + const unsigned char *msg32, + secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Encryption Verification + * + * Verifies that the adaptor decryption key can be extracted from the adaptor signature + * and the completed ECDSA signature. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object, initialized for verification + * In: adaptor_sig162: pointer to 162-byte signature to verify + * pubkey: pointer to the public key corresponding to the secret key + * used for signing + * msg32: pointer to the 32-byte message hash being verified + * enckey: pointer to the adaptor encryption public key + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_verify( + const secp256k1_context* ctx, + const unsigned char *adaptor_sig162, + const secp256k1_pubkey *pubkey, + const unsigned char *msg32, + const secp256k1_pubkey *enckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Signature Decryption + * + * Derives an ECDSA signature from an adaptor signature and an adaptor decryption key. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to the ECDSA signature to create + * In: deckey32: pointer to 32-byte decryption secret key for the adaptor + * encryption public key + * adaptor_sig162: pointer to 162-byte adaptor sig + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_decrypt( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *deckey32, + const unsigned char *adaptor_sig162 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decryption Key Recovery + * + * Extracts the adaptor decryption key from the complete signature and the adaptor + * signature. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object, initialized for signing + * Out: deckey32: pointer to 32-byte adaptor decryption key for the adaptor + * encryption public key + * In: sig: pointer to ECDSA signature to recover the adaptor decryption + * key from + * adaptor_sig162: pointer to adaptor signature to recover the adaptor + * decryption key from + * enckey: pointer to the adaptor encryption public key + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_recover( + const secp256k1_context* ctx, + unsigned char *deckey32, + const secp256k1_ecdsa_signature *sig, + const unsigned char *adaptor_sig162, + const secp256k1_pubkey *enckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 005baf0e..18e6132d 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -10,6 +10,61 @@ #include "include/secp256k1_ecdsa_adaptor.h" #include "modules/ecdsa_adaptor/dleq_impl.h" +/* (R, R', s', dleq_proof) */ +static int secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { + size_t size = 33; + + if (!secp256k1_eckey_pubkey_serialize(r, adaptor_sig162, &size, 1)) { + return 0; + } + if (!secp256k1_eckey_pubkey_serialize(rp, &adaptor_sig162[33], &size, 1)) { + return 0; + } + secp256k1_scalar_get_b32(&adaptor_sig162[66], sp); + secp256k1_scalar_get_b32(&adaptor_sig162[98], dleq_proof_e); + secp256k1_scalar_get_b32(&adaptor_sig162[130], dleq_proof_s); + + return 1; +} + +static int secp256k1_ecdsa_adaptor_sig_deserialize(secp256k1_ge *r, secp256k1_scalar *sigr, secp256k1_ge *rp, secp256k1_scalar *sp, secp256k1_scalar *dleq_proof_e, secp256k1_scalar *dleq_proof_s, const unsigned char *adaptor_sig162) { + /* If r is deserialized, require that a sigr is provided to receive + * the X-coordinate */ + VERIFY_CHECK((r == NULL) || (r != NULL && sigr != NULL)); + if (r != NULL) { + if (!secp256k1_eckey_pubkey_parse(r, &adaptor_sig162[0], 33)) { + return 0; + } + } + if (sigr != NULL) { + secp256k1_scalar_set_b32(sigr, &adaptor_sig162[1], NULL); + if (secp256k1_scalar_is_zero(sigr)) { + return 0; + } + } + if (rp != NULL) { + if (!secp256k1_eckey_pubkey_parse(rp, &adaptor_sig162[33], 33)) { + return 0; + } + } + if (sp != NULL) { + if (!secp256k1_scalar_set_b32_seckey(sp, &adaptor_sig162[66])) { + return 0; + } + } + if (dleq_proof_e != NULL) { + secp256k1_scalar_set_b32(dleq_proof_e, &adaptor_sig162[98], NULL); + } + if (dleq_proof_s != NULL) { + int overflow; + secp256k1_scalar_set_b32(dleq_proof_s, &adaptor_sig162[130], &overflow); + if (overflow) { + return 0; + } + } + return 1; +} + /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) { @@ -91,4 +146,220 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor = nonce_function_ecdsa_adaptor; +int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char *adaptor_sig162, unsigned char *seckey32, const secp256k1_pubkey *enckey, const unsigned char *msg32, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_scalar k; + secp256k1_gej rj, rpj; + secp256k1_ge r, rp; + secp256k1_ge enckey_ge; + secp256k1_scalar dleq_proof_s; + secp256k1_scalar dleq_proof_e; + secp256k1_scalar sk; + secp256k1_scalar msg; + secp256k1_scalar sp; + secp256k1_scalar sigr; + secp256k1_scalar n; + unsigned char nonce32[32] = { 0 }; + unsigned char buf33[33]; + size_t size = 33; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(enckey != NULL); + ARG_CHECK(msg32 != NULL); + + secp256k1_scalar_clear(&dleq_proof_e); + secp256k1_scalar_clear(&dleq_proof_s); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_ecdsa_adaptor; + } + + ret &= secp256k1_pubkey_load(ctx, &enckey_ge, enckey); + ret &= secp256k1_eckey_pubkey_serialize(&enckey_ge, buf33, &size, 1); + ret &= !!noncefp(nonce32, msg32, seckey32, buf33, ecdsa_adaptor_algo, sizeof(ecdsa_adaptor_algo), ndata); + secp256k1_scalar_set_b32(&k, nonce32, NULL); + ret &= !secp256k1_scalar_is_zero(&k); + secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); + + /* R' := k*G */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rpj, &k); + secp256k1_ge_set_gej(&rp, &rpj); + /* R = k*Y; */ + secp256k1_ecmult_const(&rj, &enckey_ge, &k, 256); + secp256k1_ge_set_gej(&r, &rj); + /* We declassify the non-secret values rp and r to allow using them + * as branch points. */ + secp256k1_declassify(ctx, &rp, sizeof(rp)); + secp256k1_declassify(ctx, &r, sizeof(r)); + + /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ + ret &= secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &rp, &r, noncefp, ndata); + + ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey32); + secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret); + secp256k1_scalar_set_b32(&msg, msg32, NULL); + secp256k1_fe_normalize(&r.x); + secp256k1_fe_get_b32(buf33, &r.x); + secp256k1_scalar_set_b32(&sigr, buf33, NULL); + ret &= !secp256k1_scalar_is_zero(&sigr); + /* s' = k⁻¹(m + R.x * x) */ + secp256k1_scalar_mul(&n, &sigr, &sk); + secp256k1_scalar_add(&n, &n, &msg); + secp256k1_scalar_inverse(&sp, &k); + secp256k1_scalar_mul(&sp, &sp, &n); + ret &= !secp256k1_scalar_is_zero(&sp); + + /* return (R, R', s', dleq_proof) */ + ret &= secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); + + secp256k1_memczero(adaptor_sig162, 162, !ret); + secp256k1_scalar_clear(&n); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&sk); + + return ret; +} + +int secp256k1_ecdsa_adaptor_verify(const secp256k1_context* ctx, const unsigned char *adaptor_sig162, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_pubkey *enckey) { + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_scalar msg; + secp256k1_ge pubkey_ge; + secp256k1_ge r, rp; + secp256k1_scalar sp; + secp256k1_scalar sigr; + secp256k1_ge enckey_ge; + secp256k1_gej derived_rp; + secp256k1_scalar sn, u1, u2; + secp256k1_gej pubkeyj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(enckey != NULL); + + if (!secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)) { + return 0; + } + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { + return 0; + } + /* DLEQ_verify((R', Y, R), dleq_proof) */ + if(!secp256k1_dleq_verify(&ctx->ecmult_ctx, &dleq_proof_s, &dleq_proof_e, &rp, &enckey_ge, &r)) { + return 0; + } + secp256k1_scalar_set_b32(&msg, msg32, NULL); + if (!secp256k1_pubkey_load(ctx, &pubkey_ge, pubkey)) { + return 0; + } + + /* return R' == s'⁻¹(m * G + R.x * X) */ + secp256k1_scalar_inverse_var(&sn, &sp); + secp256k1_scalar_mul(&u1, &sn, &msg); + secp256k1_scalar_mul(&u2, &sn, &sigr); + secp256k1_gej_set_ge(&pubkeyj, &pubkey_ge); + secp256k1_ecmult(&ctx->ecmult_ctx, &derived_rp, &pubkeyj, &u2, &u1); + if (secp256k1_gej_is_infinity(&derived_rp)) { + return 0; + } + secp256k1_gej_neg(&derived_rp, &derived_rp); + secp256k1_gej_add_ge_var(&derived_rp, &derived_rp, &rp, NULL); + return secp256k1_gej_is_infinity(&derived_rp); +} + +int secp256k1_ecdsa_adaptor_decrypt(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sig, const unsigned char *deckey32, const unsigned char *adaptor_sig162) { + secp256k1_scalar deckey; + secp256k1_scalar sp; + secp256k1_scalar s; + secp256k1_scalar sigr; + int overflow; + int high; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(deckey32 != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + + secp256k1_scalar_clear(&sp); + secp256k1_scalar_set_b32(&deckey, deckey32, &overflow); + ret &= !overflow; + ret &= secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, &sp, NULL, NULL, adaptor_sig162); + ret &= !secp256k1_scalar_is_zero(&deckey); + secp256k1_scalar_inverse(&s, &deckey); + /* s = s' * y⁻¹ */ + secp256k1_scalar_mul(&s, &s, &sp); + high = secp256k1_scalar_is_high(&s); + secp256k1_scalar_cond_negate(&s, high); + secp256k1_ecdsa_signature_save(sig, &sigr, &s); + + secp256k1_memczero(&sig->data[0], 64, !ret); + secp256k1_scalar_clear(&deckey); + secp256k1_scalar_clear(&sp); + secp256k1_scalar_clear(&s); + + return ret; +} + +int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char *deckey32, const secp256k1_ecdsa_signature *sig, const unsigned char *adaptor_sig162, const secp256k1_pubkey *enckey) { + secp256k1_scalar sp, adaptor_sigr; + secp256k1_scalar s, r; + secp256k1_scalar deckey; + secp256k1_ge enckey_expected_ge; + secp256k1_gej enckey_expected_gej; + unsigned char enckey33[33]; + unsigned char enckey_expected33[33]; + size_t size = 33; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(deckey32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(enckey != NULL); + + if (!secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &adaptor_sigr, NULL, &sp, NULL, NULL, adaptor_sig162)) { + return 0; + } + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + /* Check that we're not looking at some unrelated signature */ + ret &= secp256k1_scalar_eq(&adaptor_sigr, &r); + /* y = s⁻¹ * s' */ + ret &= !secp256k1_scalar_is_zero(&s); + secp256k1_scalar_inverse(&deckey, &s); + secp256k1_scalar_mul(&deckey, &deckey, &sp); + + /* Deal with ECDSA malleability */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &enckey_expected_gej, &deckey); + secp256k1_ge_set_gej(&enckey_expected_ge, &enckey_expected_gej); + /* We declassify non-secret enckey_expected_ge to allow using it as a + * branch point. */ + secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge)); + if (!secp256k1_eckey_pubkey_serialize(&enckey_expected_ge, enckey_expected33, &size, SECP256K1_EC_COMPRESSED)) { + return 0; + } + if (!secp256k1_ec_pubkey_serialize(ctx, enckey33, &size, enckey, SECP256K1_EC_COMPRESSED)) { + return 0; + } + if (secp256k1_memcmp_var(&enckey_expected33[1], &enckey33[1], 32) != 0) { + return 0; + } + if (enckey_expected33[0] != enckey33[0]) { + /* try Y_implied == -Y */ + secp256k1_scalar_negate(&deckey, &deckey); + } + secp256k1_scalar_get_b32(deckey32, &deckey); + + secp256k1_scalar_clear(&deckey); + secp256k1_scalar_clear(&sp); + secp256k1_scalar_clear(&s); + + return ret; +} + #endif From b0ffa923199c45f717adf3fb003bcef796259032 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 01:03:43 -0800 Subject: [PATCH 110/381] ecdsa_adaptor: add tests This commit adds test coverage including Cirrus scripts, Valgrind constant time tests for secret data, API tests, nonce function tests, and test vectors from the spec. --- .cirrus.yml | 11 +- ci/cirrus.sh | 2 +- src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/tests_impl.h | 1221 +++++++++++++++++ src/tests.c | 8 + src/valgrind_ctime_test.c | 42 + 6 files changed, 1281 insertions(+), 4 deletions(-) create mode 100644 src/modules/ecdsa_adaptor/tests_impl.h diff --git a/.cirrus.yml b/.cirrus.yml index 151227da..28a5d323 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -17,6 +17,7 @@ env: RANGEPROOF: no WHITELIST: no MUSIG: no + ECDSAADAPTOR: no EXPERIMENTAL: no CTIMETEST: yes BENCH: yes @@ -59,13 +60,13 @@ task: memory: 1G matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} - env: {WIDEMUL: int128} - env: {WIDEMUL: int128, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes} + - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} - env: {WIDEMUL: int128, ASM: x86_64} - env: {BIGNUM: no} - - env: {BIGNUM: no, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes} + - env: {BIGNUM: no, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} - env: {BIGNUM: no, STATICPRECOMPUTATION: no} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} @@ -85,6 +86,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes CTIMETEST: no - env: { ECMULTGENPRECISION: 2 } - env: { ECMULTGENPRECISION: 8 } @@ -101,6 +103,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes EXTRAFLAGS: "--disable-openssl-tests" BUILD: matrix: @@ -130,6 +133,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes matrix: - env: CC: i686-linux-gnu-gcc @@ -227,6 +231,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes CTIMETEST: no << : *MERGE_BASE test_script: diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 63a337f4..785a522a 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -19,7 +19,7 @@ valgrind --version || true --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-ecdsa-s2c="$ECDSA_S2C" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG"\ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --with-valgrind="$WITH_VALGRIND" \ --host="$HOST" $EXTRAFLAGS diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include index d48d028c..e855a17a 100644 --- a/src/modules/ecdsa_adaptor/Makefile.am.include +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -1,3 +1,4 @@ include_HEADERS += include/secp256k1_ecdsa_adaptor.h noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h noinst_HEADERS += src/modules/ecdsa_adaptor/dleq_impl.h +noinst_HEADERS += src/modules/ecdsa_adaptor/tests_impl.h diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h new file mode 100644 index 00000000..5a12bb74 --- /dev/null +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -0,0 +1,1221 @@ +#ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H +#define SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H + +#include "include/secp256k1_ecdsa_adaptor.h" + +void rand_scalar(secp256k1_scalar *scalar) { + unsigned char buf32[32]; + secp256k1_testrand256(buf32); + secp256k1_scalar_set_b32(scalar, buf32, NULL); +} + +void rand_point(secp256k1_ge *point) { + secp256k1_scalar x; + secp256k1_gej pointj; + rand_scalar(&x); + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pointj, &x); + secp256k1_ge_set_gej(point, &pointj); +} + +void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { + secp256k1_scalar k1, k2; + + CHECK(secp256k1_dleq_nonce(&k1, args[0], args[1], args[2], args[3], NULL, args[4]) == 1); + secp256k1_testrand_flip(args[n_flip], n_bytes); + CHECK(secp256k1_dleq_nonce(&k2, args[0], args[1], args[2], args[3], NULL, args[4]) == 1); + CHECK(secp256k1_scalar_eq(&k1, &k2) == 0); +} + +void dleq_tests(void) { + secp256k1_scalar s, e, sk, k; + secp256k1_ge gen2, p1, p2; + unsigned char *args[5]; + unsigned char sk32[32]; + unsigned char gen2_33[33]; + unsigned char p1_33[33]; + unsigned char p2_33[33]; + unsigned char aux_rand[32]; + int i; + size_t pubkey_size = 33; + + rand_point(&gen2); + rand_scalar(&sk); + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &p1, &p2, &sk, &gen2); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &gen2, &p1, &p2, NULL, NULL) == 1); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p1, &gen2, &p2) == 1); + + { + secp256k1_scalar tmp; + secp256k1_scalar_set_int(&tmp, 1); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &tmp, &e, &p1, &gen2, &p2) == 0); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &tmp, &p1, &gen2, &p2) == 0); + } + { + secp256k1_ge p_tmp; + rand_point(&p_tmp); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p_tmp, &gen2, &p2) == 0); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p1, &p_tmp, &p2) == 0); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p1, &gen2, &p_tmp) == 0); + } + { + secp256k1_ge p_inf; + secp256k1_ge_set_infinity(&p_inf); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &p_inf, &p1, &p2, NULL, NULL) == 0); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &gen2, &p_inf, &p2, NULL, NULL) == 0); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &gen2, &p1, &p_inf, NULL, NULL) == 0); + } + + /* Nonce tests */ + secp256k1_scalar_get_b32(sk32, &sk); + CHECK(secp256k1_eckey_pubkey_serialize(&gen2, gen2_33, &pubkey_size, 1)); + CHECK(secp256k1_eckey_pubkey_serialize(&p1, p1_33, &pubkey_size, 1)); + CHECK(secp256k1_eckey_pubkey_serialize(&p2, p2_33, &pubkey_size, 1)); + CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); + + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, sk32, sizeof(sk32)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, gen2_33, sizeof(gen2_33)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, p1_33, sizeof(p1_33)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, p2_33, sizeof(p2_33)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = sk32; + args[1] = gen2_33; + args[2] = p1_33; + args[3] = p2_33; + args[4] = aux_rand; + for (i = 0; i < count; i++) { + dleq_nonce_bitflip(args, 0, sizeof(sk32)); + dleq_nonce_bitflip(args, 1, sizeof(gen2_33)); + dleq_nonce_bitflip(args, 2, sizeof(p1_33)); + /* Flip p2 */ + dleq_nonce_bitflip(args, 3, sizeof(p2_33)); + /* Flip p2 again */ + dleq_nonce_bitflip(args, 3, sizeof(p2_33)); + dleq_nonce_bitflip(args, 4, sizeof(aux_rand)); + } + + /* NULL aux_rand argument is allowed. */ + CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); +} + +void rand_flip_bit(unsigned char *array, size_t n) { + array[secp256k1_testrand_int(n)] ^= 1 << secp256k1_testrand_int(8); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the adaptor signature is valid for the public and encryption keys. */ +void test_ecdsa_adaptor_spec_vectors_check_verify(const unsigned char *adaptor_sig162, const unsigned char *msg32, const unsigned char *pubkey33, const unsigned char *encryption_key33, int expected) { + secp256k1_pubkey pubkey; + secp256k1_ge pubkey_ge; + secp256k1_pubkey encryption_key; + secp256k1_ge encryption_key_ge; + + CHECK(secp256k1_eckey_pubkey_parse(&encryption_key_ge, encryption_key33, 33) == 1); + secp256k1_pubkey_save(&encryption_key, &encryption_key_ge); + CHECK(secp256k1_eckey_pubkey_parse(&pubkey_ge, pubkey33, 33) == 1); + secp256k1_pubkey_save(&pubkey, &pubkey_ge); + + CHECK(expected == secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig162, &pubkey, msg32, &encryption_key)); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the signature can be decrypted from the adaptor signature and the decryption key. */ +void test_ecdsa_adaptor_spec_vectors_check_decrypt(const unsigned char *adaptor_sig162, const unsigned char *decryption_key32, const unsigned char *signature64, int expected) { + unsigned char signature[64]; + secp256k1_ecdsa_signature s; + + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &s, decryption_key32, adaptor_sig162) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &s) == 1); + + CHECK(expected == !(secp256k1_memcmp_var(signature, signature64, 64))); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the decryption key can be recovered from the adaptor signature, encryption key, and the signature. */ +void test_ecdsa_adaptor_spec_vectors_check_recover(const unsigned char *adaptor_sig162, const unsigned char *encryption_key33, const unsigned char *decryption_key32, const unsigned char *signature64, int expected) { + unsigned char deckey32[32] = { 0 }; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey encryption_key; + secp256k1_ge encryption_key_ge; + + CHECK(secp256k1_eckey_pubkey_parse(&encryption_key_ge, encryption_key33, 33) == 1); + secp256k1_pubkey_save(&encryption_key, &encryption_key_ge); + + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature64) == 1); + CHECK(expected == secp256k1_ecdsa_adaptor_recover(ctx, deckey32, &sig, adaptor_sig162, &encryption_key)); + if (decryption_key32 != NULL) { + CHECK(expected == !(secp256k1_memcmp_var(deckey32, decryption_key32, 32))); + } +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks deserialization and serialization. */ +void test_ecdsa_adaptor_spec_vectors_check_serialization(const unsigned char *adaptor_sig162, int expected) { + unsigned char buf[162]; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_ge r, rp; + secp256k1_scalar sp; + secp256k1_scalar sigr; + + CHECK(expected == secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)); + if (expected == 1) { + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(buf, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 1); + CHECK(secp256k1_memcmp_var(buf, adaptor_sig162, 162) == 0); + } +} + +/* Test vectors according to ECDSA adaptor signature spec. See + * https://github.com/discreetlogcontracts/dlcspecs/blob/596a177375932a47306f07e7385f398f52519a83/test/ecdsa_adaptor.json. */ +void test_ecdsa_adaptor_spec_vectors(void) { + { + /* Test vector 0 */ + /* kind: verification test */ + /* plain valid adaptor signature */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, + 0x8a, 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, + 0x12, 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, + 0x7a, 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, + 0x73, 0x02, 0x23, 0xf3, 0x25, 0x04, 0x2f, 0xce, + 0x53, 0x5d, 0x04, 0x0f, 0xee, 0x52, 0xec, 0x13, + 0x23, 0x1b, 0xf7, 0x09, 0xcc, 0xd8, 0x42, 0x33, + 0xc6, 0x94, 0x4b, 0x90, 0x31, 0x7e, 0x62, 0x52, + 0x8b, 0x25, 0x27, 0xdf, 0xf9, 0xd6, 0x59, 0xa9, + 0x6d, 0xb4, 0xc9, 0x9f, 0x97, 0x50, 0x16, 0x83, + 0x08, 0x63, 0x3c, 0x18, 0x67, 0xb7, 0x0f, 0x3a, + 0x18, 0xfb, 0x0f, 0x45, 0x39, 0xa1, 0xae, 0xce, + 0xdc, 0xd1, 0xfc, 0x01, 0x48, 0xfc, 0x22, 0xf3, + 0x6b, 0x63, 0x03, 0x08, 0x3e, 0xce, 0x3f, 0x87, + 0x2b, 0x18, 0xe3, 0x5d, 0x36, 0x8b, 0x39, 0x58, + 0xef, 0xe5, 0xfb, 0x08, 0x1f, 0x77, 0x16, 0x73, + 0x6c, 0xcb, 0x59, 0x8d, 0x26, 0x9a, 0xa3, 0x08, + 0x4d, 0x57, 0xe1, 0x85, 0x5e, 0x1e, 0xa9, 0xa4, + 0x5e, 0xfc, 0x10, 0x46, 0x3b, 0xbf, 0x32, 0xae, + 0x37, 0x80, 0x29, 0xf5, 0x76, 0x3c, 0xeb, 0x40, + 0x17, 0x3f + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0xc2, 0x66, 0x2c, 0x97, 0x48, 0x8b, 0x07, + 0xb6, 0xe8, 0x19, 0x12, 0x4b, 0x89, 0x89, 0x84, + 0x92, 0x06, 0x33, 0x4a, 0x4c, 0x2f, 0xbd, 0xf6, + 0x91, 0xf7, 0xb3, 0x4d, 0x2b, 0x16, 0xe9, 0xc2, + 0x93 + }; + const unsigned char decryption_key[32] = { + 0x0b, 0x2a, 0xba, 0x63, 0xb8, 0x85, 0xa0, 0xf0, + 0xe9, 0x6f, 0xa0, 0xf3, 0x03, 0x92, 0x0c, 0x7f, + 0xb7, 0x43, 0x1d, 0xdf, 0xa9, 0x43, 0x76, 0xad, + 0x94, 0xd9, 0x69, 0xfb, 0xf4, 0x10, 0x9d, 0xc8 + }; + const unsigned char signature[64] = { + 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, 0x8a, + 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, 0x12, + 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, 0x7a, + 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, 0x73, + 0x29, 0xe8, 0x0e, 0x0e, 0xe6, 0x0e, 0x57, 0xaf, + 0x3e, 0x62, 0x5b, 0xba, 0xe1, 0x67, 0x2b, 0x1e, + 0xca, 0xa5, 0x8e, 0xff, 0xe6, 0x13, 0x42, 0x6b, + 0x02, 0x4f, 0xa1, 0x62, 0x1d, 0x90, 0x33, 0x94 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 1); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 1 */ + /* verification test */ + /* the decrypted signature is high so it must be negated first + * AND the extracted decryption key must be negated */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x60, 0x35, 0xc8, 0x98, 0x60, 0xec, 0x62, + 0xad, 0x15, 0x3f, 0x69, 0xb5, 0xb3, 0x07, 0x7b, + 0xcd, 0x08, 0xfb, 0xb0, 0xd2, 0x8d, 0xc7, 0xf7, + 0xf6, 0xdf, 0x4a, 0x05, 0xcc, 0xa3, 0x54, 0x55, + 0xbe, 0x03, 0x70, 0x43, 0xb6, 0x3c, 0x56, 0xf6, + 0x31, 0x7d, 0x99, 0x28, 0xe8, 0xf9, 0x10, 0x07, + 0x33, 0x57, 0x48, 0xc4, 0x98, 0x24, 0x22, 0x0d, + 0xb1, 0x4a, 0xd1, 0x0d, 0x80, 0xa5, 0xd0, 0x0a, + 0x96, 0x54, 0xaf, 0x09, 0x96, 0xc1, 0x82, 0x4c, + 0x64, 0xc9, 0x0b, 0x95, 0x1b, 0xb2, 0x73, 0x4a, + 0xae, 0xcf, 0x78, 0xd4, 0xb3, 0x61, 0x31, 0xa4, + 0x72, 0x38, 0xc3, 0xfa, 0x2b, 0xa2, 0x5e, 0x2c, + 0xed, 0x54, 0x25, 0x5b, 0x06, 0xdf, 0x69, 0x6d, + 0xe1, 0x48, 0x3c, 0x37, 0x67, 0x24, 0x2a, 0x37, + 0x28, 0x82, 0x6e, 0x05, 0xf7, 0x9e, 0x39, 0x81, + 0xe1, 0x25, 0x53, 0x35, 0x5b, 0xba, 0x8a, 0x01, + 0x31, 0xcd, 0x37, 0x0e, 0x63, 0xe3, 0xda, 0x73, + 0x10, 0x6f, 0x63, 0x85, 0x76, 0xa5, 0xaa, 0xb0, + 0xea, 0x6d, 0x45, 0xc0, 0x42, 0x57, 0x4c, 0x0c, + 0x8d, 0x0b, 0x14, 0xb8, 0xc7, 0xc0, 0x1c, 0xfe, + 0x90, 0x72 + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0x4e, 0xee, 0x18, 0xbe, 0x9a, 0x5a, 0x52, + 0x24, 0x00, 0x0f, 0x91, 0x6c, 0x80, 0xb3, 0x93, + 0x44, 0x79, 0x89, 0xe7, 0x19, 0x4b, 0xc0, 0xb0, + 0xf1, 0xad, 0x7a, 0x03, 0x36, 0x97, 0x02, 0xbb, + 0x51 + }; + const unsigned char decryption_key[32] = { + 0xdb, 0x2d, 0xeb, 0xdd, 0xb0, 0x02, 0x47, 0x3a, + 0x00, 0x1d, 0xd7, 0x0b, 0x06, 0xf6, 0xc9, 0x7b, + 0xdc, 0xd1, 0xc4, 0x6b, 0xa1, 0x00, 0x12, 0x37, + 0xfe, 0x0e, 0xe1, 0xae, 0xff, 0xb2, 0xb6, 0xc4 + }; + const unsigned char signature[64] = { + 0x60, 0x35, 0xc8, 0x98, 0x60, 0xec, 0x62, 0xad, + 0x15, 0x3f, 0x69, 0xb5, 0xb3, 0x07, 0x7b, 0xcd, + 0x08, 0xfb, 0xb0, 0xd2, 0x8d, 0xc7, 0xf7, 0xf6, + 0xdf, 0x4a, 0x05, 0xcc, 0xa3, 0x54, 0x55, 0xbe, + 0x4c, 0xea, 0xcf, 0x92, 0x15, 0x46, 0xc0, 0x3d, + 0xd1, 0xbe, 0x59, 0x67, 0x23, 0xad, 0x1e, 0x76, + 0x91, 0xbd, 0xac, 0x73, 0xd8, 0x8c, 0xc3, 0x6c, + 0x42, 0x1c, 0x5e, 0x7f, 0x08, 0x38, 0x43, 0x05 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 1); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 2 */ + /* verification test */ + /* proof is wrong */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xf9, 0x4d, 0xca, 0x20, 0x6d, 0x75, 0x82, + 0xc0, 0x15, 0xfb, 0x9b, 0xff, 0xe4, 0xe4, 0x3b, + 0x14, 0x59, 0x1b, 0x30, 0xef, 0x7d, 0x2b, 0x46, + 0x4d, 0x10, 0x3e, 0xc5, 0xe1, 0x16, 0x59, 0x5d, + 0xba, 0x03, 0x12, 0x7f, 0x8a, 0xc3, 0x53, 0x3d, + 0x24, 0x92, 0x80, 0x33, 0x24, 0x74, 0x33, 0x90, + 0x00, 0x92, 0x2e, 0xb6, 0xa5, 0x8e, 0x3b, 0x9b, + 0xf4, 0xfc, 0x7e, 0x01, 0xe4, 0xb4, 0xdf, 0x2b, + 0x7a, 0x41, 0x00, 0xa1, 0xe0, 0x89, 0xf1, 0x6e, + 0x5d, 0x70, 0xbb, 0x89, 0xf9, 0x61, 0x51, 0x6f, + 0x1d, 0xe0, 0x68, 0x4c, 0xc7, 0x9d, 0xb9, 0x78, + 0x49, 0x5d, 0xf2, 0xf3, 0x99, 0xb0, 0xd0, 0x1e, + 0xd7, 0x24, 0x0f, 0xa6, 0xe3, 0x25, 0x2a, 0xed, + 0xb5, 0x8b, 0xdc, 0x6b, 0x58, 0x77, 0xb0, 0xc6, + 0x02, 0x62, 0x8a, 0x23, 0x5d, 0xd1, 0xcc, 0xae, + 0xbd, 0xdd, 0xcb, 0xe9, 0x61, 0x98, 0xc0, 0xc2, + 0x1b, 0xea, 0xd7, 0xb0, 0x5f, 0x42, 0x3b, 0x67, + 0x3d, 0x14, 0xd2, 0x06, 0xfa, 0x15, 0x07, 0xb2, + 0xdb, 0xe2, 0x72, 0x2a, 0xf7, 0x92, 0xb8, 0xc2, + 0x66, 0xfc, 0x25, 0xa2, 0xd9, 0x01, 0xd7, 0xe2, + 0xc3, 0x35 + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0x14, 0xcc, 0xb7, 0x56, 0x24, 0x9a, 0xd6, + 0xe7, 0x33, 0xc8, 0x02, 0x85, 0xea, 0x7a, 0xc2, + 0xee, 0x12, 0xff, 0xeb, 0xbc, 0xee, 0x4e, 0x55, + 0x6e, 0x68, 0x10, 0x79, 0x3a, 0x60, 0xc4, 0x5a, + 0xd4 + }; + const unsigned char decryption_key[32] = { + 0x1d, 0xfc, 0xfc, 0x08, 0x80, 0xe7, 0x25, 0x09, + 0x76, 0x8a, 0xb4, 0x6f, 0x25, 0x45, 0xb3, 0x31, + 0x68, 0xb8, 0xb8, 0xdf, 0x8e, 0x4f, 0x5f, 0xeb, + 0x50, 0x59, 0xaa, 0x37, 0x50, 0xee, 0x59, 0xd0 + }; + const unsigned char signature[64] = { + 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, 0x8a, + 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, 0x12, + 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, 0x7a, + 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, 0x73, + 0x29, 0xe8, 0x0e, 0x0e, 0xe6, 0x0e, 0x57, 0xaf, + 0x3e, 0x62, 0x5b, 0xba, 0xe1, 0x67, 0x2b, 0x1e, + 0xca, 0xa5, 0x8e, 0xff, 0xe6, 0x13, 0x42, 0x6b, + 0x02, 0x4f, 0xa1, 0x62, 0x1d, 0x90, 0x33, 0x94 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 0); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 0); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 0); + } + { + /* Test vector 3 */ + /* recovery test */ + /* plain recovery */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xf2, 0xdb, 0x6e, 0x9e, 0xd3, 0x30, 0x92, + 0xcc, 0x0b, 0x89, 0x8f, 0xd6, 0xb2, 0x82, 0xe9, + 0x9b, 0xda, 0xec, 0xcb, 0x3d, 0xe8, 0x5c, 0x2d, + 0x25, 0x12, 0xd8, 0xd5, 0x07, 0xf9, 0xab, 0xab, + 0x29, 0x02, 0x10, 0xc0, 0x1b, 0x5b, 0xed, 0x70, + 0x94, 0xa1, 0x26, 0x64, 0xae, 0xaa, 0xb3, 0x40, + 0x2d, 0x87, 0x09, 0xa8, 0xf3, 0x62, 0xb1, 0x40, + 0x32, 0x8d, 0x1b, 0x36, 0xdd, 0x7c, 0xb4, 0x20, + 0xd0, 0x2f, 0xb6, 0x6b, 0x12, 0x30, 0xd6, 0x1c, + 0x16, 0xd0, 0xcd, 0x0a, 0x2a, 0x02, 0x24, 0x6d, + 0x5a, 0xc7, 0x84, 0x8d, 0xcd, 0x6f, 0x04, 0xfe, + 0x62, 0x70, 0x53, 0xcd, 0x3c, 0x70, 0x15, 0xa7, + 0xd4, 0xaa, 0x6a, 0xc2, 0xb0, 0x43, 0x47, 0x34, + 0x8b, 0xd6, 0x7d, 0xa4, 0x3b, 0xe8, 0x72, 0x25, + 0x15, 0xd9, 0x9a, 0x79, 0x85, 0xfb, 0xfa, 0x66, + 0xf0, 0x36, 0x5c, 0x70, 0x1d, 0xe7, 0x6f, 0xf0, + 0x40, 0x0d, 0xff, 0xdc, 0x9f, 0xa8, 0x4d, 0xdd, + 0xf4, 0x13, 0xa7, 0x29, 0x82, 0x3b, 0x16, 0xaf, + 0x60, 0xaa, 0x63, 0x61, 0xbc, 0x32, 0xe7, 0xcf, + 0xd6, 0x70, 0x1e, 0x32, 0x95, 0x7c, 0x72, 0xac, + 0xe6, 0x7b + }; + const unsigned char encryption_key[33] = { + 0x02, 0x7e, 0xe4, 0xf8, 0x99, 0xbc, 0x9c, 0x5f, + 0x2b, 0x62, 0x6f, 0xa1, 0xa9, 0xb3, 0x7c, 0xe2, + 0x91, 0xc0, 0x38, 0x8b, 0x52, 0x27, 0xe9, 0x0b, + 0x0f, 0xd8, 0xf4, 0xfa, 0x57, 0x61, 0x64, 0xed, + 0xe7 + }; + const unsigned char decryption_key[32] = { + 0x9c, 0xf3, 0xea, 0x9b, 0xe5, 0x94, 0x36, 0x6b, + 0x78, 0xc4, 0x57, 0x16, 0x29, 0x08, 0xaf, 0x3c, + 0x2e, 0xa1, 0x77, 0x05, 0x81, 0x77, 0xe9, 0xc6, + 0xbf, 0x99, 0x04, 0x79, 0x27, 0x77, 0x3a, 0x06 + }; + const unsigned char signature[64] = { + 0xf2, 0xdb, 0x6e, 0x9e, 0xd3, 0x30, 0x92, 0xcc, + 0x0b, 0x89, 0x8f, 0xd6, 0xb2, 0x82, 0xe9, 0x9b, + 0xda, 0xec, 0xcb, 0x3d, 0xe8, 0x5c, 0x2d, 0x25, + 0x12, 0xd8, 0xd5, 0x07, 0xf9, 0xab, 0xab, 0x29, + 0x21, 0x81, 0x1f, 0xe7, 0xb5, 0x3b, 0xec, 0xf3, + 0xb7, 0xaf, 0xfa, 0x94, 0x42, 0xab, 0xaa, 0x93, + 0xc0, 0xab, 0x8a, 0x8e, 0x45, 0xcd, 0x7e, 0xe2, + 0xea, 0x8d, 0x25, 0x8b, 0xfc, 0x25, 0xd4, 0x64 + }; + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 4 */ + /* recovery test */ + /* the R value of the signature does not match */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xaa, 0x86, 0xd7, 0x80, 0x59, 0xa9, 0x10, + 0x59, 0xc2, 0x9e, 0xc1, 0xa7, 0x57, 0xc4, 0xdc, + 0x02, 0x9f, 0xf6, 0x36, 0xa1, 0xe6, 0xc1, 0x14, + 0x2f, 0xef, 0xe1, 0xe9, 0xd7, 0x33, 0x96, 0x17, + 0xc0, 0x03, 0xa8, 0x15, 0x3e, 0x50, 0xc0, 0xc8, + 0x57, 0x4a, 0x38, 0xd3, 0x89, 0xe6, 0x1b, 0xbb, + 0x0b, 0x58, 0x15, 0x16, 0x9e, 0x06, 0x09, 0x24, + 0xe4, 0xb5, 0xf2, 0xe7, 0x8f, 0xf1, 0x3a, 0xa7, + 0xad, 0x85, 0x8e, 0x0c, 0x27, 0xc4, 0xb9, 0xee, + 0xd9, 0xd6, 0x05, 0x21, 0xb3, 0xf5, 0x4f, 0xf8, + 0x3c, 0xa4, 0x77, 0x4b, 0xe5, 0xfb, 0x3a, 0x68, + 0x0f, 0x82, 0x0a, 0x35, 0xe8, 0x84, 0x0f, 0x4a, + 0xaf, 0x2d, 0xe8, 0x8e, 0x7c, 0x5c, 0xff, 0x38, + 0xa3, 0x7b, 0x78, 0x72, 0x59, 0x04, 0xef, 0x97, + 0xbb, 0x82, 0x34, 0x13, 0x28, 0xd5, 0x59, 0x87, + 0x01, 0x9b, 0xd3, 0x8a, 0xe1, 0x74, 0x5e, 0x3e, + 0xfe, 0x0f, 0x8e, 0xa8, 0xbd, 0xfe, 0xde, 0x0d, + 0x37, 0x8f, 0xc1, 0xf9, 0x6e, 0x94, 0x4a, 0x75, + 0x05, 0x24, 0x9f, 0x41, 0xe9, 0x37, 0x81, 0x50, + 0x9e, 0xe0, 0xba, 0xde, 0x77, 0x29, 0x0d, 0x39, + 0xcd, 0x12 + }; + const unsigned char encryption_key[33] = { + 0x03, 0x51, 0x76, 0xd2, 0x41, 0x29, 0x74, 0x1b, + 0x0f, 0xca, 0xa5, 0xfd, 0x67, 0x50, 0x72, 0x7c, + 0xe3, 0x08, 0x60, 0x44, 0x7e, 0x0a, 0x92, 0xc9, + 0xeb, 0xeb, 0xde, 0xb7, 0xc3, 0xf9, 0x39, 0x95, + 0xed + }; + const unsigned char signature[64] = { + 0xf7, 0xf7, 0xfe, 0x6b, 0xd0, 0x56, 0xfc, 0x4a, + 0xbd, 0x70, 0xd3, 0x35, 0xf7, 0x2d, 0x0a, 0xa1, + 0xe8, 0x40, 0x6b, 0xba, 0x68, 0xf3, 0xe5, 0x79, + 0xe4, 0x78, 0x94, 0x75, 0x32, 0x35, 0x64, 0xa4, + 0x52, 0xc4, 0x61, 0x76, 0xc7, 0xfb, 0x40, 0xaa, + 0x37, 0xd5, 0x65, 0x13, 0x41, 0xf5, 0x56, 0x97, + 0xda, 0xb2, 0x7d, 0x84, 0xa2, 0x13, 0xb3, 0x0c, + 0x93, 0x01, 0x1a, 0x77, 0x90, 0xba, 0xce, 0x8c + }; + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, NULL, signature, 0); + } + { + /* Test vector 5 */ + /* recovery test */ + /* recovery from high s signature */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x2c, 0x63, 0x7c, 0xd7, 0x97, 0xdd, 0x8c, + 0x2c, 0xe2, 0x61, 0x90, 0x7e, 0xd4, 0x3e, 0x82, + 0xd6, 0xd1, 0xa4, 0x8c, 0xba, 0xbb, 0xbe, 0xce, + 0x80, 0x11, 0x33, 0xdd, 0x8d, 0x70, 0xa0, 0x1b, + 0x14, 0x03, 0xeb, 0x61, 0x5a, 0x3e, 0x59, 0xb1, + 0xcb, 0xbf, 0x4f, 0x87, 0xac, 0xaf, 0x64, 0x5b, + 0xe1, 0xed, 0xa3, 0x2a, 0x06, 0x66, 0x11, 0xf3, + 0x5d, 0xd5, 0x55, 0x78, 0x02, 0x80, 0x2b, 0x14, + 0xb1, 0x9c, 0x81, 0xc0, 0x4c, 0x3f, 0xef, 0xac, + 0x57, 0x83, 0xb2, 0x07, 0x7b, 0xd4, 0x3f, 0xa0, + 0xa3, 0x9a, 0xb8, 0xa6, 0x4d, 0x4d, 0x78, 0x33, + 0x2a, 0x5d, 0x62, 0x1e, 0xa2, 0x3e, 0xca, 0x46, + 0xbc, 0x01, 0x10, 0x11, 0xab, 0x82, 0xdd, 0xa6, + 0xde, 0xb8, 0x56, 0x99, 0xf5, 0x08, 0x74, 0x4d, + 0x70, 0xd4, 0x13, 0x4b, 0xea, 0x03, 0xf7, 0x84, + 0xd2, 0x85, 0xb5, 0xc6, 0xc1, 0x5a, 0x56, 0xe4, + 0xe1, 0xfa, 0xb4, 0xbc, 0x35, 0x6a, 0xbb, 0xde, + 0xbb, 0x3b, 0x8f, 0xe1, 0xe5, 0x5e, 0x6d, 0xd6, + 0xd2, 0xa9, 0xea, 0x45, 0x7e, 0x91, 0xb2, 0xe6, + 0x64, 0x2f, 0xae, 0x69, 0xf9, 0xdb, 0xb5, 0x25, + 0x88, 0x54 + }; + const unsigned char encryption_key[33] = { + 0x02, 0x04, 0x25, 0x37, 0xe9, 0x13, 0xad, 0x74, + 0xc4, 0xbb, 0xd8, 0xda, 0x96, 0x07, 0xad, 0x3b, + 0x9c, 0xb2, 0x97, 0xd0, 0x8e, 0x01, 0x4a, 0xfc, + 0x51, 0x13, 0x30, 0x83, 0xf1, 0xbd, 0x68, 0x7a, + 0x62 + }; + const unsigned char decryption_key[32] = { + 0x32, 0x47, 0x19, 0xb5, 0x1f, 0xf2, 0x47, 0x4c, + 0x94, 0x38, 0xeb, 0x76, 0x49, 0x4b, 0x0d, 0xc0, + 0xbc, 0xce, 0xeb, 0x52, 0x9f, 0x0a, 0x54, 0x28, + 0xfd, 0x19, 0x8a, 0xd8, 0xf8, 0x86, 0xe9, 0x9c + }; + const unsigned char signature[64] = { + 0x2c, 0x63, 0x7c, 0xd7, 0x97, 0xdd, 0x8c, 0x2c, + 0xe2, 0x61, 0x90, 0x7e, 0xd4, 0x3e, 0x82, 0xd6, + 0xd1, 0xa4, 0x8c, 0xba, 0xbb, 0xbe, 0xce, 0x80, + 0x11, 0x33, 0xdd, 0x8d, 0x70, 0xa0, 0x1b, 0x14, + 0xb5, 0xf2, 0x43, 0x21, 0xf5, 0x50, 0xb7, 0xb9, + 0xdd, 0x06, 0xee, 0x4f, 0xcf, 0xd8, 0x2b, 0xda, + 0xd8, 0xb1, 0x42, 0xff, 0x93, 0xa7, 0x90, 0xcc, + 0x4d, 0x9f, 0x79, 0x62, 0xb3, 0x8c, 0x6a, 0x3b + }; + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 0); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 6 */ + /* serialization test */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 7 */ + /* serialization test */ + /* R can be above curve order */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, + 0x2c, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 8 */ + /* serialization test */ + /* R_a can be above curve order */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, + 0xfc, 0x2c, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 9 */ + /* serialization test */ + /* s_a cannot be zero */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 0); + } + { + /* Test vector 10 */ + /* serialization test */ + /* s_a too high */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, + 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, + 0x41, 0x41, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 0); + } +} + +/* Nonce function that returns constant 0 */ +static int ecdsa_adaptor_nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int ecdsa_adaptor_nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0, 32); + return 1; +} + +/* Nonce function that sets nonce to 0xFF...0xFF */ +static int ecdsa_adaptor_nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0xFF, 32); + return 1; +} + +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function + */ +void nonce_function_ecdsa_adaptor_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t algolen) { + unsigned char nonces[2][32]; + CHECK(nonce_function_ecdsa_adaptor(nonces[0], args[0], args[1], args[2], args[3], algolen, args[4]) == 1); + secp256k1_testrand_flip(args[n_flip], n_bytes); + CHECK(nonce_function_ecdsa_adaptor(nonces[1], args[0], args[1], args[2], args[3], algolen, args[4]) == 1); + CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); +} + +/* Tests for the equality of two sha256 structs. This function only produces a + * correct result if an integer multiple of 64 many bytes have been written + * into the hash functions. */ +void ecdsa_adaptor_test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + CHECK(sha1->bytes == sha2->bytes); + CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); +} + +void run_nonce_function_ecdsa_adaptor_tests(void) { + unsigned char tag[16] = "ECDSAadaptor/non"; + unsigned char aux_tag[16] = "ECDSAadaptor/aux"; + unsigned char algo[16] = "ECDSAadaptor/non"; + size_t algolen = sizeof(algo); + unsigned char dleq_tag[4] = "DLEQ"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char nonce[32]; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char pk[33]; + unsigned char aux_rand[32]; + unsigned char *args[5]; + int i; + + /* Check that hash initialized by + * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha_optimized); + ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha_optimized); + ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_nonce_function_dleq_sha256_tagged_aux has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, dleq_tag, sizeof(dleq_tag)); + secp256k1_nonce_function_dleq_sha256_tagged(&sha_optimized); + ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, msg, sizeof(msg)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, key, sizeof(key)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, pk, sizeof(pk)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = msg; + args[1] = key; + args[2] = pk; + args[3] = algo; + args[4] = aux_rand; + for (i = 0; i < count; i++) { + nonce_function_ecdsa_adaptor_bitflip(args, 0, sizeof(msg), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 1, sizeof(key), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 2, sizeof(pk), algolen); + /* Flip algo special case "ECDSAadaptor/non" */ + nonce_function_ecdsa_adaptor_bitflip(args, 3, sizeof(algo), algolen); + /* Flip algo again */ + nonce_function_ecdsa_adaptor_bitflip(args, 3, sizeof(algo), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 4, sizeof(aux_rand), algolen); + } + + /* NULL algo is disallowed */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, NULL, 0, NULL) == 0); + /* Empty algo is fine */ + memset(algo, 0x00, algolen); + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); + /* Other algo is fine */ + memset(algo, 0xFF, algolen); + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); + /* dleq algo is fine */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, dleq_algo, sizeof(dleq_algo), NULL) == 1); + + /* Different algolen gives different nonce */ + for (i = 0; i < count; i++) { + unsigned char nonce2[32]; + uint32_t offset = secp256k1_testrand_int(algolen - 1); + size_t algolen_tmp = (algolen + offset) % algolen; + + CHECK(nonce_function_ecdsa_adaptor(nonce2, msg, key, pk, algo, algolen_tmp, NULL) == 1); + CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); + } + + /* NULL aux_rand argument is allowed. */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); +} + +void test_ecdsa_adaptor_api(void) { + secp256k1_pubkey pubkey; + secp256k1_pubkey enckey; + secp256k1_pubkey zero_pk; + secp256k1_ecdsa_signature sig; + unsigned char sk[32]; + unsigned char msg[32]; + unsigned char asig[162]; + unsigned char deckey[32]; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_testrand256(sk); + secp256k1_testrand256(msg); + secp256k1_testrand256(deckey); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, sk) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &enckey, deckey) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_encrypt(none, asig, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_encrypt(vrfy, asig, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, msg, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, NULL, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, NULL, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, NULL, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, NULL, msg, NULL, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &zero_pk, msg, NULL, NULL) == 0); + CHECK(ecount == 7); + + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, msg, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(none, asig, &pubkey, msg, &enckey) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(sign, asig, &pubkey, msg, &enckey) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, msg, &enckey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, NULL, &pubkey, msg, &enckey) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, NULL, &enckey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, msg, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, NULL, msg, &enckey) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &zero_pk, msg, &enckey) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, msg, &zero_pk) == 0); + CHECK(ecount == 8); + + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_decrypt(none, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(sign, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(vrfy, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, NULL, deckey, asig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, NULL, asig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, deckey, NULL) == 0); + CHECK(ecount == 3); + + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_recover(none, deckey, &sig, asig, &enckey) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_recover(vrfy, deckey, &sig, asig, &enckey) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &enckey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, NULL, &sig, asig, &enckey) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, NULL, asig, &enckey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, NULL, &enckey) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &zero_pk) == 0); + CHECK(ecount == 7); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +void adaptor_tests(void) { + unsigned char seckey[32]; + secp256k1_pubkey pubkey; + unsigned char msg[32]; + unsigned char deckey[32]; + secp256k1_pubkey enckey; + unsigned char adaptor_sig[162]; + secp256k1_ecdsa_signature sig; + unsigned char zeros162[162] = { 0 }; + unsigned char zeros64[64] = { 0 }; + unsigned char big[32]; + + secp256k1_testrand256(seckey); + secp256k1_testrand256(msg); + secp256k1_testrand256(deckey); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &enckey, deckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, NULL, NULL) == 1); + + { + /* Test overflowing seckey */ + memset(big, 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, big, &enckey, msg, NULL, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); + + /* Test different nonce functions */ + memset(adaptor_sig, 1, sizeof(adaptor_sig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_failing, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); + memset(&adaptor_sig, 1, sizeof(adaptor_sig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_0, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_overflowing, NULL) == 1); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) != 0); + } + { + /* Test adaptor_sig_serialize roundtrip */ + secp256k1_ge r, rp; + secp256k1_scalar sigr; + secp256k1_scalar sp; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_ge p_inf; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); + + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 1); + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)) == 0); + + /* Test adaptor_sig_serialize points at infinity */ + secp256k1_ge_set_infinity(&p_inf); + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &p_inf, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 0); + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &p_inf, &sp, &dleq_proof_e, &dleq_proof_s) == 0); + } + { + /* Test adaptor_sig_deserialize */ + secp256k1_ge r, rp; + secp256k1_scalar sigr; + secp256k1_scalar sp; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); + + /* r */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[0], 0xFF, 33); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* sigr */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[1], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 1); + memset(&adaptor_sig_tmp[1], 0, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* rp */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, &rp, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[33], 0xFF, 33); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, &rp, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* sp */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, &sp, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[66], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, &sp, NULL, NULL, adaptor_sig_tmp) == 0); + + /* dleq_proof_e */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, &dleq_proof_e, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[98], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, &dleq_proof_e, NULL, adaptor_sig_tmp) == 1); + + /* dleq_proof_s */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, NULL, &dleq_proof_s, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[130], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, NULL, &dleq_proof_s, adaptor_sig_tmp) == 0); + } + + /* Test adaptor_sig_verify */ + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg, &enckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &enckey, msg, &enckey) == 0); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg, &pubkey) == 0); + { + unsigned char adaptor_sig_tmp[65]; + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + rand_flip_bit(&adaptor_sig_tmp[1], sizeof(adaptor_sig_tmp) - 1); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig_tmp, &pubkey, msg, &enckey) == 0); + } + { + unsigned char msg_tmp[32]; + memcpy(msg_tmp, msg, sizeof(msg_tmp)); + rand_flip_bit(msg_tmp, sizeof(msg_tmp)); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg_tmp, &enckey) == 0); + } + { + /* Verification must check that the derived R' is not equal to the point at + * infinity before negating it. R' is derived as follows: + * + * R' == s'⁻¹(m * G + R.x * X) + * + * When the base point, G, is multiplied by the subgroup order, q, the + * result is the point at infinity, 0: + * + * q * G = 0 + * + * Thus, if we set s' equal to R.x, m equal to (q - 1) * R.x, and X equal to + * G, then our derived R' will be 0: + * + * R' = R.x⁻¹((q - 1 * R.x) * G + R.x * G) = q * G = 0 */ + + /* t := q - 1 */ + const unsigned char target[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 + }; + unsigned char seckey_tmp[32] = { 0 }; + unsigned char msg_tmp[32]; + unsigned char adaptor_sig_tmp[162]; + secp256k1_pubkey pubkey_tmp; + secp256k1_scalar sigr, t, m; + + /* m := t * sigr */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + secp256k1_scalar_set_b32(&t, target, NULL); + secp256k1_scalar_mul(&m, &t, &sigr); + secp256k1_scalar_get_b32(msg_tmp, &m); + + /* X := G */ + seckey_tmp[31] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_tmp, seckey_tmp) == 1); + + /* sp := sigr */ + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memcpy(&adaptor_sig_tmp[66], &adaptor_sig_tmp[1], 32); + + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig_tmp, &pubkey_tmp, msg_tmp, &enckey) == 0); + } + + /* Test decryption */ + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &sig, deckey, adaptor_sig) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); + + { + /* Test overflowing decryption key */ + secp256k1_ecdsa_signature s; + memset(big, 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &s, big, adaptor_sig) == 0); + CHECK(secp256k1_memcmp_var(&s.data[0], zeros64, sizeof(&s.data[0])) == 0); + } + { + /* Test key recover */ + secp256k1_ecdsa_signature sig_tmp; + unsigned char decryption_key_tmp[32]; + unsigned char adaptor_sig_tmp[162]; + const unsigned char order_le[32] = { + 0x41, 0x41, 0x36, 0xd0, 0x8c, 0x5e, 0xd2, 0xbf, + 0x3b, 0xa0, 0x48, 0xaf, 0xe6, 0xdc, 0xae, 0xba, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig, adaptor_sig, &enckey) == 1); + CHECK(secp256k1_memcmp_var(deckey, decryption_key_tmp, sizeof(deckey)) == 0); + + /* Test failed sp deserialization */ + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[66], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig, adaptor_sig_tmp, &enckey) == 0); + + /* Test failed enckey_expected serialization */ + memcpy(sig_tmp.data, sig.data, 32); + memcpy(&sig_tmp.data[32], order_le, 32); + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig_tmp, adaptor_sig, &enckey) == 0); + } +} + +void multi_hop_lock_tests(void) { + unsigned char seckey_a[32]; + unsigned char seckey_b[32]; + unsigned char pop[32]; + unsigned char tx_ab[32]; + unsigned char tx_bc[32]; + unsigned char buf[32]; + unsigned char asig_ab[162]; + unsigned char asig_bc[162]; + secp256k1_pubkey pubkey_pop; + secp256k1_pubkey pubkey_a, pubkey_b; + secp256k1_pubkey l, r; + secp256k1_ge l_ge, r_ge; + secp256k1_scalar t1, t2, tp; + secp256k1_scalar deckey; + secp256k1_ecdsa_signature sig_ab, sig_bc; + + secp256k1_testrand256(seckey_a); + secp256k1_testrand256(seckey_b); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_a, seckey_a)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_b, seckey_b)); + + /* Carol setup */ + /* Proof of payment */ + secp256k1_testrand256(pop); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_pop, pop)); + + /* Alice setup */ + secp256k1_testrand256(tx_ab); + rand_scalar(&t1); + rand_scalar(&t2); + secp256k1_scalar_add(&tp, &t1, &t2); + /* Left lock */ + secp256k1_pubkey_load(ctx, &l_ge, &pubkey_pop); + CHECK(secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &l_ge, &t1)); + secp256k1_pubkey_save(&l, &l_ge); + /* Right lock */ + secp256k1_pubkey_load(ctx, &r_ge, &pubkey_pop); + CHECK(secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &r_ge, &tp)); + secp256k1_pubkey_save(&r, &r_ge); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, asig_ab, seckey_a, &l, tx_ab, NULL, NULL)); + + /* Bob setup */ + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, asig_ab, &pubkey_a, tx_ab, &l)); + secp256k1_testrand256(tx_bc); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, asig_bc, seckey_b, &r, tx_bc, NULL, NULL)); + + /* Carol decrypt */ + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, asig_bc, &pubkey_b, tx_bc, &r)); + secp256k1_scalar_set_b32(&deckey, pop, NULL); + secp256k1_scalar_add(&deckey, &deckey, &tp); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &sig_bc, buf, asig_bc)); + CHECK(secp256k1_ecdsa_verify(ctx, &sig_bc, tx_bc, &pubkey_b)); + + /* Bob recover and decrypt */ + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, buf, &sig_bc, asig_bc, &r)); + secp256k1_scalar_set_b32(&deckey, buf, NULL); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&deckey, &deckey, &t2); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &sig_ab, buf, asig_ab)); + CHECK(secp256k1_ecdsa_verify(ctx, &sig_ab, tx_ab, &pubkey_a)); + + /* Alice recover and derive proof of payment */ + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, buf, &sig_ab, asig_ab, &l)); + secp256k1_scalar_set_b32(&deckey, buf, NULL); + secp256k1_scalar_negate(&t1, &t1); + secp256k1_scalar_add(&deckey, &deckey, &t1); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0); +} + +void run_ecdsa_adaptor_tests(void) { + int i; + run_nonce_function_ecdsa_adaptor_tests(); + + test_ecdsa_adaptor_api(); + test_ecdsa_adaptor_spec_vectors(); + for (i = 0; i < count; i++) { + dleq_tests(); + } + for (i = 0; i < count; i++) { + adaptor_tests(); + } + for (i = 0; i < count; i++) { + multi_hop_lock_tests(); + } +} + +#endif /* SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H */ diff --git a/src/tests.c b/src/tests.c index 1208d0c9..12ac3b75 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5652,6 +5652,10 @@ void run_ecdsa_openssl(void) { # include "modules/ecdsa_s2c/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +# include "modules/ecdsa_adaptor/tests_impl.h" +#endif + void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5966,6 +5970,10 @@ int main(int argc, char **argv) { run_ecdsa_s2c_tests(); #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + run_ecdsa_adaptor_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index f7081dbd..b153f2f1 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -31,6 +31,10 @@ #include "include/secp256k1_ecdsa_s2c.h" #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +#include "include/secp256k1_ecdsa_adaptor.h" +#endif + void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -199,4 +203,42 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { CHECK(ret == 1); } #endif + +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + { + unsigned char adaptor_sig[162]; + unsigned char deckey[32]; + unsigned char expected_deckey[32]; + secp256k1_pubkey enckey; + + for (i = 0; i < 32; i++) { + deckey[i] = i + 2; + } + + ret = secp256k1_ec_pubkey_create(ctx, &enckey, deckey); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + ret = secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, key, &enckey, msg, NULL, NULL); + VALGRIND_MAKE_MEM_DEFINED(adaptor_sig, sizeof(adaptor_sig)); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(deckey, 32); + ret = secp256k1_ecdsa_adaptor_decrypt(ctx, &signature, deckey, adaptor_sig); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(&signature, 32); + ret = secp256k1_ecdsa_adaptor_recover(ctx, expected_deckey, &signature, adaptor_sig, &enckey); + VALGRIND_MAKE_MEM_DEFINED(expected_deckey, sizeof(expected_deckey)); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_DEFINED(deckey, sizeof(deckey)); + ret = secp256k1_memcmp_var(deckey, expected_deckey, sizeof(expected_deckey)); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 0); + } +#endif } From d27e459861026ddaa376c9cb2acf93ad3c668ee3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 14 Jun 2021 19:54:41 +0000 Subject: [PATCH 111/381] Revert "Remove unused Jacobi symbol support" This reverts commit 20448b8d09a492afcfcae7721033c13a44a776fd. The removed functions secp256k1_ge_set_xquad and secp256k1_fe_is_quad_var are required for some modules in secp256k1-zkp. --- src/bench_internal.c | 27 +++++++++++++++++++++++---- src/field.h | 3 +++ src/field_impl.h | 5 +++++ src/group.h | 9 +++++++++ src/group_impl.h | 22 ++++++++++++++++++++-- src/tests.c | 39 ++++++++++++++++++++++++++++++++++----- 6 files changed, 94 insertions(+), 11 deletions(-) diff --git a/src/bench_internal.c b/src/bench_internal.c index 161b1c4a..2be5e450 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -245,6 +245,26 @@ void bench_group_add_affine_var(void* arg, int iters) { } } +void bench_group_jacobi_var(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + j += secp256k1_gej_has_quad_y_var(&data->gej[0]); + /* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to + secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will + generally not correspond to a point on the curve, but this is not a problem + for the code being benchmarked here. Adding and normalizing have less + overhead than EC operations (which could guarantee the point remains on the + curve). */ + secp256k1_fe_add(&data->gej[0].y, &data->fe[1]); + secp256k1_fe_add(&data->gej[0].z, &data->fe[2]); + secp256k1_fe_normalize_var(&data->gej[0].y); + secp256k1_fe_normalize_var(&data->gej[0].z); + } + CHECK(j <= iters); +} + void bench_group_to_affine_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -252,10 +272,8 @@ void bench_group_to_affine_var(void* arg, int iters) { for (i = 0; i < iters; ++i) { secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]); /* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates. - Note that the resulting coordinates will generally not correspond to a point - on the curve, but this is not a problem for the code being benchmarked here. - Adding and normalizing have less overhead than EC operations (which could - guarantee the point remains on the curve). */ + Similar to bench_group_jacobi_var, this approach does not result in + coordinates of points on the curve. */ secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y); secp256k1_fe_add(&data->gej[0].y, &data->fe[2]); secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x); @@ -364,6 +382,7 @@ int main(int argc, char **argv) { if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, iters*10); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters); if (have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters); if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters); diff --git a/src/field.h b/src/field.h index 854aaeba..cf3bf10b 100644 --- a/src/field.h +++ b/src/field.h @@ -103,6 +103,9 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); * itself. */ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); +/** Checks whether a field element is a quadratic residue. */ +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); + /** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); diff --git a/src/field_impl.h b/src/field_impl.h index 374284a1..eb8b8e20 100644 --- a/src/field_impl.h +++ b/src/field_impl.h @@ -135,6 +135,11 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { return secp256k1_fe_equal(&t1, a); } +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { + secp256k1_fe r; + return secp256k1_fe_sqrt(&r, a); +} + static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); #endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/group.h b/src/group.h index b9cd334d..2442e0cb 100644 --- a/src/group.h +++ b/src/group.h @@ -42,6 +42,12 @@ typedef struct { /** Set a group element equal to the point with given X and Y coordinates */ static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); +/** Set a group element (affine) equal to the point with the given X coordinate + * and a Y coordinate that is a quadratic residue modulo p. The return value + * is true iff a coordinate with the given X coordinate exists. + */ +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); + /** Set a group element (affine) equal to the point with the given X coordinate, and given oddness * for Y. Return value indicates whether the result is valid. */ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); @@ -89,6 +95,9 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); /** Check whether a group element is the point at infinity. */ static int secp256k1_gej_is_infinity(const secp256k1_gej *a); +/** Check whether a group element's y coordinate is a quadratic residue. */ +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); + /** Set r equal to the double of a. Constant time. */ static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a); diff --git a/src/group_impl.h b/src/group_impl.h index 47aea32b..aa7a0fba 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -206,14 +206,18 @@ static void secp256k1_ge_clear(secp256k1_ge *r) { secp256k1_fe_clear(&r->y); } -static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { secp256k1_fe x2, x3; r->x = *x; secp256k1_fe_sqr(&x2, x); secp256k1_fe_mul(&x3, x, &x2); r->infinity = 0; secp256k1_fe_add(&x3, &secp256k1_fe_const_b); - if (!secp256k1_fe_sqrt(&r->y, &x3)) { + return secp256k1_fe_sqrt(&r->y, &x3); +} + +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { + if (!secp256k1_ge_set_xquad(r, x)) { return 0; } secp256k1_fe_normalize_var(&r->y); @@ -650,6 +654,20 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { secp256k1_fe_mul(&r->x, &r->x, &beta); } +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as + * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z + is */ + secp256k1_fe_mul(&yz, &a->y, &a->z); + return secp256k1_fe_is_quad_var(&yz); +} + static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { #ifdef EXHAUSTIVE_TEST_ORDER secp256k1_gej out; diff --git a/src/tests.c b/src/tests.c index ae781704..0943d35b 100644 --- a/src/tests.c +++ b/src/tests.c @@ -3510,35 +3510,64 @@ void run_ec_commit(void) { void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; - /* Results of set_xo_var(..., 0), set_xo_var(..., 1). */ - secp256k1_ge ge_even, ge_odd; + secp256k1_fe fez; + /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ + secp256k1_ge ge_quad, ge_even, ge_odd; + secp256k1_gej gej_quad; /* Return values of the above calls. */ - int res_even, res_odd; + int res_quad, res_even, res_odd; secp256k1_fe_normalize_var(&fex); + res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); - CHECK(res_even == res_odd); + CHECK(res_quad == res_even); + CHECK(res_quad == res_odd); - if (res_even) { + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); secp256k1_fe_normalize_var(&ge_odd.x); secp256k1_fe_normalize_var(&ge_even.x); + secp256k1_fe_normalize_var(&ge_quad.y); secp256k1_fe_normalize_var(&ge_odd.y); secp256k1_fe_normalize_var(&ge_even.y); /* No infinity allowed. */ + CHECK(!ge_quad.infinity); CHECK(!ge_even.infinity); CHECK(!ge_odd.infinity); /* Check that the x coordinates check out. */ + CHECK(secp256k1_fe_equal_var(&ge_quad.x, x)); CHECK(secp256k1_fe_equal_var(&ge_even.x, x)); CHECK(secp256k1_fe_equal_var(&ge_odd.x, x)); + /* Check that the Y coordinate result in ge_quad is a square. */ + CHECK(secp256k1_fe_is_quad_var(&ge_quad.y)); + /* Check odd/even Y in ge_odd, ge_even. */ CHECK(secp256k1_fe_is_odd(&ge_odd.y)); CHECK(!secp256k1_fe_is_odd(&ge_even.y)); + + /* Check secp256k1_gej_has_quad_y_var. */ + secp256k1_gej_set_ge(&gej_quad, &ge_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); } } From 9321d42f7510e08e0e9f3c0a19fd55cfb7d07775 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 12 Jul 2021 15:12:50 +0200 Subject: [PATCH 112/381] sync-upstream: parse merge commits w/ and w/o repo identifier --- contrib/sync-upstream.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index c9f47d2e..9192f013 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -81,9 +81,9 @@ TITLE="Upstream PRs" BODY="" for COMMIT in $COMMITS do - PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\).*'/'\1'/) + PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\).*'/'\2'/) TITLE="$TITLE $PRNUM," - BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge #\([0-9]*\)'/'[bitcoin-core\/secp256k1#\1]'/)") + BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\)'/'[bitcoin-core\/secp256k1#\2]'/)") done # Remove trailing "," TITLE=${TITLE%?} From 394f49fd1a6e88d2a5f9a6c80da897ec389fc59c Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 12 Jul 2021 18:23:18 +0200 Subject: [PATCH 113/381] sync-upstream: quote variables --- contrib/sync-upstream.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 9192f013..3295d7b3 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -22,11 +22,11 @@ if [ "$#" -lt 1 ]; then fi REMOTE=upstream -REMOTE_BRANCH=$REMOTE/master +REMOTE_BRANCH="$REMOTE/master" # Makes sure you have a remote "upstream" that is up-to-date setup() { ret=0 - git fetch $REMOTE &> /dev/null || ret=$? + git fetch "$REMOTE" &> /dev/null || ret="$?" if [ ${ret} == 0 ]; then return fi @@ -36,13 +36,13 @@ setup() { [Yy]* ) ;; * ) exit 1;; esac - git remote add $REMOTE git@github.com:bitcoin-core/secp256k1.git &> /dev/null - git fetch $REMOTE &> /dev/null + git remote add "$REMOTE" git@github.com:bitcoin-core/secp256k1.git &> /dev/null + git fetch "$REMOTE" &> /dev/null } range() { - RANGESTART_COMMIT=$(git merge-base $REMOTE_BRANCH master) - RANGEEND_COMMIT=$(git rev-parse $REMOTE_BRANCH) + RANGESTART_COMMIT=$(git merge-base "$REMOTE_BRANCH" master) + RANGEEND_COMMIT=$(git rev-parse "$REMOTE_BRANCH") if [ "$#" = 1 ]; then RANGEEND_COMMIT=$1 fi @@ -101,7 +101,7 @@ git pull git checkout -b temp-merge-"$PRNUM" BASEDIR=$(dirname "$0") -FNAME=$BASEDIR/gh-pr-create.sh +FNAME="$BASEDIR/gh-pr-create.sh" cat < "$FNAME" #!/bin/sh gh pr create -t "$TITLE" -b "$BODY" --web From 907633e2e9abec15be48256f00c2f4c76855a9f6 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 12 Jul 2021 18:24:04 +0200 Subject: [PATCH 114/381] sync-upstream: fix "end" parameter for specifying range --- contrib/sync-upstream.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 3295d7b3..55dde4e1 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -47,7 +47,7 @@ range() { RANGEEND_COMMIT=$1 fi - COMMITS=$(git --no-pager log --oneline "$REMOTE_BRANCH" --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") + COMMITS=$(git --no-pager log --oneline --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") COMMITS=$(echo "$COMMITS" | tac | awk '{ print $1 }' ORS=' ') echo "Merging $COMMITS. Continue with y" read -r yn From b053e853d4f556499decb5c50af473f91996f46e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 14 Jun 2021 20:16:38 +0000 Subject: [PATCH 115/381] ecdsa_adaptor: fix test case with invalid signature Previously the ECDSA signature had an overflowing s value, which after the sync with upstream results in a failing VERIFY_CHECK in the inversion function. However, normally parsed signatures shouldn't contain overflowing s values. --- src/modules/ecdsa_adaptor/main_impl.h | 11 +++++++++++ src/modules/ecdsa_adaptor/tests_impl.h | 12 ------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 18e6132d..ba0afede 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -341,6 +341,17 @@ int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char * branch point. */ secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge)); if (!secp256k1_eckey_pubkey_serialize(&enckey_expected_ge, enckey_expected33, &size, SECP256K1_EC_COMPRESSED)) { + /* Unreachable from tests (and other VERIFY builds) and therefore this + * branch should be ignored in test coverage analysis. + * + * Proof: + * eckey_pubkey_serialize fails <=> deckey = 0 + * deckey = 0 <=> s^-1 = 0 or sp = 0 + * case 1: s^-1 = 0 impossible by the definition of multiplicative + * inverse and because the scalar_inverse implementation + * VERIFY_CHECKs that the inputs are valid scalars. + * case 2: sp = 0 impossible because ecdsa_adaptor_sig_deserialize would have already failed + */ return 0; } if (!secp256k1_ec_pubkey_serialize(ctx, enckey33, &size, enckey, SECP256K1_EC_COMPRESSED)) { diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index 5a12bb74..a9d6b4f8 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -1102,15 +1102,8 @@ void adaptor_tests(void) { } { /* Test key recover */ - secp256k1_ecdsa_signature sig_tmp; unsigned char decryption_key_tmp[32]; unsigned char adaptor_sig_tmp[162]; - const unsigned char order_le[32] = { - 0x41, 0x41, 0x36, 0xd0, 0x8c, 0x5e, 0xd2, 0xbf, - 0x3b, 0xa0, 0x48, 0xaf, 0xe6, 0xdc, 0xae, 0xba, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig, adaptor_sig, &enckey) == 1); CHECK(secp256k1_memcmp_var(deckey, decryption_key_tmp, sizeof(deckey)) == 0); @@ -1119,11 +1112,6 @@ void adaptor_tests(void) { memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); memset(&adaptor_sig_tmp[66], 0xFF, 32); CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig, adaptor_sig_tmp, &enckey) == 0); - - /* Test failed enckey_expected serialization */ - memcpy(sig_tmp.data, sig.data, 32); - memcpy(&sig_tmp.data[32], order_le, 32); - CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig_tmp, adaptor_sig, &enckey) == 0); } } From 7226cf215aaca80fcddcc5242c8ea11d2b35c85b Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 14 Jun 2021 20:57:40 +0000 Subject: [PATCH 116/381] ecdsa_adaptor: fix too small buffer in tests Also add a specific test that fails adaptor sig deserialization because with the correct size buffer that's not guaranteed anymore with the existing test. --- src/modules/ecdsa_adaptor/tests_impl.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index a9d6b4f8..48fb33b7 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -1032,7 +1032,15 @@ void adaptor_tests(void) { CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &enckey, msg, &enckey) == 0); CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg, &pubkey) == 0); { - unsigned char adaptor_sig_tmp[65]; + /* Test failed adaptor sig deserialization */ + unsigned char adaptor_sig_tmp[162]; + memset(&adaptor_sig_tmp, 0xFF, 162); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig_tmp, &pubkey, msg, &enckey) == 0); + } + { + /* Test that any flipped bit in the adaptor signature will make + * verification fail */ + unsigned char adaptor_sig_tmp[162]; memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); rand_flip_bit(&adaptor_sig_tmp[1], sizeof(adaptor_sig_tmp) - 1); CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig_tmp, &pubkey, msg, &enckey) == 0); From f09497ea3e07d7a730a6ff3479dca18b848ef729 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 15 Jun 2021 11:42:58 +0000 Subject: [PATCH 117/381] CI: tweak cirrus.yml to prevent OOM and timeout w sanitizer/valgrind --- .cirrus.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index d157e994..95805d94 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -288,10 +288,11 @@ task: # Sanitizers task: + timeout_in: 120m container: dockerfile: ci/linux-debian.Dockerfile cpu: 1 - memory: 1G + memory: 2G env: ECDH: yes RECOVERY: yes @@ -310,7 +311,7 @@ task: env: # The `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html) WRAPPER_CMD: "valgrind --error-exitcode=42" - TEST_ITERS: 16 + TEST_ITERS: 8 - name: "UBSan, ASan, LSan" env: CFLAGS: "-fsanitize=undefined,address" From cc0b279568d6edaa0b966b4333a0008f4ef63efa Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 13 Jul 2021 17:30:05 +0200 Subject: [PATCH 118/381] Eliminate a wrong -Wmaybe-uninitialized warning in GCC --- src/modules/rangeproof/main_impl.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index c7f921fc..feacff14 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -182,7 +182,15 @@ int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, c } secp256k1_scalar_set_int(&sum, 0); - for (i = 0; i < n_total; i++) { + + /* Here, n_total > 0. Thus the loop runs at least once. + Thus we may use a do-while loop, which checks the loop + condition only at the end. + + The do-while loop helps GCC prove that the loop runs at least + once and suppresses a -Wmaybe-uninitialized warning. */ + i = 0; + do { int overflow = 0; secp256k1_scalar addend; secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ @@ -207,7 +215,9 @@ int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, c secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ secp256k1_scalar_clear(&addend); - } + + i++; + } while (i < n_total); /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ secp256k1_scalar_negate(&sum, &sum); From f31affd8a613ebbdb07050a90ff1ccb2b1f0a1fd Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 2 Apr 2021 21:51:02 +0000 Subject: [PATCH 119/381] extrakeys: add hsort, in-place, iterative heapsort --- src/modules/extrakeys/Makefile.am.include | 2 + src/modules/extrakeys/hsort.h | 22 ++++ src/modules/extrakeys/hsort_impl.h | 116 ++++++++++++++++++++++ src/modules/extrakeys/main_impl.h | 1 + src/modules/extrakeys/tests_impl.h | 42 ++++++++ 5 files changed, 183 insertions(+) create mode 100644 src/modules/extrakeys/hsort.h create mode 100644 src/modules/extrakeys/hsort_impl.h diff --git a/src/modules/extrakeys/Makefile.am.include b/src/modules/extrakeys/Makefile.am.include index 0d901ec1..fe496fd2 100644 --- a/src/modules/extrakeys/Makefile.am.include +++ b/src/modules/extrakeys/Makefile.am.include @@ -2,3 +2,5 @@ include_HEADERS += include/secp256k1_extrakeys.h noinst_HEADERS += src/modules/extrakeys/tests_impl.h noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h noinst_HEADERS += src/modules/extrakeys/main_impl.h +noinst_HEADERS += src/modules/extrakeys/hsort.h +noinst_HEADERS += src/modules/extrakeys/hsort_impl.h diff --git a/src/modules/extrakeys/hsort.h b/src/modules/extrakeys/hsort.h new file mode 100644 index 00000000..0f57227c --- /dev/null +++ b/src/modules/extrakeys/hsort.h @@ -0,0 +1,22 @@ +/*********************************************************************** + * Copyright (c) 2021 Russell O'Connor, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_HSORT_H_ +#define SECP256K1_HSORT_H_ + +#include +#include + +/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This + * is preferred over standard library implementations because they generally + * make no guarantee about being fast for malicious inputs. + * + * See the qsort_r manpage for a description of the interface. + */ +static void secp256k1_hsort(void *ptr, size_t count, size_t size, + int (*cmp)(const void *, const void *, void *), + void *cmp_data); +#endif diff --git a/src/modules/extrakeys/hsort_impl.h b/src/modules/extrakeys/hsort_impl.h new file mode 100644 index 00000000..fcc4b10b --- /dev/null +++ b/src/modules/extrakeys/hsort_impl.h @@ -0,0 +1,116 @@ +/*********************************************************************** + * Copyright (c) 2021 Russell O'Connor, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_HSORT_IMPL_H_ +#define SECP256K1_HSORT_IMPL_H_ + +#include "hsort.h" + +/* An array is a heap when, for all non-zero indexes i, the element at index i + * compares as less than or equal to the element at index parent(i) = (i-1)/2. + */ + +static SECP256K1_INLINE size_t child1(size_t i) { + VERIFY_CHECK(i <= (SIZE_MAX - 1)/2); + return 2*i + 1; +} + +static SECP256K1_INLINE size_t child2(size_t i) { + VERIFY_CHECK(i <= SIZE_MAX/2 - 1); + return child1(i)+1; +} + +static SECP256K1_INLINE void swap64(unsigned char *a, size_t i, size_t j, size_t stride) { + unsigned char tmp[64]; + VERIFY_CHECK(stride <= 64); + memcpy(tmp, a + i*stride, stride); + memmove(a + i*stride, a + j*stride, stride); + memcpy(a + j*stride, tmp, stride); +} + +static SECP256K1_INLINE void swap(unsigned char *a, size_t i, size_t j, size_t stride) { + while (64 < stride) { + swap64(a + (stride - 64), i, j, 64); + stride -= 64; + } + swap64(a, i, j, stride); +} + +static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_size, size_t stride, + int (*cmp)(const void *, const void *, void *), void *cmp_data) { + while (i < heap_size/2) { + VERIFY_CHECK(i <= SIZE_MAX/2 - 1); + /* Proof: + * i < heap_size/2 + * i + 1 <= heap_size/2 + * 2*i + 2 <= heap_size <= SIZE_MAX + * 2*i <= SIZE_MAX - 2 + */ + + VERIFY_CHECK(child1(i) < heap_size); + /* Proof: + * i < heap_size/2 + * i + 1 <= heap_size/2 + * 2*i + 2 <= heap_size + * 2*i + 1 < heap_size + * child1(i) < heap_size + */ + + /* Let [x] be notation for the contents at a[x*stride]. + * + * If [child1(i)] > [i] and [child2(i)] > [i], + * swap [i] with the larger child to ensure the new parent is larger + * than both children. When [child1(i)] == [child2(i)], swap [i] with + * [child2(i)]. + * Else if [child1(i)] > [i], swap [i] with [child1(i)]. + * Else if [child2(i)] > [i], swap [i] with [child2(i)]. + */ + if (child2(i) < heap_size + && 0 <= cmp(a + child2(i)*stride, a + child1(i)*stride, cmp_data)) { + if (0 < cmp(a + child2(i)*stride, a + i*stride, cmp_data)) { + swap(a, i, child2(i), stride); + i = child2(i); + } else { + /* At this point we have [child2(i)] >= [child1(i)] and we have + * [child2(i)] <= [i], and thus [child1(i)] <= [i] which means + * that the next comparison can be skipped. */ + return; + } + } else if (0 < cmp(a + child1(i)*stride, a + i*stride, cmp_data)) { + swap(a, i, child1(i), stride); + i = child1(i); + } else { + return; + } + } + /* heap_size/2 <= i + * heap_size/2 < i + 1 + * heap_size < 2*i + 2 + * heap_size <= 2*i + 1 + * heap_size <= child1(i) + * Thus child1(i) and child2(i) are now out of bounds and we are at a leaf. + */ +} + +/* In-place heap sort. */ +static void secp256k1_hsort(void *ptr, size_t count, size_t size, + int (*cmp)(const void *, const void *, void *), + void *cmp_data ) { + size_t i; + + for(i = count/2; 0 < i; --i) { + heap_down(ptr, i-1, count, size, cmp, cmp_data); + } + for(i = count; 1 < i; --i) { + /* Extract the largest value from the heap */ + swap(ptr, 0, i-1, size); + + /* Repair the heap condition */ + heap_down(ptr, 0, i-1, size, cmp, cmp_data); + } +} + +#endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 8607bbed..8c7c819d 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -9,6 +9,7 @@ #include "../../../include/secp256k1.h" #include "../../../include/secp256k1_extrakeys.h" +#include "hsort_impl.h" static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) { return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey); diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 4a595271..07e4bbde 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -571,6 +571,46 @@ void test_keypair_add(void) { secp256k1_context_destroy(verify); } +static void test_hsort_is_sorted(int *ints, size_t n) { + size_t i; + for (i = 1; i < n; i++) { + CHECK(ints[i-1] <= ints[i]); + } +} + +static int test_hsort_cmp(const void *i1, const void *i2, void *counter) { + *(size_t*)counter += 1; + return *(int*)i1 - *(int*)i2; +} + +#define NUM 64 +void test_hsort(void) { + int ints[NUM] = { 0 }; + size_t counter = 0; + int i, j; + + secp256k1_hsort(ints, 0, sizeof(ints[0]), test_hsort_cmp, &counter); + CHECK(counter == 0); + secp256k1_hsort(ints, 1, sizeof(ints[0]), test_hsort_cmp, &counter); + CHECK(counter == 0); + secp256k1_hsort(ints, NUM, sizeof(ints[0]), test_hsort_cmp, &counter); + CHECK(counter > 0); + test_hsort_is_sorted(ints, NUM); + + /* Test hsort with length n array and random elements in + * [-interval/2, interval/2] */ + for (i = 0; i < count; i++) { + int n = secp256k1_testrand_int(NUM); + int interval = secp256k1_testrand_int(64); + for (j = 0; j < n; j++) { + ints[j] = secp256k1_testrand_int(interval) - interval/2; + } + secp256k1_hsort(ints, n, sizeof(ints[0]), test_hsort_cmp, &counter); + test_hsort_is_sorted(ints, n); + } +} +#undef NUM + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); @@ -582,6 +622,8 @@ void run_extrakeys_tests(void) { /* keypair tests */ test_keypair(); test_keypair_add(); + + test_hsort(); } #endif From 9b3d7bf53617c962cd291039d5ce97088c4513cc Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 2 Apr 2021 21:51:45 +0000 Subject: [PATCH 120/381] extrakeys: add xonly_sort function --- include/secp256k1_extrakeys.h | 14 ++++ include/secp256k1_musig.h | 6 ++ src/modules/extrakeys/main_impl.h | 22 ++++++ src/modules/extrakeys/tests_impl.h | 108 +++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index 0a37fb6b..4a3ca0b1 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -166,6 +166,20 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_ const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +/** Sorts xonly public keys according to secp256k1_xonly_pubkey_cmp + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * + * Args: ctx: pointer to a context object + * In: pubkeys: array of pointers to pubkeys to sort + * n_pubkeys: number of elements in the pubkeys array + */ +SECP256K1_API int secp256k1_xonly_sort( + const secp256k1_context* ctx, + const secp256k1_xonly_pubkey **pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + /** Compute the keypair for a secret key. * * Returns: 1: secret was valid, keypair is ready to use diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 46fe6c28..b86eeca9 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -137,8 +137,14 @@ typedef struct { } secp256k1_musig_partial_signature; /** Computes a combined public key and the hash of the given public keys. + * * Different orders of `pubkeys` result in different `combined_pk`s. * + * The pubkeys can be sorted before combining with `secp256k1_xonly_sort` which + * ensures the same resulting `combined_pk` for the same multiset of pubkeys. + * This is useful to do before pubkey_combine, such that the order of pubkeys + * does not affect the combined public key. + * * Returns: 1 if the public keys were successfully combined, 0 otherwise * Args: ctx: pointer to a context object initialized for verification * (cannot be NULL) diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 8c7c819d..beefecfd 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -155,6 +155,28 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity; } +/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api + * which expects a non-const cmp_data pointer. */ +typedef struct { + const secp256k1_context *ctx; +} secp256k1_xonly_sort_cmp_data; + +static int secp256k1_xonly_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) { + return secp256k1_xonly_pubkey_cmp(((secp256k1_xonly_sort_cmp_data*)cmp_data)->ctx, + *(secp256k1_xonly_pubkey **)pk1, + *(secp256k1_xonly_pubkey **)pk2); +} + +int secp256k1_xonly_sort(const secp256k1_context* ctx, const secp256k1_xonly_pubkey **pubkeys, size_t n_pubkeys) { + secp256k1_xonly_sort_cmp_data cmp_data; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkeys != NULL); + + cmp_data.ctx = ctx; + secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_xonly_sort_cmp, &cmp_data); + return 1; +} + static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) { secp256k1_scalar_get_b32(&keypair->data[0], sk); secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk); diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 07e4bbde..781e5f7f 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -611,6 +611,112 @@ void test_hsort(void) { } #undef NUM +void test_xonly_sort_helper(secp256k1_xonly_pubkey *pk, size_t *pk_order, size_t n_pk) { + size_t i; + const secp256k1_xonly_pubkey *pk_test[5]; + + for (i = 0; i < n_pk; i++) { + pk_test[i] = &pk[pk_order[i]]; + } + secp256k1_xonly_sort(ctx, pk_test, n_pk); + for (i = 0; i < n_pk; i++) { + CHECK(secp256k1_memcmp_var(pk_test[i], &pk[i], sizeof(*pk_test[i])) == 0); + } +} + +void permute(size_t *arr, size_t n) { + size_t i; + for (i = n - 1; i >= 1; i--) { + size_t tmp, j; + j = secp256k1_testrand_int(i + 1); + tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} + +void rand_xonly_pk(secp256k1_xonly_pubkey *pk) { + unsigned char seckey[32]; + secp256k1_keypair keypair; + secp256k1_testrand256(seckey); + CHECK(secp256k1_keypair_create(ctx, &keypair, seckey) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair) == 1); +} + +void test_xonly_sort_api(void) { + int ecount = 0; + secp256k1_xonly_pubkey pks[2]; + const secp256k1_xonly_pubkey *pks_ptr[2]; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + + pks_ptr[0] = &pks[0]; + pks_ptr[1] = &pks[1]; + + rand_xonly_pk(&pks[0]); + rand_xonly_pk(&pks[1]); + + CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1); + CHECK(secp256k1_xonly_sort(none, NULL, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_sort(none, pks_ptr, 0) == 1); + /* Test illegal public keys */ + memset(&pks[0], 0, sizeof(pks[0])); + CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1); + CHECK(ecount == 2); + memset(&pks[1], 0, sizeof(pks[1])); + CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1); + CHECK(ecount > 2); + + secp256k1_context_destroy(none); +} + +void test_xonly_sort(void) { + secp256k1_xonly_pubkey pk[5]; + unsigned char pk_ser[5][32]; + int i; + size_t pk_order[5] = { 0, 1, 2, 3, 4 }; + + for (i = 0; i < 5; i++) { + memset(pk_ser[i], 0, sizeof(pk_ser[i])); + } + pk_ser[0][0] = 5; + pk_ser[1][0] = 8; + pk_ser[2][0] = 0x0a; + pk_ser[3][0] = 0x0b; + pk_ser[4][0] = 0x0c; + for (i = 0; i < 5; i++) { + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i])); + } + + permute(pk_order, 1); + test_xonly_sort_helper(pk, pk_order, 1); + permute(pk_order, 2); + test_xonly_sort_helper(pk, pk_order, 2); + permute(pk_order, 3); + test_xonly_sort_helper(pk, pk_order, 3); + for (i = 0; i < count; i++) { + permute(pk_order, 4); + test_xonly_sort_helper(pk, pk_order, 4); + } + for (i = 0; i < count; i++) { + permute(pk_order, 5); + test_xonly_sort_helper(pk, pk_order, 5); + } + /* Check that sorting also works for random pubkeys */ + for (i = 0; i < count; i++) { + int j; + const secp256k1_xonly_pubkey *pk_ptr[5]; + for (j = 0; j < 5; j++) { + rand_xonly_pk(&pk[j]); + pk_ptr[j] = &pk[j]; + } + secp256k1_xonly_sort(ctx, pk_ptr, 5); + for (j = 1; j < 5; j++) { + CHECK(secp256k1_xonly_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], ctx) <= 0); + } + } +} + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); @@ -624,6 +730,8 @@ void run_extrakeys_tests(void) { test_keypair_add(); test_hsort(); + test_xonly_sort_api(); + test_xonly_sort(); } #endif From 9683c8a7eb6cefa070cd1a931d8dee714496ee82 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 15 Jan 2021 13:25:34 +0000 Subject: [PATCH 121/381] musig: add static test vectors for key aggregation --- src/modules/musig/tests_impl.h | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index edd43eac..929f5972 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -950,6 +950,47 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q); } +void musig_test_vectors(void) { + secp256k1_xonly_pubkey combined_pk; + unsigned char combined_pk_ser[32]; + secp256k1_xonly_pubkey pk[2]; + const unsigned char pk_ser1[32] = { + 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, + 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, + 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, + 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 + }; + const unsigned char pk_ser2[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char combined_pk_expected[32] = { + 0xD5, 0x60, 0x83, 0x72, 0xAE, 0x3C, 0xA2, 0x56, + 0xEF, 0x51, 0xF8, 0x91, 0x9C, 0xFD, 0x0F, 0x22, + 0xCD, 0x82, 0x93, 0x43, 0x95, 0x01, 0x06, 0x4E, + 0xBE, 0xE4, 0xBB, 0x12, 0xC6, 0xE7, 0xDE, 0xE2, + }; + + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[0], pk_ser1)); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[1], pk_ser2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pk, 2) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, combined_pk_ser, &combined_pk)); + /* TODO: remove */ + /* int i, j; */ + /* printf("const unsigned char combined_pk_expected[32] = {\n"); */ + /* for (i = 0; i < 4; i++) { */ + /* printf(" "); */ + /* for (j = 0; j < 8; j++) { */ + /* printf("0x%02X, ", combined_pk_ser[i*8+j]); */ + /* } */ + /* printf("\n"); */ + /* } */ + /* printf("};\n"); */ + CHECK(secp256k1_memcmp_var(combined_pk_ser, combined_pk_expected, sizeof(combined_pk_ser)) == 0); +} + void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); @@ -965,6 +1006,7 @@ void run_musig_tests(void) { scriptless_atomic_swap(scratch); musig_tweak_test(scratch); } + musig_test_vectors(); sha256_tag_test(); secp256k1_scratch_space_destroy(ctx, scratch); From 2310849f50fa71f10ebd2f44669330f7ce76fc94 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 16 Mar 2021 23:07:58 +0000 Subject: [PATCH 122/381] musig: compute musig coefficient by hashing key instead of index --- include/secp256k1_musig.h | 7 +--- src/modules/musig/example.c | 2 +- src/modules/musig/main_impl.h | 54 ++++++++++---------------- src/modules/musig/tests_impl.h | 70 +++++++++++++++++----------------- 4 files changed, 58 insertions(+), 75 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index b86eeca9..ef7e3eec 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -94,7 +94,7 @@ typedef struct { * The workflow for this structure is as follows: * * 1. This structure is initialized with `musig_session_init` or - * `musig_session_init_verifier`, which set the `index` field, and zero out + * `musig_session_init_verifier`, which initializes * all other fields. The public session is initialized with the signers' * nonce_commitments. * @@ -111,14 +111,12 @@ typedef struct { * * Fields: * present: indicates whether the signer's nonce is set - * index: index of the signer in the MuSig key aggregation * nonce: public nonce, must be a valid curvepoint if the signer is `present` * nonce_commitment: commitment to the nonce, or all-bits zero if a commitment * has not yet been set */ typedef struct { int present; - uint32_t index; secp256k1_xonly_pubkey nonce; unsigned char nonce_commitment[32]; } secp256k1_musig_session_signer_data; @@ -227,8 +225,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( * `musig_pubkey_tweak_add` (cannot be NULL). * n_signers: length of signers array. Number of signers participating in * the MuSig. Must be greater than 0 and at most 2^32 - 1. - * my_index: index of this signer in the signers array. Must be less - * than `n_signers`. * seckey: the signer's 32-byte secret key (cannot be NULL) */ SECP256K1_API int secp256k1_musig_session_init( @@ -241,7 +237,6 @@ SECP256K1_API int secp256k1_musig_session_init( const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, - size_t my_index, const unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index fa3f5833..2cdc5565 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -72,7 +72,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } fclose(frand); /* Initialize session */ - if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { + if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, seckeys[i])) { return 0; } nonce_commitment_ptr[i] = &nonce_commitment[i][0]; diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index d397eab3..04b0b812 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -45,31 +45,15 @@ static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { sha->bytes = 64; } -/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */ -static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) { +/* Compute r = SHA256(ell, x). Assumes field element x is normalized. */ +static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x) { secp256k1_sha256 sha; unsigned char buf[32]; - size_t i; secp256k1_musig_sha256_init_tagged(&sha); secp256k1_sha256_write(&sha, ell, 32); - /* We're hashing the index of the signer instead of its public key as specified - * in the MuSig paper. This reduces the total amount of data that needs to be - * hashed. - * Additionally, it prevents creating identical musig_coefficients for identical - * public keys. A participant Bob could choose his public key to be the same as - * Alice's, then replay Alice's messages (nonce and partial signature) to create - * a valid partial signature. This is not a problem for MuSig per se, but could - * result in subtle issues with protocols building on threshold signatures. - * With the assumption that public keys are unique, hashing the index is - * equivalent to hashing the public key. Because the public key can be - * identified by the index given the ordered list of public keys (included in - * ell), the index is just a different encoding of the public key.*/ - for (i = 0; i < sizeof(uint32_t); i++) { - unsigned char c = idx; - secp256k1_sha256_write(&sha, &c, 1); - idx >>= 8; - } + secp256k1_fe_get_b32(buf, x); + secp256k1_sha256_write(&sha, buf, 32); secp256k1_sha256_finalize(&sha, buf); secp256k1_scalar_set_b32(r, buf, NULL); } @@ -83,15 +67,17 @@ typedef struct { /* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; - secp256k1_musig_coefficient(sc, ctx->ell, idx); - return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); + if (!secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx])) { + return 0; + } + secp256k1_musig_coefficient(sc, ctx->ell, &pt->x); + return 1; } static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { uint32_t i; for (i = 0; i < n_signers; i++) { memset(&signers[i], 0, sizeof(signers[i])); - signers[i].index = i; signers[i].present = 0; } } @@ -159,7 +145,7 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_mus static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL; -int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { +int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, const unsigned char *seckey) { unsigned char combined_ser[32]; int overflow; secp256k1_scalar secret; @@ -183,7 +169,6 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s ARG_CHECK(n_signers > 0); ARG_CHECK(n_signers <= UINT32_MAX); - ARG_CHECK(my_index < n_signers); memset(session, 0, sizeof(*session)); @@ -206,7 +191,11 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s secp256k1_scalar_clear(&secret); return 0; } - secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_fe_normalize_var(&p.x); + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, &p.x); /* Compute the signer's public key point and determine if the secret is * negated before signing. That happens if if the signer's pubkey has an odd * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR @@ -226,9 +215,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s * b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1 * otherwise. */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); - secp256k1_ge_set_gej(&p, &pj); - secp256k1_fe_normalize(&p.y); + secp256k1_fe_normalize_var(&p.y); if((secp256k1_fe_is_odd(&p.y) + session->pre_session.pk_parity + (session->pre_session.is_tweaked @@ -596,10 +583,13 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_musig_compute_messagehash(ctx, msghash, session); secp256k1_scalar_set_b32(&e, msghash, NULL); + if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { + return 0; + } /* Multiplying the messagehash by the musig coefficient is equivalent * to multiplying the signer's public key by the coefficient, except * much easier to do. */ - secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index); + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, &pkp.x); secp256k1_scalar_mul(&e, &e, &mu); if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { @@ -619,9 +609,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* Compute rj = s*G + (-e)*pkj */ secp256k1_scalar_negate(&e, &e); - if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { - return 0; - } + secp256k1_gej_set_ge(&pkj, &pkp); secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 929f5972..79faaffc 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -45,8 +45,8 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; @@ -208,47 +208,47 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Session creation **/ ecount = 0; - CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, sk[0]) == 1); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, sk[0]) == 0); CHECK(ecount == 7); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, sk[0]) == 0); CHECK(ecount == 8); /* Uninitialized pre_session */ - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, sk[0]) == 0); CHECK(ecount == 9); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, sk[0]) == 0); CHECK(ecount == 10); /* If more than UINT32_MAX fits in a size_t, test that session_init * rejects n_signers that high. */ if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, sk[0]) == 0); CHECK(ecount == 11); } else { ecount = 11; } - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, NULL) == 0); CHECK(ecount == 12); /* secret key overflows */ - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, ones) == 0); CHECK(ecount == 12); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; @@ -526,9 +526,9 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 pks_tmp[0] = pks[0]; CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, sk_dummy) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1); CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); /* Call get_public_nonce with different signers than the signers the session was * initialized with. */ @@ -557,7 +557,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey * /* Initialize new signers */ secp256k1_testrand256(session_id); - CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1); @@ -589,7 +589,7 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo secp256k1_musig_partial_signature partial_sig; secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; @@ -650,8 +650,8 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); /* Can't combine nonces unless we're through round 1 already */ ecount = 0; CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0); @@ -774,13 +774,13 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); - CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); - CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[0])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[1])); noncommit_a_ptr[0] = noncommit_a[0]; noncommit_a_ptr[1] = noncommit_a[1]; - CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); - CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, seckey_b[0])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, seckey_b[1])); noncommit_b_ptr[0] = noncommit_b[0]; noncommit_b_ptr[1] = noncommit_b[1]; @@ -881,8 +881,8 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, cons CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, 0, sk0) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, 1, sk1) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, sk0) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, sk1) == 1); /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; @@ -967,10 +967,10 @@ void musig_test_vectors(void) { 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }; const unsigned char combined_pk_expected[32] = { - 0xD5, 0x60, 0x83, 0x72, 0xAE, 0x3C, 0xA2, 0x56, - 0xEF, 0x51, 0xF8, 0x91, 0x9C, 0xFD, 0x0F, 0x22, - 0xCD, 0x82, 0x93, 0x43, 0x95, 0x01, 0x06, 0x4E, - 0xBE, 0xE4, 0xBB, 0x12, 0xC6, 0xE7, 0xDE, 0xE2, + 0x4B, 0xFC, 0x12, 0x07, 0x07, 0x7D, 0x48, 0xEC, + 0x99, 0x98, 0xD4, 0xD4, 0xFA, 0x62, 0xD9, 0x9A, + 0x2F, 0x59, 0x1A, 0x4A, 0xC6, 0x19, 0xEC, 0xFD, + 0xA6, 0x82, 0x5D, 0xCC, 0xDF, 0xA0, 0x79, 0xF9, }; CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[0], pk_ser1)); From 4bc46d836e7877715db54ee039ade407ee44ea45 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 16 Mar 2021 23:12:12 +0000 Subject: [PATCH 123/381] musig: optimize key aggregation using const 1 for 2nd key --- include/secp256k1_musig.h | 3 + src/modules/musig/main_impl.h | 51 ++++++++--- src/modules/musig/tests_impl.h | 151 +++++++++++++++++++++++++++------ 3 files changed, 166 insertions(+), 39 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index ef7e3eec..4ff51905 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -25,6 +25,8 @@ extern "C" { * magic: Set during initialization in `pubkey_combine` to allow * detecting an uninitialized object. * pk_hash: The 32-byte hash of the original public keys + * second_pk: Serialized x-coordinate of the second public key in the list. + * Filled with zeros if there is none. * pk_parity: Whether the MuSig-aggregated point was negated when * converting it to the combined xonly pubkey. * is_tweaked: Whether the combined pubkey was tweaked @@ -35,6 +37,7 @@ extern "C" { typedef struct { uint64_t magic; unsigned char pk_hash[32]; + unsigned char second_pk[32]; int pk_parity; int is_tweaked; unsigned char tweak[32]; diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 04b0b812..01a47195 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -45,23 +45,37 @@ static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { sha->bytes = 64; } -/* Compute r = SHA256(ell, x). Assumes field element x is normalized. */ -static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x) { +/* Compute MuSig coefficient which is constant 1 for the second pubkey and + * SHA256(ell, x) otherwise. second_pk_x can be NULL in case there is no + * second_pk. Assumes both field elements x and second_pk_x are normalized. */ +static void secp256k1_musig_coefficient_internal(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x, const secp256k1_fe *second_pk_x) { secp256k1_sha256 sha; unsigned char buf[32]; - secp256k1_musig_sha256_init_tagged(&sha); - secp256k1_sha256_write(&sha, ell, 32); - secp256k1_fe_get_b32(buf, x); - secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(r, buf, NULL); + if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) { + secp256k1_scalar_set_int(r, 1); + } else { + secp256k1_musig_sha256_init_tagged(&sha); + secp256k1_sha256_write(&sha, ell, 32); + secp256k1_fe_get_b32(buf, x); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(r, buf, NULL); + } +} + +/* Assumes both field elements x and second_pk_x are normalized. */ +static void secp256k1_musig_coefficient(secp256k1_scalar *r, const secp256k1_musig_pre_session *pre_session, secp256k1_fe *x) { + secp256k1_fe second_pk_x; + secp256k1_fe_set_b32(&second_pk_x, pre_session->second_pk); + secp256k1_musig_coefficient_internal(r, pre_session->pk_hash, x, &second_pk_x); } typedef struct { const secp256k1_context *ctx; unsigned char ell[32]; const secp256k1_xonly_pubkey *pks; + secp256k1_fe second_pk_x; } secp256k1_musig_pubkey_combine_ecmult_data; /* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ @@ -70,7 +84,7 @@ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256 if (!secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx])) { return 0; } - secp256k1_musig_coefficient(sc, ctx->ell, &pt->x); + secp256k1_musig_coefficient_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x); return 1; } @@ -89,6 +103,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat secp256k1_gej pkj; secp256k1_ge pkp; int pk_parity; + size_t i; VERIFY_CHECK(ctx != NULL); ARG_CHECK(combined_pk != NULL); @@ -98,6 +113,19 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat ecmult_data.ctx = ctx; ecmult_data.pks = pubkeys; + /* No point on the curve has an X coordinate equal to 0 */ + secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0); + for (i = 1; i < n_pubkeys; i++) { + secp256k1_ge pt; + if (!secp256k1_xonly_pubkey_load(ctx, &pt, &pubkeys[i])) { + return 0; + } + if (secp256k1_memcmp_var(&pubkeys[0], &pubkeys[i], sizeof(pubkeys[0])) != 0) { + ecmult_data.second_pk_x = pt.x; + break; + } + } + if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) { return 0; } @@ -114,6 +142,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat memcpy(pre_session->pk_hash, ecmult_data.ell, 32); pre_session->pk_parity = pk_parity; pre_session->is_tweaked = 0; + secp256k1_fe_get_b32(pre_session->second_pk, &ecmult_data.second_pk_x); } return 1; } @@ -195,7 +224,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ge_set_gej(&p, &pj); secp256k1_fe_normalize_var(&p.x); - secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, &p.x); + secp256k1_musig_coefficient(&mu, &session->pre_session, &p.x); /* Compute the signer's public key point and determine if the secret is * negated before signing. That happens if if the signer's pubkey has an odd * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR @@ -589,7 +618,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* Multiplying the messagehash by the musig coefficient is equivalent * to multiplying the signer's public key by the coefficient, except * much easier to do. */ - secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, &pkp.x); + secp256k1_musig_coefficient(&mu, &session->pre_session, &pkp.x); secp256k1_scalar_mul(&e, &e, &mu); if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 79faaffc..df2b9bcd 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -950,45 +950,140 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q); } -void musig_test_vectors(void) { +void musig_test_vectors_helper(unsigned char pk_ser[][32], int n_pks, const unsigned char *combined_pk_expected, int has_second_pk, int second_pk_idx) { + secp256k1_xonly_pubkey *pk = malloc(n_pks * sizeof(secp256k1_xonly_pubkey)); secp256k1_xonly_pubkey combined_pk; unsigned char combined_pk_ser[32]; - secp256k1_xonly_pubkey pk[2]; - const unsigned char pk_ser1[32] = { - 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, - 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, - 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, - 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 - }; - const unsigned char pk_ser2[32] = { - 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, - 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, - 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, - 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 - }; - const unsigned char combined_pk_expected[32] = { - 0x4B, 0xFC, 0x12, 0x07, 0x07, 0x7D, 0x48, 0xEC, - 0x99, 0x98, 0xD4, 0xD4, 0xFA, 0x62, 0xD9, 0x9A, - 0x2F, 0x59, 0x1A, 0x4A, 0xC6, 0x19, 0xEC, 0xFD, - 0xA6, 0x82, 0x5D, 0xCC, 0xDF, 0xA0, 0x79, 0xF9, - }; + secp256k1_musig_pre_session pre_session; + secp256k1_fe second_pk_x; + int i; - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[0], pk_ser1)); - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[1], pk_ser2)); - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pk, 2) == 1); + for (i = 0; i < n_pks; i++) { + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i])); + } + + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pk, n_pks) == 1); + CHECK(secp256k1_fe_set_b32(&second_pk_x, pre_session.second_pk)); + CHECK(secp256k1_fe_is_zero(&second_pk_x) == !has_second_pk); + if (!secp256k1_fe_is_zero(&second_pk_x)) { + CHECK(secp256k1_memcmp_var(&pk_ser[second_pk_idx], &pre_session.second_pk, sizeof(pk_ser[second_pk_idx])) == 0); + } CHECK(secp256k1_xonly_pubkey_serialize(ctx, combined_pk_ser, &combined_pk)); - /* TODO: remove */ - /* int i, j; */ + /* TODO: remove when test vectors are not expected to change anymore */ + /* int k, l; */ /* printf("const unsigned char combined_pk_expected[32] = {\n"); */ - /* for (i = 0; i < 4; i++) { */ + /* for (k = 0; k < 4; k++) { */ /* printf(" "); */ - /* for (j = 0; j < 8; j++) { */ - /* printf("0x%02X, ", combined_pk_ser[i*8+j]); */ + /* for (l = 0; l < 8; l++) { */ + /* printf("0x%02X, ", combined_pk_ser[k*8+l]); */ /* } */ /* printf("\n"); */ /* } */ /* printf("};\n"); */ CHECK(secp256k1_memcmp_var(combined_pk_ser, combined_pk_expected, sizeof(combined_pk_ser)) == 0); + free(pk); +} + +void musig_test_vectors(void) { + size_t i; + unsigned char pk_ser_tmp[4][32]; + unsigned char pk_ser[3][32] = { + /* X1 */ + { + 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, + 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, + 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, + 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 + }, + /* X2 */ + { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }, + /* X3 */ + { + 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, + 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, + 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, + 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 + } + }; + const unsigned char combined_pk_expected[4][32] = { + { /* 0 */ + 0xF1, 0x94, 0x7D, 0x65, 0x53, 0x3A, 0x1D, 0x9E, + 0x46, 0xDD, 0x16, 0x60, 0x3C, 0x95, 0x04, 0x66, + 0x34, 0x31, 0xDC, 0x7E, 0xF8, 0x3B, 0x64, 0xC9, + 0xD5, 0x1C, 0xE6, 0x71, 0x8E, 0x6E, 0x57, 0x1C, + }, + { /* 1 */ + 0xA5, 0x1C, 0x71, 0x3F, 0xD4, 0xC3, 0x29, 0xCD, + 0x6D, 0x35, 0x69, 0xBC, 0x36, 0x67, 0xE4, 0x9A, + 0xC6, 0xD4, 0x75, 0x4E, 0xC2, 0x66, 0x25, 0xED, + 0x12, 0x2B, 0x24, 0x28, 0x40, 0x57, 0xC9, 0xD4, + }, + { /* 2 */ + 0xA0, 0xFD, 0x5D, 0x2F, 0xCC, 0x4F, 0x90, 0xDF, + 0x42, 0xD4, 0x26, 0x38, 0x31, 0x73, 0x0B, 0x21, + 0xC4, 0xAB, 0x0E, 0xFA, 0xD2, 0x09, 0x10, 0xD0, + 0x07, 0xED, 0xCB, 0x69, 0x1D, 0xD5, 0xD1, 0x82, + }, + { /* 3 */ + 0x2E, 0x58, 0x3B, 0x3C, 0x30, 0x8B, 0x14, 0x28, + 0x81, 0x36, 0x57, 0x9B, 0x3A, 0x63, 0xDB, 0x71, + 0x82, 0xB0, 0xFB, 0xE6, 0xE4, 0x25, 0xE7, 0xD0, + 0x30, 0x68, 0xC5, 0x9C, 0xFC, 0xAD, 0x12, 0xF3, + }, + }; + + for (i = 0; i < sizeof(combined_pk_expected)/sizeof(combined_pk_expected[0]); i++) { + size_t n_pks; + int has_second_pk; + int second_pk_idx; + switch (i) { + case 0: + /* [X1, X2, X3] */ + n_pks = 3; + memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0])); + memcpy(pk_ser_tmp[1], pk_ser[1], sizeof(pk_ser_tmp[1])); + memcpy(pk_ser_tmp[2], pk_ser[2], sizeof(pk_ser_tmp[2])); + has_second_pk = 1; + second_pk_idx = 1; + break; + case 1: + /* [X3, X2, X1] */ + n_pks = 3; + memcpy(pk_ser_tmp[2], pk_ser[0], sizeof(pk_ser_tmp[0])); + memcpy(pk_ser_tmp[1], pk_ser[1], sizeof(pk_ser_tmp[1])); + memcpy(pk_ser_tmp[0], pk_ser[2], sizeof(pk_ser_tmp[2])); + has_second_pk = 1; + second_pk_idx = 1; + break; + case 2: + /* [X1, X1, X1] */ + n_pks = 3; + memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0])); + memcpy(pk_ser_tmp[1], pk_ser[0], sizeof(pk_ser_tmp[1])); + memcpy(pk_ser_tmp[2], pk_ser[0], sizeof(pk_ser_tmp[2])); + has_second_pk = 0; + second_pk_idx = 0; /* unchecked */ + break; + case 3: + /* [X1, X1, X2, X2] */ + n_pks = 4; + memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0])); + memcpy(pk_ser_tmp[1], pk_ser[0], sizeof(pk_ser_tmp[1])); + memcpy(pk_ser_tmp[2], pk_ser[1], sizeof(pk_ser_tmp[2])); + memcpy(pk_ser_tmp[3], pk_ser[1], sizeof(pk_ser_tmp[3])); + has_second_pk = 1; + second_pk_idx = 3; + break; + default: + CHECK(0); + } + musig_test_vectors_helper(pk_ser_tmp, n_pks, combined_pk_expected[i], has_second_pk, second_pk_idx); + } } void run_musig_tests(void) { From 4a9b059b16d7925a03bd0d695efa1637ad7e9826 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 8 Jul 2021 17:06:24 +0000 Subject: [PATCH 124/381] musig: rename Musig coefficient to KeyAgg coefficient This is done to be consistent with the MuSig2 paper --- src/modules/musig/main_impl.h | 36 ++++++++++++++++---------------- src/modules/musig/tests_impl.h | 38 +++++++++++++++++----------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 01a47195..6cb2b14b 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -30,25 +30,25 @@ static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned ch } /* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */ + * SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */ static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { secp256k1_sha256_initialize(sha); - sha->s[0] = 0x0fd0690cul; - sha->s[1] = 0xfefeae97ul; - sha->s[2] = 0x996eac7ful; - sha->s[3] = 0x5c30d864ul; - sha->s[4] = 0x8c4a0573ul; - sha->s[5] = 0xaca1a22ful; - sha->s[6] = 0x6f43b801ul; - sha->s[7] = 0x85ce27cdul; + sha->s[0] = 0x6ef02c5aul; + sha->s[1] = 0x06a480deul; + sha->s[2] = 0x1f298665ul; + sha->s[3] = 0x1d1134f2ul; + sha->s[4] = 0x56a0b063ul; + sha->s[5] = 0x52da4147ul; + sha->s[6] = 0xf280d9d4ul; + sha->s[7] = 0x4484be15ul; sha->bytes = 64; } -/* Compute MuSig coefficient which is constant 1 for the second pubkey and +/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and * SHA256(ell, x) otherwise. second_pk_x can be NULL in case there is no * second_pk. Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_coefficient_internal(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x, const secp256k1_fe *second_pk_x) { +static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x, const secp256k1_fe *second_pk_x) { secp256k1_sha256 sha; unsigned char buf[32]; @@ -65,10 +65,10 @@ static void secp256k1_musig_coefficient_internal(secp256k1_scalar *r, const unsi } /* Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_coefficient(secp256k1_scalar *r, const secp256k1_musig_pre_session *pre_session, secp256k1_fe *x) { +static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_musig_pre_session *pre_session, secp256k1_fe *x) { secp256k1_fe second_pk_x; secp256k1_fe_set_b32(&second_pk_x, pre_session->second_pk); - secp256k1_musig_coefficient_internal(r, pre_session->pk_hash, x, &second_pk_x); + secp256k1_musig_keyaggcoef_internal(r, pre_session->pk_hash, x, &second_pk_x); } typedef struct { @@ -84,7 +84,7 @@ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256 if (!secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx])) { return 0; } - secp256k1_musig_coefficient_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x); + secp256k1_musig_keyaggcoef_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x); return 1; } @@ -224,7 +224,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ge_set_gej(&p, &pj); secp256k1_fe_normalize_var(&p.x); - secp256k1_musig_coefficient(&mu, &session->pre_session, &p.x); + secp256k1_musig_keyaggcoef(&mu, &session->pre_session, &p.x); /* Compute the signer's public key point and determine if the secret is * negated before signing. That happens if if the signer's pubkey has an odd * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR @@ -233,7 +233,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s * This can be seen by looking at the secret key belonging to `combined_pk`. * Let's define * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key - * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function + * point x_i*G, mu_i is the i-th KeyAgg coefficient and |.| is a function * that normalizes a point to an even Y by negating if necessary similar to * secp256k1_extrakeys_ge_even_y. Then we have * P := |P'| + t*G where t is the tweak. @@ -615,10 +615,10 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { return 0; } - /* Multiplying the messagehash by the musig coefficient is equivalent + /* Multiplying the messagehash by the KeyAgg coefficient is equivalent * to multiplying the signer's public key by the coefficient, except * much easier to do. */ - secp256k1_musig_coefficient(&mu, &session->pre_session, &pkp.x); + secp256k1_musig_keyaggcoef(&mu, &session->pre_session, &pkp.x); secp256k1_scalar_mul(&e, &e, &mu); if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index df2b9bcd..f529a158 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -829,7 +829,7 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { /* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the * expected state. */ void sha256_tag_test(void) { - char tag[17] = "MuSig coefficient"; + char tag[18] = "KeyAgg coefficient"; secp256k1_sha256 sha; secp256k1_sha256 sha_tagged; unsigned char buf[32]; @@ -837,9 +837,9 @@ void sha256_tag_test(void) { size_t i; secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, (unsigned char *) tag, 17); + secp256k1_sha256_write(&sha, (unsigned char *) tag, sizeof(tag)); secp256k1_sha256_finalize(&sha, buf); - /* buf = SHA256("MuSig coefficient") */ + /* buf = SHA256("KeyAgg coefficient") */ secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, buf, 32); @@ -1012,28 +1012,28 @@ void musig_test_vectors(void) { }; const unsigned char combined_pk_expected[4][32] = { { /* 0 */ - 0xF1, 0x94, 0x7D, 0x65, 0x53, 0x3A, 0x1D, 0x9E, - 0x46, 0xDD, 0x16, 0x60, 0x3C, 0x95, 0x04, 0x66, - 0x34, 0x31, 0xDC, 0x7E, 0xF8, 0x3B, 0x64, 0xC9, - 0xD5, 0x1C, 0xE6, 0x71, 0x8E, 0x6E, 0x57, 0x1C, + 0xEA, 0x06, 0x7B, 0x01, 0x67, 0x24, 0x5A, 0x6F, + 0xED, 0xB1, 0xB1, 0x22, 0xBB, 0x03, 0xAB, 0x7E, + 0x5D, 0x48, 0x6C, 0x81, 0x83, 0x42, 0xE0, 0xE9, + 0xB6, 0x41, 0x79, 0xAD, 0x32, 0x8D, 0x9D, 0x19, }, { /* 1 */ - 0xA5, 0x1C, 0x71, 0x3F, 0xD4, 0xC3, 0x29, 0xCD, - 0x6D, 0x35, 0x69, 0xBC, 0x36, 0x67, 0xE4, 0x9A, - 0xC6, 0xD4, 0x75, 0x4E, 0xC2, 0x66, 0x25, 0xED, - 0x12, 0x2B, 0x24, 0x28, 0x40, 0x57, 0xC9, 0xD4, + 0x14, 0xE1, 0xF8, 0x3E, 0x9E, 0x25, 0x60, 0xFB, + 0x2A, 0x6C, 0x04, 0x24, 0x55, 0x6C, 0x86, 0x8D, + 0x9F, 0xB4, 0x63, 0x35, 0xD4, 0xF7, 0x8D, 0x22, + 0x7D, 0x5D, 0x1D, 0x3C, 0x89, 0x90, 0x6F, 0x1E, }, { /* 2 */ - 0xA0, 0xFD, 0x5D, 0x2F, 0xCC, 0x4F, 0x90, 0xDF, - 0x42, 0xD4, 0x26, 0x38, 0x31, 0x73, 0x0B, 0x21, - 0xC4, 0xAB, 0x0E, 0xFA, 0xD2, 0x09, 0x10, 0xD0, - 0x07, 0xED, 0xCB, 0x69, 0x1D, 0xD5, 0xD1, 0x82, + 0x70, 0x28, 0x8D, 0xF2, 0xB7, 0x60, 0x3D, 0xBE, + 0xA0, 0xC7, 0xB7, 0x41, 0xDD, 0xAA, 0xB9, 0x46, + 0x81, 0x14, 0x4E, 0x0B, 0x19, 0x08, 0x6C, 0x69, + 0xB2, 0x34, 0x89, 0xE4, 0xF5, 0xB7, 0x01, 0x9A, }, { /* 3 */ - 0x2E, 0x58, 0x3B, 0x3C, 0x30, 0x8B, 0x14, 0x28, - 0x81, 0x36, 0x57, 0x9B, 0x3A, 0x63, 0xDB, 0x71, - 0x82, 0xB0, 0xFB, 0xE6, 0xE4, 0x25, 0xE7, 0xD0, - 0x30, 0x68, 0xC5, 0x9C, 0xFC, 0xAD, 0x12, 0xF3, + 0x93, 0xEE, 0xD8, 0x24, 0xF2, 0x3C, 0x5A, 0xE1, + 0xC1, 0x05, 0xE7, 0x31, 0x09, 0x97, 0x3F, 0xCD, + 0x4A, 0xE3, 0x3A, 0x9F, 0xA0, 0x2F, 0x0A, 0xC8, + 0x5A, 0x3E, 0x55, 0x89, 0x07, 0x53, 0xB0, 0x67, }, }; From 08fa02d579154e26097fd582a409b814ef3dedba Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 Jan 2021 14:21:20 +0000 Subject: [PATCH 125/381] musig: add key aggregation spec draft --- src/modules/musig/musig-spec.mediawiki | 102 +++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/modules/musig/musig-spec.mediawiki diff --git a/src/modules/musig/musig-spec.mediawiki b/src/modules/musig/musig-spec.mediawiki new file mode 100644 index 00000000..a408397a --- /dev/null +++ b/src/modules/musig/musig-spec.mediawiki @@ -0,0 +1,102 @@ +
+  Title: MuSig Key Aggregation
+  Author:
+  Status: Draft
+  License: BSD-2-Clause
+  Created: 2020-01-19
+
+ +== Introduction == + +=== Abstract === + +This document describes MuSig Key Aggregation in libsecp256k1-zkp. + +=== Copyright === + +This document is licensed under the 2-clause BSD license. + +=== Motivation === + +== Description == + +=== Design === + +* A function for sorting public keys allows to aggregate keys independent of the (initial) order. +* The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting. +* The second unique key in the pubkey list gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). + + +=== Specification === + +The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure schemeAmong other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.. +* Lowercase variables represent integers or byte arrays. +** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''. +** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''. +* Uppercase variables refer to points on the curve with equation ''y2 = x3 + 7'' over the integers modulo ''p''. +** ''is_infinite(P)'' returns whether or not ''P'' is the point at infinity. +** ''x(P)'' and ''y(P)'' are integers in the range ''0..p-1'' and refer to the X and Y coordinates of a point ''P'' (assuming it is not infinity). +** The constant ''G'' refers to the base point, for which ''x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'' and ''y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8''. +** Addition of points refers to the usual [https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law elliptic curve group operation]. +** [https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication Multiplication (⋅) of an integer and a point] refers to the repeated application of the group operation. +* Functions and operations: +** ''||'' refers to byte array concatenation. +** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. +** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. +** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. +** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. +** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. +** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..p-1'', returns the point ''P'' for which ''x(P) = x'' + Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: +*** Let ''c = x3 + 7 mod p''. +*** Let ''y = c(p+1)/4 mod p''. +*** Fail if ''c ≠ y2 mod p''. +*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise. +** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. + + +==== Key Sorting ==== + +Input: +* The number ''u'' of signatures with ''0 < u < 2^32'' +* The public keys ''pk1..u'': ''u'' 32-byte arrays + +The algorithm ''KeySort(pk1..u)'' is defined as: +* Return ''pk1..u'' sorted in lexicographical order. + +==== Key Aggregation ==== + +Input: +* The number ''u'' of signatures with ''0 < u < 2^32'' +* The public keys ''pk1..u'': ''u'' 32-byte arrays + +The algorithm ''KeyAgg(pk1..u)'' is defined as: +* For ''i = 1 .. u'': +** Let ''ai = KeyAggCoeff(pk1..u, i)''. +** Let ''Pi = lift_x(int(pki))''; fail if it fails. +* Let ''S = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' +* Fail if ''is_infinite(S)''. +* Return ''bytes(S)''. + +The algorithm ''HashKeys(pk1..u)'' is defined as: +* Return ''hash(pk1 || pk2 || ... || pku)'' + +The algorithm ''IsSecond(pk1..u, i)'' is defined as: +* For ''j = 1 .. u'': +** If ''pkj ≠ pk1'': +*** Return ''true'' if ''pkj = pki'', otherwise return ''false''. +* Return ''false'' + +The algorithm ''KeyAggCoeff(pk1..u, i)'' is defined as: +* Let ''L = HashKeys(pk1..u)''. +* Return 1 if ''IsSecond(pk1..u, i)'', otherwise return ''int(hashKeyAgg coefficient(L || pk) mod n''. + +== Applications == + +== Test Vectors and Reference Code == + +== Footnotes == + + + +== Acknowledgements == From 56014e8ca01e88e0fbf2f125363c4e7cc48039df Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 7 May 2021 15:26:33 +0000 Subject: [PATCH 126/381] musig: change pubkey_combine arg to array of pointers to pks ... instead of taking an array of pubkeys directly --- include/secp256k1_musig.h | 8 ++--- src/modules/musig/example.c | 10 +++--- src/modules/musig/main_impl.h | 14 ++++---- src/modules/musig/tests_impl.h | 62 +++++++++++++++++++++++----------- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 4ff51905..b85eacbb 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -154,9 +154,9 @@ typedef struct { * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in * `musig_session_init` or `musig_pubkey_tweak_add`. - * In: pubkeys: input array of public keys to combine. The order is important; - * a different order will result in a different combined public - * key (cannot be NULL) + * In: pubkeys: input array of pointers to public keys to combine. The order + * is important; a different order will result in a different + * combined public key (cannot be NULL) * n_pubkeys: length of pubkeys array. Must be greater than 0. */ SECP256K1_API int secp256k1_musig_pubkey_combine( @@ -164,7 +164,7 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, - const secp256k1_xonly_pubkey *pubkeys, + const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 2cdc5565..dffec850 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -40,7 +40,7 @@ int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k } /* Sign a message hash with the given key pairs and store the result in sig */ -int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msg32, unsigned char *sig64) { +int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey** pubkeys, const unsigned char* msg32, unsigned char *sig64) { secp256k1_musig_session musig_session[N_SIGNERS]; unsigned char nonce_commitment[N_SIGNERS][32]; const unsigned char *nonce_commitment_ptr[N_SIGNERS]; @@ -117,7 +117,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 * fine to first verify the combined sig, and only verify the individual * sigs if it does not work. */ - if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], &pubkeys[j])) { + if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], pubkeys[j])) { return 0; } } @@ -130,6 +130,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 int i; unsigned char seckeys[N_SIGNERS][32]; secp256k1_xonly_pubkey pubkeys[N_SIGNERS]; + const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; secp256k1_xonly_pubkey combined_pk; unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; unsigned char sig[64]; @@ -142,16 +143,17 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 printf("FAILED\n"); return 1; } + pubkeys_ptr[i] = &pubkeys[i]; } printf("ok\n"); printf("Combining public keys..."); - if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys, N_SIGNERS)) { + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys_ptr, N_SIGNERS)) { printf("FAILED\n"); return 1; } printf("ok\n"); printf("Signing message........."); - if (!sign(ctx, seckeys, pubkeys, msg, sig)) { + if (!sign(ctx, seckeys, pubkeys_ptr, msg, sig)) { printf("FAILED\n"); return 1; } diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 6cb2b14b..57d9da16 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -13,14 +13,14 @@ #include "hash.h" /* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey *pk, size_t np) { +static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey * const* pk, size_t np) { secp256k1_sha256 sha; size_t i; secp256k1_sha256_initialize(&sha); for (i = 0; i < np; i++) { unsigned char ser[32]; - if (!secp256k1_xonly_pubkey_serialize(ctx, ser, &pk[i])) { + if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) { return 0; } secp256k1_sha256_write(&sha, ser, 32); @@ -74,14 +74,14 @@ static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_musi typedef struct { const secp256k1_context *ctx; unsigned char ell[32]; - const secp256k1_xonly_pubkey *pks; + const secp256k1_xonly_pubkey * const* pks; secp256k1_fe second_pk_x; } secp256k1_musig_pubkey_combine_ecmult_data; /* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; - if (!secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx])) { + if (!secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx])) { return 0; } secp256k1_musig_keyaggcoef_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x); @@ -98,7 +98,7 @@ static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *si static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL; -int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys) { +int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys) { secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; secp256k1_gej pkj; secp256k1_ge pkp; @@ -117,10 +117,10 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0); for (i = 1; i < n_pubkeys; i++) { secp256k1_ge pt; - if (!secp256k1_xonly_pubkey_load(ctx, &pt, &pubkeys[i])) { + if (!secp256k1_xonly_pubkey_load(ctx, &pt, pubkeys[i])) { return 0; } - if (secp256k1_memcmp_var(&pubkeys[0], &pubkeys[i], sizeof(pubkeys[0])) != 0) { + if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) { ecmult_data.second_pk_x = pt.x; break; } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index f529a158..ff20d15b 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -30,6 +30,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { secp256k1_musig_pre_session pre_session; unsigned char session_id[2][32]; secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; const unsigned char *ncs[2]; unsigned char public_nonce[3][32]; secp256k1_musig_partial_signature partial_sig[2]; @@ -41,10 +42,12 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { secp256k1_testrand256(sk[1]); secp256k1_testrand256(msg); + pk_ptr[0] = &pk[0]; + pk_ptr[1] = &pk[1]; CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); @@ -98,6 +101,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_pre_session pre_session; secp256k1_musig_pre_session pre_session_uninitialized; secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; unsigned char tweak[32]; unsigned char sec_adaptor[32]; @@ -132,6 +136,8 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_testrand256(sec_adaptor); secp256k1_testrand256(tweak); + pk_ptr[0] = &pk[0]; + pk_ptr[1] = &pk[1]; CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); @@ -141,34 +147,34 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /* Key combination */ ecount = 0; - CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); CHECK(ecount == 2); /* pubkey_combine does not require a scratch space */ - CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk_ptr, 2) == 1); CHECK(ecount == 2); /* A small scratch space works too, but will result in using an ineffecient algorithm */ scratch_small = secp256k1_scratch_space_create(ctx, 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk_ptr, 2) == 1); secp256k1_scratch_space_destroy(ctx, scratch_small); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk_ptr, 2) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk_ptr, 2) == 1); CHECK(ecount == 3); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 0) == 0); CHECK(ecount == 5); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); /** Tweaking */ ecount = 0; @@ -517,6 +523,7 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 secp256k1_musig_session_signer_data signers_tmp[2]; unsigned char sk_dummy[32]; secp256k1_xonly_pubkey pks_tmp[2]; + const secp256k1_xonly_pubkey *pks_tmp_ptr[2]; secp256k1_xonly_pubkey combined_pk_tmp; secp256k1_musig_pre_session pre_session_tmp; unsigned char nonce[32]; @@ -525,7 +532,9 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25 secp256k1_testrand256(sk_dummy); pks_tmp[0] = pks[0]; CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); + pks_tmp_ptr[0] = &pks_tmp[0]; + pks_tmp_ptr[1] = &pks_tmp[1]; + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp_ptr, 2) == 1); CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, sk_dummy) == 1); CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1); @@ -625,6 +634,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { unsigned char msg[32]; unsigned char sk[2][32]; secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; secp256k1_xonly_pubkey combined_pk; secp256k1_musig_pre_session pre_session; unsigned char nonce[2][32]; @@ -647,9 +657,11 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { secp256k1_testrand256(sk[0]); secp256k1_testrand256(sk[1]); secp256k1_testrand256(msg); + pk_ptr[0] = &pk[0]; + pk_ptr[1] = &pk[1]; CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); /* Can't combine nonces unless we're through round 1 already */ @@ -736,7 +748,9 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { unsigned char seckey_a[2][32]; unsigned char seckey_b[2][32]; secp256k1_xonly_pubkey pk_a[2]; + const secp256k1_xonly_pubkey *pk_a_ptr[2]; secp256k1_xonly_pubkey pk_b[2]; + const secp256k1_xonly_pubkey *pk_b_ptr[2]; secp256k1_musig_pre_session pre_session_a; secp256k1_musig_pre_session pre_session_b; secp256k1_xonly_pubkey combined_pk_a; @@ -765,14 +779,18 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { secp256k1_testrand256(seckey_b[1]); secp256k1_testrand256(sec_adaptor); + pk_a_ptr[0] = &pk_a[0]; + pk_a_ptr[1] = &pk_a[1]; + pk_b_ptr[0] = &pk_b[0]; + pk_b_ptr[1] = &pk_b[1]; CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0])); CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1])); CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0])); CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1])); CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a_ptr, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b_ptr, 2)); CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[0])); CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[1])); @@ -909,6 +927,7 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, cons void musig_tweak_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; secp256k1_musig_pre_session pre_session_P; secp256k1_musig_pre_session pre_session_Q; secp256k1_xonly_pubkey P; @@ -927,9 +946,11 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { secp256k1_testrand256(sk[1]); secp256k1_testrand256(contract); + pk_ptr[0] = &pk[0]; + pk_ptr[1] = &pk[1]; CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk_ptr, 2) == 1); CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1); secp256k1_sha256_initialize(&sha); @@ -951,7 +972,8 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { } void musig_test_vectors_helper(unsigned char pk_ser[][32], int n_pks, const unsigned char *combined_pk_expected, int has_second_pk, int second_pk_idx) { - secp256k1_xonly_pubkey *pk = malloc(n_pks * sizeof(secp256k1_xonly_pubkey)); + secp256k1_xonly_pubkey *pk = malloc(n_pks * sizeof(*pk)); + const secp256k1_xonly_pubkey **pk_ptr = malloc(n_pks * sizeof(*pk_ptr)); secp256k1_xonly_pubkey combined_pk; unsigned char combined_pk_ser[32]; secp256k1_musig_pre_session pre_session; @@ -960,9 +982,10 @@ void musig_test_vectors_helper(unsigned char pk_ser[][32], int n_pks, const unsi for (i = 0; i < n_pks; i++) { CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i])); + pk_ptr[i] = &pk[i]; } - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pk, n_pks) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pk_ptr, n_pks) == 1); CHECK(secp256k1_fe_set_b32(&second_pk_x, pre_session.second_pk)); CHECK(secp256k1_fe_is_zero(&second_pk_x) == !has_second_pk); if (!secp256k1_fe_is_zero(&second_pk_x)) { @@ -982,6 +1005,7 @@ void musig_test_vectors_helper(unsigned char pk_ser[][32], int n_pks, const unsi /* printf("};\n"); */ CHECK(secp256k1_memcmp_var(combined_pk_ser, combined_pk_expected, sizeof(combined_pk_ser)) == 0); free(pk); + free(pk_ptr); } void musig_test_vectors(void) { From f27fd1d5e754fc9b919d9c9f6e47a6eb8c9e2af7 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 12 May 2021 20:37:41 +0000 Subject: [PATCH 127/381] musig: improve test coverage of pubkey_combine --- src/modules/musig/main_impl.h | 9 ++++++--- src/modules/musig/tests_impl.h | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 57d9da16..fc769348 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -81,9 +81,11 @@ typedef struct { /* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; - if (!secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx])) { - return 0; - } + int ret; + ret = secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); + /* pubkey_load can't fail because the same pks have already been loaded (and + * we test this) */ + VERIFY_CHECK(ret); secp256k1_musig_keyaggcoef_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x); return 1; } @@ -130,6 +132,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat return 0; } if (!secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) { + /* The current implementation of ecmult_multi_var makes this code unreachable with tests. */ return 0; } secp256k1_ge_set_gej(&pkp, &pkj); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ff20d15b..eb7527fe 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -102,11 +102,15 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_pre_session pre_session_uninitialized; secp256k1_xonly_pubkey pk[2]; const secp256k1_xonly_pubkey *pk_ptr[2]; + secp256k1_xonly_pubkey invalid_pk; + const secp256k1_xonly_pubkey *invalid_pk_ptr2[2]; + const secp256k1_xonly_pubkey *invalid_pk_ptr3[3]; unsigned char tweak[32]; unsigned char sec_adaptor[32]; unsigned char sec_adaptor1[32]; secp256k1_pubkey adaptor; + int i; /** setup **/ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -127,6 +131,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { * structs. */ memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); memset(&session_uninitialized, 0, sizeof(session_uninitialized)); + memset(&invalid_pk, 0, sizeof(invalid_pk)); secp256k1_testrand256(session_id[0]); secp256k1_testrand256(session_id[1]); @@ -142,6 +147,13 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); + for (i = 0; i < 2; i++) { + invalid_pk_ptr2[i] = &invalid_pk; + invalid_pk_ptr3[i] = &pk[i]; + } + /* invalid_pk_ptr3 has two valid, one invalid pk, which is important to test + * musig_pubkeys_combine */ + invalid_pk_ptr3[2] = &invalid_pk; /** main test body **/ @@ -167,10 +179,14 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 3); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, invalid_pk_ptr2, 2) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, invalid_pk_ptr3, 3) == 0); CHECK(ecount == 6); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 0) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); + CHECK(ecount == 8); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); From 5860b5e0fe78b2bd34c1defb6ce3ad879029463e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 3 Apr 2021 22:03:09 +0000 Subject: [PATCH 128/381] musig: do not also require schnorrsig module config flag Also add musig to build options output. --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 849200af..c518fd71 100644 --- a/configure.ac +++ b/configure.ac @@ -492,6 +492,7 @@ fi if test x"$enable_module_musig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) + enable_module_schnorrsig=yes fi if test x"$enable_module_recovery" = x"yes"; then @@ -513,7 +514,8 @@ fi if test x"$enable_module_surjectionproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SURJECTIONPROOF, 1, [Define this symbol to enable the surjection proof module]) fi - +# Test if extrakeys is set _after_ the MuSig module to allow the MuSig +# module to set enable_module_schnorrsig=yes if test x"$enable_module_schnorrsig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) enable_module_extrakeys=yes @@ -663,6 +665,7 @@ 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 ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" echo From 48f63efe683bf5539324a52fa43f4a2a32285a91 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 12 May 2021 17:52:32 +0000 Subject: [PATCH 129/381] musig: remove unnecessary branch in pubkey_tweak_add --- src/modules/musig/main_impl.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index fc769348..300c4445 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -152,6 +152,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { secp256k1_ge pk; + int ret; VERIFY_CHECK(ctx != NULL); ARG_CHECK(pre_session != NULL); @@ -168,9 +169,10 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_mus memcpy(pre_session->tweak, tweak32, 32); pre_session->is_tweaked = 1; - if (!secp256k1_pubkey_load(ctx, &pk, output_pubkey)) { - return 0; - } + ret = secp256k1_pubkey_load(ctx, &pk, output_pubkey); + /* Successful xonly_pubkey_tweak_add always returns valid output_pubkey */ + VERIFY_CHECK(ret); + pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk); return 1; } From fc26ca8ddef0629c7df190f1cc92157fce64e370 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 13 May 2021 18:02:29 +0000 Subject: [PATCH 130/381] musig: remove unnecessary constant time normalize in combine --- src/modules/musig/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 300c4445..6556b060 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -136,7 +136,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat return 0; } secp256k1_ge_set_gej(&pkp, &pkj); - secp256k1_fe_normalize(&pkp.y); + secp256k1_fe_normalize_var(&pkp.y); pk_parity = secp256k1_extrakeys_ge_even_y(&pkp); secp256k1_xonly_pubkey_save(combined_pk, &pkp); From a6a768a4bf3a243609e508c492307cb0fe754bda Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 27 Jul 2021 10:06:22 +0000 Subject: [PATCH 131/381] musig: make key agg test vector more precise --- src/modules/musig/tests_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index eb7527fe..c56ab23b 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -1117,7 +1117,7 @@ void musig_test_vectors(void) { memcpy(pk_ser_tmp[2], pk_ser[1], sizeof(pk_ser_tmp[2])); memcpy(pk_ser_tmp[3], pk_ser[1], sizeof(pk_ser_tmp[3])); has_second_pk = 1; - second_pk_idx = 3; + second_pk_idx = 2; /* second_pk_idx = 3 is equally valid */ break; default: CHECK(0); From 8f093be374da794b835302bfb81a72e2bdd51d26 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 27 Jul 2021 11:37:10 +0000 Subject: [PATCH 132/381] musig: use tagged hash for the list of pubkeys to aggregate This is done to use tagged hashing consistently. Changes the musig test vectors. --- src/modules/musig/main_impl.h | 22 +++++++-- src/modules/musig/musig-spec.mediawiki | 2 +- src/modules/musig/tests_impl.h | 65 +++++++++++++++----------- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 6556b060..b54953bd 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -12,12 +12,28 @@ #include "include/secp256k1_musig.h" #include "hash.h" +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */ +static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + + sha->s[0] = 0xb399d5e0ul; + sha->s[1] = 0xc8fff302ul; + sha->s[2] = 0x6badac71ul; + sha->s[3] = 0x07c5b7f1ul; + sha->s[4] = 0x9701e2eful; + sha->s[5] = 0x2a72ecf8ul; + sha->s[6] = 0x201a4c7bul; + sha->s[7] = 0xab148a38ul; + sha->bytes = 64; +} + /* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey * const* pk, size_t np) { secp256k1_sha256 sha; size_t i; - secp256k1_sha256_initialize(&sha); + secp256k1_musig_keyagglist_sha256(&sha); for (i = 0; i < np; i++) { unsigned char ser[32]; if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) { @@ -31,7 +47,7 @@ static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned ch /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */ -static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { +static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { secp256k1_sha256_initialize(sha); sha->s[0] = 0x6ef02c5aul; @@ -55,7 +71,7 @@ static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsig if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) { secp256k1_scalar_set_int(r, 1); } else { - secp256k1_musig_sha256_init_tagged(&sha); + secp256k1_musig_keyaggcoef_sha256(&sha); secp256k1_sha256_write(&sha, ell, 32); secp256k1_fe_get_b32(buf, x); secp256k1_sha256_write(&sha, buf, 32); diff --git a/src/modules/musig/musig-spec.mediawiki b/src/modules/musig/musig-spec.mediawiki index a408397a..64fa4811 100644 --- a/src/modules/musig/musig-spec.mediawiki +++ b/src/modules/musig/musig-spec.mediawiki @@ -79,7 +79,7 @@ The algorithm ''KeyAgg(pk1..u)'' is defined as: * Return ''bytes(S)''. The algorithm ''HashKeys(pk1..u)'' is defined as: -* Return ''hash(pk1 || pk2 || ... || pku)'' +* Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' The algorithm ''IsSecond(pk1..u, i)'' is defined as: * For ''j = 1 .. u'': diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index c56ab23b..6746bd88 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -860,18 +860,14 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); } -/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the - * expected state. */ -void sha256_tag_test(void) { - char tag[18] = "KeyAgg coefficient"; +void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) { secp256k1_sha256 sha; - secp256k1_sha256 sha_tagged; unsigned char buf[32]; unsigned char buf2[32]; size_t i; secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_sha256_write(&sha, tag, taglen); secp256k1_sha256_finalize(&sha, buf); /* buf = SHA256("KeyAgg coefficient") */ @@ -882,17 +878,32 @@ void sha256_tag_test(void) { CHECK((sha.bytes & 0x3F) == 0); /* Compare with tagged SHA */ - secp256k1_musig_sha256_init_tagged(&sha_tagged); for (i = 0; i < 8; i++) { - CHECK(sha_tagged.s[i] == sha.s[i]); + CHECK(sha_tagged->s[i] == sha.s[i]); } secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_write(&sha_tagged, buf, 32); + secp256k1_sha256_write(sha_tagged, buf, 32); secp256k1_sha256_finalize(&sha, buf); - secp256k1_sha256_finalize(&sha_tagged, buf2); + secp256k1_sha256_finalize(sha_tagged, buf2); CHECK(memcmp(buf, buf2, 32) == 0); } +/* Checks that the initialized tagged hashes initialized have the expected + * state. */ +void sha256_tag_test(void) { + secp256k1_sha256 sha_tagged; + { + char tag[11] = "KeyAgg list"; + secp256k1_musig_keyagglist_sha256(&sha_tagged); + sha256_tag_test_internal(&sha_tagged, (unsigned char*)tag, sizeof(tag)); + } + { + char tag[18] = "KeyAgg coefficient"; + secp256k1_musig_keyaggcoef_sha256(&sha_tagged); + sha256_tag_test_internal(&sha_tagged, (unsigned char*)tag, sizeof(tag)); + } +} + /* Attempts to create a signature for the combined public key using given secret * keys and pre_session. */ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_pre_session *pre_session) { @@ -1052,28 +1063,28 @@ void musig_test_vectors(void) { }; const unsigned char combined_pk_expected[4][32] = { { /* 0 */ - 0xEA, 0x06, 0x7B, 0x01, 0x67, 0x24, 0x5A, 0x6F, - 0xED, 0xB1, 0xB1, 0x22, 0xBB, 0x03, 0xAB, 0x7E, - 0x5D, 0x48, 0x6C, 0x81, 0x83, 0x42, 0xE0, 0xE9, - 0xB6, 0x41, 0x79, 0xAD, 0x32, 0x8D, 0x9D, 0x19, + 0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, + 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37, 0xCB, 0xE5, + 0xFB, 0x73, 0x0E, 0xBE, 0xAB, 0x80, 0xEC, 0x51, + 0x4C, 0xF8, 0x8A, 0x87, 0x7C, 0xEE, 0xEE, 0x0B, }, { /* 1 */ - 0x14, 0xE1, 0xF8, 0x3E, 0x9E, 0x25, 0x60, 0xFB, - 0x2A, 0x6C, 0x04, 0x24, 0x55, 0x6C, 0x86, 0x8D, - 0x9F, 0xB4, 0x63, 0x35, 0xD4, 0xF7, 0x8D, 0x22, - 0x7D, 0x5D, 0x1D, 0x3C, 0x89, 0x90, 0x6F, 0x1E, + 0xD7, 0x0C, 0xD6, 0x9A, 0x26, 0x47, 0xF7, 0x39, + 0x09, 0x73, 0xDF, 0x48, 0xCB, 0xFA, 0x2C, 0xCC, + 0x40, 0x7B, 0x8B, 0x2D, 0x60, 0xB0, 0x8C, 0x5F, + 0x16, 0x41, 0x18, 0x5C, 0x79, 0x98, 0xA2, 0x90, }, { /* 2 */ - 0x70, 0x28, 0x8D, 0xF2, 0xB7, 0x60, 0x3D, 0xBE, - 0xA0, 0xC7, 0xB7, 0x41, 0xDD, 0xAA, 0xB9, 0x46, - 0x81, 0x14, 0x4E, 0x0B, 0x19, 0x08, 0x6C, 0x69, - 0xB2, 0x34, 0x89, 0xE4, 0xF5, 0xB7, 0x01, 0x9A, + 0x81, 0xA8, 0xB0, 0x93, 0x91, 0x2C, 0x9E, 0x48, + 0x14, 0x08, 0xD0, 0x97, 0x76, 0xCE, 0xFB, 0x48, + 0xAE, 0xB8, 0xB6, 0x54, 0x81, 0xB6, 0xBA, 0xAF, + 0xB3, 0xC5, 0x81, 0x01, 0x06, 0x71, 0x7B, 0xEB, }, { /* 3 */ - 0x93, 0xEE, 0xD8, 0x24, 0xF2, 0x3C, 0x5A, 0xE1, - 0xC1, 0x05, 0xE7, 0x31, 0x09, 0x97, 0x3F, 0xCD, - 0x4A, 0xE3, 0x3A, 0x9F, 0xA0, 0x2F, 0x0A, 0xC8, - 0x5A, 0x3E, 0x55, 0x89, 0x07, 0x53, 0xB0, 0x67, + 0x2E, 0xB1, 0x88, 0x51, 0x88, 0x7E, 0x7B, 0xDC, + 0x5E, 0x83, 0x0E, 0x89, 0xB1, 0x9D, 0xDB, 0xC2, + 0x80, 0x78, 0xF1, 0xFA, 0x88, 0xAA, 0xD0, 0xAD, + 0x01, 0xCA, 0x06, 0xFE, 0x4F, 0x80, 0x21, 0x0B, }, }; @@ -1141,8 +1152,8 @@ void run_musig_tests(void) { scriptless_atomic_swap(scratch); musig_tweak_test(scratch); } - musig_test_vectors(); sha256_tag_test(); + musig_test_vectors(); secp256k1_scratch_space_destroy(ctx, scratch); } From 6ad66de6802a47687d451ac6d5369dc36c558874 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 27 Jul 2021 18:15:58 +0000 Subject: [PATCH 133/381] rangeproof: add an (unnecessary) variable initialization to shut up CI --- src/modules/rangeproof/rangeproof_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 8056f0a7..df9f64dc 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -369,7 +369,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * secp256k1_scalar stmp; unsigned char prep[4096]; unsigned char tmp[32]; - uint64_t value; + uint64_t value = 0; size_t offset; size_t i; size_t j; From 9124ce0d9cd76312ac74207cb4733c04a82738b3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 18 Aug 2021 14:02:29 +0000 Subject: [PATCH 134/381] musig: fix session_init argument NULL check --- include/secp256k1_musig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index b85eacbb..72432248 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -241,7 +241,7 @@ SECP256K1_API int secp256k1_musig_session_init( const secp256k1_musig_pre_session *pre_session, size_t n_signers, const unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(10); /** Gets the signer's public nonce given a list of all signers' data with * commitments. Called by participating signers after From 95ee1fa0303fc72ca10f594db5b1c4a8551fed3d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 15 Sep 2021 20:09:35 +0000 Subject: [PATCH 135/381] sync-upstream: fix quoting Otherwise strings in $TITLE and $BODAY that are enclosed in ` are executed in gh-pr-create.sh. --- contrib/sync-upstream.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 55dde4e1..b72744f4 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -100,11 +100,20 @@ git checkout master git pull git checkout -b temp-merge-"$PRNUM" +# Escape single quote +# ' -> '\'' +quote() { + local quoted=${1//\'/\'\\\'\'} + printf "%s" "$quoted" +} +TITLE=$(quote "$TITLE") +BODY=$(quote "$BODY") + BASEDIR=$(dirname "$0") FNAME="$BASEDIR/gh-pr-create.sh" cat < "$FNAME" #!/bin/sh -gh pr create -t "$TITLE" -b "$BODY" --web +gh pr create -t '$TITLE' -b '$BODY' --web # Remove temporary branch git checkout master git branch -D temp-merge-"$PRNUM" From b9ebee1490cc10286780c824a2bfac6bbb961cee Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 14 Oct 2021 21:21:30 +0000 Subject: [PATCH 136/381] fix a couple things to make Elements 22's linter happy --- contrib/sync-upstream.sh | 2 +- src/modules/ecdsa_s2c/main_impl.h | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 src/modules/ecdsa_s2c/main_impl.h diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index b72744f4..687420d8 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eou pipefail diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h old mode 100755 new mode 100644 From c8ac14d9dcebf763698619117fb870f6a01fbf8d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 6 Oct 2021 10:39:46 +0000 Subject: [PATCH 137/381] whitelist: fix SECP256K1_WHITELIST_MAX_N_KEYS constant "MAX" should mean inclusive. And the whitelisting functions handled this inconsistently. --- include/secp256k1_whitelist.h | 2 +- src/modules/whitelist/main_impl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index c536c11a..c0dafd91 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -13,7 +13,7 @@ extern "C" { #endif -#define SECP256K1_WHITELIST_MAX_N_KEYS 256 +#define SECP256K1_WHITELIST_MAX_N_KEYS 255 /** Opaque data structure that holds a parsed whitelist proof * diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 5ce780d4..f16ea845 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -144,7 +144,7 @@ int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_ } sig->n_keys = input[0]; - if (sig->n_keys >= MAX_KEYS || input_len != 1 + 32 * (sig->n_keys + 1)) { + if (sig->n_keys > MAX_KEYS || input_len != 1 + 32 * (sig->n_keys + 1)) { return 0; } memcpy(&sig->data[0], &input[1], 32 * (sig->n_keys + 1)); From 27d1c3b6a1738b586014c938e99d0ddb7290c7e9 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 6 Oct 2021 10:41:24 +0000 Subject: [PATCH 138/381] whitelist: add test for MAX_N_KEYS Don't test all MAX_N_KEYS because it is quite slow. --- src/modules/whitelist/tests_impl.h | 80 +++++++++++++++++------------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 7cf1fb09..c420518c 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -9,7 +9,39 @@ #include "include/secp256k1_whitelist.h" -void test_whitelist_end_to_end(const size_t n_keys) { +void test_whitelist_end_to_end_internal(const unsigned char *summed_seckey, const unsigned char *online_seckey, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey, const size_t signer_i, const size_t n_keys) { + unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; + size_t slen = sizeof(serialized); + secp256k1_whitelist_signature sig; + secp256k1_whitelist_signature sig1; + + CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey, online_seckey, summed_seckey, signer_i, NULL, NULL)); + CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey) == 1); + /* Check that exchanging keys causes a failure */ + CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); + /* Serialization round trip */ + CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &slen, &sig) == 1); + CHECK(slen == 33 + 32 * n_keys); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen) == 1); + /* (Check various bad-length conditions) */ + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 32) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 1) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen - 1) == 0); + CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, 0) == 0); + CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey) == 1); + CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); + + /* Test n_keys */ + CHECK(secp256k1_whitelist_signature_n_keys(&sig) == n_keys); + CHECK(secp256k1_whitelist_signature_n_keys(&sig1) == n_keys); + + /* Test bad number of keys in signature */ + sig.n_keys = n_keys + 1; + CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); + sig.n_keys = n_keys; +} + +void test_whitelist_end_to_end(const size_t n_keys, int test_all_keys) { unsigned char **online_seckey = (unsigned char **) malloc(n_keys * sizeof(*online_seckey)); unsigned char **summed_seckey = (unsigned char **) malloc(n_keys * sizeof(*summed_seckey)); secp256k1_pubkey *online_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*online_pubkeys)); @@ -51,36 +83,15 @@ void test_whitelist_end_to_end(const size_t n_keys) { } /* Sign/verify with each one */ - for (i = 0; i < n_keys; i++) { - unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; - size_t slen = sizeof(serialized); - secp256k1_whitelist_signature sig; - secp256k1_whitelist_signature sig1; - - CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey, online_seckey[i], summed_seckey[i], i, NULL, NULL)); - CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey) == 1); - /* Check that exchanging keys causes a failure */ - CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1); - /* Serialization round trip */ - CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &slen, &sig) == 1); - CHECK(slen == 33 + 32 * n_keys); - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen) == 1); - /* (Check various bad-length conditions) */ - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 32) == 0); - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 1) == 0); - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen - 1) == 0); - CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, 0) == 0); - CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey) == 1); - CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1); - - /* Test n_keys */ - CHECK(secp256k1_whitelist_signature_n_keys(&sig) == n_keys); - CHECK(secp256k1_whitelist_signature_n_keys(&sig1) == n_keys); - - /* Test bad number of keys in signature */ - sig.n_keys = n_keys + 1; - CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1); - sig.n_keys = n_keys; + if (test_all_keys) { + for (i = 0; i < n_keys; i++) { + test_whitelist_end_to_end_internal(summed_seckey[i], online_seckey[i], online_pubkeys, offline_pubkeys, &sub_pubkey, i, n_keys); + } + } else { + uint32_t rand_idx = secp256k1_testrand_int(n_keys-1); + test_whitelist_end_to_end_internal(summed_seckey[0], online_seckey[0], online_pubkeys, offline_pubkeys, &sub_pubkey, 0, n_keys); + test_whitelist_end_to_end_internal(summed_seckey[rand_idx], online_seckey[rand_idx], online_pubkeys, offline_pubkeys, &sub_pubkey, rand_idx, n_keys); + test_whitelist_end_to_end_internal(summed_seckey[n_keys-1], online_seckey[n_keys-1], online_pubkeys, offline_pubkeys, &sub_pubkey, n_keys-1, n_keys); } for (i = 0; i < n_keys; i++) { @@ -142,9 +153,10 @@ void run_whitelist_tests(void) { test_whitelist_bad_parse(); test_whitelist_bad_serialize(); for (i = 0; i < count; i++) { - test_whitelist_end_to_end(1); - test_whitelist_end_to_end(10); - test_whitelist_end_to_end(50); + test_whitelist_end_to_end(1, 1); + test_whitelist_end_to_end(10, 1); + test_whitelist_end_to_end(50, 1); + test_whitelist_end_to_end(SECP256K1_WHITELIST_MAX_N_KEYS, 0); } } From 22c88815c76e6edb23baf9401f820e1a944c3ecf Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 5 May 2021 15:45:31 +0000 Subject: [PATCH 139/381] musig: replace MuSig(1) with MuSig2 --- .gitignore | 2 + Makefile.am | 9 + examples/musig.c | 178 +++ include/secp256k1_musig.h | 795 +++++------ src/modules/musig/Makefile.am.include | 18 +- src/modules/musig/adaptor_impl.h | 101 ++ src/modules/musig/example.c | 170 --- src/modules/musig/keyagg.h | 34 + src/modules/musig/keyagg_impl.h | 280 ++++ src/modules/musig/main_impl.h | 734 +---------- src/modules/musig/musig.md | 221 +--- src/modules/musig/session.h | 25 + src/modules/musig/session_impl.h | 755 +++++++++++ src/modules/musig/tests_impl.h | 1749 ++++++++++++++----------- src/valgrind_ctime_test.c | 71 + 15 files changed, 2886 insertions(+), 2256 deletions(-) create mode 100644 examples/musig.c create mode 100644 src/modules/musig/adaptor_impl.h delete mode 100644 src/modules/musig/example.c create mode 100644 src/modules/musig/keyagg.h create mode 100644 src/modules/musig/keyagg_impl.h create mode 100644 src/modules/musig/session.h create mode 100644 src/modules/musig/session_impl.h diff --git a/.gitignore b/.gitignore index c4e5d9cc..277c9e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,5 @@ build-aux/test-driver src/stamp-h1 libsecp256k1.pc contrib/gh-pr-create.sh + +example_musig \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index fa455f97..2c29baa6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,15 @@ exhaustive_tests_LDFLAGS = -static TESTS += exhaustive_tests endif +if ENABLE_MODULE_MUSIG +noinst_PROGRAMS += example_musig +example_musig_SOURCES = examples/musig.c +example_musig_CPPFLAGS = -I$(top_srcdir)/include +example_musig_LDADD = libsecp256k1.la +example_musig_LDFLAGS = -static +TESTS += example_musig +endif + EXTRA_PROGRAMS = gen_ecmult_static_pre_g gen_ecmult_static_pre_g_SOURCES = src/gen_ecmult_static_pre_g.c # See Automake manual, Section "Errors with distclean" diff --git a/examples/musig.c b/examples/musig.c new file mode 100644 index 00000000..7cd664af --- /dev/null +++ b/examples/musig.c @@ -0,0 +1,178 @@ +/*********************************************************************** + * Copyright (c) 2018 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * This file demonstrates how to use the MuSig module to create a multisignature. + * Additionally, see the documentation in include/secp256k1_musig.h. + */ + +#include +#include +#include +#include +#include + +struct signer_secrets { + secp256k1_keypair keypair; + secp256k1_musig_secnonce secnonce; +}; + +struct signer { + secp256k1_xonly_pubkey pubkey; + secp256k1_musig_pubnonce pubnonce; + secp256k1_musig_partial_sig partial_sig; +}; + + /* Number of public keys involved in creating the aggregate signature */ +#define N_SIGNERS 3 +/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */ +int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) { + unsigned char seckey[32]; + FILE *frand = fopen("/dev/urandom", "r"); + if (frand == NULL) { + return 0; + } + do { + if(!fread(seckey, sizeof(seckey), 1, frand)) { + fclose(frand); + return 0; + } + /* The probability that this not a valid secret key is approximately 2^-128 */ + } while (!secp256k1_ec_seckey_verify(ctx, seckey)); + fclose(frand); + 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; + } + return 1; +} + +/* Sign a message hash with the given key pairs and store the result in sig */ +int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, unsigned char *sig64) { + int i; + const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS]; + const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS]; + const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS]; + /* The same for all signers */ + secp256k1_musig_keyagg_cache cache; + secp256k1_musig_session session; + + for (i = 0; i < N_SIGNERS; i++) { + FILE *frand; + unsigned char seckey[32]; + unsigned char session_id[32]; + /* Create random session ID. It is absolutely necessary that the session ID + * is unique for every call of secp256k1_musig_nonce_gen. Otherwise + * it's trivial for an attacker to extract the secret key! */ + frand = fopen("/dev/urandom", "r"); + if(frand == NULL) { + return 0; + } + if (!fread(session_id, 32, 1, frand)) { + fclose(frand); + return 0; + } + fclose(frand); + 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 other signers. */ + if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) { + return 0; + } + pubkeys[i] = &signer[i].pubkey; + pubnonces[i] = &signer[i].pubnonce; + } + /* Communication round 1: A production system would exchange public nonces + * here before moving on. */ + for (i = 0; i < N_SIGNERS; i++) { + secp256k1_musig_aggnonce agg_pubnonce; + + /* Create aggregate pubkey, aggregate nonce and initialize signer data */ + if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) { + return 0; + } + if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) { + return 0; + } + if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, &cache, NULL)) { + return 0; + } + /* partial_sign will clear the secnonce by setting it to 0. That's because + * you must _never_ reuse the secnonce (or use the same session_id to + * create a secnonce). If you do, you effectively reuse the nonce and + * leak the secret key. */ + if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, &cache, &session)) { + return 0; + } + partial_sigs[i] = &signer[i].partial_sig; + } + /* Communication round 2: A production system would exchange + * partial signatures here before moving on. */ + for (i = 0; i < N_SIGNERS; i++) { + /* To check whether signing was successful, it suffices to either verify + * the aggregate signature with the aggregate public key using + * secp256k1_schnorrsig_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_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, &cache, &session)) { + return 0; + } + } + return secp256k1_musig_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]; + secp256k1_xonly_pubkey agg_pk; + unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; + unsigned char sig[64]; + + /* Create a context for signing and verification */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + printf("Creating key pairs......"); + for (i = 0; i < N_SIGNERS; i++) { + if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) { + printf("FAILED\n"); + return 1; + } + pubkeys_ptr[i] = &signers[i].pubkey; + } + printf("ok\n"); + printf("Combining public keys..."); + if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, NULL, pubkeys_ptr, N_SIGNERS)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Signing message........."); + if (!sign(ctx, signer_secrets, signers, msg, sig)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Verifying signature....."); + if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + secp256k1_context_destroy(ctx); + return 0; +} diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 72432248..79c6dc48 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -7,364 +7,169 @@ extern "C" { #endif -#include +#include -/** This module implements a Schnorr-based multi-signature scheme called MuSig - * (https://eprint.iacr.org/2018/068.pdf). It is compatible with bip-schnorr. +/** This module implements a Schnorr-based multi-signature scheme called MuSig2 + * (https://eprint.iacr.org/2020/1261, see Appendix B for the exact variant). + * Signatures are compatible with BIP-340 ("Schnorr"). * There's an example C source file in the module's directory - * (src/modules/musig/example.c) that demonstrates how it can be used. + * (examples/musig.c) that demonstrates how it can be used. * - * The documentation in this include file is for reference and may not be sufficient - * for users to begin using the library. A full description of API usage can be found - * in src/modules/musig/musig.md + * The module also supports BIP-341 ("Taproot") public key tweaking and adaptor + * signatures as described in + * https://github.com/ElementsProject/scriptless-scripts/pull/24. + * + * It is recommended to read the documentation in this include file carefully. + * Further notes on API usage can be found in src/modules/musig/musig.md + * + * You may know that the MuSig2 scheme uses two "nonces" instead of one. This + * is not wrong, but only a technical detail we don't want to bother the user + * with. Therefore, the API only uses the singular term "nonce". + * + * Since the first version of MuSig is essentially replaced by MuSig2, when + * writing MuSig or musig here we mean MuSig2. */ -/** Data structure containing auxiliary data generated in `pubkey_combine` and - * required for `session_*_init`. - * Fields: - * magic: Set during initialization in `pubkey_combine` to allow - * detecting an uninitialized object. - * pk_hash: The 32-byte hash of the original public keys - * second_pk: Serialized x-coordinate of the second public key in the list. - * Filled with zeros if there is none. - * pk_parity: Whether the MuSig-aggregated point was negated when - * converting it to the combined xonly pubkey. - * is_tweaked: Whether the combined pubkey was tweaked - * tweak: If is_tweaked, array with the 32-byte tweak - * internal_key_parity: If is_tweaked, the parity of the combined pubkey - * before tweaking - */ -typedef struct { - uint64_t magic; - unsigned char pk_hash[32]; - unsigned char second_pk[32]; - int pk_parity; - int is_tweaked; - unsigned char tweak[32]; - int internal_key_parity; -} secp256k1_musig_pre_session; - -/** Data structure containing data related to a signing session resulting in a single - * signature. - * - * This structure is not opaque, but it MUST NOT be copied or read or written to it - * 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. See https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ - * for more details about the risks associated with serializing or deserializing this - * structure. - * - * Fields: - * magic: Set in `musig_session_init` to allow detecting an - * uninitialized object. - * round: Current round of the session - * pre_session: Auxiliary data created in `pubkey_combine` - * combined_pk: MuSig-computed combined xonly public key - * n_signers: Number of signers - * msg: The 32-byte message (hash) to be signed - * is_msg_set: Whether the above message has been set - * has_secret_data: Whether this session object has a signers' secret data; if this - * is `false`, it may still be used for verification purposes. - * seckey: If `has_secret_data`, the signer's secret key - * secnonce: If `has_secret_data`, the signer's secret nonce - * nonce: If `has_secret_data`, the signer's public nonce - * nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all - * signers' commitments - * combined_nonce: If round >= 2, the summed combined public nonce - * combined_nonce_parity: If round >= 2, the parity of the Y coordinate of above - * nonce. - */ -typedef struct { - uint64_t magic; - int round; - secp256k1_musig_pre_session pre_session; - secp256k1_xonly_pubkey combined_pk; - uint32_t n_signers; - int is_msg_set; - unsigned char msg[32]; - int has_secret_data; - unsigned char seckey[32]; - unsigned char secnonce[32]; - secp256k1_xonly_pubkey nonce; - int partial_nonce_parity; - unsigned char nonce_commitments_hash[32]; - secp256k1_xonly_pubkey combined_nonce; - int combined_nonce_parity; -} secp256k1_musig_session; - -/** Data structure containing data on all signers in a single session. - * - * The workflow for this structure is as follows: - * - * 1. This structure is initialized with `musig_session_init` or - * `musig_session_init_verifier`, which initializes - * all other fields. The public session is initialized with the signers' - * nonce_commitments. - * - * 2. In a non-public session the nonce_commitments are set with the function - * `musig_get_public_nonce`, which also returns the signer's public nonce. This - * ensures that the public nonce is not exposed until all commitments have been - * received. - * - * 3. Each individual data struct should be updated with `musig_set_nonce` once a - * nonce is available. This function takes a single signer data struct rather than - * an array because it may fail in the case that the provided nonce does not match - * the commitment. In this case, it is desirable to identify the exact party whose - * nonce was inconsistent. - * - * Fields: - * present: indicates whether the signer's nonce is set - * nonce: public nonce, must be a valid curvepoint if the signer is `present` - * nonce_commitment: commitment to the nonce, or all-bits zero if a commitment - * has not yet been set - */ -typedef struct { - int present; - secp256k1_xonly_pubkey nonce; - unsigned char nonce_commitment[32]; -} secp256k1_musig_session_signer_data; - -/** Opaque data structure that holds a MuSig partial signature. +/** Opaque data structures * * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is however - * guaranteed to be 32 bytes in size, and can be safely copied/moved. If you need - * to convert to a format suitable for storage, transmission, or comparison, use the - * `musig_partial_signature_serialize` and `musig_partial_signature_parse` - * functions. + * guaranteed to be portable between different platforms or versions. 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 caches information about public key aggregation. + * + * Guaranteed to be 165 bytes in size. It can be safely copied/moved. No + * serialization and parsing functions (yet). */ typedef struct { - unsigned char data[32]; -} secp256k1_musig_partial_signature; + unsigned char data[165]; +} secp256k1_musig_keyagg_cache; -/** Computes a combined public key and the hash of the given public keys. +/** Opaque data structure that holds a signer's _secret_ nonce. * - * Different orders of `pubkeys` result in different `combined_pk`s. + * Guaranteed to be 68 bytes in size. * - * The pubkeys can be sorted before combining with `secp256k1_xonly_sort` which - * ensures the same resulting `combined_pk` for the same multiset of pubkeys. - * This is useful to do before pubkey_combine, such that the order of pubkeys - * does not affect the combined public key. + * 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. See + * https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for + * more details about the risks associated with serializing or deserializing + * this structure. * - * Returns: 1 if the public keys were successfully combined, 0 otherwise - * Args: ctx: pointer to a context object initialized for verification - * (cannot be NULL) - * scratch: scratch space used to compute the combined pubkey by - * multiexponentiation. If NULL, an inefficient algorithm is used. - * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) - * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in - * `musig_session_init` or `musig_pubkey_tweak_add`. - * In: pubkeys: input array of pointers to public keys to combine. The order - * is important; a different order will result in a different - * combined public key (cannot be NULL) - * n_pubkeys: length of pubkeys array. Must be greater than 0. + * We repeat, copying this data structure can result in nonce reuse which will + * leak the secret signing key. */ -SECP256K1_API int secp256k1_musig_pubkey_combine( - const secp256k1_context* ctx, - secp256k1_scratch_space *scratch, - secp256k1_xonly_pubkey *combined_pk, - secp256k1_musig_pre_session *pre_session, - const secp256k1_xonly_pubkey * const* pubkeys, - size_t n_pubkeys -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); +typedef struct { + unsigned char data[68]; +} secp256k1_musig_secnonce; -/** Tweak an x-only public key by adding the generator multiplied with tweak32 - * to it. The resulting output_pubkey with the given internal_pubkey and tweak - * passes `secp256k1_xonly_pubkey_tweak_test`. - * - * This function is only useful before initializing a signing session. If you - * are only computing a public key, but not intending to create a signature for - * it, you can just use `secp256k1_xonly_pubkey_tweak_add`. Can only be called - * once with a given pre_session. - * - * Returns: 0 if the arguments are invalid or the resulting public key would be - * invalid (only when the tweak is the negation of the corresponding - * secret key). 1 otherwise. - * Args: ctx: pointer to a context object initialized for verification - * (cannot be NULL) - * pre_session: pointer to a `musig_pre_session` struct initialized in - * `musig_pubkey_combine` (cannot be NULL) - * Out: output_pubkey: pointer to a public key to store the result. Will be set - * to an invalid value if this function returns 0 (cannot - * be NULL) - * In: internal_pubkey: pointer to the `combined_pk` from - * `musig_pubkey_combine` to which the tweak is applied. - * (cannot be NULL). - * tweak32: pointer to a 32-byte tweak. If the tweak is invalid - * according to secp256k1_ec_seckey_verify, this function - * returns 0. For uniformly random 32-byte arrays the - * chance of being invalid is negligible (around 1 in - * 2^128) (cannot be NULL). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( - const secp256k1_context* ctx, - secp256k1_musig_pre_session *pre_session, - secp256k1_pubkey *output_pubkey, - const secp256k1_xonly_pubkey *internal_pubkey, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +/** Opaque data structure that holds a signer's public nonce. +* +* Guaranteed to be 132 bytes in size. It can be safely copied/moved. Serialized +* and parsed with `musig_pubnonce_serialize` and `musig_pubnonce_parse`. +*/ +typedef struct { + unsigned char data[132]; +} secp256k1_musig_pubnonce; -/** Initializes a signing session for a signer +/** Opaque data structure that holds an aggregate public nonce. * - * Returns: 1: session is successfully initialized - * 0: session could not be initialized: secret key or secret nonce overflow - * Args: ctx: pointer to a context object, initialized for signing (cannot - * be NULL) - * Out: session: the session structure to initialize (cannot be NULL) - * signers: an array of signers' data to be initialized. Array length must - * equal to `n_signers` (cannot be NULL) - * nonce_commitment32: filled with a 32-byte commitment to the generated nonce - * (cannot be NULL) - * In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be - * NULL). If a non-unique session_id32 was given then a partial - * signature will LEAK THE SECRET KEY. - * msg32: the 32-byte message to be signed. Shouldn't be NULL unless you - * require sharing nonce commitments before the message is known - * because it reduces nonce misuse resistance. If NULL, must be - * set with `musig_session_get_public_nonce`. - * combined_pk: the combined xonly public key of all signers (cannot be NULL) - * pre_session: pointer to a musig_pre_session struct after initializing - * it with `musig_pubkey_combine` and optionally provided to - * `musig_pubkey_tweak_add` (cannot be NULL). - * n_signers: length of signers array. Number of signers participating in - * the MuSig. Must be greater than 0 and at most 2^32 - 1. - * seckey: the signer's 32-byte secret key (cannot be NULL) + * Guaranteed to be 132 bytes in size. It can be safely copied/moved. + * Serialized and parsed with `musig_aggnonce_serialize` and + * `musig_aggnonce_parse`. */ -SECP256K1_API int secp256k1_musig_session_init( - const secp256k1_context* ctx, - secp256k1_musig_session *session, - secp256k1_musig_session_signer_data *signers, - unsigned char *nonce_commitment32, - const unsigned char *session_id32, - const unsigned char *msg32, - const secp256k1_xonly_pubkey *combined_pk, - const secp256k1_musig_pre_session *pre_session, - size_t n_signers, - const unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(10); +typedef struct { + unsigned char data[132]; +} secp256k1_musig_aggnonce; -/** Gets the signer's public nonce given a list of all signers' data with - * commitments. Called by participating signers after - * `secp256k1_musig_session_init` and after all nonce commitments have - * been collected +/** Opaque data structure that holds a MuSig session. * - * Returns: 1: public nonce is written in nonce - * 0: signer data is missing commitments or session isn't initialized - * for signing - * Args: ctx: pointer to a context object (cannot be NULL) - * session: the signing session to get the nonce from (cannot be NULL) - * signers: an array of signers' data initialized with - * `musig_session_init`. Array length must equal to - * `n_commitments` (cannot be NULL) - * Out: nonce32: filled with a 32-byte public nonce which is supposed to be - * sent to the other signers and then used in `musig_set nonce` - * (cannot be NULL) - * In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL) - * n_commitments: the length of commitments and signers array. Must be the total - * number of signers participating in the MuSig. - * msg32: the 32-byte message to be signed. Must be NULL if already - * set with `musig_session_init` otherwise can not be NULL. + * This structure is not required to be kept secret for the signing protocol to + * be secure. Guaranteed to be 133 bytes in size. It can be safely + * copied/moved. No serialization and parsing functions (yet). */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( - const secp256k1_context* ctx, - secp256k1_musig_session *session, - secp256k1_musig_session_signer_data *signers, - unsigned char *nonce32, - const unsigned char *const *commitments, - size_t n_commitments, - const unsigned char *msg32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +typedef struct { + unsigned char data[133]; +} secp256k1_musig_session; -/** Initializes a verifier session that can be used for verifying nonce commitments - * and partial signatures. It does not have secret key material and therefore can not - * be used to create signatures. +/** Opaque data structure that holds a partial MuSig signature. * - * Returns: 1 when session is successfully initialized, 0 otherwise - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: session: the session structure to initialize (cannot be NULL) - * signers: an array of signers' data to be initialized. Array length must - * equal to `n_signers`(cannot be NULL) - * In: msg32: the 32-byte message to be signed (cannot be NULL) - * combined_pk: the combined xonly public key of all signers (cannot be NULL) - * pre_session: pointer to a musig_pre_session struct from - * `musig_pubkey_combine` (cannot be NULL) - * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) - * commitments: array of pointers to 32-byte nonce commitments. Array - * length must equal to `n_signers` (cannot be NULL) - * n_signers: length of signers and commitments array. Number of signers - * participating in the MuSig. Must be greater than 0 and at most - * 2^32 - 1. + * Guaranteed to be 36 bytes in size. Serialized and parsed with + * `musig_partial_sig_serialize` and `musig_partial_sig_parse`. */ -SECP256K1_API int secp256k1_musig_session_init_verifier( - const secp256k1_context* ctx, - secp256k1_musig_session *session, - secp256k1_musig_session_signer_data *signers, - const unsigned char *msg32, - const secp256k1_xonly_pubkey *combined_pk, - const secp256k1_musig_pre_session *pre_session, - const unsigned char *const *commitments, - 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) SECP256K1_ARG_NONNULL(7); +typedef struct { + unsigned char data[36]; +} secp256k1_musig_partial_sig; -/** Checks a signer's public nonce against a commitment to said nonce, and update - * data structure if they match +/** Parse a signer's public nonce. * - * Returns: 1: commitment was valid, data structure updated - * 0: commitment was invalid, nothing happened - * Args: ctx: pointer to a context object (cannot be NULL) - * signer: pointer to the signer data to update (cannot be NULL). Must have - * been used with `musig_session_get_public_nonce` or initialized - * with `musig_session_init_verifier`. - * In: nonce32: signer's alleged public nonce (cannot be NULL) + * Returns: 1 when the nonce could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 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_musig_set_nonce( +SECP256K1_API int secp256k1_musig_pubnonce_parse( const secp256k1_context* ctx, - secp256k1_musig_session_signer_data *signer, - const unsigned char *nonce32 + secp256k1_musig_pubnonce* nonce, + const unsigned char *in66 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Updates a session with the combined public nonce of all signers. The combined - * public nonce is the sum of every signer's public nonce. +/** Serialize a signer's public nonce * - * Returns: 1: nonces are successfully combined - * 0: a signer's nonce is missing - * Args: ctx: pointer to a context object (cannot be NULL) - * session: session to update with the combined public nonce (cannot be - * NULL) - * signers: an array of signers' data, which must have had public nonces - * set with `musig_set_nonce`. Array length must equal to `n_signers` - * (cannot be NULL) - * n_signers: the length of the signers array. Must be the total number of - * signers participating in the MuSig. - * Out: nonce_parity: if non-NULL, a pointer to an integer that indicates the - * parity of the combined public nonce. Used for adaptor - * signatures. - * adaptor: point to add to the combined public nonce. If NULL, nothing is - * added to the combined nonce. + * Returns: 1 when the nonce could be serialized, 0 otherwise + * Args: ctx: a secp256k1 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_musig_session_combine_nonces( +SECP256K1_API int secp256k1_musig_pubnonce_serialize( const secp256k1_context* ctx, - secp256k1_musig_session *session, - const secp256k1_musig_session_signer_data *signers, - size_t n_signers, - int *nonce_parity, - const secp256k1_pubkey *adaptor + unsigned char *out66, + const secp256k1_musig_pubnonce* nonce ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Serialize a MuSig partial signature or adaptor signature +/** Parse an aggregate public nonce. + * + * Returns: 1 when the nonce could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: nonce: pointer to a nonce object + * In: in66: pointer to the 66-byte nonce to be parsed + */ +SECP256K1_API int secp256k1_musig_aggnonce_parse( + const secp256k1_context* ctx, + secp256k1_musig_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 when the nonce could be serialized, 0 otherwise + * Args: ctx: a secp256k1 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_musig_aggnonce_serialize( + const secp256k1_context* ctx, + unsigned char *out66, + const secp256k1_musig_aggnonce* nonce +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a MuSig partial signature * * Returns: 1 when the signature could be serialized, 0 otherwise * Args: ctx: a secp256k1 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_musig_partial_signature_serialize( +SECP256K1_API int secp256k1_musig_partial_sig_serialize( const secp256k1_context* ctx, unsigned char *out32, - const secp256k1_musig_partial_signature* sig + const secp256k1_musig_partial_sig* sig ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Parse and verify a MuSig partial signature. +/** Parse a MuSig partial signature. * * Returns: 1 when the signature could be parsed, 0 otherwise. * Args: ctx: a secp256k1 context object @@ -375,113 +180,337 @@ SECP256K1_API int secp256k1_musig_partial_signature_serialize( * encoded numbers are out of range, signature verification with it is * guaranteed to fail for every message and public key. */ -SECP256K1_API int secp256k1_musig_partial_signature_parse( +SECP256K1_API int secp256k1_musig_partial_sig_parse( const secp256k1_context* ctx, - secp256k1_musig_partial_signature* sig, + secp256k1_musig_partial_sig* sig, const unsigned char *in32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Computes an aggregate public key and uses it to initialize a keyagg_cache + * + * Different orders of `pubkeys` result in different `agg_pk`s. + * + * The pubkeys can be sorted before combining with `secp256k1_xonly_sort` which + * ensures the same `agg_pk` result for the same multiset of pubkeys. + * This is useful to do before `pubkey_agg`, such that the order of pubkeys + * does not affect the aggregate public key. + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object initialized for verification + * scratch: scratch space used to compute the aggregate pubkey by + * multiexponentiation. Generally, the larger the scratch + * space, the faster this function. However, the returns of + * providing a larger scratch space are diminishing. If NULL, + * an inefficient algorithm is used. + * Out: agg_pk: the MuSig-aggregated x-only public key. If you do not need it, + * this arg can be NULL. + * keyagg_cache: if non-NULL, pointer to a musig_keyagg_cache struct that + * is required for signing (or observing the signing session + * and verifying partial signatures). + * In: pubkeys: input array of pointers to public keys to aggregate. The order + * is important; a different order will result in a different + * aggregate public key. + * n_pubkeys: length of pubkeys array. Must be greater than 0. + */ +SECP256K1_API int secp256k1_musig_pubkey_agg( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + secp256k1_xonly_pubkey *agg_pk, + secp256k1_musig_keyagg_cache *keyagg_cache, + const secp256k1_xonly_pubkey * const* pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); + +/** Tweak an x-only public key in a given keyagg_cache by adding + * the generator multiplied with `tweak32` to it. + * + * The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in + * the following pseudocode xonly_pubkey_tweak_add_check (absent earlier + * failures) returns 1. + * + * secp256k1_musig_pubkey_agg(..., agg_pk, keyagg_cache, pubkeys, ...) + * secp256k1_musig_pubkey_tweak_add(..., output_pubkey, tweak32, keyagg_cache) + * secp256k1_xonly_pubkey_serialize(..., buf, output_pubkey) + * secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32) + * + * This function is required if you want to _sign_ for a tweaked aggregate key. + * On the other hand, if you are only computing a public key, but not intending + * to create a signature for it, you can just use + * `secp256k1_xonly_pubkey_tweak_add`. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for verification + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0. If you + * do not need it, this arg can be NULL. + * In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by + * `musig_pubkey_agg` + * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid + * according to secp256k1_ec_seckey_verify, this function + * returns 0. For uniformly random 32-byte arrays the + * chance of being invalid is negligible (around 1 in + * 2^128). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *output_pubkey, + secp256k1_musig_keyagg_cache *keyagg_cache, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** 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 other signers. + * + * MuSig differs from regular Schnorr 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_id32 that must NOT BE + * REUSED in subsequent calls to this function. + * If you do not provide a seckey, session_id32 _must_ be UNIFORMLY RANDOM + * AND KEPT SECRET (even from other signers). If you do provide a seckey, + * session_id32 can instead be a counter (that must never repeat!). However, + * it is recommended to always choose session_id32 uniformly at random. + * 2. If you already know the seckey, message or aggregate public key + * cache, they 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. + * + * Remember that nonce reuse will leak the secret key! + * Note that using the same seckey for multiple MuSig sessions is fine. + * + * Returns: 0 if the arguments are invalid and 1 otherwise + * Args: ctx: pointer to a context object, initialized for signing + * Out: secnonce: pointer to a structure to store the secret nonce + * pubnonce: pointer to a structure to store the public nonce + * In: session_id32: a 32-byte session_id32 as explained above. Must be unique to this + * call to secp256k1_musig_nonce_gen and must be uniformly random + * unless you really know what you are doing. + * seckey: the 32-byte secret key that will later be used for signing, if + * already known (can be NULL) + * msg32: the 32-byte message that will later be signed, if already known + * (can be NULL) + * keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate + * (and potentially tweaked) public key if already known + * (can be NULL) + * extra_input32: an optional 32-byte array that is input to the nonce + * derivation function (can be NULL) + */ +SECP256K1_API int secp256k1_musig_nonce_gen( + const secp256k1_context* ctx, + secp256k1_musig_secnonce *secnonce, + secp256k1_musig_pubnonce *pubnonce, + const unsigned char *session_id32, + const unsigned char *seckey, + const unsigned char *msg32, + const secp256k1_musig_keyagg_cache *keyagg_cache, + const unsigned char *extra_input32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Aggregates the nonces of all signers into a single nonce + * + * This can be done by an untrusted party to reduce the communication + * between signers. Instead of everyone sending nonces to everyone else, there + * can be one party receiving all nonces, aggregating the nonces with this + * function and then sending only the aggregate nonce back to the signers. + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: aggnonce: pointer to an aggregate public nonce object for + * musig_nonce_process + * In: pubnonces: array of pointers to public nonces sent by the + * signers + * n_pubnonces: number of elements in the pubnonces array. Must be + * greater than 0. + */ +SECP256K1_API int secp256k1_musig_nonce_agg( + const secp256k1_context* ctx, + secp256k1_musig_aggnonce *aggnonce, + const secp256k1_musig_pubnonce * const* pubnonces, + size_t n_pubnonces +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Takes the public nonces of all signers and computes a session that is + * required for signing and verification of partial signatures. + * + * If the adaptor argument is non-NULL, then the output of + * musig_partial_sig_agg will be a pre-signature which is not a valid Schnorr + * signature. In order to create a valid signature, the pre-signature and the + * secret adaptor must be provided to `musig_adapt`. + * + * Returns: 0 if the arguments are invalid or if some signer sent invalid + * pubnonces, 1 otherwise + * Args: ctx: pointer to a context object, initialized for verification + * Out: session: pointer to a struct to store the session + * In: aggnonce: pointer to an aggregate public nonce object that is the + * output of musig_nonce_agg + * msg32: the 32-byte message to sign + * keyagg_cache: pointer to the keyagg_cache that was used to create the + * aggregate (and potentially tweaked) pubkey + * adaptor: optional pointer to an adaptor point encoded as a public + * key if this signing session is part of an adaptor + * signature protocol (can be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + const secp256k1_musig_aggnonce *aggnonce, + const unsigned char *msg32, + const secp256k1_musig_keyagg_cache *keyagg_cache, + const secp256k1_pubkey *adaptor +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + /** Produces a partial signature * - * Returns: 1: partial signature constructed - * 0: session in incorrect or inconsistent state - * Args: ctx: pointer to a context object (cannot be NULL) - * session: active signing session for which the combined nonce has been - * computed (cannot be NULL) - * Out: partial_sig: partial signature (cannot be NULL) + * 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! + * + * 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 + * Out: partial_sig: pointer to struct to store the partial signature + * In/Out: secnonce: pointer to the secnonce struct created in + * musig_nonce_gen that has been never used in a + * partial_sign call before + * In: keypair: pointer to keypair to sign the message with + * keyagg_cache: pointer to the keyagg_cache that was output when the + * aggregate public key for this session + * session: pointer to the session that was created with + * musig_nonce_process */ SECP256K1_API int secp256k1_musig_partial_sign( const secp256k1_context* ctx, - const secp256k1_musig_session *session, - secp256k1_musig_partial_signature *partial_sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + 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_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); -/** Checks that an individual partial signature verifies +/** Verifies an individual signer's partial signature * * This function is essential when using protocols with adaptor signatures. - * However, it is not essential for regular MuSig's, in the sense that if any - * partial signatures does not verify, the full signature will also not verify, so the + * However, it is not essential for regular MuSig sessions, in the sense that if any + * partial signature does not verify, the full signature will not verify either, so the * problem will be caught. But this function allows determining the specific party - * who produced an invalid signature, so that signing can be restarted without them. + * who produced an invalid signature. * - * Returns: 1: partial signature verifies - * 0: invalid signature or bad data - * Args: ctx: pointer to a context object (cannot be NULL) - * session: active session for which the combined nonce has been computed - * (cannot be NULL) - * signer: data for the signer who produced this signature (cannot be NULL) - * In: partial_sig: signature to verify (cannot be NULL) - * pubkey: public key of the signer who produced the signature (cannot be NULL) + * Returns: 0 if the arguments are invalid or the partial signature does not + * verify, 1 otherwise + * Args ctx: pointer to a context object, initialized for verification + * In: partial_sig: pointer to partial signature to verify + * pubnonce: public nonce sent by the signer who produced the signature + * pubkey: public key of the signer who produced the signature + * keyagg_cache: pointer to the keyagg_cache that was output when the + * aggregate public key for this session + * session: pointer to the session that was created with + * musig_nonce_process */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( const secp256k1_context* ctx, - const secp256k1_musig_session *session, - const secp256k1_musig_session_signer_data *signer, - const secp256k1_musig_partial_signature *partial_sig, - const secp256k1_xonly_pubkey *pubkey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + const secp256k1_musig_partial_sig *partial_sig, + const secp256k1_musig_pubnonce *pubnonce, + const secp256k1_xonly_pubkey *pubkey, + const secp256k1_musig_keyagg_cache *keyagg_cache, + const secp256k1_musig_session *session +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); -/** Combines partial signatures +/** Aggregates partial signatures * - * Returns: 1: all partial signatures have values in range. Does NOT mean the - * resulting signature verifies. - * 0: some partial signature are missing or had s or r out of range - * Args: ctx: pointer to a context object (cannot be NULL) - * session: initialized session for which the combined nonce has been - * computed (cannot be NULL) - * Out: sig64: complete signature (cannot be NULL) - * In: partial_sigs: array of partial signatures to combine (cannot be NULL) - * n_sigs: number of signatures in the partial_sigs array + * 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) Schnorr signature + * In: session: pointer to the session that was created with + * musig_nonce_process + * 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. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( +SECP256K1_API int secp256k1_musig_partial_sig_agg( const secp256k1_context* ctx, - const secp256k1_musig_session *session, unsigned char *sig64, - const secp256k1_musig_partial_signature *partial_sigs, + const secp256k1_musig_session *session, + const secp256k1_musig_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); -/** Converts a partial signature to an adaptor signature by adding a given secret - * adaptor. +/** Extracts the nonce_parity bit from a session * - * Returns: 1: signature and secret adaptor contained valid values - * 0: otherwise - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: adaptor_sig: adaptor signature to produce (cannot be NULL) - * In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL) - * sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot - * be NULL) - * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces` + * This is used for adaptor signatures. + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: nonce_parity: pointer to an integer that indicates the parity + * of the aggregate public nonce. Used for adaptor + * signatures. + * In: session: pointer to the session that was created with + * musig_nonce_process */ -SECP256K1_API int secp256k1_musig_partial_sig_adapt( +SECP256K1_API int secp256k1_musig_nonce_parity( const secp256k1_context* ctx, - secp256k1_musig_partial_signature *adaptor_sig, - const secp256k1_musig_partial_signature *partial_sig, + int *nonce_parity, + const secp256k1_musig_session *session +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Creates a signature from a pre-signature and an adaptor. + * + * If the sec_adaptor32 argument is incorrect, the output signature will be + * invalid. This function does not verify the signature. + * + * Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain + * invalid (overflowing) values. 1 otherwise (which does NOT mean the + * signature or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out: sig64: 64-byte signature. This pointer may point to the same + * memory area as `pre_sig`. + * In: pre_sig64: 64-byte pre-signature + * sec_adaptor32: 32-byte secret adaptor to add to the pre-signature + * nonce_parity: the output of `musig_nonce_parity` called with the + * session used for producing the pre-signature + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_adapt( + const secp256k1_context* ctx, + unsigned char *sig64, + const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -/** Extracts a secret adaptor from a MuSig, given all parties' partial - * signatures. This function will not fail unless given grossly invalid data; if it - * is merely given signatures that do not verify, the returned value will be - * nonsense. It is therefore important that all data be verified at earlier steps of - * any protocol that uses this function. +/** Extracts a secret adaptor from a MuSig pre-signature and corresponding + * signature * - * Returns: 1: signatures contained valid data such that an adaptor could be extracted - * 0: otherwise - * Args: ctx: pointer to a context object (cannot be NULL) - * Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL) - * In: sig64: complete 2-of-2 signature (cannot be NULL) - * partial_sigs: array of partial signatures (cannot be NULL) - * n_partial_sigs: number of elements in partial_sigs array - * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces` + * This function will not fail unless given grossly invalid data; if it is + * merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier + * steps of any protocol that uses this function. In particular, this includes + * verifying all partial signatures that were aggregated into pre_sig64. + * + * Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain + * grossly invalid (overflowing) values. 1 otherwise (which does NOT + * mean the signatures or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out:sec_adaptor32: 32-byte secret adaptor + * In: sig64: complete, valid 64-byte signature + * pre_sig64: the pre-signature corresponding to sig64, i.e., the + * aggregate of partial signatures without the secret + * adaptor + * nonce_parity: the output of `musig_nonce_parity` called with the + * session used for producing sig64 */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_adaptor( const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, - const secp256k1_musig_partial_signature *partial_sigs, - size_t n_partial_sigs, + const unsigned char *pre_sig64, int nonce_parity ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); diff --git a/src/modules/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include index 0cd254d8..dc2f77f1 100644 --- a/src/modules/musig/Makefile.am.include +++ b/src/modules/musig/Makefile.am.include @@ -1,16 +1,8 @@ include_HEADERS += include/secp256k1_musig.h noinst_HEADERS += src/modules/musig/main_impl.h +noinst_HEADERS += src/modules/musig/keyagg.h +noinst_HEADERS += src/modules/musig/keyagg_impl.h +noinst_HEADERS += src/modules/musig/session.h +noinst_HEADERS += src/modules/musig/session_impl.h +noinst_HEADERS += src/modules/musig/adaptor_impl.h noinst_HEADERS += src/modules/musig/tests_impl.h - -noinst_PROGRAMS += example_musig -example_musig_SOURCES = src/modules/musig/example.c -example_musig_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include $(SECP_INCLUDES) -if !ENABLE_COVERAGE -example_musig_CPPFLAGS += -DVERIFY -endif -example_musig_LDADD = libsecp256k1.la $(SECP_LIBS) -example_musig_LDFLAGS = -static - -if USE_TESTS -TESTS += example_musig -endif diff --git a/src/modules/musig/adaptor_impl.h b/src/modules/musig/adaptor_impl.h new file mode 100644 index 00000000..3830e8a2 --- /dev/null +++ b/src/modules/musig/adaptor_impl.h @@ -0,0 +1,101 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H +#define SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_musig.h" + +#include "session.h" +#include "../../scalar.h" + +int secp256k1_musig_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_musig_session *session) { + secp256k1_musig_session_internal session_i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce_parity != NULL); + ARG_CHECK(session != NULL); + + if (!secp256k1_musig_session_load(ctx, &session_i, session)) { + return 0; + } + *nonce_parity = session_i.fin_nonce_parity; + return 1; +} + +int secp256k1_musig_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + ret &= !overflow; + + /* Determine if the secret adaptor should be negated. + * + * The musig_session stores the X-coordinate and the parity of the "final nonce" + * (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor. + * + * Since a BIP340 signature requires an x-only public nonce, in the case where + * (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce + * corresponding to the signature is actually (-r - t)*G. Thus adapting a + * pre-signature requires negating t in this case. + */ + if (nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(&sig64[32], &s); + memmove(sig64, pre_sig64, 32); + secp256k1_scalar_clear(&t); + return ret; +} + +int secp256k1_musig_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); + ret &= !overflow; + secp256k1_scalar_negate(&t, &t); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + + if (!nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return ret; +} + +#endif diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c deleted file mode 100644 index 66cad749..00000000 --- a/src/modules/musig/example.c +++ /dev/null @@ -1,170 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -/** - * This file demonstrates how to use the MuSig module to create a multisignature. - * Additionally, see the documentation in include/secp256k1_musig.h. - */ - -#include -#include -#include -#include -#include - - /* Number of public keys involved in creating the aggregate signature */ -#define N_SIGNERS 3 - /* Create a key pair and store it in seckey and pubkey */ -int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k1_xonly_pubkey *pubkey) { - int ret; - secp256k1_keypair keypair; - FILE *frand = fopen("/dev/urandom", "r"); - if (frand == NULL) { - return 0; - } - do { - if(!fread(seckey, 32, 1, frand)) { - fclose(frand); - return 0; - } - /* The probability that this not a valid secret key is approximately 2^-128 */ - } while (!secp256k1_ec_seckey_verify(ctx, seckey)); - fclose(frand); - ret = secp256k1_keypair_create(ctx, &keypair, seckey); - ret &= secp256k1_keypair_xonly_pub(ctx, pubkey, NULL, &keypair); - - return ret; -} - -/* Sign a message hash with the given key pairs and store the result in sig */ -int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey** pubkeys, const unsigned char* msg32, unsigned char *sig64) { - secp256k1_musig_session musig_session[N_SIGNERS]; - unsigned char nonce_commitment[N_SIGNERS][32]; - const unsigned char *nonce_commitment_ptr[N_SIGNERS]; - secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; - unsigned char nonce[N_SIGNERS][32]; - int i, j; - secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; - - for (i = 0; i < N_SIGNERS; i++) { - FILE *frand; - unsigned char session_id32[32]; - secp256k1_xonly_pubkey combined_pk; - secp256k1_musig_pre_session pre_session; - - /* Create combined pubkey and initialize signer data */ - if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pubkeys, N_SIGNERS)) { - return 0; - } - /* Create random session ID. It is absolutely necessary that the session ID - * is unique for every call of secp256k1_musig_session_init. Otherwise - * it's trivial for an attacker to extract the secret key! */ - frand = fopen("/dev/urandom", "r"); - if(frand == NULL) { - return 0; - } - if (!fread(session_id32, 32, 1, frand)) { - fclose(frand); - return 0; - } - fclose(frand); - /* Initialize session */ - if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, seckeys[i])) { - return 0; - } - nonce_commitment_ptr[i] = &nonce_commitment[i][0]; - } - /* Communication round 1: Exchange nonce commitments */ - for (i = 0; i < N_SIGNERS; i++) { - /* Set nonce commitments in the signer data and get the own public nonce */ - if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) { - return 0; - } - } - /* Communication round 2: Exchange nonces */ - for (i = 0; i < N_SIGNERS; i++) { - for (j = 0; j < N_SIGNERS; j++) { - if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], nonce[j])) { - /* Signer j's nonce does not match the nonce commitment. In this case - * abort the protocol. If you make another attempt at finishing the - * protocol, create a new session (with a fresh session ID!). */ - return 0; - } - } - if (!secp256k1_musig_session_combine_nonces(ctx, &musig_session[i], signer_data[i], N_SIGNERS, NULL, NULL)) { - return 0; - } - } - for (i = 0; i < N_SIGNERS; i++) { - if (!secp256k1_musig_partial_sign(ctx, &musig_session[i], &partial_sig[i])) { - return 0; - } - } - /* Communication round 3: Exchange partial signatures */ - for (i = 0; i < N_SIGNERS; i++) { - for (j = 0; j < N_SIGNERS; j++) { - /* To check whether signing was successful, it suffices to either verify - * the combined signature with the combined public key using - * secp256k1_schnorrsig_verify, or verify all partial signatures of all - * signers individually. Verifying the combined 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 combined - * signature to be invalid and thus the protocol run to fail. It's also - * fine to first verify the combined sig, and only verify the individual - * sigs if it does not work. - */ - if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], pubkeys[j])) { - return 0; - } - } - } - return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig64, partial_sig, N_SIGNERS); -} - - int main(void) { - secp256k1_context* ctx; - int i; - unsigned char seckeys[N_SIGNERS][32]; - secp256k1_xonly_pubkey pubkeys[N_SIGNERS]; - const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; - secp256k1_xonly_pubkey combined_pk; - unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; - unsigned char sig[64]; - - /* Create a context for signing and verification */ - ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - printf("Creating key pairs......"); - for (i = 0; i < N_SIGNERS; i++) { - if (!create_keypair(ctx, seckeys[i], &pubkeys[i])) { - printf("FAILED\n"); - return 1; - } - pubkeys_ptr[i] = &pubkeys[i]; - } - printf("ok\n"); - printf("Combining public keys..."); - if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys_ptr, N_SIGNERS)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - printf("Signing message........."); - if (!sign(ctx, seckeys, pubkeys_ptr, msg, sig)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - printf("Verifying signature....."); - if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &combined_pk)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - secp256k1_context_destroy(ctx); - return 0; -} - diff --git a/src/modules/musig/keyagg.h b/src/modules/musig/keyagg.h new file mode 100644 index 00000000..69c1f34a --- /dev/null +++ b/src/modules/musig/keyagg.h @@ -0,0 +1,34 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_H +#define SECP256K1_MODULE_MUSIG_KEYAGG_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_musig.h" + +#include "../../field.h" +#include "../../group.h" +#include "../../scalar.h" + +typedef struct { + secp256k1_ge pk; + secp256k1_fe second_pk_x; + unsigned char pk_hash[32]; + secp256k1_scalar tweak; + int internal_key_parity; +} secp256k1_keyagg_cache_internal; + +/* Requires that the saved point is not infinity */ +static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge); + +static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data); + +static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache); + +static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_fe *x); + +#endif diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h new file mode 100644 index 00000000..9a747f4d --- /dev/null +++ b/src/modules/musig/keyagg_impl.h @@ -0,0 +1,280 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_IMPL_H +#define SECP256K1_MODULE_MUSIG_KEYAGG_IMPL_H + +#include + +#include "keyagg.h" +#include "../../eckey.h" +#include "../../ecmult.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../util.h" + +static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge) { + if (sizeof(secp256k1_ge_storage) == 64) { + secp256k1_ge_storage s; + secp256k1_ge_to_storage(&s, ge); + memcpy(data, &s, sizeof(s)); + } else { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(data, &ge->x); + secp256k1_fe_get_b32(data + 32, &ge->y); + } +} + +static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data) { + if (sizeof(secp256k1_ge_storage) == 64) { + /* When the secp256k1_ge_storage type is exactly 64 byte, use its + * representation as conversion is very fast. */ + secp256k1_ge_storage s; + memcpy(&s, data, sizeof(s)); + secp256k1_ge_from_storage(ge, &s); + } else { + /* Otherwise, fall back to 32-byte big endian for X and Y. */ + secp256k1_fe x, y; + secp256k1_fe_set_b32(&x, data); + secp256k1_fe_set_b32(&y, data + 32); + secp256k1_ge_set_xy(ge, &x, &y); + } +} + +static const unsigned char secp256k1_musig_keyagg_cache_magic[4] = { 0xf4, 0xad, 0xbb, 0xdf }; + +/* A keyagg cache consists of + * - 4 byte magic set during initialization to allow detecting an uninitialized + * object. + * - 64 byte aggregate (and potentially tweaked) public key + * - 32 byte X-coordinate of "second" public key (0 if not present) + * - 32 byte hash of all public keys + * - 1 byte the parity of the internal key (if tweaked, otherwise 0) + * - 32 byte tweak + */ +/* Requires that cache_i->pk is not infinity and cache_i->second_pk_x to be normalized. */ +static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, secp256k1_keyagg_cache_internal *cache_i) { + unsigned char *ptr = cache->data; + memcpy(ptr, secp256k1_musig_keyagg_cache_magic, 4); + ptr += 4; + secp256k1_point_save(ptr, &cache_i->pk); + ptr += 64; + secp256k1_fe_get_b32(ptr, &cache_i->second_pk_x); + ptr += 32; + memcpy(ptr, cache_i->pk_hash, 32); + ptr += 32; + *ptr = cache_i->internal_key_parity; + ptr += 1; + secp256k1_scalar_get_b32(ptr, &cache_i->tweak); +} + +static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache) { + const unsigned char *ptr = cache->data; + ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_keyagg_cache_magic, 4) == 0); + ptr += 4; + secp256k1_point_load(&cache_i->pk, ptr); + ptr += 64; + secp256k1_fe_set_b32(&cache_i->second_pk_x, ptr); + ptr += 32; + memcpy(cache_i->pk_hash, ptr, 32); + ptr += 32; + cache_i->internal_key_parity = *ptr & 1; + ptr += 1; + secp256k1_scalar_set_b32(&cache_i->tweak, ptr, NULL); + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */ +static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + + sha->s[0] = 0xb399d5e0ul; + sha->s[1] = 0xc8fff302ul; + sha->s[2] = 0x6badac71ul; + sha->s[3] = 0x07c5b7f1ul; + sha->s[4] = 0x9701e2eful; + sha->s[5] = 0x2a72ecf8ul; + sha->s[6] = 0x201a4c7bul; + sha->s[7] = 0xab148a38ul; + sha->bytes = 64; +} + +/* Computes pk_hash = tagged_hash(pk[0], ..., pk[np-1]) */ +static int secp256k1_musig_compute_pk_hash(const secp256k1_context *ctx, unsigned char *pk_hash, const secp256k1_xonly_pubkey * const* pk, size_t np) { + secp256k1_sha256 sha; + size_t i; + + secp256k1_musig_keyagglist_sha256(&sha); + for (i = 0; i < np; i++) { + unsigned char ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) { + return 0; + } + secp256k1_sha256_write(&sha, ser, 32); + } + secp256k1_sha256_finalize(&sha, pk_hash); + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */ +static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + + sha->s[0] = 0x6ef02c5aul; + sha->s[1] = 0x06a480deul; + sha->s[2] = 0x1f298665ul; + sha->s[3] = 0x1d1134f2ul; + sha->s[4] = 0x56a0b063ul; + sha->s[5] = 0x52da4147ul; + sha->s[6] = 0xf280d9d4ul; + sha->s[7] = 0x4484be15ul; + sha->bytes = 64; +} + +/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and + * tagged_hash(pk_hash, x) where pk_hash is the hash of public keys otherwise. + * second_pk_x can be 0 in case there is no second_pk. Assumes both field + * elements x and second_pk_x are normalized. */ +static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, const secp256k1_fe *x, const secp256k1_fe *second_pk_x) { + secp256k1_sha256 sha; + unsigned char buf[32]; + + if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) { + secp256k1_scalar_set_int(r, 1); + } else { + secp256k1_musig_keyaggcoef_sha256(&sha); + secp256k1_sha256_write(&sha, pk_hash, 32); + secp256k1_fe_get_b32(buf, x); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(r, buf, NULL); + } + +} + +/* Assumes both field elements x and second_pk_x are normalized. */ +static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_fe *x) { + secp256k1_musig_keyaggcoef_internal(r, cache_i->pk_hash, x, &cache_i->second_pk_x); +} + +typedef struct { + const secp256k1_context *ctx; + /* pk_hash is the hash of the public keys */ + unsigned char pk_hash[32]; + const secp256k1_xonly_pubkey * const* pks; + secp256k1_fe second_pk_x; +} secp256k1_musig_pubkey_agg_ecmult_data; + +/* Callback for batch EC multiplication to compute keyaggcoef_0*P0 + keyaggcoef_1*P1 + ... */ +static int secp256k1_musig_pubkey_agg_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_musig_pubkey_agg_ecmult_data *ctx = (secp256k1_musig_pubkey_agg_ecmult_data *) data; + int ret; + ret = secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); + /* pubkey_load can't fail because the same pks have already been loaded in + * `musig_compute_pk_hash` (and we test this). */ + VERIFY_CHECK(ret); + secp256k1_musig_keyaggcoef_internal(sc, ctx->pk_hash, &pt->x, &ctx->second_pk_x); + return 1; +} + +int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys) { + secp256k1_musig_pubkey_agg_ecmult_data ecmult_data; + secp256k1_gej pkj; + secp256k1_ge pkp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + if (agg_pk != NULL) { + memset(agg_pk, 0, sizeof(*agg_pk)); + } + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(n_pubkeys > 0); + + ecmult_data.ctx = ctx; + ecmult_data.pks = pubkeys; + /* No point on the curve has an X coordinate equal to 0 */ + secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0); + for (i = 1; i < n_pubkeys; i++) { + if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) { + secp256k1_ge pt; + if (!secp256k1_xonly_pubkey_load(ctx, &pt, pubkeys[i])) { + return 0; + } + ecmult_data.second_pk_x = pt.x; + break; + } + } + + if (!secp256k1_musig_compute_pk_hash(ctx, ecmult_data.pk_hash, pubkeys, n_pubkeys)) { + return 0; + } + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &pkj, NULL, secp256k1_musig_pubkey_agg_callback, (void *) &ecmult_data, n_pubkeys)) { + /* In order to reach this line with the current implementation of + * ecmult_multi_var one would need to provide a callback that can + * fail. */ + return 0; + } + secp256k1_ge_set_gej(&pkp, &pkj); + secp256k1_fe_normalize_var(&pkp.y); + /* The resulting public key is infinity with negligible probability */ + VERIFY_CHECK(!secp256k1_ge_is_infinity(&pkp)); + if (keyagg_cache != NULL) { + secp256k1_keyagg_cache_internal cache_i = { 0 }; + cache_i.pk = pkp; + cache_i.second_pk_x = ecmult_data.second_pk_x; + memcpy(cache_i.pk_hash, ecmult_data.pk_hash, sizeof(cache_i.pk_hash)); + secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); + } + + secp256k1_extrakeys_ge_even_y(&pkp); + if (agg_pk != NULL) { + secp256k1_xonly_pubkey_save(agg_pk, &pkp); + } + return 1; +} + +int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { + secp256k1_keyagg_cache_internal cache_i; + int overflow = 0; + secp256k1_scalar tweak; + + VERIFY_CHECK(ctx != NULL); + if (output_pubkey != NULL) { + memset(output_pubkey, 0, sizeof(*output_pubkey)); + } + ARG_CHECK(keyagg_cache != NULL); + ARG_CHECK(tweak32 != NULL); + + if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + return 0; + } + secp256k1_scalar_set_b32(&tweak, tweak32, &overflow); + if (overflow) { + return 0; + } + if (secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { + cache_i.internal_key_parity ^= 1; + secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak); + } + secp256k1_scalar_add(&cache_i.tweak, &cache_i.tweak, &tweak); + if (!secp256k1_eckey_pubkey_tweak_add(&cache_i.pk, &tweak)) { + return 0; + } + /* eckey_pubkey_tweak_add fails if cache_i.pk is infinity */ + VERIFY_CHECK(!secp256k1_ge_is_infinity(&cache_i.pk)); + secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); + if (output_pubkey != NULL) { + secp256k1_pubkey_save(output_pubkey, &cache_i.pk); + } + return 1; +} + +#endif diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 0ceacece..53a62979 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -4,735 +4,11 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_MUSIG_MAIN_ -#define _SECP256K1_MODULE_MUSIG_MAIN_ +#ifndef SECP256K1_MODULE_MUSIG_MAIN +#define SECP256K1_MODULE_MUSIG_MAIN -#include -#include "include/secp256k1.h" -#include "include/secp256k1_musig.h" -#include "hash.h" - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */ -static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - - sha->s[0] = 0xb399d5e0ul; - sha->s[1] = 0xc8fff302ul; - sha->s[2] = 0x6badac71ul; - sha->s[3] = 0x07c5b7f1ul; - sha->s[4] = 0x9701e2eful; - sha->s[5] = 0x2a72ecf8ul; - sha->s[6] = 0x201a4c7bul; - sha->s[7] = 0xab148a38ul; - sha->bytes = 64; -} - -/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey * const* pk, size_t np) { - secp256k1_sha256 sha; - size_t i; - - secp256k1_musig_keyagglist_sha256(&sha); - for (i = 0; i < np; i++) { - unsigned char ser[32]; - if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) { - return 0; - } - secp256k1_sha256_write(&sha, ser, 32); - } - secp256k1_sha256_finalize(&sha, ell); - return 1; -} - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */ -static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - - sha->s[0] = 0x6ef02c5aul; - sha->s[1] = 0x06a480deul; - sha->s[2] = 0x1f298665ul; - sha->s[3] = 0x1d1134f2ul; - sha->s[4] = 0x56a0b063ul; - sha->s[5] = 0x52da4147ul; - sha->s[6] = 0xf280d9d4ul; - sha->s[7] = 0x4484be15ul; - sha->bytes = 64; -} - -/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and - * SHA256(ell, x) otherwise. second_pk_x can be NULL in case there is no - * second_pk. Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x, const secp256k1_fe *second_pk_x) { - secp256k1_sha256 sha; - unsigned char buf[32]; - - if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) { - secp256k1_scalar_set_int(r, 1); - } else { - secp256k1_musig_keyaggcoef_sha256(&sha); - secp256k1_sha256_write(&sha, ell, 32); - secp256k1_fe_get_b32(buf, x); - secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(r, buf, NULL); - } -} - -/* Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_musig_pre_session *pre_session, secp256k1_fe *x) { - secp256k1_fe second_pk_x; - secp256k1_fe_set_b32(&second_pk_x, pre_session->second_pk); - secp256k1_musig_keyaggcoef_internal(r, pre_session->pk_hash, x, &second_pk_x); -} - -typedef struct { - const secp256k1_context *ctx; - unsigned char ell[32]; - const secp256k1_xonly_pubkey * const* pks; - secp256k1_fe second_pk_x; -} secp256k1_musig_pubkey_combine_ecmult_data; - -/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ -static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { - secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; - int ret; - ret = secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); - /* pubkey_load can't fail because the same pks have already been loaded (and - * we test this) */ - VERIFY_CHECK(ret); - secp256k1_musig_keyaggcoef_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x); - return 1; -} - -static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { - uint32_t i; - for (i = 0; i < n_signers; i++) { - memset(&signers[i], 0, sizeof(signers[i])); - signers[i].present = 0; - } -} - -static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL; - -int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys) { - secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; - secp256k1_gej pkj; - secp256k1_ge pkp; - int pk_parity; - size_t i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pubkeys != NULL); - ARG_CHECK(n_pubkeys > 0); - - ecmult_data.ctx = ctx; - ecmult_data.pks = pubkeys; - /* No point on the curve has an X coordinate equal to 0 */ - secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0); - for (i = 1; i < n_pubkeys; i++) { - secp256k1_ge pt; - if (!secp256k1_xonly_pubkey_load(ctx, &pt, pubkeys[i])) { - return 0; - } - if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) { - ecmult_data.second_pk_x = pt.x; - break; - } - } - - if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) { - return 0; - } - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) { - /* The current implementation of ecmult_multi_var makes this code unreachable with tests. */ - return 0; - } - secp256k1_ge_set_gej(&pkp, &pkj); - secp256k1_fe_normalize_var(&pkp.y); - pk_parity = secp256k1_extrakeys_ge_even_y(&pkp); - secp256k1_xonly_pubkey_save(combined_pk, &pkp); - - if (pre_session != NULL) { - pre_session->magic = pre_session_magic; - memcpy(pre_session->pk_hash, ecmult_data.ell, 32); - pre_session->pk_parity = pk_parity; - pre_session->is_tweaked = 0; - secp256k1_fe_get_b32(pre_session->second_pk, &ecmult_data.second_pk_x); - } - return 1; -} - -int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { - secp256k1_ge pk; - int ret; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pre_session != NULL); - ARG_CHECK(pre_session->magic == pre_session_magic); - /* This function can only be called once because otherwise signing would not - * succeed */ - ARG_CHECK(pre_session->is_tweaked == 0); - - pre_session->internal_key_parity = pre_session->pk_parity; - if(!secp256k1_xonly_pubkey_tweak_add(ctx, output_pubkey, internal_pubkey, tweak32)) { - return 0; - } - - memcpy(pre_session->tweak, tweak32, 32); - pre_session->is_tweaked = 1; - - ret = secp256k1_pubkey_load(ctx, &pk, output_pubkey); - /* Successful xonly_pubkey_tweak_add always returns valid output_pubkey */ - VERIFY_CHECK(ret); - - pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk); - return 1; -} - -static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL; - -int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, const unsigned char *seckey) { - unsigned char combined_ser[32]; - int overflow; - secp256k1_scalar secret; - secp256k1_scalar mu; - secp256k1_sha256 sha; - secp256k1_gej pj; - secp256k1_ge p; - unsigned char nonce_ser[32]; - size_t nonce_ser_size = sizeof(nonce_ser); - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(session != NULL); - ARG_CHECK(signers != NULL); - ARG_CHECK(nonce_commitment32 != NULL); - ARG_CHECK(session_id32 != NULL); - ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pre_session != NULL); - ARG_CHECK(pre_session->magic == pre_session_magic); - ARG_CHECK(seckey != NULL); - - ARG_CHECK(n_signers > 0); - ARG_CHECK(n_signers <= UINT32_MAX); - - memset(session, 0, sizeof(*session)); - - session->magic = session_magic; - if (msg32 != NULL) { - memcpy(session->msg, msg32, 32); - session->is_msg_set = 1; - } else { - session->is_msg_set = 0; - } - memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); - session->pre_session = *pre_session; - session->has_secret_data = 1; - session->n_signers = (uint32_t) n_signers; - secp256k1_musig_signers_init(signers, session->n_signers); - - /* Compute secret key */ - secp256k1_scalar_set_b32(&secret, seckey, &overflow); - if (overflow) { - secp256k1_scalar_clear(&secret); - return 0; - } - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); - secp256k1_ge_set_gej(&p, &pj); - secp256k1_fe_normalize_var(&p.x); - secp256k1_musig_keyaggcoef(&mu, &session->pre_session, &p.x); - /* Compute the signer's public key point and determine if the secret is - * negated before signing. That happens if if the signer's pubkey has an odd - * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR - * (if tweaked) the internal key has an odd Y coordinate. - * - * This can be seen by looking at the secret key belonging to `combined_pk`. - * Let's define - * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key - * point x_i*G, mu_i is the i-th KeyAgg coefficient and |.| is a function - * that normalizes a point to an even Y by negating if necessary similar to - * secp256k1_extrakeys_ge_even_y. Then we have - * P := |P'| + t*G where t is the tweak. - * And the combined xonly public key is - * |P| = x*G - * where x = sum_i(b_i*mu_i*x_i) + b'*t - * b' = -1 if P != |P|, 1 otherwise - * b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1 - * otherwise. - */ - secp256k1_fe_normalize_var(&p.y); - if((secp256k1_fe_is_odd(&p.y) - + session->pre_session.pk_parity - + (session->pre_session.is_tweaked - && session->pre_session.internal_key_parity)) - % 2 == 1) { - secp256k1_scalar_negate(&secret, &secret); - } - secp256k1_scalar_mul(&secret, &secret, &mu); - secp256k1_scalar_get_b32(session->seckey, &secret); - - /* Compute secret nonce */ - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, session_id32, 32); - if (session->is_msg_set) { - secp256k1_sha256_write(&sha, msg32, 32); - } - secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk); - secp256k1_sha256_write(&sha, combined_ser, 32); - secp256k1_sha256_write(&sha, seckey, 32); - secp256k1_sha256_finalize(&sha, session->secnonce); - secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow); - if (overflow) { - secp256k1_scalar_clear(&secret); - return 0; - } - - /* Compute public nonce and commitment */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); - secp256k1_ge_set_gej(&p, &pj); - secp256k1_fe_normalize_var(&p.y); - session->partial_nonce_parity = secp256k1_extrakeys_ge_even_y(&p); - secp256k1_xonly_pubkey_save(&session->nonce, &p); - - secp256k1_sha256_initialize(&sha); - secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce); - secp256k1_sha256_write(&sha, nonce_ser, nonce_ser_size); - secp256k1_sha256_finalize(&sha, nonce_commitment32); - - session->round = 0; - secp256k1_scalar_clear(&secret); - return 1; -} - -int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) { - secp256k1_sha256 sha; - unsigned char nonce_commitments_hash[32]; - size_t i; - unsigned char nonce_ser[32]; - size_t nonce_ser_size = sizeof(nonce_ser); - (void) ctx; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(session->magic == session_magic); - ARG_CHECK(signers != NULL); - ARG_CHECK(nonce != NULL); - ARG_CHECK(commitments != NULL); - - ARG_CHECK(session->round == 0); - /* If the message was not set during initialization it must be set now. */ - ARG_CHECK(!(!session->is_msg_set && msg32 == NULL)); - /* The message can only be set once. */ - ARG_CHECK(!(session->is_msg_set && msg32 != NULL)); - ARG_CHECK(session->has_secret_data); - ARG_CHECK(n_commitments == session->n_signers); - for (i = 0; i < n_commitments; i++) { - ARG_CHECK(commitments[i] != NULL); - } - - if (msg32 != NULL) { - memcpy(session->msg, msg32, 32); - session->is_msg_set = 1; - } - secp256k1_sha256_initialize(&sha); - for (i = 0; i < n_commitments; i++) { - memcpy(signers[i].nonce_commitment, commitments[i], 32); - secp256k1_sha256_write(&sha, commitments[i], 32); - } - secp256k1_sha256_finalize(&sha, nonce_commitments_hash); - memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); - - secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce); - memcpy(nonce, &nonce_ser, nonce_ser_size); - session->round = 1; - return 1; -} - -int secp256k1_musig_session_init_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { - size_t i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(signers != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pre_session != NULL); - ARG_CHECK(pre_session->magic == pre_session_magic); - ARG_CHECK(commitments != NULL); - /* Check n_signers before checking commitments to allow testing the case where - * n_signers is big without allocating the space. */ - ARG_CHECK(n_signers > 0); - ARG_CHECK(n_signers <= UINT32_MAX); - for (i = 0; i < n_signers; i++) { - ARG_CHECK(commitments[i] != NULL); - } - (void) ctx; - - memset(session, 0, sizeof(*session)); - - session->magic = session_magic; - memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); - session->pre_session = *pre_session; - session->n_signers = (uint32_t) n_signers; - secp256k1_musig_signers_init(signers, session->n_signers); - - session->pre_session = *pre_session; - session->is_msg_set = 1; - memcpy(session->msg, msg32, 32); - session->has_secret_data = 0; - - for (i = 0; i < n_signers; i++) { - memcpy(signers[i].nonce_commitment, commitments[i], 32); - } - session->round = 1; - return 1; -} - -int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const unsigned char *nonce) { - secp256k1_sha256 sha; - unsigned char commit[32]; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(signer != NULL); - ARG_CHECK(nonce != NULL); - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, nonce, 32); - secp256k1_sha256_finalize(&sha, commit); - - if (memcmp(commit, signer->nonce_commitment, 32) != 0) { - return 0; - } - memcpy(&signer->nonce, nonce, sizeof(*nonce)); - if (!secp256k1_xonly_pubkey_parse(ctx, &signer->nonce, nonce)) { - return 0; - } - signer->present = 1; - return 1; -} - -int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_parity, const secp256k1_pubkey *adaptor) { - secp256k1_gej combined_noncej; - secp256k1_ge combined_noncep; - secp256k1_ge noncep; - secp256k1_sha256 sha; - unsigned char nonce_commitments_hash[32]; - size_t i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(signers != NULL); - ARG_CHECK(session->magic == session_magic); - ARG_CHECK(session->round == 1); - ARG_CHECK(n_signers == session->n_signers); - - secp256k1_sha256_initialize(&sha); - secp256k1_gej_set_infinity(&combined_noncej); - for (i = 0; i < n_signers; i++) { - if (!signers[i].present) { - return 0; - } - secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32); - secp256k1_xonly_pubkey_load(ctx, &noncep, &signers[i].nonce); - secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); - } - secp256k1_sha256_finalize(&sha, nonce_commitments_hash); - /* If the signers' commitments changed between get_public_nonce and now we - * have to abort because in that case they may have seen our nonce before - * creating their commitment. That can happen if the signer_data given to - * this function is different to the signer_data given to get_public_nonce. - * */ - if (session->has_secret_data - && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { - return 0; - } - - /* Add public adaptor to nonce */ - if (adaptor != NULL) { - secp256k1_pubkey_load(ctx, &noncep, adaptor); - secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); - } - - /* Negate nonce if Y coordinate is not square */ - secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); - secp256k1_fe_normalize_var(&combined_noncep.y); - session->combined_nonce_parity = secp256k1_extrakeys_ge_even_y(&combined_noncep); - if (nonce_parity != NULL) { - *nonce_parity = session->combined_nonce_parity; - } - secp256k1_xonly_pubkey_save(&session->combined_nonce, &combined_noncep); - session->round = 2; - return 1; -} - -int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out32 != NULL); - ARG_CHECK(sig != NULL); - memcpy(out32, sig->data, 32); - return 1; -} - -int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp256k1_musig_partial_signature* sig, const unsigned char *in32) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(in32 != NULL); - memcpy(sig->data, in32, 32); - return 1; -} - -/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ -static void secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { - unsigned char buf[32]; - secp256k1_ge rp; - secp256k1_sha256 sha; - - VERIFY_CHECK(session->round >= 2); - - secp256k1_schnorrsig_sha256_tagged(&sha); - secp256k1_xonly_pubkey_load(ctx, &rp, &session->combined_nonce); - secp256k1_fe_get_b32(buf, &rp.x); - secp256k1_sha256_write(&sha, buf, 32); - - secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk); - secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_write(&sha, session->msg, 32); - secp256k1_sha256_finalize(&sha, msghash); -} - -int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) { - unsigned char msghash[32]; - int overflow; - secp256k1_scalar sk; - secp256k1_scalar e, k; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(session->magic == session_magic); - ARG_CHECK(session->round == 2); - ARG_CHECK(session->has_secret_data); - - /* build message hash */ - secp256k1_musig_compute_messagehash(ctx, msghash, session); - secp256k1_scalar_set_b32(&e, msghash, NULL); - - secp256k1_scalar_set_b32(&sk, session->seckey, &overflow); - if (overflow) { - secp256k1_scalar_clear(&sk); - return 0; - } - - secp256k1_scalar_set_b32(&k, session->secnonce, &overflow); - if (overflow || secp256k1_scalar_is_zero(&k)) { - secp256k1_scalar_clear(&sk); - secp256k1_scalar_clear(&k); - return 0; - } - if (session->partial_nonce_parity != session->combined_nonce_parity) { - secp256k1_scalar_negate(&k, &k); - } - - /* Sign */ - secp256k1_scalar_mul(&e, &e, &sk); - secp256k1_scalar_add(&e, &e, &k); - secp256k1_scalar_get_b32(&partial_sig->data[0], &e); - secp256k1_scalar_clear(&sk); - secp256k1_scalar_clear(&k); - - return 1; -} - -int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { - size_t i; - secp256k1_scalar s; - secp256k1_ge noncep; - (void) ctx; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(partial_sigs != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(session->magic == session_magic); - ARG_CHECK(session->round == 2); - - if (n_sigs != session->n_signers) { - return 0; - } - secp256k1_scalar_clear(&s); - for (i = 0; i < n_sigs; i++) { - int overflow; - secp256k1_scalar term; - - secp256k1_scalar_set_b32(&term, partial_sigs[i].data, &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_add(&s, &s, &term); - } - - /* If there is a tweak then add (or subtract) `msghash` times `tweak` to `s`.*/ - if (session->pre_session.is_tweaked) { - unsigned char msghash[32]; - secp256k1_scalar e, scalar_tweak; - int overflow = 0; - - secp256k1_musig_compute_messagehash(ctx, msghash, session); - secp256k1_scalar_set_b32(&e, msghash, NULL); - secp256k1_scalar_set_b32(&scalar_tweak, session->pre_session.tweak, &overflow); - if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) { - /* This mimics the behavior of secp256k1_ec_seckey_tweak_mul regarding - * overflow and tweak being 0. */ - return 0; - } - if (session->pre_session.pk_parity) { - secp256k1_scalar_negate(&e, &e); - } - secp256k1_scalar_add(&s, &s, &e); - } - - secp256k1_xonly_pubkey_load(ctx, &noncep, &session->combined_nonce); - VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); - secp256k1_fe_normalize(&noncep.x); - secp256k1_fe_get_b32(&sig64[0], &noncep.x); - secp256k1_scalar_get_b32(&sig64[32], &s); - - return 1; -} - -int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_xonly_pubkey *pubkey) { - unsigned char msghash[32]; - secp256k1_scalar s; - secp256k1_scalar e; - secp256k1_scalar mu; - secp256k1_gej pkj; - secp256k1_gej rj; - secp256k1_ge pkp; - secp256k1_ge rp; - int overflow; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(signer != NULL); - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(pubkey != NULL); - ARG_CHECK(session->magic == session_magic); - ARG_CHECK(session->round == 2); - ARG_CHECK(signer->present); - - secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); - if (overflow) { - return 0; - } - secp256k1_musig_compute_messagehash(ctx, msghash, session); - secp256k1_scalar_set_b32(&e, msghash, NULL); - - if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { - return 0; - } - /* Multiplying the messagehash by the KeyAgg coefficient is equivalent - * to multiplying the signer's public key by the coefficient, except - * much easier to do. */ - secp256k1_musig_keyaggcoef(&mu, &session->pre_session, &pkp.x); - secp256k1_scalar_mul(&e, &e, &mu); - - if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { - return 0; - } - - /* If the MuSig-combined point has an odd Y coordinate, the signers will - * sign for the negation of their individual xonly public key such that the - * combined signature is valid for the MuSig aggregated xonly key. If the - * MuSig-combined point was tweaked then `e` is negated if the combined key - * has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/ - if (session->pre_session.pk_parity - != (session->pre_session.is_tweaked - && session->pre_session.internal_key_parity)) { - secp256k1_scalar_negate(&e, &e); - } - - /* Compute rj = s*G + (-e)*pkj */ - secp256k1_scalar_negate(&e, &e); - - secp256k1_gej_set_ge(&pkj, &pkp); - secp256k1_ecmult(&rj, &pkj, &e, &s); - - if (!session->combined_nonce_parity) { - secp256k1_ge_neg(&rp, &rp); - } - secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL); - - return secp256k1_gej_is_infinity(&rj); -} - -int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_parity) { - secp256k1_scalar s; - secp256k1_scalar t; - int overflow; - - (void) ctx; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(adaptor_sig != NULL); - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(sec_adaptor32 != NULL); - - secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); - if (overflow) { - secp256k1_scalar_clear(&t); - return 0; - } - - if (nonce_parity) { - secp256k1_scalar_negate(&t, &t); - } - - secp256k1_scalar_add(&s, &s, &t); - secp256k1_scalar_get_b32(adaptor_sig->data, &s); - secp256k1_scalar_clear(&t); - return 1; -} - -int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_parity) { - secp256k1_scalar t; - secp256k1_scalar s; - int overflow; - size_t i; - - (void) ctx; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sec_adaptor32 != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(partial_sigs != NULL); - - secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_negate(&t, &t); - - for (i = 0; i < n_partial_sigs; i++) { - secp256k1_scalar_set_b32(&s, partial_sigs[i].data, &overflow); - if (overflow) { - secp256k1_scalar_clear(&t); - return 0; - } - secp256k1_scalar_add(&t, &t, &s); - } - - if (!nonce_parity) { - secp256k1_scalar_negate(&t, &t); - } - secp256k1_scalar_get_b32(sec_adaptor32, &t); - secp256k1_scalar_clear(&t); - return 1; -} +#include "keyagg_impl.h" +#include "session_impl.h" +#include "adaptor_impl.h" #endif diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index 240e85ca..814cdc74 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -1,198 +1,63 @@ -MuSig - Rogue-Key-Resistant Multisignatures Module +Notes on the musig module API =========================== -This module implements the MuSig [1] multisignature scheme. The majority of -the module is an API designed to be used by signing or auditing participants -in a multisignature scheme. This involves a somewhat complex state machine -and significant effort has been taken to prevent accidental misuse of the -API in ways that could lead to accidental signatures or loss of key material. +The following sections contain additional notes on the API of the musig module (`include/secp256k1_musig.h`). +A usage example can be found in `examples/musig.c`. -The resulting signatures are valid Schnorr signatures as described in [2]. +# API misuse -# Theory +The musig API is designed to be as misuse resistant as possible. +However, the MuSig protocol has some additional failure modes (mainly due to interactivity) that do not appear in single-signing. +While the results can be catastrophic (e.g. leaking of the secret key), it is unfortunately not possible for the musig implementation to rule out all such failure modes. -In MuSig all signers contribute key material to a single signing key, -using the equation +Therefore, users of the musig module must take great care to make sure of the following: - P = sum_i µ_i * P_i +1. A unique nonce per signing session is generated in `secp256k1_musig_nonce_gen`. + See the corresponding comment in `include/secp256k1_musig.h` for how to ensure that. +2. The `secp256k1_musig_secnonce` structure is never copied or serialized. + See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`. +3. Opaque data structures are never written to or read from directly. + Instead, only the provided accessor functions are used. +4. If adaptor signatures are used, all partial signatures are verified. -where `P_i` is the public key of the `i`th signer and `µ_i` is a so-called -_MuSig coefficient_ computed according to the following equation +# Key Aggregation and (Taproot) Tweaking - L = H(P_1 || P_2 || ... || P_n) - µ_i = H(L || i) +Given a set of public keys, the aggregate public key is computed with `secp256k1_musig_pubkey_agg`. +A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly_pubkey_tweak_add`. -where H is a hash function modelled as a random oracle. +# Signing -To produce a multisignature `(s, R)` on a message `m` using verification key -`P`, signers act as follows: +This is covered by `examples/musig.c`. +Essentially, the protocol proceeds in the following steps: -1. Each computes a nonce, or ephemeral keypair, `(k_i, R_i)`. Every signer - communicates `H(R_i)` to every participant (both signers and auditors). -2. Upon receipt of every `H(R_i)`, each signer communicates `R_i` to every - participant. The recipients check that each `R_i` is consistent with the - previously-communicated hash. -3. Each signer computes a combined nonce - `R = sum_i R_i` - and shared challenge - `e = H(R || P || m)` - and partial signature - `s_i = k_i + µ_i*x_i*e` - where `x_i` is the secret key corresponding to `P_i`. +1. Generate a keypair with `secp256k1_keypair_create` and obtain the xonly public key with `secp256k1_keypair_xonly_pub`. +2. Call `secp256k1_musig_pubkey_agg` with the xonly pubkeys of all participants. +3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_tweak_add`. +4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers. +5. Someone (not necessarily the signer) aggregates the public nonce with `secp256k1_musig_nonce_agg` and sends it to the signers. +6. Process the aggregate nonce with `secp256k1_musig_nonce_process`. +7. Create a partial signature with `secp256k1_musig_partial_sign`. +8. Verify the partial signatures (optional in some scenarios) with `secp256k1_musig_partial_sig_verify`. +9. Someone (not necessarily the signer) obtains all partial signatures and aggregates them into the final Schnorr signature using `secp256k1_musig_partial_sig_agg`. -The complete signature is then the `(s, R)` where `s = sum_i s_i` and `R = sum_i R_i`. +The aggregate signature can be verified with `secp256k1_schnorrsig_verify`. -# API Usage +Note that steps 1 to 6 can happen before the message to be signed is known to the signers. +Therefore, the communication round to exchange nonces can be viewed as a pre-processing step that is run whenever convenient to the signers. +This disables some of the defense-in-depth measures that may protect against API misuse in some cases. +Similarly, the API supports an alternative protocol flow where generating the aggregate key (steps 1 to 3) is allowed to happen after exchanging nonces (steps 4 to 6). -The following sections describe use of our API, and are mirrored in code in `src/modules/musig/example.c`. +# Verification -It is essential to security that signers use a unique uniformly random nonce for all -signing sessions, and that they do not reuse these nonces even in the case that a -signing session fails to complete. To that end, all signing state is encapsulated -in the data structure `secp256k1_musig_session`. The API does not expose any -functionality to serialize or deserialize this structure; it is designed to exist -only in memory. +A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7. -Users who need to persist this structure must take additional security measures -which cannot be enforced by a C API. Some guidance is provided in the documentation -for this data structure in `include/secp256k1_musig.h`. - -## Key Generation - -To use MuSig, users must first compute their combined public key `P`, which is -suitable for use on a blockchain or other public key repository. They do this -by calling `secp256k1_musig_pubkey_combine`. - -This function takes as input a list of public keys `P_i` in the argument -`pubkeys`. It outputs the combined public key `P` in the out-pointer `combined_pk` -and hash `L` in the out-pointer `pk_hash32`, if this pointer is non-NULL. - -## Signing - -A participant who wishes to sign a message (as opposed to observing/auditing the -signature process, which is also a supported mode) acts as follows. - -### Signing Participant - -1. The signer starts the session by calling `secp256k1_musig_session_init`. - This function outputs - - an initialized session state in the out-pointer `session` - - an array of initialized signer data in the out-pointer `signers` - - a commitment `H(R_i)` to a nonce in the out-pointer `nonce_commitment32` - It takes as input - - a unique session ID `session_id32` - - (optionally) a message to be signed `msg32` - - the combined public key output from `secp256k1_musig_pubkey_combine` - - the public key hash output from `secp256k1_musig_pubkey_combine` - - the signer's index `i` `my_index` - - the signer's secret key `seckey` -2. The signer then communicates `H(R_i)` to all other signers, and receives - commitments `H(R_j)` from all other signers `j`. These hashes are simply - length-32 byte arrays which can be communicated however is communicated. -3. Once all signers nonce commitments have been received, the signer records - these commitments with the function `secp256k1_musig_session_get_public_nonce`. - If the signer did not provide a message to `secp256k1_musig_session_init`, - a message must be provided now. - This function updates in place - - the session state `session` - - the array of signer data `signers` - taking in as input the list of commitments `commitments` and outputting the - signer's public nonce `R_i` in the out-pointer `nonce`. -4. The signer then communicates `R_i` to all other signers, and receives `R_j` - from each signer `j`. On receipt of a nonce `R_j` he calls the function - `secp256k1_musig_set_nonce` to record this fact. This function checks that - the received nonce is consistent with the previously-received nonce and will - return 0 in this case. The signer must also call this function with his own - nonce and his own index `i`. - These nonces `R_i` are secp256k1 public keys; they should be serialized using - `secp256k1_ec_pubkey_serialize` and parsed with `secp256k1_ec_pubkey_parse`. -5. Once all nonces have been exchanged in this way, signers are able to compute - their partial signatures. They do so by calling `secp256k1_musig_session_combine_nonces` - which updates in place - - the session state `session` - - the array of signer data `signers` - It outputs an auxiliary integer `nonce_is_negated` and has an auxiliary input - `adaptor`. Both of these may be set to NULL for ordinary signing purposes. -6. The signer computes a partial signature `s_i` using the function - `secp256k1_musig_partial_sign` which takes the session state as input and - partial signature as output. -7. The signer then communicates the partial signature `s_i` to all other signers, or - to a central coordinator. These partial signatures should be serialized using - `musig_partial_signature_serialize` and parsed using `musig_partial_signature_parse`. -8. Each signer calls `secp256k1_musig_partial_sig_verify` on the other signers' partial - signatures to verify their correctness. If only the validity of the final signature - is important, not assigning blame, this step can be skipped. -9. Any signer, or central coordinator, may combine the partial signatures to obtain - a complete signature using `secp256k1_musig_partial_sig_combine`. This function takes - a signing session and array of MuSig partial signatures, and outputs a single - Schnorr signature. - -### Non-signing Participant - -A participant who wants to verify the signing process, i.e. check that nonce commitments -are consistent and partial signatures are correct without contributing a partial signature, -may do so using the above instructions except for the following changes: - -1. A signing session should be produced using `musig_session_init_verifier` - rather than `musig_session_init`; this function takes no secret data or - signer index. -2. The participant receives nonce commitments, public nonces and partial signatures, - but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce` - and `secp256k1_musig_partial_sign` are not called. - -### Verifier - -The final signature is simply a valid Schnorr signature using the combined public key. It -can be verified using the `secp256k1_schnorrsig_verify` with the correct message and -public key output from `secp256k1_musig_pubkey_combine`. - -## Atomic Swaps +# Atomic Swaps The signing API supports the production of "adaptor signatures", modified partial signatures which are offset by an auxiliary secret known to one party. That is, 1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`. -2. When combining nonces, each party adds `T` to the total nonce used in the signature. -3. The party who knows `t` must "adapt" their partial signature with `t` to complete the - signature. -4. Any party who sees both the final signature and the original partial signatures - can compute `t`. - -Using these adaptor signatures, two 2-of-2 MuSig signing protocols can be executed in -parallel such that one party's partial signatures are made atomic. That is, when the other -party learns one partial signature, she automatically learns the other. This has applications -in cross-chain atomic swaps. - -Such a protocol can be executed as follows. Consider two participants, Alice and Bob, who -are simultaneously producing 2-of-2 multisignatures for two blockchains A and B. They act -as follows. - -1. Before the protocol begins, Bob chooses a 32-byte auxiliary secret `t` at random and - computes a corresponding public point `T` by calling `secp256k1_ec_pubkey_create`. - He communicates `T` to Alice. -2. Together, the parties execute steps 1-4 of the signing protocol above. -3. At step 5, when combining the two parties' public nonces, both parties call - `secp256k1_musig_session_combine_nonces` with `adaptor` set to `T` and `nonce_is_negated` - set to a non-NULL pointer to int. -4. Steps 6 and 7 proceed as before. Step 8, verifying the partial signatures, is now - essential to the security of the protocol and must not be omitted! - -The above steps are executed identically for both signing sessions. However, step 9 will -not work as before, since the partial signatures will not add up to a valid total signature. -Additional steps must be taken, and it is at this point that the two signing sessions -diverge. From here on we consider "Session A" which benefits Alice (e.g. which sends her -coins) and "Session B" which benefits Bob (e.g. which sends him coins). - -5. In Session B, Bob calls `secp256k1_musig_partial_sig_adapt` with his partial signature - and `t`, to produce an adaptor signature. He can then call `secp256k1_musig_partial_sig_combine` - with this adaptor signature and Alice's partial signature, to produce a complete - signature for blockchain B. -6. Alice reads this signature from blockchain B. She calls `secp256k1_musig_extract_secret_adaptor`, - passing the complete signature along with her and Bob's partial signatures from Session B. - This function outputs `t`, which until this point was only known to Bob. -7. In Session A, Alice is now able to replicate Bob's action, calling - `secp256k1_musig_partial_sig_adapt` with her own partial signature and `t`, ultimately - producing a complete signature on blockchain A. - -[1] https://eprint.iacr.org/2018/068 -[2] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki - +2. When calling `secp256k1_musig_nonce_process`, the public adaptor `T` is provided as the `adaptor` argument. +3. The party who is going to extract the secret adaptor `t` later must verify all partial signatures. +4. Due to step 2, the signature output of `secp256k1_musig_partial_sig_agg` is a pre-signature and not a valid Schnorr signature. All parties involved extract this session's `nonce_parity` with `secp256k1_musig_nonce_parity`. +5. The party who knows `t` must "adapt" the pre-signature with `t` (and the `nonce_parity` using `secp256k1_musig_adapt` to complete the signature. +6. Any party who sees both the final signature and the pre-signature (and has the `nonce_parity`) can extract `t` with `secp256k1_musig_extract_adaptor`. diff --git a/src/modules/musig/session.h b/src/modules/musig/session.h new file mode 100644 index 00000000..dfaa5e0d --- /dev/null +++ b/src/modules/musig/session.h @@ -0,0 +1,25 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_SESSION_H +#define SECP256K1_MODULE_MUSIG_SESSION_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_musig.h" + +#include "../../scalar.h" + +typedef struct { + int fin_nonce_parity; + unsigned char fin_nonce[32]; + secp256k1_scalar noncecoef; + secp256k1_scalar challenge; + secp256k1_scalar s_part; +} secp256k1_musig_session_internal; + +static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session); + +#endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h new file mode 100644 index 00000000..7cdcebe3 --- /dev/null +++ b/src/modules/musig/session_impl.h @@ -0,0 +1,755 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_SESSION_IMPL_H +#define SECP256K1_MODULE_MUSIG_SESSION_IMPL_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_musig.h" + +#include "keyagg.h" +#include "session.h" +#include "../../eckey.h" +#include "../../hash.h" +#include "../../scalar.h" +#include "../../util.h" + +static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 }; + +static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, secp256k1_scalar *k) { + 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]); +} + +static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_musig_secnonce *secnonce) { + int is_zero; + ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0); + secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL); + secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL); + /* We make very sure that the nonce isn't invalidated by checking the values + * in addition to the magic. */ + is_zero = secp256k1_scalar_is_zero(&k[0]) & secp256k1_scalar_is_zero(&k[1]); + secp256k1_declassify(ctx, &is_zero, sizeof(is_zero)); + ARG_CHECK(!is_zero); + return 1; +} + +/* If flag is true, invalidate the secnonce; otherwise leave it. Constant-time. */ +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, above code makes the magic + * classified. However, we need the magic to be declassified to be able to + * compare it during secnonce_load. */ + secp256k1_declassify(ctx, secnonce->data, sizeof(secp256k1_musig_secnonce_magic)); +} + +static const unsigned char secp256k1_musig_pubnonce_magic[4] = { 0xf5, 0x7a, 0x3d, 0xa0 }; + +/* Requires that none of the provided group elements is infinity. Works for both + * musig_pubnonce and musig_aggnonce. */ +static void secp256k1_musig_pubnonce_save(secp256k1_musig_pubnonce* nonce, secp256k1_ge* ge) { + int i; + memcpy(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4); + for (i = 0; i < 2; i++) { + secp256k1_point_save(nonce->data + 4+64*i, &ge[i]); + } +} + +/* Works for both musig_pubnonce and musig_aggnonce. Returns 1 unless the nonce + * wasn't properly initialized */ +static int secp256k1_musig_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge* ge, 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_point_load(&ge[i], nonce->data + 4 + 64*i); + } + return 1; +} + +static void secp256k1_musig_aggnonce_save(secp256k1_musig_aggnonce* nonce, secp256k1_ge* ge) { + secp256k1_musig_pubnonce_save((secp256k1_musig_pubnonce *) nonce, ge); +} + +static int secp256k1_musig_aggnonce_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_musig_aggnonce* nonce) { + return secp256k1_musig_pubnonce_load(ctx, ge, (secp256k1_musig_pubnonce *) nonce); +} + +static const unsigned char secp256k1_musig_session_cache_magic[4] = { 0x9d, 0xed, 0xe9, 0x17 }; + +/* A session consists of + * - 4 byte session cache magic + * - 1 byte the parity of the final nonce + * - 32 byte serialized x-only final nonce + * - 32 byte nonce coefficient b + * - 32 byte signature challenge hash e + * - 32 byte scalar s that is added to the partial signatures of the signers + */ +static void secp256k1_musig_session_save(secp256k1_musig_session *session, const secp256k1_musig_session_internal *session_i) { + unsigned char *ptr = session->data; + + memcpy(ptr, secp256k1_musig_session_cache_magic, 4); + ptr += 4; + *ptr = session_i->fin_nonce_parity; + ptr += 1; + memcpy(ptr, session_i->fin_nonce, 32); + ptr += 32; + secp256k1_scalar_get_b32(ptr, &session_i->noncecoef); + ptr += 32; + secp256k1_scalar_get_b32(ptr, &session_i->challenge); + ptr += 32; + secp256k1_scalar_get_b32(ptr, &session_i->s_part); +} + +static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session) { + const unsigned char *ptr = session->data; + + ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_session_cache_magic, 4) == 0); + ptr += 4; + session_i->fin_nonce_parity = *ptr; + ptr += 1; + memcpy(session_i->fin_nonce, ptr, 32); + ptr += 32; + secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL); + ptr += 32; + secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL); + ptr += 32; + secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL); + return 1; +} + +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); +} + +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; +} + +int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_pubnonce* nonce) { + secp256k1_ge ge[2]; + int i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out66 != NULL); + memset(out66, 0, 66); + ARG_CHECK(nonce != NULL); + + if (!secp256k1_musig_pubnonce_load(ctx, ge, nonce)) { + return 0; + } + for (i = 0; i < 2; i++) { + int ret; + size_t size = 33; + ret = secp256k1_eckey_pubkey_serialize(&ge[i], &out66[33*i], &size, 1); + /* serialize must succeed because the point was just loaded */ + VERIFY_CHECK(ret && size == 33); + } + return 1; +} + +int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig_pubnonce* nonce, const unsigned char *in66) { + secp256k1_ge ge[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(&ge[i], &in66[33*i], 33)) { + return 0; + } + if (!secp256k1_ge_is_in_correct_subgroup(&ge[i])) { + return 0; + } + } + /* The group elements can not be infinity because they were just parsed */ + secp256k1_musig_pubnonce_save(nonce, ge); + return 1; +} + +int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_aggnonce* nonce) { + return secp256k1_musig_pubnonce_serialize(ctx, out66, (secp256k1_musig_pubnonce*) nonce); +} + +int secp256k1_musig_aggnonce_parse(const secp256k1_context* ctx, secp256k1_musig_aggnonce* nonce, const unsigned char *in66) { + return secp256k1_musig_pubnonce_parse(ctx, (secp256k1_musig_pubnonce*) nonce, in66); +} + +int secp256k1_musig_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_sig* sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out32 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out32, &sig->data[4], 32); + return 1; +} + +int secp256k1_musig_partial_sig_parse(const secp256k1_context* ctx, secp256k1_musig_partial_sig* sig, const unsigned char *in32) { + secp256k1_scalar tmp; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in32 != NULL); + + secp256k1_scalar_set_b32(&tmp, in32, &overflow); + if (overflow) { + return 0; + } + secp256k1_musig_partial_sig_save(sig, &tmp); + return 1; +} + +/* Normalizes the x-coordinate of the given group element. */ +static int secp256k1_xonly_ge_serialize(unsigned char *output32, secp256k1_ge *ge) { + if (secp256k1_ge_is_infinity(ge)) { + return 0; + } + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_get_b32(output32, &ge->x); + return 1; +} + +static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *msg32, const unsigned char *key32, const unsigned char *agg_pk32, const unsigned char *extra_input32) { + secp256k1_sha256 sha; + unsigned char seed[32]; + unsigned char i; + enum { n_extra_in = 4 }; + const unsigned char *extra_in[n_extra_in]; + + /* TODO: this doesn't have the same sidechannel resistance as the BIP340 + * nonce function because the seckey feeds directly into SHA. */ + + /* Subtract one from `sizeof` to avoid hashing the implicit null byte */ + secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/nonce", sizeof("MuSig/nonce") - 1); + secp256k1_sha256_write(&sha, session_id, 32); + extra_in[0] = msg32; + extra_in[1] = key32; + extra_in[2] = agg_pk32; + extra_in[3] = extra_input32; + for (i = 0; i < n_extra_in; i++) { + unsigned char len; + if (extra_in[i] != NULL) { + len = 32; + secp256k1_sha256_write(&sha, &len, 1); + secp256k1_sha256_write(&sha, extra_in[i], 32); + } else { + len = 0; + secp256k1_sha256_write(&sha, &len, 1); + } + } + secp256k1_sha256_finalize(&sha, seed); + + for (i = 0; i < 2; i++) { + unsigned char buf[32]; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, seed, 32); + secp256k1_sha256_write(&sha, &i, sizeof(i)); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(&k[i], buf, NULL); + } +} + +int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *session_id32, const unsigned char *seckey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) { + secp256k1_keyagg_cache_internal cache_i; + secp256k1_scalar k[2]; + secp256k1_ge nonce_pt[2]; + int i; + unsigned char pk_ser[32]; + unsigned char *pk_ser_ptr = NULL; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secnonce != NULL); + memset(secnonce, 0, sizeof(*secnonce)); + ARG_CHECK(pubnonce != NULL); + memset(pubnonce, 0, sizeof(*pubnonce)); + ARG_CHECK(session_id32 != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + if (seckey == NULL) { + /* Check in constant time that the session_id is not 0 as a + * defense-in-depth measure that may protect against a faulty RNG. */ + unsigned char acc = 0; + for (i = 0; i < 32; i++) { + acc |= session_id32[i]; + } + ret &= !!acc; + memset(&acc, 0, sizeof(acc)); + } + + /* 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 (keyagg_cache != NULL) { + int ret_tmp; + if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + return 0; + } + ret_tmp = secp256k1_xonly_ge_serialize(pk_ser, &cache_i.pk); + /* Serialization can not fail because the loaded point can not be infinity. */ + VERIFY_CHECK(ret_tmp); + pk_ser_ptr = pk_ser; + } + secp256k1_nonce_function_musig(k, session_id32, msg32, seckey, pk_ser_ptr, extra_input32); + VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); + VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1])); + VERIFY_CHECK(!secp256k1_scalar_eq(&k[0], &k[1])); + secp256k1_musig_secnonce_save(secnonce, k); + secp256k1_musig_secnonce_invalidate(ctx, secnonce, !ret); + + for (i = 0; i < 2; i++) { + secp256k1_gej nonce_ptj; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj, &k[i]); + secp256k1_ge_set_gej(&nonce_pt[i], &nonce_ptj); + secp256k1_declassify(ctx, &nonce_pt[i], sizeof(nonce_pt)); + secp256k1_scalar_clear(&k[i]); + } + /* nonce_pt won't be infinity because k != 0 with overwhelming probability */ + secp256k1_musig_pubnonce_save(pubnonce, nonce_pt); + return ret; +} + +static int secp256k1_musig_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) { + size_t i; + int j; + + secp256k1_gej_set_infinity(&summed_nonces[0]); + secp256k1_gej_set_infinity(&summed_nonces[1]); + + for (i = 0; i < n_pubnonces; i++) { + secp256k1_ge nonce_pt[2]; + if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonces[i])) { + return 0; + } + for (j = 0; j < 2; j++) { + secp256k1_gej_add_ge_var(&summed_nonces[j], &summed_nonces[j], &nonce_pt[j], NULL); + } + } + return 1; +} + +int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggnonce *aggnonce, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) { + secp256k1_gej aggnonce_ptj[2]; + secp256k1_ge aggnonce_pt[2]; + int i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(aggnonce != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(n_pubnonces > 0); + + if (!secp256k1_musig_sum_nonces(ctx, aggnonce_ptj, pubnonces, n_pubnonces)) { + return 0; + } + for (i = 0; i < 2; i++) { + if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { + /* There must be at least one dishonest signer. If we would return 0 + here, we will never be able to determine who it is. Therefore, we + should continue such that the culprit is revealed when collecting + and verifying partial signatures. + + However, dealing with the point at infinity (loading, + de-/serializing) would require a lot of extra code complexity. + Instead, we set the aggregate nonce to some arbitrary point (the + generator). This is secure, because it only restricts the + abilities of the attacker: an attacker that forces the sum of + nonces to be infinity by sending some maliciously generated nonce + pairs can be turned into an attacker that forces the sum to be + the generator (by simply adding the generator to one of the + malicious nonces), and this does not change the winning condition + of the EUF-CMA game. */ + aggnonce_pt[i] = secp256k1_ge_const_g; + } else { + secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); + } + } + secp256k1_musig_aggnonce_save(aggnonce, aggnonce_pt); + return 1; +} + +/* tagged_hash(aggnonce[0], aggnonce[1], agg_pk, msg) */ +static int secp256k1_musig_compute_noncehash(unsigned char *noncehash, secp256k1_ge *aggnonce, const unsigned char *agg_pk32, const unsigned char *msg) { + unsigned char buf[33]; + secp256k1_sha256 sha; + int i; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/noncecoef", sizeof("MuSig/noncecoef") - 1); + for (i = 0; i < 2; i++) { + size_t size; + if (!secp256k1_eckey_pubkey_serialize(&aggnonce[i], buf, &size, 1)) { + return 0; + } + VERIFY_CHECK(size == sizeof(buf)); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + } + secp256k1_sha256_write(&sha, agg_pk32, 32); + secp256k1_sha256_write(&sha, msg, 32); + secp256k1_sha256_finalize(&sha, noncehash); + return 1; +} + +static int secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *agg_pk32, const unsigned char *msg) { + unsigned char noncehash[32]; + secp256k1_ge fin_nonce_pt; + secp256k1_gej fin_nonce_ptj; + secp256k1_ge aggnonce[2]; + + secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); + secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); + if (!secp256k1_musig_compute_noncehash(noncehash, aggnonce, agg_pk32, msg)) { + return 0; + } + /* fin_nonce = aggnonce[0] + b*aggnonce[1] */ + secp256k1_scalar_set_b32(b, noncehash, NULL); + secp256k1_ecmult(&fin_nonce_ptj, &aggnoncej[1], b, NULL); + secp256k1_gej_add_ge(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0]); + secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj); + if (!secp256k1_xonly_ge_serialize(fin_nonce, &fin_nonce_pt)) { + /* unreachable with overwhelming probability */ + return 0; + } + secp256k1_fe_normalize_var(&fin_nonce_pt.y); + *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); + return 1; +} + +int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey *adaptor) { + secp256k1_keyagg_cache_internal cache_i; + secp256k1_ge aggnonce_pt[2]; + secp256k1_gej aggnonce_ptj[2]; + unsigned char fin_nonce[32]; + secp256k1_musig_session_internal session_i; + unsigned char agg_pk32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(aggnonce != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(keyagg_cache != NULL); + + if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + return 0; + } + secp256k1_fe_get_b32(agg_pk32, &cache_i.pk.x); + + if (!secp256k1_musig_aggnonce_load(ctx, aggnonce_pt, aggnonce)) { + return 0; + } + secp256k1_gej_set_ge(&aggnonce_ptj[0], &aggnonce_pt[0]); + secp256k1_gej_set_ge(&aggnonce_ptj[1], &aggnonce_pt[1]); + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_ge adaptorp; + if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { + return 0; + } + secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL); + } + if (!secp256k1_musig_nonce_process_internal(&session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, agg_pk32, msg32)) { + return 0; + } + + secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, agg_pk32); + + /* If there is a tweak then set `challenge` times `tweak` to the `s`-part.*/ + secp256k1_scalar_set_int(&session_i.s_part, 0); + if (!secp256k1_scalar_is_zero(&cache_i.tweak)) { + secp256k1_scalar e_tmp; + secp256k1_scalar_mul(&e_tmp, &session_i.challenge, &cache_i.tweak); + if (secp256k1_fe_is_odd(&cache_i.pk.y)) { + secp256k1_scalar_negate(&e_tmp, &e_tmp); + } + secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &e_tmp); + } + memcpy(session_i.fin_nonce, fin_nonce, sizeof(session_i.fin_nonce)); + secp256k1_musig_session_save(session, &session_i); + return 1; +} + +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; + secp256k1_scalar k[2]; + secp256k1_scalar mu, s; + secp256k1_keyagg_cache_internal cache_i; + secp256k1_musig_session_internal session_i; + int ret; + + VERIFY_CHECK(ctx != NULL); + + ARG_CHECK(secnonce != NULL); + /* Fails if the magic doesn't match */ + ret = secp256k1_musig_secnonce_load(ctx, k, secnonce); + /* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls + * of this function to fail */ + memset(secnonce, 0, sizeof(*secnonce)); + if (!ret) { + secp256k1_musig_partial_sign_clear(&sk, k); + return 0; + } + + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(keypair != NULL); + ARG_CHECK(keyagg_cache != NULL); + ARG_CHECK(session != NULL); + + if (!secp256k1_keypair_load(ctx, &sk, &pk, keypair)) { + secp256k1_musig_partial_sign_clear(&sk, k); + return 0; + } + if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + secp256k1_musig_partial_sign_clear(&sk, k); + return 0; + } + secp256k1_fe_normalize_var(&pk.y); + /* Determine if the secret key sk should be negated before signing. + * + * We use the following notation: + * - |.| is a function that normalizes a point to an even Y by negating + * if necessary, similar to secp256k1_extrakeys_ge_even_y + * - mu[i] is the i-th KeyAgg coefficient + * - t[i] is the i-th tweak + * + * The following public keys arise as intermediate steps: + * - P[i] is the i-th public key with corresponding secret key x[i] + * P[i] := x[i]*G + * - P_agg is the aggregate public key + * P_agg := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| + * - P_tweak[i] is the tweaked public key after the i-th tweaking operation + * P_tweak[0] := P_agg + * P_tweak[i] := |P_tweak[i-1]| + t[i]*G for i = 1, ..., m + * + * Note that our goal is to produce a partial signature corresponding to + * the final public key after m tweaking operations P_final = |P_tweak[m]|. + * + * Define d[i], d_agg, and d_tweak[i] so that: + * - |P[i]| = d[i]*P[i] + * - |P_agg| = d_agg*P_agg + * - |P_tweak[i]| = d_tweak[i]*P_tweak[i] + * + * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise; + * similarly for d_agg and d_tweak[i]. + * + * The (xonly) final public key is P_final = |P_tweak[m]| + * = d_tweak[m]*P_tweak[m] + * = d_tweak[m]*(|P_tweak[m-1]| + t[m]*G) + * = d_tweak[m]*(d_tweak[m-1]*(|P_tweak[m-2]| + t[m-1]*G) + t[m]*G) + * = d_tweak[m]*...*d_tweak[1]*|P_agg| + (d_tweak[m]*t[m]+...+*d_tweak[1]*t[1])*G. + * To simplify the equation let us define + * t := d_tweak[m]*t[m]+...+*d_tweak[1]*t[1] + * d_tweak := d_tweak[m]*...*d_tweak[1]. + * Then we have + * P_final - t*G + * = d_tweak*|P_agg| + * = d_tweak*d_agg*P_agg + * = d_tweak*d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) + * = d_tweak*d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) + * = sum((d_tweak*d_agg*d[i])*mu[i]*x[i])*G. + * + * Thus whether signer i should use the negated x[i] depends on the product + * d_tweak[m]*...*d_tweak[1]*d_agg*d[i]. In other words, negate if and only + * if the following holds: + * (P[i] has odd y) XOR (P_agg has odd y) + * XOR (P_tweak[1] has odd y) XOR ... XOR (P_tweak[m] has odd y) + * + * Let us now look at how the terms in the equation correspond to the if + * condition below for some values of m: + * m = 0: P_i has odd y = secp256k1_fe_is_odd(&pk.y) + * P_agg has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * cache_i.internal_key_parity = 0 + * m = 1: P_i has odd y = secp256k1_fe_is_odd(&pk.y) + * P_agg has odd y = cache_i.internal_key_parity + * P_tweak[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 2: P_i has odd y = secp256k1_fe_is_odd(&pk.y) + * P_agg has odd y XOR P_tweak[1] has odd y = cache_i.internal_key_parity + * P_tweak[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * etc. + */ + if ((secp256k1_fe_is_odd(&pk.y) + != secp256k1_fe_is_odd(&cache_i.pk.y)) + != cache_i.internal_key_parity) { + secp256k1_scalar_negate(&sk, &sk); + } + + /* Multiply KeyAgg coefficient */ + secp256k1_fe_normalize_var(&pk.x); + /* TODO Cache mu */ + secp256k1_musig_keyaggcoef(&mu, &cache_i, &pk.x); + secp256k1_scalar_mul(&sk, &sk, &mu); + + if (!secp256k1_musig_session_load(ctx, &session_i, session)) { + secp256k1_musig_partial_sign_clear(&sk, k); + return 0; + } + + if (session_i.fin_nonce_parity) { + secp256k1_scalar_negate(&k[0], &k[0]); + secp256k1_scalar_negate(&k[1], &k[1]); + } + + /* Sign */ + secp256k1_scalar_mul(&s, &session_i.challenge, &sk); + secp256k1_scalar_mul(&k[1], &session_i.noncecoef, &k[1]); + 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); + return 1; +} + +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_xonly_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { + secp256k1_keyagg_cache_internal cache_i; + secp256k1_musig_session_internal session_i; + secp256k1_scalar mu, e, s; + secp256k1_gej pkj; + secp256k1_ge nonce_pt[2]; + secp256k1_gej rj; + secp256k1_gej tmp; + secp256k1_ge pkp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(pubnonce != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(keyagg_cache != NULL); + ARG_CHECK(session != NULL); + + if (!secp256k1_musig_session_load(ctx, &session_i, session)) { + return 0; + } + + /* Compute "effective" nonce rj = aggnonce[0] + b*aggnonce[1] */ + /* TODO: use multiexp to compute -s*G + e*mu*pubkey + aggnonce[0] + b*aggnonce[1] */ + if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonce)) { + return 0; + } + secp256k1_gej_set_ge(&rj, &nonce_pt[1]); + secp256k1_ecmult(&rj, &rj, &session_i.noncecoef, NULL); + secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pt[0], NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { + return 0; + } + if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + return 0; + } + /* Multiplying the challenge by the KeyAgg coefficient is equivalent + * to multiplying the signer's public key by the coefficient, except + * much easier to do. */ + secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); + secp256k1_scalar_mul(&e, &session_i.challenge, &mu); + + /* If the MuSig-aggregate point has an odd Y coordinate, the signers will + * sign for the negation of their individual xonly public key. If the + * aggregate key is untweaked, then internal_key_parity is 0, so `e` is + * negated exactly when the aggregate key parity is odd. If the aggregate + * key is tweaked, then negation happens when the aggregate key has an odd Y + * coordinate XOR the internal key has an odd Y coordinate.*/ + + /* When producing a partial signature, signer i uses a possibly + * negated secret key: + * + * sk[i] = (d_tweak*d_agg*d[i])*x[i] + * + * to ensure that the aggregate signature will correspond to + * an aggregate public key with even Y coordinate (see the + * notation and explanation in musig_partial_sign). + * + * We use the following additional notation: + * - e is the (Schnorr signature) challenge + * - r[i] is the i-th signer's secret nonce + * - R[i] = r[i]*G is the i-th signer's public nonce + * - R is the aggregated public nonce + * - d_nonce is chosen so that |R| = d_nonce*R + * + * The i-th partial signature is: + * + * s[i] = d_nonce*r[i] + mu[i]*e*sk[i] + * + * In order to verify this partial signature, we need to check: + * + * s[i]*G = d_nonce*R[i] + mu[i]*e*sk[i]*G + * + * The verifier doesn't have access to sk[i]*G, but can construct + * it using the xonly public key |P[i]| as follows: + * + * sk[i]*G = d_tweak*d_agg*d[i]*x[i]*G + * = d_tweak*d_agg*d[i]*P[i] + * = d_tweak*d_agg*|P[i]| + * + * The if condition below is true whenever d_tweak*d_agg is + * negative (again, see the explanation in musig_partial_sign). In + * this case, the verifier negates e which will have the same end + * result as negating |P[i]|, since they are multiplied later anyway. + */ + if (secp256k1_fe_is_odd(&cache_i.pk.y) + != cache_i.internal_key_parity) { + secp256k1_scalar_negate(&e, &e); + } + + if (!secp256k1_musig_partial_sig_load(ctx, &s, partial_sig)) { + return 0; + } + /* Compute -s*G + e*pkj + rj (e already includes the keyagg coefficient mu) */ + secp256k1_scalar_negate(&s, &s); + secp256k1_gej_set_ge(&pkj, &pkp); + secp256k1_ecmult(&tmp, &pkj, &e, &s); + if (session_i.fin_nonce_parity) { + secp256k1_gej_neg(&rj, &rj); + } + secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL); + + return secp256k1_gej_is_infinity(&tmp); +} + +int secp256k1_musig_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, const secp256k1_musig_session *session, const secp256k1_musig_partial_sig * const* partial_sigs, size_t n_sigs) { + size_t i; + secp256k1_musig_session_internal session_i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(partial_sigs != NULL); + ARG_CHECK(n_sigs > 0); + + if (!secp256k1_musig_session_load(ctx, &session_i, session)) { + return 0; + } + for (i = 0; i < n_sigs; i++) { + secp256k1_scalar term; + if (!secp256k1_musig_partial_sig_load(ctx, &term, partial_sigs[i])) { + return 0; + } + secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &term); + } + secp256k1_scalar_get_b32(&sig64[32], &session_i.s_part); + memcpy(&sig64[0], session_i.fin_nonce, 32); + return 1; +} + +#endif diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index a500f3bc..383b4161 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -1,112 +1,156 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ +/*********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ -#ifndef _SECP256K1_MODULE_MUSIG_TESTS_ -#define _SECP256K1_MODULE_MUSIG_TESTS_ +#ifndef SECP256K1_MODULE_MUSIG_TESTS_IMPL_H +#define SECP256K1_MODULE_MUSIG_TESTS_IMPL_H -#include "secp256k1_musig.h" +#include +#include -int secp256k1_xonly_pubkey_create(secp256k1_xonly_pubkey *pk, const unsigned char *seckey) { +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_extrakeys.h" +#include "../../../include/secp256k1_musig.h" + +#include "session.h" +#include "keyagg.h" +#include "../../scalar.h" +#include "../../scratch.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../util.h" + +static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_xonly_pubkey *pk, const unsigned char *sk) { int ret; - secp256k1_keypair keypair; - ret = secp256k1_keypair_create(ctx, &keypair, seckey); - ret &= secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair); + 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; } -/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig combine, sign, verify +/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig aggregate, sign, verify * test. */ void musig_simple_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; - secp256k1_musig_session session[2]; - secp256k1_musig_session_signer_data signer0[2]; - secp256k1_musig_session_signer_data signer1[2]; - unsigned char nonce_commitment[2][32]; + secp256k1_keypair keypair[2]; + secp256k1_musig_pubnonce pubnonce[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr[2]; + secp256k1_musig_aggnonce aggnonce; unsigned char msg[32]; - secp256k1_xonly_pubkey combined_pk; - secp256k1_musig_pre_session pre_session; + secp256k1_xonly_pubkey agg_pk; + secp256k1_musig_keyagg_cache keyagg_cache; unsigned char session_id[2][32]; + secp256k1_musig_secnonce secnonce[2]; secp256k1_xonly_pubkey pk[2]; const secp256k1_xonly_pubkey *pk_ptr[2]; - const unsigned char *ncs[2]; - unsigned char public_nonce[3][32]; - secp256k1_musig_partial_signature partial_sig[2]; + secp256k1_musig_partial_sig partial_sig[2]; + const secp256k1_musig_partial_sig *partial_sig_ptr[2]; unsigned char final_sig[64]; + secp256k1_musig_session session; + int i; - secp256k1_testrand256(session_id[0]); - secp256k1_testrand256(session_id[1]); - secp256k1_testrand256(sk[0]); - secp256k1_testrand256(sk[1]); secp256k1_testrand256(msg); + for (i = 0; i < 2; i++) { + secp256k1_testrand256(session_id[i]); + secp256k1_testrand256(sk[i]); + pk_ptr[i] = &pk[i]; + pubnonce_ptr[i] = &pubnonce[i]; + partial_sig_ptr[i] = &partial_sig[i]; - pk_ptr[0] = &pk[0]; - pk_ptr[1] = &pk[1]; - CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); - CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i])); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[i], &pubnonce[i], session_id[i], sk[i], NULL, NULL, NULL) == 1); + } - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); + CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 2) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); - ncs[0] = nonce_commitment[0]; - ncs[1] = nonce_commitment[1]; + for (i = 0; i < 2; i++) { + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig[i], &secnonce[i], &keypair[i], &keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig[i], &pubnonce[i], &pk[i], &keyagg_cache, &session) == 1); + } - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_agg(ctx, final_sig, &session, partial_sig_ptr, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), &agg_pk) == 1); +} - CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], public_nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], public_nonce[1]) == 1); +void pubnonce_summing_to_inf(secp256k1_musig_pubnonce *pubnonce) { + secp256k1_ge ge[2]; + int i; + secp256k1_gej summed_nonces[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr[2]; - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1); + ge[0] = secp256k1_ge_const_g; + ge[1] = secp256k1_ge_const_g; - CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + for (i = 0; i < 2; i++) { + secp256k1_musig_pubnonce_save(&pubnonce[i], ge); + pubnonce_ptr[i] = &pubnonce[i]; + secp256k1_ge_neg(&ge[0], &ge[0]); + secp256k1_ge_neg(&ge[1], &ge[1]); + } - CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), &combined_pk) == 1); + secp256k1_musig_sum_nonces(ctx, summed_nonces, pubnonce_ptr, 2); + CHECK(secp256k1_gej_is_infinity(&summed_nonces[0])); + CHECK(secp256k1_gej_is_infinity(&summed_nonces[1])); +} + +int memcmp_and_randomize(unsigned char *value, const unsigned char *expected, size_t len) { + int ret; + size_t i; + ret = secp256k1_memcmp_var(value, expected, len); + for (i = 0; i < len; i++) { + value[i] = secp256k1_testrand_bits(8); + } + return ret; } void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_scratch_space *scratch_small; - secp256k1_musig_session session[2]; - secp256k1_musig_session session_uninitialized; - secp256k1_musig_session verifier_session; - secp256k1_musig_session_signer_data signer0[2]; - secp256k1_musig_session_signer_data signer1[2]; - secp256k1_musig_session_signer_data verifier_signer_data[2]; - secp256k1_musig_partial_signature partial_sig[2]; - secp256k1_musig_partial_signature partial_sig_adapted[2]; - secp256k1_musig_partial_signature partial_sig_overflow; + secp256k1_musig_partial_sig partial_sig[2]; + const secp256k1_musig_partial_sig *partial_sig_ptr[2]; + secp256k1_musig_partial_sig invalid_partial_sig; + const secp256k1_musig_partial_sig *invalid_partial_sig_ptr[2]; unsigned char final_sig[64]; - unsigned char final_sig_cmp[64]; - + unsigned char pre_sig[64]; unsigned char buf[32]; unsigned char sk[2][32]; - unsigned char ones[32]; + secp256k1_keypair keypair[2]; + secp256k1_keypair invalid_keypair; + unsigned char max64[64]; + unsigned char zeros68[68] = { 0 }; unsigned char session_id[2][32]; - unsigned char nonce_commitment[2][32]; - int combined_nonce_parity; - const unsigned char *ncs[2]; + secp256k1_musig_secnonce secnonce[2]; + secp256k1_musig_secnonce secnonce_tmp; + secp256k1_musig_secnonce invalid_secnonce; + secp256k1_musig_pubnonce pubnonce[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr[2]; + unsigned char pubnonce_ser[66]; + secp256k1_musig_pubnonce inf_pubnonce[2]; + const secp256k1_musig_pubnonce *inf_pubnonce_ptr[2]; + secp256k1_musig_pubnonce invalid_pubnonce; + const secp256k1_musig_pubnonce *invalid_pubnonce_ptr[1]; + secp256k1_musig_aggnonce aggnonce; + unsigned char aggnonce_ser[66]; unsigned char msg[32]; - secp256k1_xonly_pubkey combined_pk; - secp256k1_musig_pre_session pre_session; - secp256k1_musig_pre_session pre_session_uninitialized; + secp256k1_xonly_pubkey agg_pk; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_musig_keyagg_cache invalid_keyagg_cache; + secp256k1_musig_session session; + secp256k1_musig_session invalid_session; secp256k1_xonly_pubkey pk[2]; const secp256k1_xonly_pubkey *pk_ptr[2]; secp256k1_xonly_pubkey invalid_pk; const secp256k1_xonly_pubkey *invalid_pk_ptr2[2]; const secp256k1_xonly_pubkey *invalid_pk_ptr3[3]; unsigned char tweak[32]; - + int nonce_parity; unsigned char sec_adaptor[32]; unsigned char sec_adaptor1[32]; secp256k1_pubkey adaptor; @@ -125,616 +169,480 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); - memset(ones, 0xff, 32); + memset(max64, 0xff, sizeof(max64)); + memset(&invalid_keypair, 0, sizeof(invalid_keypair)); + memset(&invalid_pk, 0, sizeof(invalid_pk)); + memset(&invalid_secnonce, 0, sizeof(invalid_secnonce)); + memset(&invalid_partial_sig, 0, sizeof(invalid_partial_sig)); + pubnonce_summing_to_inf(inf_pubnonce); /* Simulate structs being uninitialized by setting it to 0s. We don't want * to produce undefined behavior by actually providing uninitialized * structs. */ - memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); - memset(&session_uninitialized, 0, sizeof(session_uninitialized)); + memset(&invalid_keyagg_cache, 0, sizeof(invalid_keyagg_cache)); memset(&invalid_pk, 0, sizeof(invalid_pk)); + memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce)); + memset(&invalid_session, 0, sizeof(invalid_session)); - secp256k1_testrand256(session_id[0]); - secp256k1_testrand256(session_id[1]); - secp256k1_testrand256(sk[0]); - secp256k1_testrand256(sk[1]); - secp256k1_testrand256(msg); secp256k1_testrand256(sec_adaptor); + secp256k1_testrand256(msg); secp256k1_testrand256(tweak); - - pk_ptr[0] = &pk[0]; - pk_ptr[1] = &pk[1]; - CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); - CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); - for (i = 0; i < 2; i++) { + pk_ptr[i] = &pk[i]; invalid_pk_ptr2[i] = &invalid_pk; invalid_pk_ptr3[i] = &pk[i]; + pubnonce_ptr[i] = &pubnonce[i]; + inf_pubnonce_ptr[i] = &inf_pubnonce[i]; + partial_sig_ptr[i] = &partial_sig[i]; + invalid_partial_sig_ptr[i] = &partial_sig[i]; + secp256k1_testrand256(session_id[i]); + secp256k1_testrand256(sk[i]); + CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i])); } + invalid_pubnonce_ptr[0] = &invalid_pubnonce; + invalid_partial_sig_ptr[0] = &invalid_partial_sig; /* invalid_pk_ptr3 has two valid, one invalid pk, which is important to test - * musig_pubkeys_combine */ + * musig_pubkey_agg */ invalid_pk_ptr3[2] = &invalid_pk; /** main test body **/ - /* Key combination */ + /** Key aggregation **/ ecount = 0; - CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - /* pubkey_combine does not require a scratch space */ - CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(none, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(sign, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + /* pubkey_agg does not require a scratch space */ + CHECK(secp256k1_musig_pubkey_agg(vrfy, NULL, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); /* A small scratch space works too, but will result in using an ineffecient algorithm */ scratch_small = secp256k1_scratch_space_create(ctx, 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch_small, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); secp256k1_scratch_space_destroy(ctx, scratch_small); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk_ptr, 2) == 0); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, NULL, &keyagg_cache, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, NULL, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, NULL, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk_ptr, 2) == 1); - CHECK(ecount == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, invalid_pk_ptr2, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, invalid_pk_ptr2, 2) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, invalid_pk_ptr3, 3) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, invalid_pk_ptr3, 3) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, pk_ptr, 0) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 0) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, NULL, 0) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); - CHECK(ecount == 6); + CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(none, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(sign, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - /** Tweaking */ + /** Tweaking **/ ecount = 0; { - secp256k1_xonly_pubkey tmp_internal_pk = combined_pk; secp256k1_pubkey tmp_output_pk; - secp256k1_musig_pre_session tmp_pre_session = pre_session; - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); - /* Reset pre_session */ - tmp_pre_session = pre_session; - CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); - tmp_pre_session = pre_session; - CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); - tmp_pre_session = pre_session; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); - tmp_pre_session = pre_session; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + secp256k1_musig_keyagg_cache tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + /* Reset keyagg_cache */ + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, NULL, tweak) == 0); CHECK(ecount == 1); - /* Uninitialized pre_session */ - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &pre_session_uninitialized, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, NULL) == 0); CHECK(ecount == 2); - /* Using the same pre_session twice does not work */ - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + /* Uninitialized keyagg_cache */ + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &invalid_keyagg_cache, tweak) == 0); CHECK(ecount == 3); - tmp_pre_session = pre_session; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, NULL, &tmp_internal_pk, tweak) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, NULL, tweak) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, NULL) == 0); - CHECK(ecount == 6); - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, ones) == 0); - CHECK(ecount == 6); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); } /** Session creation **/ ecount = 0; - CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); + CHECK(secp256k1_musig_nonce_gen(none, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); + CHECK(secp256k1_musig_nonce_gen(vrfy, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 1); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, NULL, &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], NULL, session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], NULL, sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, sk[0]) == 0); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + /* no seckey and session_id is 0 */ + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, NULL, msg, &keyagg_cache, max64) == 0); + CHECK(ecount == 5); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + /* session_id 0 is fine when a seckey is provided */ + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, sk[0], msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, msg, &keyagg_cache, max64) == 1); + CHECK(ecount == 5); + /* invalid seckey */ + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], max64, msg, &keyagg_cache, max64) == 0); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, &keyagg_cache, max64) == 1); + CHECK(ecount == 5); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, NULL, max64) == 1); + CHECK(ecount == 5); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &invalid_keyagg_cache, max64) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, sk[0]) == 1); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, NULL) == 1); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, sk[0]) == 0); + + /* Every in-argument except session_id can be NULL */ + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[1], &pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1); + + /** Serialize and parse public nonces **/ + ecount = 0; + CHECK(secp256k1_musig_pubnonce_serialize(none, NULL, &pubnonce[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(pubnonce_ser, zeros68, sizeof(pubnonce_ser)) == 0); + CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, &invalid_pubnonce) == 0); + CHECK(ecount == 3); + CHECK(memcmp_and_randomize(pubnonce_ser, zeros68, sizeof(pubnonce_ser)) == 0); + CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, &pubnonce[0]) == 1); + + ecount = 0; + CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], pubnonce_ser) == 1); + CHECK(secp256k1_musig_pubnonce_parse(none, NULL, pubnonce_ser) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], zeros68) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], pubnonce_ser) == 1); + + { + /* Check that serialize and parse results in the same value */ + secp256k1_musig_pubnonce tmp; + CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, &pubnonce[0]) == 1); + CHECK(secp256k1_musig_pubnonce_parse(none, &tmp, pubnonce_ser) == 1); + CHECK(memcmp(&tmp, &pubnonce[0], sizeof(tmp)) == 0); + } + + /** Receive nonces and aggregate **/ + ecount = 0; + CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, pubnonce_ptr, 2) == 1); + CHECK(secp256k1_musig_nonce_agg(none, NULL, pubnonce_ptr, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, NULL, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, pubnonce_ptr, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, invalid_pubnonce_ptr, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, inf_pubnonce_ptr, 2) == 1); + { + /* Check that the aggnonce is set to G */ + secp256k1_ge aggnonce_pt[2]; + secp256k1_musig_pubnonce_load(ctx, aggnonce_pt, (secp256k1_musig_pubnonce*)&aggnonce); + for (i = 0; i < 2; i++) { + ge_equals_ge(&aggnonce_pt[i], &secp256k1_ge_const_g); + } + } + CHECK(ecount == 4); + CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, pubnonce_ptr, 2) == 1); + + /** Serialize and parse aggregate nonces **/ + ecount = 0; + CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, &aggnonce) == 1); + CHECK(secp256k1_musig_aggnonce_serialize(none, NULL, &aggnonce) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(aggnonce_ser, zeros68, sizeof(aggnonce_ser)) == 0); + CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, (secp256k1_musig_aggnonce*) &invalid_pubnonce) == 0); + CHECK(ecount == 3); + CHECK(memcmp_and_randomize(aggnonce_ser, zeros68, sizeof(aggnonce_ser)) == 0); + CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, &aggnonce) == 1); + + ecount = 0; + CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, aggnonce_ser) == 1); + CHECK(secp256k1_musig_aggnonce_parse(none, NULL, aggnonce_ser) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, zeros68) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, aggnonce_ser) == 1); + + { + /* Check that serialize and parse results in the same value */ + secp256k1_musig_aggnonce tmp; + CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, &aggnonce) == 1); + CHECK(secp256k1_musig_aggnonce_parse(none, &tmp, aggnonce_ser) == 1); + CHECK(memcmp(&tmp, &aggnonce, sizeof(tmp)) == 0); + } + + /** Process nonces **/ + ecount = 0; + CHECK(secp256k1_musig_nonce_process(none, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); + CHECK(secp256k1_musig_nonce_process(sign, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); + CHECK(secp256k1_musig_nonce_process(vrfy, NULL, &aggnonce, msg, &keyagg_cache, &adaptor) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, NULL, msg, &keyagg_cache, &adaptor) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache, &adaptor) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, &aggnonce, NULL, &keyagg_cache, &adaptor) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, &aggnonce, msg, NULL, &adaptor) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, &aggnonce, msg, &invalid_keyagg_cache, &adaptor) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_musig_nonce_process(vrfy, &session, &aggnonce, msg, &keyagg_cache, (secp256k1_pubkey *)&invalid_pk) == 0); CHECK(ecount == 7); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, sk[0]) == 0); - CHECK(ecount == 8); - /* Uninitialized pre_session */ - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, sk[0]) == 0); - CHECK(ecount == 9); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, sk[0]) == 0); - CHECK(ecount == 10); - /* If more than UINT32_MAX fits in a size_t, test that session_init - * rejects n_signers that high. */ - if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, sk[0]) == 0); - CHECK(ecount == 11); - } else { - ecount = 11; - } - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, NULL) == 0); - CHECK(ecount == 12); - /* secret key overflows */ - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, ones) == 0); - CHECK(ecount == 12); - CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); - CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); - ncs[0] = nonce_commitment[0]; - ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_nonce_process(vrfy, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); ecount = 0; - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_musig_session_init_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1); + /* The secnonce is set to 0 and subsequent signing attempts fail */ + CHECK(memcmp(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, NULL, &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], NULL, &keypair[0], &keyagg_cache, &session) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &invalid_secnonce, &keypair[0], &keyagg_cache, &session) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, NULL, &keyagg_cache, &session) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &invalid_keypair, &keyagg_cache, &session) == 0); CHECK(ecount == 6); - if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); - CHECK(ecount == 7); - } else { - ecount = 7; - } - CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], NULL, &session) == 0); + CHECK(ecount == 7); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &invalid_keyagg_cache, &session) == 0); + CHECK(ecount == 8); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, NULL) == 0); + CHECK(ecount == 9); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &invalid_session) == 0); + CHECK(ecount == 10); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce[0], &keypair[0], &keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[1], &secnonce[1], &keypair[1], &keyagg_cache, &session) == 1); - /** Signing step 0 -- exchange nonce commitments */ ecount = 0; - { - unsigned char nonce[32]; - secp256k1_musig_session session_0_tmp; - - memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - - /* Can obtain public nonce after commitments have been exchanged; still can't sign */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, nonce, ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_partial_sign(none, &session_0_tmp, &partial_sig[0]) == 0); - CHECK(ecount == 1); - } - - /** Signing step 1 -- exchange nonces */ - ecount = 0; - { - unsigned char public_nonce[3][32]; - secp256k1_musig_session session_0_tmp; - - memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 2, NULL) == 1); - CHECK(ecount == 0); - /* Reset session */ - memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, public_nonce[0], ncs, 2, NULL) == 0); - CHECK(ecount == 1); - /* uninitialized session */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, public_nonce[0], ncs, 2, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, public_nonce[0], ncs, 2, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], NULL, 2, NULL) == 0); - CHECK(ecount == 5); - /* Number of commitments and number of signers are different */ - CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 1, NULL) == 0); - CHECK(ecount == 6); - - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1); - - CHECK(secp256k1_musig_set_nonce(none, &signer0[0], public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[0]) == 0); - CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1); - CHECK(ecount == 6); - - CHECK(secp256k1_musig_set_nonce(none, NULL, public_nonce[0]) == 0); - CHECK(ecount == 7); - CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); - CHECK(ecount == 8); - - CHECK(secp256k1_musig_set_nonce(none, &signer1[0], public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &signer1[1], public_nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], public_nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], public_nonce[1]) == 1); - - ecount = 0; - memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, &adaptor) == 1); - memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &combined_nonce_parity, &adaptor) == 0); - CHECK(ecount == 1); - /* Uninitialized session */ - CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &combined_nonce_parity, &adaptor) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &combined_nonce_parity, &adaptor) == 0); - CHECK(ecount == 3); - /* Number of signers differs from number during intialization */ - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &combined_nonce_parity, &adaptor) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1); - CHECK(ecount == 4); - memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); - CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, NULL) == 1); - - CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &combined_nonce_parity, &adaptor) == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &combined_nonce_parity, &adaptor) == 1); - CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &combined_nonce_parity, &adaptor) == 1); - } - - /** Signing step 2 -- partial signatures */ - ecount = 0; - CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0); + CHECK(secp256k1_musig_partial_sig_serialize(none, buf, &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sig_serialize(none, NULL, &partial_sig[0]) == 0); CHECK(ecount == 1); - /* Uninitialized session */ - CHECK(secp256k1_musig_partial_sign(none, &session_uninitialized, &partial_sig[0]) == 0); + CHECK(secp256k1_musig_partial_sig_serialize(none, buf, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); + CHECK(secp256k1_musig_partial_sig_parse(none, &partial_sig[0], buf) == 1); + CHECK(secp256k1_musig_partial_sig_parse(none, NULL, buf) == 0); CHECK(ecount == 3); - - CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); - /* observer can't sign */ - CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); + CHECK(secp256k1_musig_partial_sig_parse(none, &partial_sig[0], max64) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_parse(none, &partial_sig[0], NULL) == 0); CHECK(ecount == 4); - ecount = 0; - CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_signature_serialize(none, NULL, &partial_sig[0]) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_signature_serialize(none, buf, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], buf) == 1); - CHECK(secp256k1_musig_partial_signature_parse(none, NULL, buf) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], NULL) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig_overflow, ones) == 1); + { + /* Check that serialize and parse results in the same value */ + secp256k1_musig_partial_sig tmp; + CHECK(secp256k1_musig_partial_sig_serialize(none, buf, &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sig_parse(none, &tmp, buf) == 1); + CHECK(memcmp(&tmp, &partial_sig[0], sizeof(tmp)) == 0); + } /** Partial signature verification */ ecount = 0; - CHECK(secp256k1_musig_partial_sig_verify(none, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(sign, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[1], &pk[0]) == 0); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(secp256k1_musig_partial_sig_verify(none, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sig_verify(sign, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[1], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 0); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &pubnonce[0], &pk[0], &keyagg_cache, &session) == 0); CHECK(ecount == 1); - /* Unitialized session */ - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session_uninitialized, &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &invalid_partial_sig, &pubnonce[0], &pk[0], &keyagg_cache, &session) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], NULL, &pk[0], &keyagg_cache, &session) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &invalid_pubnonce, &pk[0], &keyagg_cache, &session) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], NULL, &keyagg_cache, &session) == 0); CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &invalid_pk, &keyagg_cache, &session) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &pk[0], NULL, &session) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &pk[0], &invalid_keyagg_cache, &session) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, NULL) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &invalid_session) == 0); + CHECK(ecount == 10); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &partial_sig[1], &pubnonce[1], &pk[1], &keyagg_cache, &session) == 1); + + /** Signature aggregation and verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &session, partial_sig_ptr, 2) == 1); + CHECK(secp256k1_musig_partial_sig_agg(none, NULL, &session, partial_sig_ptr, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, NULL, partial_sig_ptr, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &invalid_session, partial_sig_ptr, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &session, NULL, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &session, invalid_partial_sig_ptr, 2) == 0); CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &session, partial_sig_ptr, 0) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &session, partial_sig_ptr, 1) == 1); + CHECK(secp256k1_musig_partial_sig_agg(none, pre_sig, &session, partial_sig_ptr, 2) == 1); /** Adaptor signature verification */ - memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); ecount = 0; - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, combined_nonce_parity) == 1); - CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0); + CHECK(secp256k1_musig_nonce_parity(none, &nonce_parity, &session) == 1); + CHECK(secp256k1_musig_nonce_parity(none, NULL, &session) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0); + CHECK(secp256k1_musig_nonce_parity(none, &nonce_parity, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, combined_nonce_parity) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, combined_nonce_parity) == 0); + CHECK(secp256k1_musig_nonce_parity(none, &nonce_parity, &invalid_session) == 0); CHECK(ecount == 3); - /** Signing combining and verification */ ecount = 0; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); - CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); - CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); - - CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0); + CHECK(secp256k1_musig_adapt(none, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_musig_adapt(none, NULL, pre_sig, sec_adaptor, 0) == 0); CHECK(ecount == 1); - /* Unitialized session */ - CHECK(secp256k1_musig_partial_sig_combine(none, &session_uninitialized, final_sig, partial_sig_adapted, 2) == 0); + CHECK(secp256k1_musig_adapt(none, final_sig, NULL, sec_adaptor, 0) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); + CHECK(secp256k1_musig_adapt(none, final_sig, max64, sec_adaptor, 0) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_adapt(none, final_sig, pre_sig, NULL, 0) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); - CHECK(ecount == 4); - { - secp256k1_musig_partial_signature partial_sig_tmp[2]; - partial_sig_tmp[0] = partial_sig_adapted[0]; - partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0); - } - CHECK(ecount == 4); - /* Wrong number of partial sigs */ - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_adapt(none, final_sig, pre_sig, max64, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_adapt(none, final_sig, pre_sig, sec_adaptor, 2) == 0); CHECK(ecount == 4); + /* sig and pre_sig argument point to the same location */ + memcpy(final_sig, pre_sig, sizeof(final_sig)); + CHECK(secp256k1_musig_adapt(none, final_sig, final_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, sizeof(msg), &agg_pk) == 1); - CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, sizeof(msg), &combined_pk) == 1); + CHECK(secp256k1_musig_adapt(none, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, sizeof(msg), &agg_pk) == 1); /** Secret adaptor can be extracted from signature */ ecount = 0; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, combined_nonce_parity) == 1); + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1); CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); - CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0); + /* wrong nonce parity */ + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1); + CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) != 0); + CHECK(secp256k1_musig_extract_adaptor(none, NULL, final_sig, pre_sig, 0) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0); + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, NULL, pre_sig, 0) == 0); CHECK(ecount == 2); - { - unsigned char final_sig_tmp[64]; - memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp)); - memcpy(&final_sig_tmp[32], ones, 32); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, combined_nonce_parity) == 0); - } + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, max64, pre_sig, 0) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0); + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, NULL, 0) == 0); CHECK(ecount == 3); - { - secp256k1_musig_partial_signature partial_sig_tmp[2]; - partial_sig_tmp[0] = partial_sig[0]; - partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, combined_nonce_parity) == 0); - } + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, max64, 0) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, 1) == 1); + CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, pre_sig, 2) == 0); + CHECK(ecount == 4); /** cleanup **/ - memset(&session, 0, sizeof(session)); secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); } -/* Initializes two sessions, one use the given parameters (session_id, - * nonce_commitments, etc.) except that `session_tmp` uses new signers with different - * public keys. The point of this test is to call `musig_session_get_public_nonce` - * with signers from `session_tmp` who have different public keys than the correct - * ones and return the resulting messagehash. This should not result in a different - * messagehash because the public keys of the signers are only used during session - * initialization. */ -void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id) { - secp256k1_musig_session session; - secp256k1_musig_session session_tmp; - unsigned char nonce_commitment[32]; - secp256k1_musig_session_signer_data signers[2]; - secp256k1_musig_session_signer_data signers_tmp[2]; - unsigned char sk_dummy[32]; - secp256k1_xonly_pubkey pks_tmp[2]; - const secp256k1_xonly_pubkey *pks_tmp_ptr[2]; - secp256k1_xonly_pubkey combined_pk_tmp; - secp256k1_musig_pre_session pre_session_tmp; - unsigned char nonce[32]; +void musig_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { + secp256k1_scalar k1[2], k2[2]; - /* Set up signers with different public keys */ - secp256k1_testrand256(sk_dummy); - pks_tmp[0] = pks[0]; - CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); - pks_tmp_ptr[0] = &pks_tmp[0]; - pks_tmp_ptr[1] = &pks_tmp[1]; - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp_ptr, 2) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, sk_dummy) == 1); - - CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1); - CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); - /* Call get_public_nonce with different signers than the signers the session was - * initialized with. */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, nonce, nonce_commitments, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, nonce, nonce_commitments, 2, NULL) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); - - secp256k1_musig_compute_messagehash(ctx, msghash, &session); + secp256k1_nonce_function_musig(k1, args[0], args[1], args[2], args[3], args[4]); + secp256k1_testrand_flip(args[n_flip], n_bytes); + secp256k1_nonce_function_musig(k2, args[0], args[1], args[2], args[3], args[4]); + CHECK(secp256k1_scalar_eq(&k1[0], &k2[0]) == 0); + CHECK(secp256k1_scalar_eq(&k1[1], &k2[1]) == 0); } -/* Creates a new session (with a different session id) and tries to use that session - * to combine nonces with given signers_other. This should fail, because the nonce - * commitments of signers_other do not match the nonce commitments the new session - * was initialized with. If do_test is 0, the correct signers are being used and - * therefore the function should return 1. */ -int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { - secp256k1_musig_session session; - secp256k1_musig_session_signer_data signers[2]; - secp256k1_musig_session_signer_data *signers_to_use; - unsigned char nonce_commitment[32]; +void musig_nonce_test(void) { + unsigned char *args[5]; unsigned char session_id[32]; - unsigned char nonce[32]; - const unsigned char *ncs[2]; - - /* Initialize new signers */ - secp256k1_testrand256(session_id); - CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1); - ncs[0] = nonce_commitment_other; - ncs[1] = nonce_commitment; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); - secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL); - if (do_test) { - signers_to_use = signers_other; - } else { - signers_to_use = signers; - } - return secp256k1_musig_session_combine_nonces(ctx, &session, signers_to_use, 2, NULL, NULL); -} - -/* Initializaes a session with the given session_id, signers, pk, msg etc. - * parameters but without a message. Will test that the message must be - * provided with `get_public_nonce`. - */ -void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { - /* Create context for testing ARG_CHECKs by setting an illegal_callback. */ - secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - int ecount = 0; - secp256k1_musig_session session; - secp256k1_musig_session_signer_data signers[2]; - unsigned char nonce_commitment[32]; - const unsigned char *ncs[2]; - unsigned char nonce[32]; - secp256k1_musig_partial_signature partial_sig; - - secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, sk) == 1); - ncs[0] = nonce_commitment_other; - ncs[1] = nonce_commitment; - - /* Trying to get the nonce without providing a message fails. */ - CHECK(ecount == 0); - CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, NULL) == 0); - CHECK(ecount == 1); - - /* Providing a message should make get_public_nonce succeed. */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, msg) == 1); - /* Trying to set the message again fails. */ - CHECK(ecount == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, msg) == 0); - CHECK(ecount == 2); - - /* Check that it's working */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig)); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); - secp256k1_context_destroy(ctx_tmp); -} - -void musig_state_machine_tests(secp256k1_scratch_space *scratch) { - secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_VERIFY); - size_t i; - secp256k1_musig_session session[2]; - secp256k1_musig_session_signer_data signers0[2]; - secp256k1_musig_session_signer_data signers1[2]; - unsigned char nonce_commitment[2][32]; - unsigned char session_id[2][32]; + unsigned char sk[32]; unsigned char msg[32]; - unsigned char sk[2][32]; - secp256k1_xonly_pubkey pk[2]; - const secp256k1_xonly_pubkey *pk_ptr[2]; - secp256k1_xonly_pubkey combined_pk; - secp256k1_musig_pre_session pre_session; - unsigned char nonce[2][32]; - const unsigned char *ncs[2]; - secp256k1_musig_partial_signature partial_sig[2]; - unsigned char sig[64]; - unsigned char msghash1[32]; - unsigned char msghash2[32]; - int ecount; + unsigned char agg_pk[32]; + unsigned char extra_input[32]; + int i, j; + secp256k1_scalar k[5][2]; - secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); - ecount = 0; + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, session_id, sizeof(session_id)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, sk, sizeof(sk)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, msg, sizeof(msg)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, agg_pk, sizeof(agg_pk)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, extra_input, sizeof(extra_input)); - /* Run state machine with the same objects twice to test that it's allowed to - * reinitialize session and session_signer_data. */ - for (i = 0; i < 2; i++) { - /* Setup */ - secp256k1_testrand256(session_id[0]); - secp256k1_testrand256(session_id[1]); - secp256k1_testrand256(sk[0]); - secp256k1_testrand256(sk[1]); - secp256k1_testrand256(msg); - pk_ptr[0] = &pk[0]; - pk_ptr[1] = &pk[1]; - CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); - CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1); - /* Can't combine nonces unless we're through round 1 already */ - ecount = 0; - CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0); - CHECK(ecount == 1); - - /* Set nonce commitments */ - ncs[0] = nonce_commitment[0]; - ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1); - /* Calling the function again is not okay */ - ecount = 0; - CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, nonce[0], ncs, 2, NULL) == 0); - CHECK(ecount == 1); - - /* Get nonce for signer 1 */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1); - - /* Set nonces */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1); - /* Can't set nonce that doesn't match nonce commitment */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[0]) == 0); - /* Set correct nonce */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1); - - /* Combine nonces */ - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); - /* Not everyone is present from signer 1's view */ - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0); - /* Make everyone present */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1); - - /* Can't combine nonces from signers of a different session */ - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 1) == 0); - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 0) == 1); - - /* Partially sign */ - CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); - /* Can't verify, sign or combine signatures until nonce is combined */ - ecount = 0; - CHECK(secp256k1_musig_partial_sig_verify(ctx_tmp, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sign(ctx_tmp, &session[1], &partial_sig[1]) == 0); - CHECK(ecount == 2); - memset(&partial_sig[1], 0, sizeof(partial_sig[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx_tmp, &session[1], sig, partial_sig, 2) == 0); - CHECK(ecount == 3); - - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); - /* messagehash should be the same as a session whose get_public_nonce was called - * with different signers (i.e. they diff in public keys). This is because the - * public keys of the signers is set in stone when initializing the session. */ - secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]); - musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, nonce[0], sk[1], session_id[1]); - CHECK(memcmp(msghash1, msghash2, 32) == 0); - CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); - - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[1], &pk[1]) == 1); - /* Wrong signature */ - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); - /* Can't get the public nonce until msg is set */ - musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], nonce[0], sk[1], session_id[1], msg); + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = session_id; + args[1] = msg; + args[2] = sk; + args[3] = agg_pk; + args[4] = extra_input; + for (i = 0; i < count; i++) { + musig_nonce_bitflip(args, 0, sizeof(session_id)); + musig_nonce_bitflip(args, 1, sizeof(msg)); + musig_nonce_bitflip(args, 2, sizeof(sk)); + musig_nonce_bitflip(args, 3, sizeof(agg_pk)); + musig_nonce_bitflip(args, 4, sizeof(extra_input)); + } + /* Check that if any argument is NULL, a different nonce is produced than if + * any other argument is NULL. */ + memcpy(msg, session_id, sizeof(msg)); + memcpy(sk, session_id, sizeof(sk)); + memcpy(agg_pk, session_id, sizeof(agg_pk)); + memcpy(extra_input, session_id, sizeof(extra_input)); + secp256k1_nonce_function_musig(k[0], args[0], args[1], args[2], args[3], args[4]); + secp256k1_nonce_function_musig(k[1], args[0], NULL, args[2], args[3], args[4]); + secp256k1_nonce_function_musig(k[2], args[0], args[1], NULL, args[3], args[4]); + secp256k1_nonce_function_musig(k[3], args[0], args[1], args[2], NULL, args[4]); + secp256k1_nonce_function_musig(k[4], args[0], args[1], args[2], args[3], NULL); + for (i = 0; i < 4; i++) { + for (j = i+1; j < 5; j++) { + CHECK(secp256k1_scalar_eq(&k[i][0], &k[j][0]) == 0); + CHECK(secp256k1_scalar_eq(&k[i][1], &k[j][1]) == 0); + } } - secp256k1_context_destroy(ctx_tmp); } void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { @@ -742,112 +650,104 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { * while the indices 0 and 1 refer to the two signers. Here signer 0 is * sending a-coins to signer 1, while signer 1 is sending b-coins to signer * 0. Signer 0 produces the adaptor signatures. */ + unsigned char pre_sig_a[64]; unsigned char final_sig_a[64]; + unsigned char pre_sig_b[64]; unsigned char final_sig_b[64]; - secp256k1_musig_partial_signature partial_sig_a[2]; - secp256k1_musig_partial_signature partial_sig_b_adapted[2]; - secp256k1_musig_partial_signature partial_sig_b[2]; + secp256k1_musig_partial_sig partial_sig_a[2]; + const secp256k1_musig_partial_sig *partial_sig_a_ptr[2]; + secp256k1_musig_partial_sig partial_sig_b[2]; + const secp256k1_musig_partial_sig *partial_sig_b_ptr[2]; unsigned char sec_adaptor[32]; unsigned char sec_adaptor_extracted[32]; secp256k1_pubkey pub_adaptor; - - unsigned char seckey_a[2][32]; - unsigned char seckey_b[2][32]; + unsigned char sk_a[2][32]; + unsigned char sk_b[2][32]; + secp256k1_keypair keypair_a[2]; + secp256k1_keypair keypair_b[2]; secp256k1_xonly_pubkey pk_a[2]; const secp256k1_xonly_pubkey *pk_a_ptr[2]; secp256k1_xonly_pubkey pk_b[2]; const secp256k1_xonly_pubkey *pk_b_ptr[2]; - secp256k1_musig_pre_session pre_session_a; - secp256k1_musig_pre_session pre_session_b; - secp256k1_xonly_pubkey combined_pk_a; - secp256k1_xonly_pubkey combined_pk_b; - secp256k1_musig_session musig_session_a[2]; - secp256k1_musig_session musig_session_b[2]; - unsigned char noncommit_a[2][32]; - unsigned char noncommit_b[2][32]; - const unsigned char *noncommit_a_ptr[2]; - const unsigned char *noncommit_b_ptr[2]; - unsigned char pubnon_a[2][32]; - unsigned char pubnon_b[2][32]; - int combined_nonce_parity_a; - int combined_nonce_parity_b; - secp256k1_musig_session_signer_data data_a[2]; - secp256k1_musig_session_signer_data data_b[2]; - - const unsigned char seed[32] = "still tired of choosing seeds..."; + secp256k1_musig_keyagg_cache keyagg_cache_a; + secp256k1_musig_keyagg_cache keyagg_cache_b; + secp256k1_xonly_pubkey agg_pk_a; + secp256k1_xonly_pubkey agg_pk_b; + secp256k1_musig_secnonce secnonce_a[2]; + secp256k1_musig_secnonce secnonce_b[2]; + secp256k1_musig_pubnonce pubnonce_a[2]; + secp256k1_musig_pubnonce pubnonce_b[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr_a[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr_b[2]; + secp256k1_musig_aggnonce aggnonce_a; + secp256k1_musig_aggnonce aggnonce_b; + secp256k1_musig_session session_a, session_b; + int nonce_parity_a; + int nonce_parity_b; + unsigned char seed_a[2][32] = { "a0", "a1" }; + unsigned char seed_b[2][32] = { "b0", "b1" }; const unsigned char msg32_a[32] = "this is the message blockchain a"; const unsigned char msg32_b[32] = "this is the message blockchain b"; + int i; /* Step 1: key setup */ - secp256k1_testrand256(seckey_a[0]); - secp256k1_testrand256(seckey_a[1]); - secp256k1_testrand256(seckey_b[0]); - secp256k1_testrand256(seckey_b[1]); + for (i = 0; i < 2; i++) { + pk_a_ptr[i] = &pk_a[i]; + pk_b_ptr[i] = &pk_b[i]; + pubnonce_ptr_a[i] = &pubnonce_a[i]; + pubnonce_ptr_b[i] = &pubnonce_b[i]; + partial_sig_a_ptr[i] = &partial_sig_a[i]; + partial_sig_b_ptr[i] = &partial_sig_b[i]; + + secp256k1_testrand256(sk_a[i]); + secp256k1_testrand256(sk_b[i]); + CHECK(create_keypair_and_pk(&keypair_a[i], &pk_a[i], sk_a[i]) == 1); + CHECK(create_keypair_and_pk(&keypair_b[i], &pk_b[i], sk_b[i]) == 1); + } secp256k1_testrand256(sec_adaptor); + CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor) == 1); - pk_a_ptr[0] = &pk_a[0]; - pk_a_ptr[1] = &pk_a[1]; - pk_b_ptr[0] = &pk_b[0]; - pk_b_ptr[1] = &pk_b[1]; - CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0])); - CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1])); - CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0])); - CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); + CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk_a, &keyagg_cache_a, pk_a_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a_ptr, 2)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b_ptr, 2)); - - CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[0])); - CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[1])); - noncommit_a_ptr[0] = noncommit_a[0]; - noncommit_a_ptr[1] = noncommit_a[1]; - - CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, seckey_b[0])); - CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, seckey_b[1])); - noncommit_b_ptr[0] = noncommit_b[0]; - noncommit_b_ptr[1] = noncommit_b[1]; + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], NULL, NULL, NULL) == 1); /* Step 2: Exchange nonces */ - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, pubnon_a[0], noncommit_a_ptr, 2, NULL)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, pubnon_a[1], noncommit_a_ptr, 2, NULL)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, pubnon_b[0], noncommit_b_ptr, 2, NULL)); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, pubnon_b[1], noncommit_b_ptr, 2, NULL)); - CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], pubnon_a[0])); - CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], pubnon_a[1])); - CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], pubnon_b[0])); - CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], pubnon_b[1])); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &combined_nonce_parity_a, &pub_adaptor)); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &combined_nonce_parity_b, &pub_adaptor)); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor)); + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce_a, pubnonce_ptr_a, 2) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session_a, &aggnonce_a, msg32_a, &keyagg_cache_a, &pub_adaptor) == 1); + CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity_a, &session_a) == 1); + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce_b, pubnonce_ptr_b, 2) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session_b, &aggnonce_b, msg32_b, &keyagg_cache_b, &pub_adaptor) == 1); + CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity_b, &session_b) == 1); /* Step 3: Signer 0 produces partial signatures for both chains. */ - CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[0], &partial_sig_a[0])); - CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[0], &partial_sig_b[0])); + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig_a[0], &secnonce_a[0], &keypair_a[0], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig_b[0], &secnonce_b[0], &keypair_b[0], &keyagg_cache_b, &session_b) == 1); /* Step 4: Signer 1 receives partial signatures, verifies them and creates a * partial signature to send B-coins to signer 0. */ - CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_a[1], data_a, &partial_sig_a[0], &pk_a[0]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_b[1], data_b, &partial_sig_b[0], &pk_b[0]) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[1], &partial_sig_b[1])); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig_a[0], &pubnonce_a[0], &pk_a[0], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig_b[0], &pubnonce_b[0], &pk_b[0], &keyagg_cache_b, &session_b) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig_b[1], &secnonce_b[1], &keypair_b[1], &keyagg_cache_b, &session_b) == 1); - /* Step 5: Signer 0 adapts its own partial signature and combines it with the - * partial signature from signer 1. This results in a complete signature which - * is broadcasted by signer 0 to take B-coins. */ - CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, combined_nonce_parity_b)); - memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, sizeof(msg32_b), &combined_pk_b) == 1); + /* Step 5: Signer 0 aggregates its own partial signature with the partial + * signature from signer 1 and adapts it. This results in a complete + * signature which is broadcasted by signer 0 to take B-coins. */ + CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig_b, &session_b, partial_sig_b_ptr, 2) == 1); + CHECK(secp256k1_musig_adapt(ctx, final_sig_b, pre_sig_b, sec_adaptor, nonce_parity_b) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, sizeof(msg32_b), &agg_pk_b) == 1); - /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to - * other partial signature, and takes A-coins. */ - CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, combined_nonce_parity_b) == 1); + /* Step 6: Signer 1 signs, extracts adaptor from the published signature, + * and adapts the signature to take A-coins. */ + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig_a[1], &secnonce_a[1], &keypair_a[1], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig_a, &session_a, partial_sig_a_ptr, 2) == 1); + CHECK(secp256k1_musig_extract_adaptor(ctx, sec_adaptor_extracted, final_sig_b, pre_sig_b, nonce_parity_b) == 1); CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ - CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, combined_nonce_parity_a)); - CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, sizeof(msg32_a), &combined_pk_a) == 1); + CHECK(secp256k1_musig_adapt(ctx, final_sig_a, pre_sig_a, sec_adaptor_extracted, nonce_parity_a) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1); } void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) { @@ -859,7 +759,7 @@ void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, tag, taglen); secp256k1_sha256_finalize(&sha, buf); - /* buf = SHA256("KeyAgg coefficient") */ + /* buf = SHA256(tag) */ secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, buf, 32); @@ -894,164 +794,158 @@ void sha256_tag_test(void) { } } -/* Attempts to create a signature for the combined public key using given secret - * keys and pre_session. */ -void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_pre_session *pre_session) { - secp256k1_musig_session session[2]; - secp256k1_musig_session_signer_data signers0[2]; - secp256k1_musig_session_signer_data signers1[2]; +/* Attempts to create a signature for the aggregate public key using given secret + * keys and keyagg_cache. */ +void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_keyagg_cache *keyagg_cache) { secp256k1_xonly_pubkey pk[2]; unsigned char session_id[2][32]; unsigned char msg[32]; - unsigned char nonce_commitment[2][32]; - unsigned char nonce[2][32]; - const unsigned char *ncs[2]; - secp256k1_musig_partial_signature partial_sig[2]; + secp256k1_musig_secnonce secnonce[2]; + secp256k1_musig_pubnonce pubnonce[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr[2]; + secp256k1_musig_aggnonce aggnonce; + secp256k1_keypair keypair[2]; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig[2]; + const secp256k1_musig_partial_sig *partial_sig_ptr[2]; unsigned char final_sig[64]; + int i; - secp256k1_testrand256(session_id[0]); - secp256k1_testrand256(session_id[1]); + for (i = 0; i < 2; i++) { + pubnonce_ptr[i] = &pubnonce[i]; + partial_sig_ptr[i] = &partial_sig[i]; + + secp256k1_testrand256(session_id[i]); + } + CHECK(create_keypair_and_pk(&keypair[0], &pk[0], sk0) == 1); + CHECK(create_keypair_and_pk(&keypair[1], &pk[1], sk1) == 1); secp256k1_testrand256(msg); - CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1); - CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[0], &pubnonce[0], session_id[0], sk0, NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[1], &pubnonce[1], session_id[1], sk1, NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, sk0) == 1); - CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, sk1) == 1); - /* Set nonce commitments */ - ncs[0] = nonce_commitment[0]; - ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1); - /* Set nonces */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2)); - CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), combined_pubkey) == 1); + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 2) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, keyagg_cache, NULL) == 1); + + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig[0], &secnonce[0], &keypair[0], keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig[1], &secnonce[1], &keypair[1], keyagg_cache, &session) == 1); + + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig[0], &pubnonce[0], &pk[0], keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig[1], &pubnonce[1], &pk[1], keyagg_cache, &session) == 1); + + CHECK(secp256k1_musig_partial_sig_agg(ctx, final_sig, &session, partial_sig_ptr, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), agg_pk) == 1); } -/* In this test we create a combined public key P and a commitment Q = P + - * hash(P, contract)*G. Then we test that we can sign for both public keys. In - * order to sign for Q we use the tweak32 argument of partial_sig_combine. */ +/* Create aggregate public key P[0], tweak multiple times and test signing. */ void musig_tweak_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; secp256k1_xonly_pubkey pk[2]; const secp256k1_xonly_pubkey *pk_ptr[2]; - secp256k1_musig_pre_session pre_session_P; - secp256k1_musig_pre_session pre_session_Q; - secp256k1_xonly_pubkey P; - unsigned char P_serialized[32]; - secp256k1_pubkey Q; - int Q_parity; - secp256k1_xonly_pubkey Q_xonly; - unsigned char Q_serialized[32]; + secp256k1_musig_keyagg_cache keyagg_cache; + enum { N_TWEAKS = 8 }; + secp256k1_pubkey P[N_TWEAKS + 1]; + secp256k1_xonly_pubkey P_xonly[N_TWEAKS + 1]; + int i; - secp256k1_sha256 sha; - unsigned char contract[32]; - unsigned char ec_commit_tweak[32]; + /* Key Setup */ + for (i = 0; i < 2; i++) { + pk_ptr[i] = &pk[i]; + secp256k1_testrand256(sk[i]); + CHECK(create_keypair_and_pk(NULL, &pk[i], sk[i]) == 1); + } + /* Compute P0 = keyagg(pk0, pk1) and test signing for it */ + CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &P_xonly[0], &keyagg_cache, pk_ptr, 2) == 1); + musig_tweak_test_helper(&P_xonly[0], sk[0], sk[1], &keyagg_cache); - /* Setup */ - secp256k1_testrand256(sk[0]); - secp256k1_testrand256(sk[1]); - secp256k1_testrand256(contract); + /* Compute Pi = |Pj| + tweaki*G where where j = i-1 and try signing for + * that key. The function |.| normalizes the point to have an even + * X-coordinate. This results in ordinary "xonly-tweaking". */ + for (i = 1; i < N_TWEAKS; i++) { + unsigned char tweak[32]; + int P_parity; + unsigned char P_serialized[32]; - pk_ptr[0] = &pk[0]; - pk_ptr[1] = &pk[1]; - CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); - CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk_ptr, 2) == 1); - - CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1); - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, P_serialized, 32); - secp256k1_sha256_write(&sha, contract, 32); - secp256k1_sha256_finalize(&sha, ec_commit_tweak); - pre_session_Q = pre_session_P; - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &pre_session_Q, &Q, &P, ec_commit_tweak) == 1); - CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &Q_xonly, &Q_parity, &Q)); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, Q_serialized, &Q_xonly)); - /* Check that musig_pubkey_tweak_add produces same result as - * xonly_pubkey_tweak_add. */ - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, Q_serialized, Q_parity, &P, ec_commit_tweak) == 1); - - /* Test signing for P */ - musig_tweak_test_helper(&P, sk[0], sk[1], &pre_session_P); - /* Test signing for Q */ - musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q); + secp256k1_testrand256(tweak); + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &P_xonly[i], &P_parity, &P[i])); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P_xonly[i])); + /* Check that musig_pubkey_tweak_add produces same result as + * xonly_pubkey_tweak_add. */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1); + /* Test signing for P[i] */ + musig_tweak_test_helper(&P_xonly[i], sk[0], sk[1], &keyagg_cache); + } } -void musig_test_vectors_helper(unsigned char pk_ser[][32], int n_pks, const unsigned char *combined_pk_expected, int has_second_pk, int second_pk_idx) { +void musig_test_vectors_keyagg_helper(const unsigned char **pk_ser, int n_pks, const unsigned char *agg_pk_expected, int has_second_pk, int second_pk_idx) { secp256k1_xonly_pubkey *pk = malloc(n_pks * sizeof(*pk)); const secp256k1_xonly_pubkey **pk_ptr = malloc(n_pks * sizeof(*pk_ptr)); - secp256k1_xonly_pubkey combined_pk; - unsigned char combined_pk_ser[32]; - secp256k1_musig_pre_session pre_session; - secp256k1_fe second_pk_x; + secp256k1_keyagg_cache_internal cache_i; + secp256k1_xonly_pubkey agg_pk; + unsigned char agg_pk_ser[32]; + secp256k1_musig_keyagg_cache keyagg_cache; int i; for (i = 0; i < n_pks; i++) { - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i])); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i]) == 1); pk_ptr[i] = &pk[i]; } - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pk_ptr, n_pks) == 1); - CHECK(secp256k1_fe_set_b32(&second_pk_x, pre_session.second_pk)); - CHECK(secp256k1_fe_is_zero(&second_pk_x) == !has_second_pk); - if (!secp256k1_fe_is_zero(&second_pk_x)) { - CHECK(secp256k1_memcmp_var(&pk_ser[second_pk_idx], &pre_session.second_pk, sizeof(pk_ser[second_pk_idx])) == 0); + CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &keyagg_cache, pk_ptr, n_pks) == 1); + CHECK(secp256k1_keyagg_cache_load(ctx, &cache_i, &keyagg_cache) == 1); + CHECK(secp256k1_fe_is_zero(&cache_i.second_pk_x) == !has_second_pk); + if (!secp256k1_fe_is_zero(&cache_i.second_pk_x)) { + secp256k1_ge pk_pt; + CHECK(secp256k1_xonly_pubkey_load(ctx, &pk_pt, &pk[second_pk_idx]) == 1); + CHECK(secp256k1_fe_equal_var(&pk_pt.x, &cache_i.second_pk_x) == 1); } - CHECK(secp256k1_xonly_pubkey_serialize(ctx, combined_pk_ser, &combined_pk)); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, agg_pk_ser, &agg_pk) == 1); /* TODO: remove when test vectors are not expected to change anymore */ /* int k, l; */ - /* printf("const unsigned char combined_pk_expected[32] = {\n"); */ + /* printf("const unsigned char agg_pk_expected[32] = {\n"); */ /* for (k = 0; k < 4; k++) { */ /* printf(" "); */ /* for (l = 0; l < 8; l++) { */ - /* printf("0x%02X, ", combined_pk_ser[k*8+l]); */ + /* printf("0x%02X, ", agg_pk_ser[k*8+l]); */ /* } */ /* printf("\n"); */ /* } */ /* printf("};\n"); */ - CHECK(secp256k1_memcmp_var(combined_pk_ser, combined_pk_expected, sizeof(combined_pk_ser)) == 0); + CHECK(secp256k1_memcmp_var(agg_pk_ser, agg_pk_expected, sizeof(agg_pk_ser)) == 0); free(pk); free(pk_ptr); } -void musig_test_vectors(void) { +/* Test vector public keys */ +const unsigned char vec_pk[3][32] = { + /* X1 */ + { + 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, + 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, + 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, + 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 + }, + /* X2 */ + { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }, + /* X3 */ + { + 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, + 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, + 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, + 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 + } +}; + +void musig_test_vectors_keyagg(void) { size_t i; - unsigned char pk_ser_tmp[4][32]; - unsigned char pk_ser[3][32] = { - /* X1 */ - { - 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, - 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, - 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, - 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 - }, - /* X2 */ - { - 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, - 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, - 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, - 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 - }, - /* X3 */ - { - 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, - 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, - 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, - 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 - } - }; - const unsigned char combined_pk_expected[4][32] = { + const unsigned char *pk[4]; + const unsigned char agg_pk_expected[4][32] = { { /* 0 */ 0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37, 0xCB, 0xE5, @@ -1078,7 +972,7 @@ void musig_test_vectors(void) { }, }; - for (i = 0; i < sizeof(combined_pk_expected)/sizeof(combined_pk_expected[0]); i++) { + for (i = 0; i < sizeof(agg_pk_expected)/sizeof(agg_pk_expected[0]); i++) { size_t n_pks; int has_second_pk; int second_pk_idx; @@ -1086,44 +980,331 @@ void musig_test_vectors(void) { case 0: /* [X1, X2, X3] */ n_pks = 3; - memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0])); - memcpy(pk_ser_tmp[1], pk_ser[1], sizeof(pk_ser_tmp[1])); - memcpy(pk_ser_tmp[2], pk_ser[2], sizeof(pk_ser_tmp[2])); + pk[0] = vec_pk[0]; + pk[1] = vec_pk[1]; + pk[2] = vec_pk[2]; has_second_pk = 1; second_pk_idx = 1; break; case 1: /* [X3, X2, X1] */ n_pks = 3; - memcpy(pk_ser_tmp[2], pk_ser[0], sizeof(pk_ser_tmp[0])); - memcpy(pk_ser_tmp[1], pk_ser[1], sizeof(pk_ser_tmp[1])); - memcpy(pk_ser_tmp[0], pk_ser[2], sizeof(pk_ser_tmp[2])); + pk[2] = vec_pk[0]; + pk[1] = vec_pk[1]; + pk[0] = vec_pk[2]; has_second_pk = 1; second_pk_idx = 1; break; case 2: /* [X1, X1, X1] */ n_pks = 3; - memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0])); - memcpy(pk_ser_tmp[1], pk_ser[0], sizeof(pk_ser_tmp[1])); - memcpy(pk_ser_tmp[2], pk_ser[0], sizeof(pk_ser_tmp[2])); + pk[0] = vec_pk[0]; + pk[1] = vec_pk[0]; + pk[2] = vec_pk[0]; has_second_pk = 0; second_pk_idx = 0; /* unchecked */ break; case 3: /* [X1, X1, X2, X2] */ n_pks = 4; - memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0])); - memcpy(pk_ser_tmp[1], pk_ser[0], sizeof(pk_ser_tmp[1])); - memcpy(pk_ser_tmp[2], pk_ser[1], sizeof(pk_ser_tmp[2])); - memcpy(pk_ser_tmp[3], pk_ser[1], sizeof(pk_ser_tmp[3])); + pk[0] = vec_pk[0]; + pk[1] = vec_pk[0]; + pk[2] = vec_pk[1]; + pk[3] = vec_pk[1]; has_second_pk = 1; second_pk_idx = 2; /* second_pk_idx = 3 is equally valid */ break; default: CHECK(0); } - musig_test_vectors_helper(pk_ser_tmp, n_pks, combined_pk_expected[i], has_second_pk, second_pk_idx); + musig_test_vectors_keyagg_helper(pk, n_pks, agg_pk_expected[i], has_second_pk, second_pk_idx); + } +} + +void musig_test_vectors_noncegen(void) { + enum { N = 3 }; + secp256k1_scalar k[N][2]; + const unsigned char k32_expected[N][2][32] = { + { + { + 0x8D, 0xD0, 0x99, 0x51, 0x79, 0x50, 0x5E, 0xB1, + 0x27, 0x3A, 0x07, 0x11, 0x58, 0x23, 0xC8, 0x6E, + 0xF7, 0x14, 0x39, 0x0F, 0xDE, 0x2D, 0xEE, 0xB6, + 0xF9, 0x31, 0x6A, 0xEE, 0xBE, 0x5C, 0x71, 0xFC, + }, + { + 0x73, 0x29, 0x2E, 0x47, 0x11, 0x34, 0x7D, 0xD3, + 0x9E, 0x36, 0x05, 0xEE, 0xD6, 0x45, 0x65, 0x49, + 0xB3, 0x0F, 0x3B, 0xC7, 0x16, 0x22, 0x5A, 0x18, + 0x65, 0xBA, 0xE1, 0xD9, 0x84, 0xEF, 0xF8, 0x9D, + }, + }, + /* msg32 is NULL */ + { + { + 0x67, 0x02, 0x5A, 0xF2, 0xA3, 0x56, 0x0B, 0xFC, + 0x1D, 0x95, 0xBD, 0xA6, 0xB2, 0x0B, 0x21, 0x50, + 0x97, 0x63, 0xDB, 0x17, 0x3B, 0xD9, 0x37, 0x30, + 0x17, 0x24, 0x66, 0xEC, 0xAF, 0xA2, 0x60, 0x3B, + }, + { + 0x0B, 0x1D, 0x9E, 0x8F, 0x43, 0xBD, 0xAE, 0x69, + 0x99, 0x6E, 0x0E, 0x3A, 0xBC, 0x30, 0x06, 0x4C, + 0x52, 0x37, 0x3E, 0x05, 0x3E, 0x70, 0xC6, 0xD6, + 0x18, 0x4B, 0xFA, 0xDA, 0xE0, 0xF0, 0xE2, 0xD9, + }, + }, + /* All fields except session_id are NULL */ + { + { + 0xA6, 0xC3, 0x24, 0xC7, 0xE8, 0xD1, 0x8A, 0xAA, + 0x59, 0xD7, 0xB4, 0x74, 0xDD, 0x73, 0x82, 0x6D, + 0x7E, 0x74, 0x91, 0x3F, 0x9B, 0x36, 0x12, 0xE4, + 0x4F, 0x28, 0x6E, 0x07, 0x54, 0x14, 0x58, 0x21, + }, + { + 0x4E, 0x75, 0xD3, 0x81, 0xCD, 0xB7, 0x3C, 0x68, + 0xA0, 0x7E, 0x64, 0x15, 0xE0, 0x0E, 0x89, 0x32, + 0x44, 0x21, 0x87, 0x4F, 0x4E, 0x03, 0xE8, 0x67, + 0x73, 0x4E, 0x33, 0x20, 0xCE, 0x24, 0xBA, 0x8E, + }, + }, + }; + unsigned char args[5][32]; + int i, j; + + for (i = 0; i < 5; i++) { + memset(args[i], i, sizeof(args[i])); + } + + secp256k1_nonce_function_musig(k[0], args[0], args[1], args[2], args[3], args[4]); + secp256k1_nonce_function_musig(k[1], args[0], NULL, args[2], args[3], args[4]); + secp256k1_nonce_function_musig(k[2], args[0], NULL, NULL, NULL, NULL); + /* TODO: remove when test vectors are not expected to change anymore */ + /* int t, u; */ + /* printf("const unsigned char k32_expected[N][2][32] = {\n"); */ + /* for (i = 0; i < N; i++) { */ + /* printf(" {\n"); */ + /* for (j = 0; j < 2; j++) { */ + /* unsigned char k32[32]; */ + /* secp256k1_scalar_get_b32(k32, &k[i][j]); */ + /* printf(" {\n"); */ + /* for (t = 0; t < 4; t++) { */ + /* printf(" "); */ + /* for (u = 0; u < 8; u++) { */ + /* printf("0x%02X, ", k32[t*8+u]); */ + /* } */ + /* printf("\n"); */ + /* } */ + /* printf(" },\n"); */ + /* } */ + /* printf(" },\n"); */ + /* } */ + /* printf("};\n"); */ + for (i = 0; i < N; i++) { + for (j = 0; j < 2; j++) { + unsigned char k32[32]; + secp256k1_scalar_get_b32(k32, &k[i][j]); + CHECK(memcmp(k32, k32_expected[i][j], 32) == 0); + } + } +} + +void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char *tweak, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) { + secp256k1_keypair signer_keypair; + secp256k1_musig_secnonce secnonce; + secp256k1_xonly_pubkey pk[3]; + const secp256k1_xonly_pubkey *pk_ptr[3]; + secp256k1_xonly_pubkey agg_pk; + secp256k1_musig_session session; + secp256k1_musig_aggnonce agg_pubnonce; + secp256k1_musig_partial_sig partial_sig; + int i; + + CHECK(create_keypair_and_pk(&signer_keypair, &pk[signer_pos], sk) == 1); + for (i = 0; i < 3; i++) { + if (i != signer_pos) { + int offset = i < signer_pos ? 0 : -1; + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i + offset]) == 1); + } + pk_ptr[i] = &pk[i]; + } + CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, keyagg_cache, pk_ptr, 3) == 1); + if (tweak != NULL) { + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + } + memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); + memcpy(&secnonce.data[4], secnonce_bytes, sizeof(secnonce.data) - 4); + CHECK(secp256k1_musig_aggnonce_parse(ctx, &agg_pubnonce, agg_pubnonce_ser) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg, keyagg_cache, adaptor) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &signer_keypair, keyagg_cache, &session) == 1); + CHECK(secp256k1_musig_nonce_parity(ctx, fin_nonce_parity, &session) == 1); + memcpy(sig, &partial_sig.data[4], 32); +} + +int musig_test_pk_parity(const secp256k1_musig_keyagg_cache *keyagg_cache) { + secp256k1_keyagg_cache_internal cache_i; + CHECK(secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache) == 1); + return secp256k1_fe_is_odd(&cache_i.pk.y); +} + +int musig_test_is_second_pk(const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *sk) { + secp256k1_ge pkp; + secp256k1_xonly_pubkey pk; + secp256k1_keyagg_cache_internal cache_i; + CHECK(create_keypair_and_pk(NULL, &pk, sk)); + CHECK(secp256k1_xonly_pubkey_load(ctx, &pkp, &pk)); + CHECK(secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)); + return secp256k1_fe_equal_var(&cache_i.second_pk_x, &pkp.x); +} + +/* TODO: Add test vectors for failed signing */ +void musig_test_vectors_sign(void) { + unsigned char sig[32]; + secp256k1_musig_keyagg_cache keyagg_cache; + int fin_nonce_parity; + const unsigned char secnonce[64] = { + 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, + 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, + 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, + 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, + 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, + 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, + 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, + 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, + }; + /* The nonces are already aggregated */ + const unsigned char agg_pubnonce[66] = { + 0x02, + 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, + 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, + 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, + 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, + 0x03, + 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, + 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, + 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, + 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, + }; + const unsigned char sk[32] = { + 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, + 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, + 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, + 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71, + }; + const unsigned char msg[32] = { + 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, + 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, + 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, + 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF, + }; + const unsigned char *pk[2] = { vec_pk[0], vec_pk[1] }; + + { + /* This is a test where the combined public key point has an _odd_ y + * coordinate, the signer _is not_ the second pubkey in the list and the + * nonce parity is 1. */ + const unsigned char sig_expected[32] = { + 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, + 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, + 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, + 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B, + }; + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 0); + /* TODO: remove when test vectors are not expected to change anymore */ + /* int k, l; */ + /* printf("const unsigned char sig_expected[32] = {\n"); */ + /* for (k = 0; k < 4; k++) { */ + /* printf(" "); */ + /* for (l = 0; l < 8; l++) { */ + /* printf("0x%02X, ", sig[k*8+l]); */ + /* } */ + /* printf("\n"); */ + /* } */ + /* printf("};\n"); */ + + /* Check that the description of the test vector is correct */ + CHECK(musig_test_pk_parity(&keyagg_cache) == 1); + CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(fin_nonce_parity == 1); + CHECK(memcmp(sig, sig_expected, 32) == 0); + } + { + /* This is a test where the aggregate public key point has an _even_ y + * coordinate, the signer _is_ the second pubkey in the list and the + * nonce parity is 0. */ + const unsigned char sig_expected[32] = { + 0x2D, 0xF6, 0x7B, 0xFF, 0xF1, 0x8E, 0x3D, 0xE7, + 0x97, 0xE1, 0x3C, 0x64, 0x75, 0xC9, 0x63, 0x04, + 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, + 0x7C, 0xEC, 0xA7, 0xC8, 0x42, 0x42, 0x95, 0xEA, + }; + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 1); + /* Check that the description of the test vector is correct */ + CHECK(musig_test_pk_parity(&keyagg_cache) == 0); + CHECK(musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(fin_nonce_parity == 0); + CHECK(memcmp(sig, sig_expected, 32) == 0); + } + { + /* This is a test where the parity of aggregate public key point (1) is unequal to the + * nonce parity (0). */ + const unsigned char sig_expected[32] = { + 0x0D, 0x5B, 0x65, 0x1E, 0x6D, 0xE3, 0x4A, 0x29, + 0xA1, 0x2D, 0xE7, 0xA8, 0xB4, 0x18, 0x3B, 0x4A, + 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, + 0xA4, 0xA3, 0xD1, 0xBC, 0xAA, 0xBC, 0x75, 0x17, + }; + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 2); + /* Check that the description of the test vector is correct */ + CHECK(musig_test_pk_parity(&keyagg_cache) == 1); + CHECK(fin_nonce_parity == 0); + CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(memcmp(sig, sig_expected, 32) == 0); + } + { + /* This is a test that includes a public key tweak. */ + const unsigned char sig_expected[32] = { + 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, + 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, 0x04, 0xA2, + 0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B, + 0x49, 0x13, 0x64, 0x17, 0x75, 0xE1, 0x37, 0x5B, + }; + const unsigned char tweak[32] = { + 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, + 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, + 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, + 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, + }; + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, NULL, pk, 2); + + CHECK(musig_test_pk_parity(&keyagg_cache) == 1); + CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(fin_nonce_parity == 1); + CHECK(memcmp(sig, sig_expected, 32) == 0); + } + { + /* This is a test that includes an adaptor. */ + const unsigned char sig_expected[32] = { + 0xD7, 0x67, 0xD0, 0x7D, 0x9A, 0xB8, 0x19, 0x8C, + 0x9F, 0x64, 0xE3, 0xFD, 0x9F, 0x7B, 0x8B, 0xAA, + 0xC6, 0x05, 0xF1, 0x8D, 0xFF, 0x18, 0x95, 0x24, + 0x2D, 0x93, 0x95, 0xD9, 0xC8, 0xE6, 0xDD, 0x7C, + }; + const unsigned char sec_adaptor[32] = { + 0xD5, 0x6A, 0xD1, 0x85, 0x00, 0xF2, 0xD7, 0x8A, + 0xB9, 0x54, 0x80, 0x53, 0x76, 0xF3, 0x9D, 0x1B, + 0x6D, 0x62, 0x04, 0x95, 0x12, 0x39, 0x04, 0x6D, + 0x99, 0x3A, 0x9C, 0x31, 0xE0, 0xF4, 0x78, 0x71, + }; + secp256k1_pubkey pub_adaptor; + CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor) == 1); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, &pub_adaptor, pk, 2); + + CHECK(musig_test_pk_parity(&keyagg_cache) == 1); + CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(fin_nonce_parity == 1); + CHECK(memcmp(sig, sig_expected, 32) == 0); } } @@ -1135,7 +1316,7 @@ void run_musig_tests(void) { musig_simple_test(scratch); } musig_api_tests(scratch); - musig_state_machine_tests(scratch); + musig_nonce_test(); for (i = 0; i < count; i++) { /* Run multiple times to ensure that pk and nonce have different y * parities */ @@ -1143,7 +1324,9 @@ void run_musig_tests(void) { musig_tweak_test(scratch); } sha256_tag_test(); - musig_test_vectors(); + musig_test_vectors_keyagg(); + musig_test_vectors_noncegen(); + musig_test_vectors_sign(); secp256k1_scratch_space_destroy(ctx, scratch); } diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 10c00c47..aaab0a8f 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -6,6 +6,7 @@ #include #include +#include #include "../include/secp256k1.h" #include "assumptions.h" @@ -35,6 +36,10 @@ #include "include/secp256k1_ecdsa_adaptor.h" #endif +#ifdef ENABLE_MODULE_MUSIG +#include "include/secp256k1_musig.h" +#endif + void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -241,4 +246,70 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { CHECK(ret == 0); } #endif + +#ifdef ENABLE_MODULE_MUSIG + { + secp256k1_xonly_pubkey pk; + const secp256k1_xonly_pubkey *pk_ptr[1]; + secp256k1_xonly_pubkey agg_pk; + unsigned char session_id[32]; + secp256k1_musig_secnonce secnonce; + secp256k1_musig_pubnonce pubnonce; + const secp256k1_musig_pubnonce *pubnonce_ptr[1]; + secp256k1_musig_aggnonce aggnonce; + secp256k1_musig_keyagg_cache cache; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig; + const secp256k1_musig_partial_sig *partial_sig_ptr[1]; + unsigned char extra_input[32]; + unsigned char sec_adaptor[32]; + secp256k1_pubkey adaptor; + unsigned char pre_sig[64]; + int nonce_parity; + + pk_ptr[0] = &pk; + pubnonce_ptr[0] = &pubnonce; + VALGRIND_MAKE_MEM_DEFINED(key, 32); + memcpy(session_id, key, sizeof(session_id)); + session_id[0] = session_id[0] + 1; + memcpy(extra_input, key, sizeof(extra_input)); + extra_input[0] = extra_input[0] + 2; + memcpy(sec_adaptor, key, sizeof(sec_adaptor)); + sec_adaptor[0] = extra_input[0] + 3; + partial_sig_ptr[0] = &partial_sig; + + CHECK(secp256k1_keypair_create(ctx, &keypair, key)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pk_ptr, 1)); + CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor)); + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + VALGRIND_MAKE_MEM_UNDEFINED(session_id, sizeof(session_id)); + VALGRIND_MAKE_MEM_UNDEFINED(extra_input, sizeof(extra_input)); + VALGRIND_MAKE_MEM_UNDEFINED(sec_adaptor, sizeof(sec_adaptor)); + ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_id, key, msg, &cache, extra_input); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1)); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache, &adaptor) == 1); + + ret = secp256k1_keypair_create(ctx, &keypair, key); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &cache, &session); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_DEFINED(&partial_sig, sizeof(partial_sig)); + CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1)); + VALGRIND_MAKE_MEM_DEFINED(pre_sig, sizeof(pre_sig)); + + CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity, &session)); + ret = secp256k1_musig_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_musig_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + } +#endif } From 3c79d97bd92ec22cc204ff5a08c9b0e5adda12e6 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 17 Dec 2021 11:04:46 +0000 Subject: [PATCH 140/381] ci: increase timeout for macOS tasks --- .cirrus.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 0a65bc90..b1849af4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -119,8 +119,8 @@ task: name: "x86_64: macOS Catalina" macos_instance: image: catalina-base - # As of d4ca81f48e tasks with valgrind enabled take about 60 minutes - timeout_in: 90m + # tasks with valgrind enabled take about 90 minutes + timeout_in: 120m env: HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 From ac1e36769dda3964f7294319ecb06fb5c414938d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 17 Dec 2021 13:41:47 +0000 Subject: [PATCH 141/381] musig: turn off multiexponentiation for now Before turning it on we need to have a discussion about our confidence in the correctness of the multiexponentiation code. --- include/secp256k1_musig.h | 12 +++++++----- src/modules/musig/keyagg_impl.h | 5 ++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 79c6dc48..17ddf7d2 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -197,11 +197,13 @@ SECP256K1_API int secp256k1_musig_partial_sig_parse( * * Returns: 0 if the arguments are invalid, 1 otherwise * Args: ctx: pointer to a context object initialized for verification - * scratch: scratch space used to compute the aggregate pubkey by - * multiexponentiation. Generally, the larger the scratch - * space, the faster this function. However, the returns of - * providing a larger scratch space are diminishing. If NULL, - * an inefficient algorithm is used. + * scratch: should be NULL because it is not yet implemented. If it + * was implemented then the scratch space would be used to + * compute the aggregate pubkey by multiexponentiation. + * Generally, the larger the scratch space, the faster this + * function. However, the returns of providing a larger + * scratch space are diminishing. If NULL, an inefficient + * algorithm is used. * Out: agg_pk: the MuSig-aggregated x-only public key. If you do not need it, * this arg can be NULL. * keyagg_cache: if non-NULL, pointer to a musig_keyagg_cache struct that diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 9a747f4d..5299edca 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -190,6 +190,7 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s secp256k1_gej pkj; secp256k1_ge pkp; size_t i; + (void) scratch; VERIFY_CHECK(ctx != NULL); if (agg_pk != NULL) { @@ -216,7 +217,9 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s if (!secp256k1_musig_compute_pk_hash(ctx, ecmult_data.pk_hash, pubkeys, n_pubkeys)) { return 0; } - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &pkj, NULL, secp256k1_musig_pubkey_agg_callback, (void *) &ecmult_data, n_pubkeys)) { + /* TODO: actually use optimized ecmult_multi algorithms by providing a + * scratch space */ + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_musig_pubkey_agg_callback, (void *) &ecmult_data, n_pubkeys)) { /* In order to reach this line with the current implementation of * ecmult_multi_var one would need to provide a callback that can * fail. */ From b1094953c4497947222df12ef8f9adb2191e2b17 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 30 Dec 2021 16:57:30 +0000 Subject: [PATCH 142/381] musig: remove superfluous comment This was simply forgotten to be removed. --- src/modules/musig/session_impl.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 7cdcebe3..d32132bc 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -664,13 +664,6 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - /* If the MuSig-aggregate point has an odd Y coordinate, the signers will - * sign for the negation of their individual xonly public key. If the - * aggregate key is untweaked, then internal_key_parity is 0, so `e` is - * negated exactly when the aggregate key parity is odd. If the aggregate - * key is tweaked, then negation happens when the aggregate key has an odd Y - * coordinate XOR the internal key has an odd Y coordinate.*/ - /* When producing a partial signature, signer i uses a possibly * negated secret key: * From 588009d26ffc58864b6e9fc3f1ab2eae633476c3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 30 Dec 2021 17:50:57 +0000 Subject: [PATCH 143/381] musig: improve doc of partial_sig_verify regarding signing sessions --- include/secp256k1_musig.h | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 17ddf7d2..121cdf6a 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -398,6 +398,18 @@ SECP256K1_API int secp256k1_musig_partial_sign( ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); /** Verifies an individual signer's partial signature + * + * The signature is verified for a specific signing session. In order to avoid + * accidentally verifying a signature from a different or non-existing signing + * session, you must ensure the following: + * 1. The `keyagg_cache` argument is identical to the one used to create the + * `session` with `musig_nonce_process`. + * 2. The `pubkey` argument must be identical to the one sent by the signer + * before aggregating it with `musig_pubkey_agg` to create the + * `keyagg_cache`. + * 3. The `pubnonce` argument must be identical to the one sent by the signer + * before aggregating it with `musig_nonce_agg` and using the result to + * create the `session` with `musig_nonce_process`. * * This function is essential when using protocols with adaptor signatures. * However, it is not essential for regular MuSig sessions, in the sense that if any @@ -408,13 +420,14 @@ SECP256K1_API int secp256k1_musig_partial_sign( * Returns: 0 if the arguments are invalid or the partial signature does not * verify, 1 otherwise * Args ctx: pointer to a context object, initialized for verification - * In: partial_sig: pointer to partial signature to verify - * pubnonce: public nonce sent by the signer who produced the signature - * pubkey: public key of the signer who produced the signature + * In: partial_sig: pointer to partial signature to verify, sent by + * the signer associated with `pubnonce` and `pubkey` + * pubnonce: public nonce of the signer in the signing session + * pubkey: public key of the signer in the signing session * keyagg_cache: pointer to the keyagg_cache that was output when the - * aggregate public key for this session + * aggregate public key for this signing session * session: pointer to the session that was created with - * musig_nonce_process + * `musig_nonce_process` */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( const secp256k1_context* ctx, From d895b10c18b8aa19a77f0a80f318e1a2052e7c9b Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 31 Dec 2021 17:06:40 +0000 Subject: [PATCH 144/381] musig: mention musig.md in example --- examples/musig.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 7cd664af..856c5d4b 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -4,9 +4,9 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -/** - * This file demonstrates how to use the MuSig module to create a multisignature. - * Additionally, see the documentation in include/secp256k1_musig.h. +/** This file demonstrates how to use the MuSig module to create a + * 3-of-3 multisignature. Additionally, see the documentation in + * include/secp256k1_musig.h and src/modules/musig/musig.md. */ #include From b7ebe6436cd9ea6e91829589b2010c587a033c40 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 4 Jan 2022 12:57:57 +0000 Subject: [PATCH 145/381] Test APIs of funcs that need an ecmult_gen ctx with static ctx The API tests of upstream functions were similarly amended in commit 3b0c2185eab0fe5cb910fffee4c88e134f6d3cad. --- src/modules/ecdsa_adaptor/tests_impl.h | 28 +++-- src/modules/ecdsa_s2c/tests_impl.h | 9 ++ src/modules/generator/tests_impl.h | 24 ++-- src/modules/musig/tests_impl.h | 30 +++-- src/modules/rangeproof/tests_impl.h | 150 +++++++++++++------------ src/modules/surjection/tests_impl.h | 65 ++++++----- 6 files changed, 176 insertions(+), 130 deletions(-) diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index c31c4040..37547ace 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -828,16 +828,19 @@ void test_ecdsa_adaptor_api(void) { secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); int ecount; secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_testrand256(sk); secp256k1_testrand256(msg); @@ -852,16 +855,18 @@ void test_ecdsa_adaptor_api(void) { CHECK(secp256k1_ecdsa_adaptor_encrypt(vrfy, asig, sk, &enckey, msg, NULL, NULL) == 1); CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, msg, NULL, NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, NULL, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sttc, asig, sk, &enckey, msg, NULL, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, NULL, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, NULL, sk, &enckey, msg, NULL, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, NULL, &enckey, msg, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, NULL, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, NULL, msg, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, NULL, &enckey, msg, NULL, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &zero_pk, msg, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, NULL, msg, NULL, NULL) == 0); CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &zero_pk, msg, NULL, NULL) == 0); + CHECK(ecount == 6); ecount = 0; CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, msg, NULL, NULL) == 1); @@ -900,21 +905,24 @@ void test_ecdsa_adaptor_api(void) { CHECK(secp256k1_ecdsa_adaptor_recover(vrfy, deckey, &sig, asig, &enckey) == 1); CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &enckey) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdsa_adaptor_recover(sign, NULL, &sig, asig, &enckey) == 0); + CHECK(secp256k1_ecdsa_adaptor_recover(sttc, deckey, &sig, asig, &enckey) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, NULL, asig, &enckey) == 0); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, NULL, &sig, asig, &enckey) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, NULL, &enckey) == 0); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, NULL, asig, &enckey) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, NULL) == 0); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, NULL, &enckey) == 0); CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &zero_pk) == 0); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, NULL) == 0); CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &zero_pk) == 0); + CHECK(ecount == 6); secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); secp256k1_context_destroy(both); + secp256k1_context_destroy(sttc); } void adaptor_tests(void) { diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index dfcfc46b..1868c76b 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -93,6 +93,7 @@ static void test_ecdsa_s2c_api(void) { secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); secp256k1_ecdsa_s2c_opening s2c_opening; secp256k1_ecdsa_signature sig; @@ -108,6 +109,7 @@ static void test_ecdsa_s2c_api(void) { secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sec)); ecount = 0; @@ -125,6 +127,8 @@ static void test_ecdsa_s2c_api(void) { CHECK(secp256k1_ecdsa_s2c_sign(vrfy, &sig, &s2c_opening, msg, sec, s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, &s2c_opening, msg, sec, s2c_data) == 1); CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_s2c_sign(sttc, &sig, &s2c_opening, msg, sec, s2c_data) == 0); + CHECK(ecount == 5); CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pk) == 1); @@ -168,6 +172,8 @@ static void test_ecdsa_s2c_api(void) { CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(vrfy, &s2c_opening, msg, sec, hostrand_commitment) == 1); CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(sign, &s2c_opening, msg, sec, hostrand_commitment) == 1); CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(sttc, &s2c_opening, msg, sec, hostrand_commitment) == 0); + CHECK(ecount == 5); ecount = 0; CHECK(secp256k1_anti_exfil_sign(both, NULL, msg, sec, hostrand) == 0); @@ -182,6 +188,8 @@ static void test_ecdsa_s2c_api(void) { CHECK(secp256k1_anti_exfil_sign(vrfy, &sig, msg, sec, hostrand) == 1); CHECK(secp256k1_anti_exfil_sign(both, &sig, msg, sec, hostrand) == 1); CHECK(ecount == 4); + CHECK(secp256k1_anti_exfil_sign(sttc, &sig, msg, sec, hostrand) == 0); + CHECK(ecount == 5); ecount = 0; CHECK(secp256k1_anti_exfil_host_verify(both, NULL, msg, &pk, hostrand, &s2c_opening) == 0); @@ -203,6 +211,7 @@ static void test_ecdsa_s2c_api(void) { secp256k1_context_destroy(vrfy); secp256k1_context_destroy(sign); secp256k1_context_destroy(none); + secp256k1_context_destroy(sttc); } /* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */ diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index f41f18eb..068c5f39 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -24,15 +24,18 @@ void test_generator_api(void) { secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); secp256k1_generator gen; int32_t ecount = 0; secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_testrand256(key); secp256k1_testrand256(blind); @@ -49,31 +52,34 @@ void test_generator_api(void) { CHECK(ecount == 2); CHECK(secp256k1_generator_generate_blinded(none, &gen, key, blind) == 1); CHECK(ecount == 2); - CHECK(secp256k1_generator_generate_blinded(vrfy, NULL, key, blind) == 0); + CHECK(secp256k1_generator_generate_blinded(sttc, &gen, key, blind) == 0); CHECK(ecount == 3); - CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, NULL, blind) == 0); + CHECK(secp256k1_generator_generate_blinded(vrfy, NULL, key, blind) == 0); CHECK(ecount == 4); - CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, key, NULL) == 0); + CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, NULL, blind) == 0); CHECK(ecount == 5); + CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, key, NULL) == 0); + CHECK(ecount == 6); CHECK(secp256k1_generator_serialize(none, sergen, &gen) == 1); - CHECK(ecount == 5); - CHECK(secp256k1_generator_serialize(none, NULL, &gen) == 0); CHECK(ecount == 6); - CHECK(secp256k1_generator_serialize(none, sergen, NULL) == 0); + CHECK(secp256k1_generator_serialize(none, NULL, &gen) == 0); CHECK(ecount == 7); + CHECK(secp256k1_generator_serialize(none, sergen, NULL) == 0); + CHECK(ecount == 8); CHECK(secp256k1_generator_serialize(none, sergen, &gen) == 1); CHECK(secp256k1_generator_parse(none, &gen, sergen) == 1); - CHECK(ecount == 7); - CHECK(secp256k1_generator_parse(none, NULL, sergen) == 0); CHECK(ecount == 8); - CHECK(secp256k1_generator_parse(none, &gen, NULL) == 0); + CHECK(secp256k1_generator_parse(none, NULL, sergen) == 0); CHECK(ecount == 9); + CHECK(secp256k1_generator_parse(none, &gen, NULL) == 0); + CHECK(ecount == 10); secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(sttc); } void test_shallue_van_de_woestijne(void) { diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index f1fb4817..69a91c16 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -160,14 +160,17 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); int ecount; secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); memset(max64, 0xff, sizeof(max64)); memset(&invalid_keypair, 0, sizeof(invalid_keypair)); @@ -280,34 +283,36 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_nonce_gen(vrfy, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 1); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 1); CHECK(ecount == 0); - CHECK(secp256k1_musig_nonce_gen(sign, NULL, &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sttc, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], NULL, session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, NULL, &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], NULL, sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], NULL, session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 3); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], NULL, sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(ecount == 4); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); /* no seckey and session_id is 0 */ CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, NULL, msg, &keyagg_cache, max64) == 0); - CHECK(ecount == 3); + CHECK(ecount == 4); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); /* session_id 0 is fine when a seckey is provided */ CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, sk[0], msg, &keyagg_cache, max64) == 1); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, msg, &keyagg_cache, max64) == 1); - CHECK(ecount == 3); + CHECK(ecount == 4); /* invalid seckey */ CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], max64, msg, &keyagg_cache, max64) == 0); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, &keyagg_cache, max64) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, NULL, max64) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &invalid_keyagg_cache, max64) == 0); CHECK(ecount == 4); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, &keyagg_cache, max64) == 1); + CHECK(ecount == 4); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, NULL, max64) == 1); + CHECK(ecount == 4); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &invalid_keyagg_cache, max64) == 0); + CHECK(ecount == 5); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, NULL) == 1); - CHECK(ecount == 4); + CHECK(ecount == 5); /* Every in-argument except session_id can be NULL */ CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, NULL, NULL, NULL) == 1); @@ -583,6 +588,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(sttc); } void musig_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 875ed0a3..41c4d616 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -16,7 +16,7 @@ #include "include/secp256k1_rangeproof.h" -static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const int32_t *ecount) { +static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *sttc, const int32_t *ecount) { secp256k1_pedersen_commitment commit; const secp256k1_pedersen_commitment *commit_ptr = &commit; unsigned char blind[32]; @@ -30,51 +30,53 @@ static void test_pedersen_api(const secp256k1_context *none, const secp256k1_con CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) != 0); CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); CHECK(*ecount == 0); + CHECK(secp256k1_pedersen_commit(sttc, &commit, blind, val, secp256k1_generator_h) == 0); + CHECK(*ecount == 1); CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 1); - CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); CHECK(*ecount == 2); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); CHECK(*ecount == 3); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); + CHECK(*ecount == 4); CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0); - CHECK(*ecount == 3); - CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); CHECK(*ecount == 4); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); + CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); CHECK(*ecount == 5); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); + CHECK(*ecount == 6); CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); - CHECK(*ecount == 6); + CHECK(*ecount == 7); CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); - CHECK(*ecount == 6); + CHECK(*ecount == 7); CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0); CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0); CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0); CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0); - CHECK(*ecount == 6); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); CHECK(*ecount == 7); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); CHECK(*ecount == 8); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); + CHECK(*ecount == 9); CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); - CHECK(*ecount == 8); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); CHECK(*ecount == 9); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); CHECK(*ecount == 10); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); CHECK(*ecount == 11); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); CHECK(*ecount == 12); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); CHECK(*ecount == 13); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); + CHECK(*ecount == 14); } -static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const int32_t *ecount) { +static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, const int32_t *ecount) { unsigned char proof[5134]; unsigned char blind[32]; secp256k1_pedersen_commitment commit; @@ -95,29 +97,31 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(secp256k1_rangeproof_sign(vrfy, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); CHECK(*ecount == 0); + CHECK(secp256k1_rangeproof_sign(sttc, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 1); CHECK(secp256k1_rangeproof_sign(both, NULL, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 1); - CHECK(secp256k1_rangeproof_sign(both, proof, NULL, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 2); - CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, NULL, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_sign(both, proof, NULL, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 3); - CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, NULL, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, NULL, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 4); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, NULL, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 5); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, NULL, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 5); + CHECK(*ecount == 6); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, vmin - 1, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 5); + CHECK(*ecount == 6); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 6); + CHECK(*ecount == 7); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 6); + CHECK(*ecount == 7); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 7); - CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, secp256k1_generator_h) != 0); - CHECK(*ecount == 7); - CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, NULL) == 0); CHECK(*ecount == 8); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, secp256k1_generator_h) != 0); + CHECK(*ecount == 8); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, NULL) == 0); + CHECK(*ecount == 9); CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); { @@ -133,17 +137,17 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(max_value >= val); CHECK(secp256k1_rangeproof_info(none, NULL, &mantissa, &min_value, &max_value, proof, len) == 0); - CHECK(*ecount == 9); - CHECK(secp256k1_rangeproof_info(none, &exp, NULL, &min_value, &max_value, proof, len) == 0); CHECK(*ecount == 10); - CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, NULL, &max_value, proof, len) == 0); + CHECK(secp256k1_rangeproof_info(none, &exp, NULL, &min_value, &max_value, proof, len) == 0); CHECK(*ecount == 11); - CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, NULL, proof, len) == 0); + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, NULL, &max_value, proof, len) == 0); CHECK(*ecount == 12); + CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, NULL, proof, len) == 0); + CHECK(*ecount == 13); CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, NULL, len) == 0); - CHECK(*ecount == 13); + CHECK(*ecount == 14); CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, proof, 0) == 0); - CHECK(*ecount == 13); + CHECK(*ecount == 14); } { uint64_t min_value; @@ -151,24 +155,24 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(secp256k1_rangeproof_verify(none, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 1); CHECK(secp256k1_rangeproof_verify(sign, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 1); CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 13); + CHECK(*ecount == 14); CHECK(secp256k1_rangeproof_verify(vrfy, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 14); - CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 15); - CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 16); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 17); CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 17); + CHECK(*ecount == 18); CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 17); + CHECK(*ecount == 18); CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 18); - CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); - CHECK(*ecount == 18); - CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); CHECK(*ecount == 19); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK(*ecount == 19); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); + CHECK(*ecount == 20); } { unsigned char blind_out[32]; @@ -179,12 +183,14 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c size_t message_len = sizeof(message_out); CHECK(secp256k1_rangeproof_rewind(none, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 19); + CHECK(*ecount == 20); CHECK(secp256k1_rangeproof_rewind(sign, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 1); CHECK(secp256k1_rangeproof_rewind(vrfy, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 19); + CHECK(*ecount == 20); CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 19); + CHECK(*ecount == 20); + CHECK(secp256k1_rangeproof_rewind(sttc, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 21); CHECK(min_value == vmin); CHECK(max_value >= val); @@ -193,31 +199,31 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(memcmp(message, message_out, sizeof(message_out)) == 0); CHECK(secp256k1_rangeproof_rewind(both, NULL, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 19); /* blindout may be NULL */ + CHECK(*ecount == 21); /* blindout may be NULL */ CHECK(secp256k1_rangeproof_rewind(both, blind_out, NULL, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 19); /* valueout may be NULL */ + CHECK(*ecount == 21); /* valueout may be NULL */ CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 20); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); - CHECK(*ecount == 20); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, NULL, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 21); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 22); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); + CHECK(*ecount == 22); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, NULL, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 23); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); CHECK(*ecount == 24); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 25); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 26); CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 25); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 25); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0); - CHECK(*ecount == 26); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); - CHECK(*ecount == 26); - CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); CHECK(*ecount == 27); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 27); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0); + CHECK(*ecount == 28); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK(*ecount == 28); + CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); + CHECK(*ecount == 29); } } @@ -226,6 +232,7 @@ static void test_api(void) { secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); int32_t ecount; int i; @@ -233,22 +240,25 @@ static void test_api(void) { secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); for (i = 0; i < count; i++) { ecount = 0; - test_pedersen_api(none, sign, vrfy, &ecount); + test_pedersen_api(none, sign, vrfy, sttc, &ecount); ecount = 0; - test_rangeproof_api(none, sign, vrfy, both, &ecount); + test_rangeproof_api(none, sign, vrfy, both, sttc, &ecount); } secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); secp256k1_context_destroy(both); + secp256k1_context_destroy(sttc); } static void test_pedersen(void) { diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 488637a5..89ba4e1a 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -19,6 +19,7 @@ static void test_surjectionproof_api(void) { secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); secp256k1_fixed_asset_tag fixed_input_tags[10]; secp256k1_fixed_asset_tag fixed_output_tag; secp256k1_generator ephemeral_input_tags[10]; @@ -39,10 +40,13 @@ static void test_surjectionproof_api(void) { secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); + for (i = 0; i < n_inputs; i++) { secp256k1_testrand256(input_blinding_key[i]); @@ -127,76 +131,79 @@ static void test_surjectionproof_api(void) { CHECK(secp256k1_surjectionproof_generate(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 1); CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0); CHECK(ecount == 7); + CHECK(secp256k1_surjectionproof_generate(sttc, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + CHECK(ecount == 8); CHECK(secp256k1_surjectionproof_generate(both, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); - CHECK(ecount == 8); + CHECK(ecount == 9); CHECK(secp256k1_surjectionproof_generate(both, &proof, NULL, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); - CHECK(ecount == 9); + CHECK(ecount == 10); CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); - CHECK(ecount == 9); + CHECK(ecount == 10); CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); - CHECK(ecount == 9); + CHECK(ecount == 10); CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, 0, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); - CHECK(ecount == 9); + CHECK(ecount == 10); CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, NULL, 0, input_blinding_key[0], output_blinding_key) == 0); - CHECK(ecount == 10); - CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 1, input_blinding_key[0], output_blinding_key) != 0); - CHECK(ecount == 10); /* the above line "succeeds" but generates an invalid proof as the input_index is wrong. it is fairly expensive to detect this. should we? */ - CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, n_inputs + 1, input_blinding_key[0], output_blinding_key) != 0); - CHECK(ecount == 10); - CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, NULL, output_blinding_key) == 0); CHECK(ecount == 11); - CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], NULL) == 0); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 1, input_blinding_key[0], output_blinding_key) != 0); + CHECK(ecount == 11); /* the above line "succeeds" but generates an invalid proof as the input_index is wrong. it is fairly expensive to detect this. should we? */ + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, n_inputs + 1, input_blinding_key[0], output_blinding_key) != 0); + CHECK(ecount == 11); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, NULL, output_blinding_key) == 0); CHECK(ecount == 12); + CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], NULL) == 0); + CHECK(ecount == 13); CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0); /* check verify */ CHECK(secp256k1_surjectionproof_verify(none, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 1); CHECK(secp256k1_surjectionproof_verify(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 1); CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 1); - CHECK(ecount == 12); + CHECK(ecount == 13); CHECK(secp256k1_surjectionproof_verify(vrfy, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0); - CHECK(ecount == 13); + CHECK(ecount == 14); CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, NULL, n_inputs, &ephemeral_output_tag) == 0); - CHECK(ecount == 14); - CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag) == 0); - CHECK(ecount == 14); - CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag) == 0); - CHECK(ecount == 14); - CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, NULL) == 0); CHECK(ecount == 15); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag) == 0); + CHECK(ecount == 15); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag) == 0); + CHECK(ecount == 15); + CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, NULL) == 0); + CHECK(ecount == 16); /* Check serialize */ serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0); - CHECK(ecount == 15); - serialized_len = sizeof(serialized_proof); - CHECK(secp256k1_surjectionproof_serialize(none, NULL, &serialized_len, &proof) == 0); CHECK(ecount == 16); serialized_len = sizeof(serialized_proof); - CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, NULL, &proof) == 0); + CHECK(secp256k1_surjectionproof_serialize(none, NULL, &serialized_len, &proof) == 0); CHECK(ecount == 17); serialized_len = sizeof(serialized_proof); - CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, NULL) == 0); + CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, NULL, &proof) == 0); CHECK(ecount == 18); + serialized_len = sizeof(serialized_proof); + CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, NULL) == 0); + CHECK(ecount == 19); serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0); /* Check parse */ CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, serialized_len) != 0); - CHECK(ecount == 18); - CHECK(secp256k1_surjectionproof_parse(none, NULL, serialized_proof, serialized_len) == 0); CHECK(ecount == 19); + CHECK(secp256k1_surjectionproof_parse(none, NULL, serialized_proof, serialized_len) == 0); + CHECK(ecount == 20); CHECK(secp256k1_surjectionproof_parse(none, &proof, NULL, serialized_len) == 0); - CHECK(ecount == 20); + CHECK(ecount == 21); CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, 0) == 0); - CHECK(ecount == 20); + CHECK(ecount == 21); secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); secp256k1_context_destroy(both); + secp256k1_context_destroy(sttc); } static void test_input_selection(size_t n_inputs) { From 11d675dce8edab6cece4524c3ffb3de809bea72f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 6 Jan 2022 19:12:14 +0000 Subject: [PATCH 146/381] whitelist: remove ability to specific nonce function This functionality is inappropriate to expose for a zero-knowledge proof, and was confusingly (and potentially dangerously) implemented. --- include/secp256k1_whitelist.h | 8 ++------ src/bench_whitelist.c | 2 +- src/modules/whitelist/main_impl.h | 10 +++------- src/modules/whitelist/tests_impl.h | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index c0dafd91..5b14df7c 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -101,8 +101,6 @@ SECP256K1_API int secp256k1_whitelist_signature_serialize( * online_seckey: the secret key to the signer's online pubkey * summed_seckey: the secret key to the sum of (whitelisted key, signer's offline pubkey) * index: the signer's index in the lists of keys - * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) * Out: sig: The produced signature. * * The signatures are of the list of all passed pubkeys in the order @@ -120,10 +118,8 @@ SECP256K1_API int secp256k1_whitelist_sign( const size_t n_keys, const secp256k1_pubkey *sub_pubkey, const unsigned char *online_seckey, - const unsigned char *summed_seckey, - const size_t index, - secp256k1_nonce_function noncefp, - const void *noncedata + const unsigned char *summed_seckeyx, + const size_t index ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); /** Verify a whitelist signature diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c index 8964cd19..e4908306 100644 --- a/src/bench_whitelist.c +++ b/src/bench_whitelist.c @@ -39,7 +39,7 @@ static void bench_whitelist(void* arg, int iters) { static void bench_whitelist_setup(void* arg) { bench_data* data = (bench_data*)arg; int i = 0; - CHECK(secp256k1_whitelist_sign(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey, data->online_seckey[i], data->summed_seckey[i], i, NULL, NULL)); + CHECK(secp256k1_whitelist_sign(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey, data->online_seckey[i], data->summed_seckey[i], i)); } static void run_test(bench_data* data, int iters) { diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index f16ea845..a37e16ff 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -12,17 +12,13 @@ #define MAX_KEYS SECP256K1_WHITELIST_MAX_N_KEYS /* shorter alias */ -int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey, const unsigned char *online_seckey, const unsigned char *summed_seckey, const size_t index, secp256k1_nonce_function noncefp, const void *noncedata) { +int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey, const unsigned char *online_seckey, const unsigned char *summed_seckey, const size_t index) { secp256k1_gej pubs[MAX_KEYS]; secp256k1_scalar s[MAX_KEYS]; secp256k1_scalar sec, non; unsigned char msg32[32]; int ret; - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_default; - } - /* Sanity checks */ VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); @@ -53,7 +49,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s size_t i; unsigned char nonce32[32]; int done; - ret = noncefp(nonce32, msg32, seckey32, NULL, (void*)noncedata, count); + ret = secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, NULL, count); if (!ret) { break; } @@ -67,7 +63,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s for (i = 0; i < n_keys; i++) { msg32[0] ^= i + 1; msg32[1] ^= (i + 1) / 0x100; - ret = noncefp(&sig->data[32 * (i + 1)], msg32, seckey32, NULL, (void*)noncedata, count); + ret = secp256k1_nonce_function_default(&sig->data[32 * (i + 1)], msg32, seckey32, NULL, NULL, count); if (!ret) { break; } diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index c420518c..10e8693e 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -15,7 +15,7 @@ void test_whitelist_end_to_end_internal(const unsigned char *summed_seckey, cons secp256k1_whitelist_signature sig; secp256k1_whitelist_signature sig1; - CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey, online_seckey, summed_seckey, signer_i, NULL, NULL)); + CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey, online_seckey, summed_seckey, signer_i)); CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey) == 1); /* Check that exchanging keys causes a failure */ CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, sub_pubkey) != 1); From 44001ad716a789520550dcdd304daf90abdf58c5 Mon Sep 17 00:00:00 2001 From: Kalle Rosenbaum Date: Sat, 15 Jan 2022 12:31:00 +0100 Subject: [PATCH 147/381] Typo fix, add subscript i --- src/modules/musig/musig-spec.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/musig-spec.mediawiki b/src/modules/musig/musig-spec.mediawiki index 64fa4811..09ebd6c9 100644 --- a/src/modules/musig/musig-spec.mediawiki +++ b/src/modules/musig/musig-spec.mediawiki @@ -89,7 +89,7 @@ The algorithm ''IsSecond(pk1..u, i)'' is defined as: The algorithm ''KeyAggCoeff(pk1..u, i)'' is defined as: * Let ''L = HashKeys(pk1..u)''. -* Return 1 if ''IsSecond(pk1..u, i)'', otherwise return ''int(hashKeyAgg coefficient(L || pk) mod n''. +* Return 1 if ''IsSecond(pk1..u, i)'', otherwise return ''int(hashKeyAgg coefficient(L || pki) mod n''. == Applications == From c519b468791670654f8b66368a675655cd337ae8 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 25 Oct 2021 21:57:30 +0000 Subject: [PATCH 148/381] musig: add pubkey_get to obtain a full pubkey from a keyagg_cache --- include/secp256k1_musig.h | 18 ++++++++++++++++++ src/modules/musig/keyagg_impl.h | 14 ++++++++++++++ src/modules/musig/tests_impl.h | 10 ++++++++++ 3 files changed, 42 insertions(+) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 17ddf7d2..c4ddffbb 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -223,6 +223,24 @@ SECP256K1_API int secp256k1_musig_pubkey_agg( size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); +/** Obtain the aggregate public key from a keyagg_cache. + * + * This is only useful if you need the non-xonly public key, in particular for + * ordinary (non-xonly) tweaking or batch-verifying multiple key aggregations + * (not implemented). + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: agg_pk: the MuSig-aggregated public key. + * In: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by + * `musig_pubkey_agg` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( + const secp256k1_context* ctx, + secp256k1_pubkey *agg_pk, + secp256k1_musig_keyagg_cache *keyagg_cache +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + /** Tweak an x-only public key in a given keyagg_cache by adding * the generator multiplied with `tweak32` to it. * diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 5299edca..b8cb5d10 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -244,6 +244,20 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s return 1; } +int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache) { + secp256k1_keyagg_cache_internal cache_i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(agg_pk != NULL); + memset(agg_pk, 0, sizeof(*agg_pk)); + ARG_CHECK(keyagg_cache != NULL); + + if(!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + return 0; + } + secp256k1_pubkey_save(agg_pk, &cache_i.pk); + return 1; +} + int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { secp256k1_keyagg_cache_internal cache_i; int overflow = 0; diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 69a91c16..01d1a9c8 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -140,6 +140,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { unsigned char aggnonce_ser[66]; unsigned char msg[32]; secp256k1_xonly_pubkey agg_pk; + secp256k1_pubkey full_agg_pk; secp256k1_musig_keyagg_cache keyagg_cache; secp256k1_musig_keyagg_cache invalid_keyagg_cache; secp256k1_musig_session session; @@ -243,6 +244,15 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_agg(sign, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); + /* pubkey_get */ + ecount = 0; + CHECK(secp256k1_musig_pubkey_get(none, &full_agg_pk, &keyagg_cache) == 1); + CHECK(secp256k1_musig_pubkey_get(none, NULL, &keyagg_cache) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_get(none, &full_agg_pk, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros68, sizeof(full_agg_pk)) == 0); + /** Tweaking **/ ecount = 0; { From 37107361a0ff3b8764903e3b384cfc12ed484e7a Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 25 Oct 2021 21:50:08 +0000 Subject: [PATCH 149/381] musig: allow ordinary, non-xonly tweaking --- include/secp256k1_musig.h | 53 +++++++++++++++-- src/modules/musig/keyagg_impl.h | 12 +++- src/modules/musig/session_impl.h | 95 ++++++++++++++++-------------- src/modules/musig/tests_impl.h | 99 +++++++++++++++++++------------- 4 files changed, 169 insertions(+), 90 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index c4ddffbb..45f4d32e 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -241,16 +241,59 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( secp256k1_musig_keyagg_cache *keyagg_cache ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Tweak an x-only public key in a given keyagg_cache by adding - * the generator multiplied with `tweak32` to it. +/** Apply ordinary "EC" tweaking to a public key in a given keyagg_cache by + * adding the generator multiplied with `tweak32` to it. This is useful for + * deriving child keys from an aggregate public key via BIP32. + * + * The tweaking method is the same as `secp256k1_ec_pubkey_tweak_add`. So after + * the following pseudocode buf and buf2 have identical contents (absent + * earlier failures). + * + * secp256k1_musig_pubkey_agg(..., keyagg_cache, pubkeys, ...) + * secp256k1_musig_pubkey_get(..., agg_pk, keyagg_cache) + * secp256k1_musig_pubkey_ec_tweak_add(..., output_pk, tweak32, keyagg_cache) + * secp256k1_ec_pubkey_serialize(..., buf, output_pk) + * secp256k1_ec_pubkey_tweak_add(..., agg_pk, tweak32) + * secp256k1_ec_pubkey_serialize(..., buf2, agg_pk) + * + * This function is required if you want to _sign_ for a tweaked aggregate key. + * On the other hand, if you are only computing a public key, but not intending + * to create a signature for it, you can just use + * `secp256k1_ec_pubkey_tweak_add`. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for verification + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0. If you + * do not need it, this arg can be NULL. + * In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by + * `musig_pubkey_agg` + * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid + * according to `secp256k1_ec_seckey_verify`, this function + * returns 0. For uniformly random 32-byte arrays the + * chance of being invalid is negligible (around 1 in + * 2^128). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_ec_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *output_pubkey, + secp256k1_musig_keyagg_cache *keyagg_cache, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Apply x-only tweaking to a public key in a given keyagg_cache by adding the + * generator multiplied with `tweak32` to it. This is useful for creating + * Taproot outputs. * * The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in * the following pseudocode xonly_pubkey_tweak_add_check (absent earlier * failures) returns 1. * * secp256k1_musig_pubkey_agg(..., agg_pk, keyagg_cache, pubkeys, ...) - * secp256k1_musig_pubkey_tweak_add(..., output_pubkey, tweak32, keyagg_cache) - * secp256k1_xonly_pubkey_serialize(..., buf, output_pubkey) + * secp256k1_musig_pubkey_xonly_tweak_add(..., output_pk, tweak32, keyagg_cache) + * secp256k1_xonly_pubkey_serialize(..., buf, output_pk) * secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32) * * This function is required if you want to _sign_ for a tweaked aggregate key. @@ -273,7 +316,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( * chance of being invalid is negligible (around 1 in * 2^128). */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_tweak_add( const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index b8cb5d10..c7f18b37 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -258,7 +258,7 @@ int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *a return 1; } -int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { +static int secp256k1_musig_pubkey_tweak_add_internal(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32, int xonly) { secp256k1_keyagg_cache_internal cache_i; int overflow = 0; secp256k1_scalar tweak; @@ -277,7 +277,7 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pub if (overflow) { return 0; } - if (secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { + if (xonly && secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { cache_i.internal_key_parity ^= 1; secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak); } @@ -294,4 +294,12 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pub return 1; } +int secp256k1_musig_pubkey_ec_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { + return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 0); +} + +int secp256k1_musig_pubkey_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { + return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 1); +} + #endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 7cdcebe3..b85881c4 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -538,56 +538,65 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p * The following public keys arise as intermediate steps: * - P[i] is the i-th public key with corresponding secret key x[i] * P[i] := x[i]*G - * - P_agg is the aggregate public key - * P_agg := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| - * - P_tweak[i] is the tweaked public key after the i-th tweaking operation - * P_tweak[0] := P_agg - * P_tweak[i] := |P_tweak[i-1]| + t[i]*G for i = 1, ..., m + * - P_agg[0] is the aggregate public key + * P_agg[0] := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| + * - P_agg[i] for 1 <= i <= m is the tweaked public key after the i-th + * tweaking operation. There are two types of tweaking: x-only and ordinary + * "EC" tweaking. We define a boolean predicate xonly(i) that is true if + * the i-th tweaking operation is x-only tweaking and false otherwise + * (ordinary tweaking). + * Let + * P_agg[i] := f(i, P_agg[i-1]) + t[i]*G for i = 1, ..., m + * where f(i, X) := |X| if xonly(i) + * f(i, X) := X otherwise * * Note that our goal is to produce a partial signature corresponding to - * the final public key after m tweaking operations P_final = |P_tweak[m]|. + * the final public key after m tweaking operations P_final = |P_agg[m]|. * - * Define d[i], d_agg, and d_tweak[i] so that: + * Define d[i] for 0 <= i <= n-1 and d_agg[i] for 0 <= i <= m so that: * - |P[i]| = d[i]*P[i] - * - |P_agg| = d_agg*P_agg - * - |P_tweak[i]| = d_tweak[i]*P_tweak[i] + * - f(i+1, P_agg[i]) = d_agg[i]*P_agg[i] for 0 <= i <= m - 1 + * - |P_agg[m]| = d_agg[m]*P_agg[m] * - * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise; - * similarly for d_agg and d_tweak[i]. + * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise. + * For 0 <= i <= m-1, d_agg[i] is -1 if and only if xonly(i+1) is true and + * P_agg[i] has an odd Y coordinate. * - * The (xonly) final public key is P_final = |P_tweak[m]| - * = d_tweak[m]*P_tweak[m] - * = d_tweak[m]*(|P_tweak[m-1]| + t[m]*G) - * = d_tweak[m]*(d_tweak[m-1]*(|P_tweak[m-2]| + t[m-1]*G) + t[m]*G) - * = d_tweak[m]*...*d_tweak[1]*|P_agg| + (d_tweak[m]*t[m]+...+*d_tweak[1]*t[1])*G. - * To simplify the equation let us define - * t := d_tweak[m]*t[m]+...+*d_tweak[1]*t[1] - * d_tweak := d_tweak[m]*...*d_tweak[1]. + * The (x-only) final public key is P_final = |P_agg[m]| + * = d_agg[m]*P_agg[m] + * = d_agg[m]*(f(m, P_agg[m-1]) + t[m]*G) + * = d_agg[m]*(d_agg[m-1]*(f(m-1, P_agg[m-2]) + t[m-1]*G) + t[m]*G) + * = d_agg[m]*...*d_agg[0]*P_agg[0] + (d_agg[m]*t[m]+...+*d_agg[1]*t[1])*G. + * To simplify the equation let us define + * d_agg := d_agg[m]*...*d_agg[0]. + * t := d_agg[m]*t[m]+...+*d_agg[1]*t[1] if m > 0, otherwise t := 0 * Then we have * P_final - t*G - * = d_tweak*|P_agg| - * = d_tweak*d_agg*P_agg - * = d_tweak*d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) - * = d_tweak*d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) - * = sum((d_tweak*d_agg*d[i])*mu[i]*x[i])*G. + * = d_agg*P_agg[0] + * = d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) + * = d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) + * = sum((d_agg*d[i])*mu[i]*x[i])*G. * * Thus whether signer i should use the negated x[i] depends on the product - * d_tweak[m]*...*d_tweak[1]*d_agg*d[i]. In other words, negate if and only + * d_agg[m]*...*d_agg[1]*d_agg[0]*d[i]. In other words, negate if and only * if the following holds: - * (P[i] has odd y) XOR (P_agg has odd y) - * XOR (P_tweak[1] has odd y) XOR ... XOR (P_tweak[m] has odd y) + * (P[i] has odd y) XOR (xonly(1) and P_agg[0] has odd y) + * XOR (xonly(2) and P_agg[1] has odd y) + * XOR ... XOR (xonly(m) and P_agg[m-1] has odd y) + * XOR (P_agg[m] has odd y) * * Let us now look at how the terms in the equation correspond to the if * condition below for some values of m: - * m = 0: P_i has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 0: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) + * P_agg[0] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) * cache_i.internal_key_parity = 0 - * m = 1: P_i has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg has odd y = cache_i.internal_key_parity - * P_tweak[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * m = 2: P_i has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg has odd y XOR P_tweak[1] has odd y = cache_i.internal_key_parity - * P_tweak[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 1: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) + * xonly(1) and P_agg[0] has odd y = cache_i.internal_key_parity + * P_agg[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 2: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) + * (xonly(1) and P_agg[0] has odd y) + XOR (xonly(2) and P_agg[1] has odd y) = cache_i.internal_key_parity + * P_agg[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) * etc. */ if ((secp256k1_fe_is_odd(&pk.y) @@ -674,7 +683,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* When producing a partial signature, signer i uses a possibly * negated secret key: * - * sk[i] = (d_tweak*d_agg*d[i])*x[i] + * sk[i] = (d_agg*d[i])*x[i] * * to ensure that the aggregate signature will correspond to * an aggregate public key with even Y coordinate (see the @@ -698,14 +707,14 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 * The verifier doesn't have access to sk[i]*G, but can construct * it using the xonly public key |P[i]| as follows: * - * sk[i]*G = d_tweak*d_agg*d[i]*x[i]*G - * = d_tweak*d_agg*d[i]*P[i] - * = d_tweak*d_agg*|P[i]| + * sk[i]*G = d_agg*d[i]*x[i]*G + * = d_agg*d[i]*P[i] + * = d_agg*|P[i]| * - * The if condition below is true whenever d_tweak*d_agg is - * negative (again, see the explanation in musig_partial_sign). In - * this case, the verifier negates e which will have the same end - * result as negating |P[i]|, since they are multiplied later anyway. + * The if condition below is true whenever d_agg is negative (again, see the + * explanation in musig_partial_sign). In this case, the verifier negates e + * which will have the same end result as negating |P[i]|, since they are + * multiplied later anyway. */ if (secp256k1_fe_is_odd(&cache_i.pk.y) != cache_i.internal_key_parity) { diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 01d1a9c8..ece8a96e 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -254,37 +254,42 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros68, sizeof(full_agg_pk)) == 0); /** Tweaking **/ - ecount = 0; { - secp256k1_pubkey tmp_output_pk; - secp256k1_musig_keyagg_cache tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - /* Reset keyagg_cache */ - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, NULL, tweak) == 0); - CHECK(ecount == 1); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, NULL) == 0); - CHECK(ecount == 2); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); - CHECK(ecount == 2); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - /* Uninitialized keyagg_cache */ - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &invalid_keyagg_cache, tweak) == 0); - CHECK(ecount == 3); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + int (*tweak_func[2]) (const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32); + tweak_func[0] = secp256k1_musig_pubkey_ec_tweak_add; + tweak_func[1] = secp256k1_musig_pubkey_xonly_tweak_add; + for (i = 0; i < 2; i++) { + secp256k1_pubkey tmp_output_pk; + secp256k1_musig_keyagg_cache tmp_keyagg_cache = keyagg_cache; + ecount = 0; + CHECK((*tweak_func[i])(ctx, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + /* Reset keyagg_cache */ + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(none, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(sign, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, NULL, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, NULL, tweak) == 0); + CHECK(ecount == 1); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + /* Uninitialized keyagg_cache */ + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &invalid_keyagg_cache, tweak) == 0); + CHECK(ecount == 3); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + } } /** Session creation **/ @@ -851,7 +856,8 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigne CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), agg_pk) == 1); } -/* Create aggregate public key P[0], tweak multiple times and test signing. */ +/* Create aggregate public key P[0], tweak multiple times (using xonly and + * ordinary tweaking) and test signing. */ void musig_tweak_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; secp256k1_xonly_pubkey pk[2]; @@ -871,22 +877,35 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { /* Compute P0 = keyagg(pk0, pk1) and test signing for it */ CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &P_xonly[0], &keyagg_cache, pk_ptr, 2) == 1); musig_tweak_test_helper(&P_xonly[0], sk[0], sk[1], &keyagg_cache); + CHECK(secp256k1_musig_pubkey_get(ctx, &P[0], &keyagg_cache)); - /* Compute Pi = |Pj| + tweaki*G where where j = i-1 and try signing for - * that key. The function |.| normalizes the point to have an even - * X-coordinate. This results in ordinary "xonly-tweaking". */ + /* Compute Pi = f(Pj) + tweaki*G where where j = i-1 and try signing for + * that key. If xonly is set to true, the function f is normalizes the input + * point to have an even X-coordinate ("xonly-tweaking"). + * Otherwise, the function f is the identity function. */ for (i = 1; i < N_TWEAKS; i++) { unsigned char tweak[32]; int P_parity; - unsigned char P_serialized[32]; + int xonly = secp256k1_testrand_bits(1); secp256k1_testrand256(tweak); - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + if (xonly) { + CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + } else { + CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + } CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &P_xonly[i], &P_parity, &P[i])); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P_xonly[i])); /* Check that musig_pubkey_tweak_add produces same result as - * xonly_pubkey_tweak_add. */ - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1); + * xonly_pubkey_tweak_add or ec_pubkey_tweak_add. */ + if (xonly) { + unsigned char P_serialized[32]; + CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P_xonly[i])); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1); + } else { + secp256k1_pubkey tmp_key = P[i-1]; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &tmp_key, tweak)); + CHECK(memcmp(&tmp_key, &P[i], sizeof(tmp_key)) == 0); + } /* Test signing for P[i] */ musig_tweak_test_helper(&P_xonly[i], sk[0], sk[1], &keyagg_cache); } @@ -1145,7 +1164,7 @@ void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, } CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, keyagg_cache, pk_ptr, 3) == 1); if (tweak != NULL) { - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); } memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); memcpy(&secnonce.data[4], secnonce_bytes, sizeof(secnonce.data) - 4); From 57a17929fc0056efb5436a6001597d656591e1ad Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 26 Oct 2021 15:52:10 +0000 Subject: [PATCH 150/381] musig: add ordinary and xonly tweaking to the example --- examples/musig.c | 68 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 7cd664af..1106d874 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -52,14 +52,51 @@ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_s return 1; } +/* Tweak the pubkey corresponding to the provided keyagg cache, update the cache + * and return the tweaked aggregate pk. */ +int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *cache) { + secp256k1_pubkey output_pk; + unsigned char ordinary_tweak[32] = "this could be a BIP32 tweak...."; + unsigned char xonly_tweak[32] = "this could be a taproot tweak.."; + + + /* Ordinary tweaking which, for example, allows deriving multiple child + * public keys from a single aggregate key using BIP32 */ + if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, ordinary_tweak)) { + return 0; + } + /* Note that we did not provided an output_pk argument, because the + * resulting pk is also saved in the cache and so if one is just interested + * in signing the output_pk argument is unnecessary. On the other hand, if + * one is not interested in signing, the same output_pk can be obtained by + * calling `secp256k1_musig_pubkey_get` right after key aggregation to get + * the full pubkey and then call `secp256k1_ec_pubkey_tweak_add`. */ + + /* Xonly tweaking which, for example, allows creating taproot commitments */ + if (!secp256k1_musig_pubkey_xonly_tweak_add(ctx, &output_pk, cache, xonly_tweak)) { + return 0; + } + /* Note that if we wouldn't care about signing, we can arrive at the same + * output_pk by providing the untweaked public key to + * `secp256k1_xonly_pubkey_tweak_add` (after converting it to an xonly pubkey + * if necessary with `secp256k1_xonly_pubkey_from_pubkey`). */ + + /* Now we convert the output_pk to an xonly pubkey to allow to later verify + * the Schnorr signature against it. For this purpose we can ignore the + * `pk_parity` output argument; we would need it if we would have to open + * the taproot commitment. */ + if (!secp256k1_xonly_pubkey_from_pubkey(ctx, agg_pk, NULL, &output_pk)) { + return 0; + } + return 1; +} + /* Sign a message hash with the given key pairs and store the result in sig */ -int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, unsigned char *sig64) { +int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const secp256k1_musig_keyagg_cache *cache, const unsigned char *msg32, unsigned char *sig64) { int i; - const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS]; const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS]; const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS]; /* The same for all signers */ - secp256k1_musig_keyagg_cache cache; secp256k1_musig_session session; for (i = 0; i < N_SIGNERS; i++) { @@ -86,7 +123,6 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) { return 0; } - pubkeys[i] = &signer[i].pubkey; pubnonces[i] = &signer[i].pubnonce; } /* Communication round 1: A production system would exchange public nonces @@ -94,21 +130,18 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st for (i = 0; i < N_SIGNERS; i++) { secp256k1_musig_aggnonce agg_pubnonce; - /* Create aggregate pubkey, aggregate nonce and initialize signer data */ - if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) { - return 0; - } + /* Create aggregate nonce and initialize the session */ if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) { return 0; } - if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, &cache, NULL)) { + if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache, NULL)) { return 0; } /* partial_sign will clear the secnonce by setting it to 0. That's because * you must _never_ reuse the secnonce (or use the same session_id to * create a secnonce). If you do, you effectively reuse the nonce and * leak the secret key. */ - if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, &cache, &session)) { + if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, cache, &session)) { return 0; } partial_sigs[i] = &signer[i].partial_sig; @@ -127,7 +160,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st * fine to first verify the aggregate sig, and only verify the individual * sigs if it does not work. */ - if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, &cache, &session)) { + if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, cache, &session)) { return 0; } } @@ -141,6 +174,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st struct signer signers[N_SIGNERS]; const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; secp256k1_xonly_pubkey agg_pk; + secp256k1_musig_keyagg_cache cache; unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; unsigned char sig[64]; @@ -156,13 +190,21 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st } printf("ok\n"); printf("Combining public keys..."); - if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, NULL, pubkeys_ptr, N_SIGNERS)) { + /* If you just want to aggregate and not sign the cache can be NULL */ + if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pubkeys_ptr, N_SIGNERS)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Tweaking................"); + /* Optionally tweak the aggregate key */ + if (!tweak(ctx, &agg_pk, &cache)) { printf("FAILED\n"); return 1; } printf("ok\n"); printf("Signing message........."); - if (!sign(ctx, signer_secrets, signers, msg, sig)) { + if (!sign(ctx, signer_secrets, signers, &cache, msg, sig)) { printf("FAILED\n"); return 1; } From 8088eddc534cbbb89dd5f892828c4013416c4f2b Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Tue, 4 Jan 2022 12:09:43 -0800 Subject: [PATCH 151/381] musig: add test vector for ordinary (non xonly) tweaking --- src/modules/musig/tests_impl.h | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ece8a96e..70512360 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -1143,7 +1143,7 @@ void musig_test_vectors_noncegen(void) { } } -void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char *tweak, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) { +void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char *tweak, int xonly_tweak, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) { secp256k1_keypair signer_keypair; secp256k1_musig_secnonce secnonce; secp256k1_xonly_pubkey pk[3]; @@ -1164,7 +1164,11 @@ void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, } CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, keyagg_cache, pk_ptr, 3) == 1); if (tweak != NULL) { - CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + if (xonly_tweak) { + CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + } else { + CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + } } memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); memcpy(&secnonce.data[4], secnonce_bytes, sizeof(secnonce.data) - 4); @@ -1243,7 +1247,7 @@ void musig_test_vectors_sign(void) { 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 0); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 0); /* TODO: remove when test vectors are not expected to change anymore */ /* int k, l; */ /* printf("const unsigned char sig_expected[32] = {\n"); */ @@ -1272,7 +1276,7 @@ void musig_test_vectors_sign(void) { 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, 0x7C, 0xEC, 0xA7, 0xC8, 0x42, 0x42, 0x95, 0xEA, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 1); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 1); /* Check that the description of the test vector is correct */ CHECK(musig_test_pk_parity(&keyagg_cache) == 0); CHECK(musig_test_is_second_pk(&keyagg_cache, sk)); @@ -1288,7 +1292,7 @@ void musig_test_vectors_sign(void) { 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, 0xA4, 0xA3, 0xD1, 0xBC, 0xAA, 0xBC, 0x75, 0x17, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 2); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 2); /* Check that the description of the test vector is correct */ CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(fin_nonce_parity == 0); @@ -1296,7 +1300,7 @@ void musig_test_vectors_sign(void) { CHECK(memcmp(sig, sig_expected, 32) == 0); } { - /* This is a test that includes a public key tweak. */ + /* This is a test that includes an xonly public key tweak. */ const unsigned char sig_expected[32] = { 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, 0x04, 0xA2, @@ -1309,13 +1313,34 @@ void musig_test_vectors_sign(void) { 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, NULL, pk, 2); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, 1, NULL, pk, 2); CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 1); CHECK(memcmp(sig, sig_expected, 32) == 0); } + { + /* This is a test that includes an ordinary public key tweak. */ + const unsigned char sig_expected[32] = { + 0x78, 0x40, 0x8D, 0xDC, 0xAB, 0x48, 0x13, 0xD1, + 0x39, 0x4C, 0x97, 0xD4, 0x93, 0xEF, 0x10, 0x84, + 0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD, + 0x7B, 0xC5, 0x99, 0x16, 0x44, 0xE4, 0x4D, 0xDD, + }; + const unsigned char tweak[32] = { + 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, + 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, + 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, + 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, + }; + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, 0, NULL, pk, 2); + + CHECK(musig_test_pk_parity(&keyagg_cache) == 1); + CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(fin_nonce_parity == 0); + CHECK(memcmp(sig, sig_expected, 32) == 0); + } { /* This is a test that includes an adaptor. */ const unsigned char sig_expected[32] = { @@ -1332,7 +1357,7 @@ void musig_test_vectors_sign(void) { }; secp256k1_pubkey pub_adaptor; CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor) == 1); - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, &pub_adaptor, pk, 2); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, &pub_adaptor, pk, 2); CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); From b8f4e75d89071515231be03727d47a34b1c12cab Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 1 Jan 2022 20:49:39 +0000 Subject: [PATCH 152/381] musig-spec: move to doc directory --- {src/modules/musig => doc}/musig-spec.mediawiki | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src/modules/musig => doc}/musig-spec.mediawiki (100%) diff --git a/src/modules/musig/musig-spec.mediawiki b/doc/musig-spec.mediawiki similarity index 100% rename from src/modules/musig/musig-spec.mediawiki rename to doc/musig-spec.mediawiki From e0bb2d7009eebe2b25dfe977fe3534ad507251ab Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 29 Dec 2021 19:40:18 +0000 Subject: [PATCH 153/381] musig-spec: improve KeyAgg description It's easier to identify a signer with a public key instead of an index in KeyAggCoef because it doesn't force the signer to know its index. --- doc/musig-spec.mediawiki | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 09ebd6c9..f94e5a7a 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -22,10 +22,11 @@ This document is licensed under the 2-clause BSD license. === Design === -* A function for sorting public keys allows to aggregate keys independent of the (initial) order. +* The output of the ''KeyAgg'' algorithm depends on the order of the input public keys. +* It is possible to sort the public keys with the ''KeySort'' algorithm before key aggregation to ensure the same output, independent of the (initial) order. * The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting. -* The second unique key in the pubkey list gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). - +* The second unique key in the pubkey list given to ''KeyAgg'' (as well as any keys identical to this key) gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). +* The public key inputs are serialized using x-only (32 byte) instead of compressed (33 byte) serialization. The reason for this is that as x-only keys are becoming more common, the full key may not be available. === Specification === @@ -44,8 +45,9 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. ** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. ** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. -** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. +** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. +** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. ** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..p-1'', returns the point ''P'' for which ''x(P) = x'' Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: *** Let ''c = x3 + 7 mod p''. @@ -67,34 +69,39 @@ The algorithm ''KeySort(pk1..u)'' is defined as: ==== Key Aggregation ==== Input: -* The number ''u'' of signatures with ''0 < u < 2^32'' +* The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays The algorithm ''KeyAgg(pk1..u)'' is defined as: * For ''i = 1 .. u'': -** Let ''ai = KeyAggCoeff(pk1..u, i)''. +** Let ''ai = KeyAggCoeff(pk1..u, pki)''. ** Let ''Pi = lift_x(int(pki))''; fail if it fails. -* Let ''S = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' -* Fail if ''is_infinite(S)''. -* Return ''bytes(S)''. +* Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' +* Fail if ''is_infinite(Q)''. +* Return ''bytes(Q)''. The algorithm ''HashKeys(pk1..u)'' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' -The algorithm ''IsSecond(pk1..u, i)'' is defined as: +The algorithm ''IsSecond(pk1..u, pk')'' is defined as: * For ''j = 1 .. u'': ** If ''pkj ≠ pk1'': -*** Return ''true'' if ''pkj = pki'', otherwise return ''false''. +*** Return ''true'' if ''pkj = pk' '', otherwise return ''false''. * Return ''false'' -The algorithm ''KeyAggCoeff(pk1..u, i)'' is defined as: +The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as: * Let ''L = HashKeys(pk1..u)''. -* Return 1 if ''IsSecond(pk1..u, i)'', otherwise return ''int(hashKeyAgg coefficient(L || pki) mod n''. +* If ''IsSecond(pk1..u, pk')'': +** Return 1 +* Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' == Applications == == Test Vectors and Reference Code == +There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. +Search for the ''musig_test_vectors_keyagg'' function. + == Footnotes == From 3c122d07807dfaea6457d8a48ba4adc7a15f1182 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 5 Jan 2022 22:53:58 +0000 Subject: [PATCH 154/381] musig-spec: improve definition of lift_x --- doc/musig-spec.mediawiki | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index f94e5a7a..28db422e 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -48,12 +48,14 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. ** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. ** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. -** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..p-1'', returns the point ''P'' for which ''x(P) = x'' - Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: +** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' + Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: +*** Fail if ''x > p-1''. *** Let ''c = x3 + 7 mod p''. -*** Let ''y = c(p+1)/4 mod p''. -*** Fail if ''c ≠ y2 mod p''. -*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise. +*** Let ''y' = c(p+1)/4 mod p''. +*** Fail if ''c ≠ y'2 mod p''. +*** Let ''y = y' '' if ''y' mod 2 = 0'', otherwise let ''y = p - y' ''. +*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y''. ** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. From 4824220bb71102064babf832372f4e5ae43ef16f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 5 Jan 2022 22:54:24 +0000 Subject: [PATCH 155/381] musig-spec: describe NonceGen, NonceAgg, Sign,PartialSig{Verify,Agg} --- doc/musig-spec.mediawiki | 139 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 4 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 28db422e..7d5fca5d 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -27,6 +27,7 @@ This document is licensed under the 2-clause BSD license. * The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting. * The second unique key in the pubkey list given to ''KeyAgg'' (as well as any keys identical to this key) gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). * The public key inputs are serialized using x-only (32 byte) instead of compressed (33 byte) serialization. The reason for this is that as x-only keys are becoming more common, the full key may not be available. +* The public nonces are serialized in compressed format (33 bytes). We accept the small overhead compared to x-only serialization to avoid complicating the specification. === Specification === @@ -46,7 +47,7 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. ** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. -** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. +** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is a byte that is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. ** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. ** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: @@ -56,6 +57,8 @@ The following conventions are used, with constants as defined for [https://www.s *** Fail if ''c ≠ y'2 mod p''. *** Let ''y = y' '' if ''y' mod 2 = 0'', otherwise let ''y = p - y' ''. *** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y''. +** The function ''point(x)'', where ''x'' is a 32-byte array ("x-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. +** The function ''pointc(x)'', where ''x'' is a 33-byte array (compressed serialization), sets ''P = lift_x(int(x[1:33]))'' and fails if that fails. If ''x[0] = 2'' it returns ''P'' and if ''x[0] = 3'' it returns ''-P''. Otherwise, it fails. ** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. @@ -75,12 +78,16 @@ Input: * The public keys ''pk1..u'': ''u'' 32-byte arrays The algorithm ''KeyAgg(pk1..u)'' is defined as: +* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails. +* Return ''bytes(Q)''. + +The algorithm ''KeyAggInternal(pk1..u)'' is defined as: * For ''i = 1 .. u'': ** Let ''ai = KeyAggCoeff(pk1..u, pki)''. -** Let ''Pi = lift_x(int(pki))''; fail if it fails. +** Let ''Pi = point(pki)''; fail if that fails. * Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' * Fail if ''is_infinite(Q)''. -* Return ''bytes(Q)''. +* Return ''Q''. The algorithm ''HashKeys(pk1..u)'' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' @@ -97,12 +104,136 @@ The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as: ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' +==== Nonce Generation ==== + +The algorithm ''NonceGen()'' is defined as: +* Generate two random integers ''k1, k2'' in the range ''1...n-1'' +* Let ''R*1 = k1⋅G, R*2 = k2⋅G'' +* Let ''pubnonce = cbytes(R*1) || cbytes(R*2)'' +* Let ''secnonce = bytes(k1) || bytes(k2)'' +* Return ''secnonce'' and ''pubnonce'' + +==== Nonce Aggregation ==== + +* The number ''u'' of ''pubnonces'' with ''0 < u < 2^32'' +* The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays + +The algorithm ''NonceAgg(pubnonce1..u)'' is defined as: +* For ''i = 1 .. 2'': +** For ''j = 1 .. u'': +*** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails +** Let ''R'i = Ri,1 + Ri,2 + ... + Ri,u'' +** Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' +* Return ''aggnonce = cbytes(R1) || cbytes(R2)'' + +==== Signing ==== + +Input: +* The secret nonce ''secnonce'' that has never been used as input to ''Sign'' before: a 64-byte array +* The secret key ''sk'': a 32-byte array +* The aggregate public nonce ''aggnonce'': a 66-byte array +* The number ''u'' of public keys with ''0 < u < 2^32'' +* The public keys ''pk1..u'': ''u'' 32-byte arrays +* The message ''m'': a 32-byte array + +The algorithm ''Sign(secnonce, sk, aggnonce, pk1..u, m)'' is defined as: +* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails +* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails +* Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' +* Let ''R = R1 + b⋅R2'' +* Fail if ''is_infinite(R)'' +* Let ''k'1 = int(secnonce[0:32]), k'2 = int(secnonce[32:64])'' +* Fail if ''k'i = 0'' or ''k'i ≥ n'' for ''i = 1..2'' +* Let ''k1 = k'1, k2 = k'2 '' if ''has_even_y(R)'', otherwise let ''k1 = n - k'1, k2 = n - k2'' +* Let ''d' = int(sk)'' +* Fail if ''d' = 0'' or ''d' ≥ n'' +* Let ''P = d'⋅G'' +* Let ''d = n - d' '' if ''has_even_y(P) `XOR` has_even_y(Q)'', otherwise let ''d = d' '' +* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' +* Let ''mu = KeyAggCoeff(pk1..u, bytes(P))'' +* Let ''s = (k1 + b⋅k2 + e⋅mu⋅d) mod n'' +* Let ''psig = bytes(s)'' +* Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' +* If ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, bytes(P), m)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. +* Return partial signature ''psig + +==== Partial Signature Verification ==== + +Input: +* The partial signature ''psig'': a 32-byte array +* The number ''u'' of public nonces and public keys with ''0 < u < 2^32'' +* The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays +* The public keys ''pk1..u'': ''u'' 32-byte arrays +* The message ''m'': a 32-byte array +* The index of the signer ''i'' in the public nonces and public keys with ''0 < i <= u'' + +The algorithm ''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)'' is defined as: +* Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails +* Run ''PartialSigVerifyInternal(psig, pubnoncei, aggnonce, pk1..u, pki, m)'' +* Return success iff no failure occurred before reaching this point. + +===== PartialSigVerifyInternal ===== + +Input: +* The partial signature ''psig'': a 32-byte array +* The public nonce of the signer ''pubnonce'': a 66-byte array +* The aggregate public nonce ''aggnonce'': a 66-byte array +* The number ''u'' of public keys with ''0 < u < 2^32'' +* The public keys ''pk1..u'': ''u'' 32-byte arrays +* The public key of the signer ''pk*'' (in ''pk1..u''): a 32-byte array +* The message ''m'': a 32-byte array + +The algorithm ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, pk*, m)'' is defined as: +* Let ''s = int(psig)''; fail if ''s ≥ n'' +* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails +* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails +* Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' +* Let ''R = R1 + b⋅R2'' +* Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' +* Let ''R*' = R*1 + b⋅R*2'' +* Let ''R* = R*' '' if ''has_even_y(R)'', otherwise let ''R* = -R*' '' +* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' +* Let ''mu = KeyAggCoeff(pk1..u, pk*)'' +* Let ''P' = point(pk*)''; fail if that fails +* Let ''P = P' '' if ''has_even_y(Q)'', otherwise let ''P = -P' '' +* Fail if ''s⋅G ≠ R* + e⋅mu⋅P'' +* Return success iff no failure occurred before reaching this point. + +==== Partial Signature Aggregation ==== + +Input: +* The final nonce ''R'' as created during ''Sign'' or ''PartialSigVerify'': a point +* The number ''u'' of signatures with ''0 < u < 2^32'' +* The partial signatures ''sig1..u'': ''u'' 32-byte arrays + +The algorithm ''SigAgg(R, sig1..u)'' is defined as: +* For ''i = 1 .. u'': +** Let ''si = int(sigi)''; fail if ''si ≥ n''. +* Let ''s = s1 + ... + su mod n'' +* Return ''sig = ''bytes(R) || bytes(s)'' + +=== Signing Flow === + +Note that this specification unnecessarily recomputes intermediary values (such as the aggregate public key) that can be cached in real implementations. + +There are multiple ways to use above algorithms and arrive at a final Schnorr signature. +One of them can be described as follows: +The signers ''1'' to ''n'' each run ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. +Every signer sends its public key and ''pubnonce'' to every other signer and all signers agree on a single message to sign. +Then, the signers run ''NonceAgg'' and ''Sign'' with their secret signing key and ''secnonce''. +They send the resulting partial signature to every other signer and combine them with the ''SigAgg'' algorithm. + +''IMPORTANT'': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. +Otherwise, it is possible to extract the secret signing key from the partial signatures. +An implementation may invalidate the secnonce argument after ''Sign'' to avoid any reuse. +Avoiding reuse also implies that the ''NonceGen'' algorithm must compute unbiased, uniformly random values ''k1'' and ''k2''. + == Applications == == Test Vectors and Reference Code == There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. -Search for the ''musig_test_vectors_keyagg'' function. +Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' functions. == Footnotes == From 69b392f3cbd4dbff953ec8f2ff44f6a8f612b661 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 31 Dec 2021 20:56:49 +0000 Subject: [PATCH 156/381] musig: move explanation for aggnonce=inf to spec --- doc/musig-spec.mediawiki | 13 +++++++++++++ src/modules/musig/session_impl.h | 16 +--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 7d5fca5d..7a6eea78 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -126,6 +126,19 @@ The algorithm ''NonceAgg(pubnonce1..u)'' is defined as: ** Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' * Return ''aggnonce = cbytes(R1) || cbytes(R2)'' +===== Note on ''is_infinite(R'i)'' ===== + +If ''is_infinite(R'i)'' there is at least one dishonest signer (except with negligible probability). +If we would fail here, we will never be able to determine who it is. +Therefore, we should continue such that the culprit is revealed when collecting and verifying partial signatures. +However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. +Instead, we set the aggregate nonce to some arbitrary point, the generator. + +This modification does not affect the security of the scheme. +''NonceAgg'' (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). +Thus in the multi-signature security game (EUF-CMA), we can consider ''NonceAgg'' to be performed by the adversary (rather than the challenger) without loss of generality. +The modification changes neither the behavior of the EUF-CMA challenger nor the condition required to win the security game (the adversary still has to output a valid forgery according to the unmodified MuSig2* scheme). Since we've already proved that MuSig2* is secure against an arbitrary adversary, we can conclude that the modified scheme is still secure. + ==== Signing ==== Input: diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 7cdcebe3..1231b2fc 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -362,21 +362,7 @@ int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggn } for (i = 0; i < 2; i++) { if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { - /* There must be at least one dishonest signer. If we would return 0 - here, we will never be able to determine who it is. Therefore, we - should continue such that the culprit is revealed when collecting - and verifying partial signatures. - - However, dealing with the point at infinity (loading, - de-/serializing) would require a lot of extra code complexity. - Instead, we set the aggregate nonce to some arbitrary point (the - generator). This is secure, because it only restricts the - abilities of the attacker: an attacker that forces the sum of - nonces to be infinity by sending some maliciously generated nonce - pairs can be turned into an attacker that forces the sum to be - the generator (by simply adding the generator to one of the - malicious nonces), and this does not change the winning condition - of the EUF-CMA game. */ + /* Set to G according to the specification */ aggnonce_pt[i] = secp256k1_ge_const_g; } else { secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); From aa1acb4bd101c83c6977961a62666f92535581e6 Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Wed, 26 Jan 2022 18:24:39 -0800 Subject: [PATCH 157/381] musig-spec: improve security argument for handling infinity Co-authored-by: Tim Ruffing --- doc/musig-spec.mediawiki | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 7a6eea78..198982ad 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -129,15 +129,22 @@ The algorithm ''NonceAgg(pubnonce1..u)'' is defined as: ===== Note on ''is_infinite(R'i)'' ===== If ''is_infinite(R'i)'' there is at least one dishonest signer (except with negligible probability). -If we would fail here, we will never be able to determine who it is. -Therefore, we should continue such that the culprit is revealed when collecting and verifying partial signatures. -However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. -Instead, we set the aggregate nonce to some arbitrary point, the generator. +If we fail here, we will never be able to determine who it is. +Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. -This modification does not affect the security of the scheme. -''NonceAgg'' (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). -Thus in the multi-signature security game (EUF-CMA), we can consider ''NonceAgg'' to be performed by the adversary (rather than the challenger) without loss of generality. -The modification changes neither the behavior of the EUF-CMA challenger nor the condition required to win the security game (the adversary still has to output a valid forgery according to the unmodified MuSig2* scheme). Since we've already proved that MuSig2* is secure against an arbitrary adversary, we can conclude that the modified scheme is still secure. +However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. +Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: +* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). +* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). + +The entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). +In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. + +The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". +This modification only depends on the publicly available ''aggnonce''. +Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. + +We conclude that these two modifications preserve the security of the MuSig2* scheme. ==== Signing ==== From f0edc9075539bddff2726065271c86fd7b480c9f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 4 Feb 2022 10:54:11 +0000 Subject: [PATCH 158/381] musig: fix number of tweaks in tweak_test --- src/modules/musig/tests_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 70512360..aab68615 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -883,7 +883,7 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { * that key. If xonly is set to true, the function f is normalizes the input * point to have an even X-coordinate ("xonly-tweaking"). * Otherwise, the function f is the identity function. */ - for (i = 1; i < N_TWEAKS; i++) { + for (i = 1; i <= N_TWEAKS; i++) { unsigned char tweak[32]; int P_parity; int xonly = secp256k1_testrand_bits(1); From 5b760cc172ec288ac0b0069cb11ba54c73e01cb7 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 7 Feb 2022 13:58:23 +0000 Subject: [PATCH 159/381] musig-spec: consistently call partial sigs psig --- doc/musig-spec.mediawiki | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 198982ad..239f2c48 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -175,7 +175,7 @@ The algorithm ''Sign(secnonce, sk, aggnonce, pk1..u, m)'' is defined * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' * If ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, bytes(P), m)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. -* Return partial signature ''psig +* Return partial signature ''psig'' ==== Partial Signature Verification ==== @@ -185,7 +185,7 @@ Input: * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays * The public keys ''pk1..u'': ''u'' 32-byte arrays * The message ''m'': a 32-byte array -* The index of the signer ''i'' in the public nonces and public keys with ''0 < i <= u'' +* The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' The algorithm ''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)'' is defined as: * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails @@ -224,11 +224,11 @@ The algorithm ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u1..u'': ''u'' 32-byte arrays +* The partial signatures ''psig1..u'': ''u'' 32-byte arrays -The algorithm ''SigAgg(R, sig1..u)'' is defined as: +The algorithm ''PartialSigAgg(R, psig1..u)'' is defined as: * For ''i = 1 .. u'': -** Let ''si = int(sigi)''; fail if ''si ≥ n''. +** Let ''si = int(psigi)''; fail if ''si ≥ n''. * Let ''s = s1 + ... + su mod n'' * Return ''sig = ''bytes(R) || bytes(s)'' @@ -241,7 +241,7 @@ One of them can be described as follows: The signers ''1'' to ''n'' each run ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. Every signer sends its public key and ''pubnonce'' to every other signer and all signers agree on a single message to sign. Then, the signers run ''NonceAgg'' and ''Sign'' with their secret signing key and ''secnonce''. -They send the resulting partial signature to every other signer and combine them with the ''SigAgg'' algorithm. +They send the resulting partial signature to every other signer and combine them with the ''PartialSigAgg'' algorithm. ''IMPORTANT'': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. Otherwise, it is possible to extract the secret signing key from the partial signatures. From 628d52c7186af72e9bd8fe4b89c1befdbaec2dfd Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 21 Mar 2022 19:10:50 +0000 Subject: [PATCH 160/381] musig-spec: fix title/abstract and make algo names bold --- doc/musig-spec.mediawiki | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 239f2c48..b3f967bb 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,5 +1,5 @@
-  Title: MuSig Key Aggregation
+  Title: MuSig
   Author:
   Status: Draft
   License: BSD-2-Clause
@@ -10,7 +10,7 @@
 
 === Abstract ===
 
-This document describes MuSig Key Aggregation in libsecp256k1-zkp.
+This document proposes a standard for the MuSig2 protocol that supports ''tweaking'' and outputs [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures.
 
 === Copyright ===
 
@@ -65,10 +65,10 @@ The following conventions are used, with constants as defined for [https://www.s
 ==== Key Sorting ====
 
 Input:
-* The number ''u'' of signatures with ''0 < u < 2^32''
+* The number ''u'' of public keys with ''0 < u < 2^32''
 * The public keys ''pk1..u'': ''u'' 32-byte arrays
 
-The algorithm ''KeySort(pk1..u)'' is defined as:
+The algorithm '''''KeySort(pk1..u)''''' is defined as:
 * Return ''pk1..u'' sorted in lexicographical order.
 
 ==== Key Aggregation ====
@@ -77,11 +77,11 @@ Input:
 * The number ''u'' of public keys with ''0 < u < 2^32''
 * The public keys ''pk1..u'': ''u'' 32-byte arrays
 
-The algorithm ''KeyAgg(pk1..u)'' is defined as:
+The algorithm '''''KeyAgg(pk1..u)''''' is defined as:
 * Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails.
 * Return ''bytes(Q)''.
 
-The algorithm ''KeyAggInternal(pk1..u)'' is defined as:
+The algorithm '''''KeyAggInternal(pk1..u)''''' is defined as:
 * For ''i = 1 .. u'':
 ** Let ''ai = KeyAggCoeff(pk1..u, pki)''.
 ** Let ''Pi = point(pki)''; fail if that fails.
@@ -89,16 +89,16 @@ The algorithm ''KeyAggInternal(pk1..u)'' is defined as:
 * Fail if ''is_infinite(Q)''.
 * Return ''Q''.
 
-The algorithm ''HashKeys(pk1..u)'' is defined as:
+The algorithm '''''HashKeys(pk1..u)''''' is defined as:
 * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)''
 
-The algorithm ''IsSecond(pk1..u, pk')'' is defined as:
+The algorithm '''''IsSecond(pk1..u, pk')''''' is defined as:
 * For ''j = 1 .. u'':
 ** If ''pkj ≠ pk1'':
 *** Return ''true'' if ''pkj = pk' '', otherwise return ''false''.
 * Return ''false''
 
-The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as:
+The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as:
 * Let ''L = HashKeys(pk1..u)''.
 * If ''IsSecond(pk1..u, pk')'':
 ** Return 1
@@ -106,7 +106,7 @@ The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as:
 
 ==== Nonce Generation ====
 
-The algorithm ''NonceGen()'' is defined as:
+The algorithm '''''NonceGen()''''' is defined as:
 * Generate two random integers ''k1, k2'' in the range ''1...n-1''
 * Let ''R*1 = k1⋅G, R*2 = k2⋅G''
 * Let ''pubnonce = cbytes(R*1) || cbytes(R*2)''
@@ -118,7 +118,7 @@ The algorithm ''NonceGen()'' is defined as:
 * The number ''u'' of ''pubnonces'' with ''0 < u < 2^32''
 * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays
 
-The algorithm ''NonceAgg(pubnonce1..u)'' is defined as:
+The algorithm '''''NonceAgg(pubnonce1..u)''''' is defined as:
 * For ''i = 1 .. 2'':
 ** For ''j = 1 .. u'':
 *** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails
@@ -156,7 +156,7 @@ Input:
 * The public keys ''pk1..u'': ''u'' 32-byte arrays
 * The message ''m'': a 32-byte array
 
-The algorithm ''Sign(secnonce, sk, aggnonce, pk1..u, m)'' is defined as:
+The algorithm '''''Sign(secnonce, sk, aggnonce, pk1..u, m)''''' is defined as:
 * Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails
 * Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails
 * Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n''
@@ -187,7 +187,7 @@ Input:
 * The message ''m'': a 32-byte array
 * The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u''
 
-The algorithm ''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)'' is defined as:
+The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)''''' is defined as:
 * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails
 * Run ''PartialSigVerifyInternal(psig, pubnoncei, aggnonce, pk1..u, pki, m)''
 * Return success iff no failure occurred before reaching this point.
@@ -203,7 +203,7 @@ Input:
 * The public key of the signer ''pk*'' (in ''pk1..u''): a 32-byte array
 * The message ''m'': a 32-byte array
 
-The algorithm ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, pk*, m)'' is defined as:
+The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, pk*, m)''''' is defined as:
 * Let ''s = int(psig)''; fail if ''s ≥ n''
 * Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails
 * Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails
@@ -226,7 +226,7 @@ Input:
 * The number ''u'' of signatures with ''0 < u < 2^32''
 * The partial signatures ''psig1..u'': ''u'' 32-byte arrays
 
-The algorithm ''PartialSigAgg(R, psig1..u)'' is defined as:
+The algorithm '''''PartialSigAgg(R, psig1..u)''''' is defined as:
 * For ''i = 1 .. u'':
 ** Let ''si = int(psigi)''; fail if ''si ≥ n''.
 * Let ''s = s1 + ... + su mod n''

From 3aec4332b59d496b24ecca42d076f96d36121908 Mon Sep 17 00:00:00 2001
From: Jonas Nick 
Date: Mon, 21 Mar 2022 19:49:43 +0000
Subject: [PATCH 161/381] musig-spec: move remarks on spec below specification
 section

We will need more of these explanations and it's better if they do not interfere
the specification section. The remarks section is intended for content that's
not required for implementing the spec.
---
 doc/musig-spec.mediawiki | 44 +++++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki
index b3f967bb..d042363f 100644
--- a/doc/musig-spec.mediawiki
+++ b/doc/musig-spec.mediawiki
@@ -123,29 +123,9 @@ The algorithm '''''NonceAgg(pubnonce1..u)''''' is defined as:
 ** For ''j = 1 .. u'':
 *** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails
 ** Let ''R'i = Ri,1 + Ri,2 + ... + Ri,u''
-** Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G''
+** 
Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' (see [[#dealing-with-infinity-in-nonce-aggregation|Dealing with Infinity in Nonce Aggregation]]) * Return ''aggnonce = cbytes(R1) || cbytes(R2)'' -===== Note on ''is_infinite(R'i)'' ===== - -If ''is_infinite(R'i)'' there is at least one dishonest signer (except with negligible probability). -If we fail here, we will never be able to determine who it is. -Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. - -However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. -Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: -* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). -* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). - -The entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). -In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. - -The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". -This modification only depends on the publicly available ''aggnonce''. -Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. - -We conclude that these two modifications preserve the security of the MuSig2* scheme. - ==== Signing ==== Input: @@ -248,6 +228,28 @@ Otherwise, it is possible to extract the secret signing key from the partial sig An implementation may invalidate the secnonce argument after ''Sign'' to avoid any reuse. Avoiding reuse also implies that the ''NonceGen'' algorithm must compute unbiased, uniformly random values ''k1'' and ''k2''. +=== Remarks on Security and Correctness === + +==== Dealing with Infinity in Nonce Aggregation ==== + +If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). +If we fail here, we will never be able to determine who it is. +Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. + +However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. +Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: +* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). +* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). + +The entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). +In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. + +The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". +This modification only depends on the publicly available ''aggnonce''. +Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. + +We conclude that these two modifications preserve the security of the MuSig2* scheme. + == Applications == == Test Vectors and Reference Code == From fb060a0c4e36486fed4d1981b2314949b6a3fbb8 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 7 Feb 2022 13:14:26 +0000 Subject: [PATCH 162/381] musig-spec: add Session Context to simplify sign/verify/sigagg Besides reducing the number of arguments, this also removes the R argument from PartialSigAgg which was not defined precisely: * The final nonce ''R'' as created during ''Sign'' or ''PartialSigVerify'': a point Moreover, this paves the way for adding the tweaking, which requires PartialSigAgg to also have access to challenge e and can now be easily computed from the Session Context. --- doc/musig-spec.mediawiki | 61 ++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index d042363f..5f0f72ff 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -60,6 +60,8 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''point(x)'', where ''x'' is a 32-byte array ("x-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. ** The function ''pointc(x)'', where ''x'' is a 33-byte array (compressed serialization), sets ''P = lift_x(int(x[1:33]))'' and fails if that fails. If ''x[0] = 2'' it returns ''P'' and if ''x[0] = 3'' it returns ''-P''. Otherwise, it fails. ** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. +* Other: +** Tuples are written by listing the elements within parentheses and separated by commas. For example, ''(2, 3, 1)'' is a tuple. ==== Key Sorting ==== @@ -126,35 +128,51 @@ The algorithm '''''NonceAgg(pubnonce1..u)''''' is defined as: **
Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' (see [[#dealing-with-infinity-in-nonce-aggregation|Dealing with Infinity in Nonce Aggregation]]) * Return ''aggnonce = cbytes(R1) || cbytes(R2)'' -==== Signing ==== +==== Session Context ==== -Input: -* The secret nonce ''secnonce'' that has never been used as input to ''Sign'' before: a 64-byte array -* The secret key ''sk'': a 32-byte array +The Session Context is a data structure consisting of the following elements: * The aggregate public nonce ''aggnonce'': a 66-byte array * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays * The message ''m'': a 32-byte array -The algorithm '''''Sign(secnonce, sk, aggnonce, pk1..u, m)''''' is defined as: -* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails +We write "Let ''(aggnonce, u, pk1..u, m) = session_ctx''" to assign names to the elements of a Session Context. + +The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: +* Let ''(aggnonce, u, pk1..u, m) = session_ctx'' * Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails * Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' +* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails * Let ''R = R1 + b⋅R2'' * Fail if ''is_infinite(R)'' +* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' +* Return ''(Q, b, R, e)'' + +The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: +* Let ''(_, u, pk1..u, _) = session_ctx'' +* Return ''KeyAggCoeff(pk1..u, bytes(P))'' + +==== Signing ==== + +Input: +* The secret nonce ''secnonce'' that has never been used as input to ''Sign'' before: a 64-byte array +* The secret key ''sk'': a 32-byte array +* The ''session_ctx'': a [[#session-context|Session Context]] data structure + +The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: +* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''k'1 = int(secnonce[0:32]), k'2 = int(secnonce[32:64])'' * Fail if ''k'i = 0'' or ''k'i ≥ n'' for ''i = 1..2'' * Let ''k1 = k'1, k2 = k'2 '' if ''has_even_y(R)'', otherwise let ''k1 = n - k'1, k2 = n - k2'' * Let ''d' = int(sk)'' * Fail if ''d' = 0'' or ''d' ≥ n'' * Let ''P = d'⋅G'' +* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails * Let ''d = n - d' '' if ''has_even_y(P) `XOR` has_even_y(Q)'', otherwise let ''d = d' '' -* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' -* Let ''mu = KeyAggCoeff(pk1..u, bytes(P))'' * Let ''s = (k1 + b⋅k2 + e⋅mu⋅d) mod n'' * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' -* If ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, bytes(P), m)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. +* If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. * Return partial signature ''psig'' ==== Partial Signature Verification ==== @@ -169,7 +187,8 @@ Input: The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)''''' is defined as: * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails -* Run ''PartialSigVerifyInternal(psig, pubnoncei, aggnonce, pk1..u, pki, m)'' +* Let ''session_ctx = (aggnonce, u, pk1..u, m)'' +* Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. ===== PartialSigVerifyInternal ===== @@ -177,36 +196,30 @@ The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u1..u'': ''u'' 32-byte arrays -* The public key of the signer ''pk*'' (in ''pk1..u''): a 32-byte array -* The message ''m'': a 32-byte array +* The public key of the signer ''pk*'' (in ''pk1..u'' of the session_ctx''): a 32-byte array +* The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, pk*, m)''''' is defined as: +The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''' is defined as: +* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' -* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails -* Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' -* Let ''R = R1 + b⋅R2'' * Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' * Let ''R*' = R*1 + b⋅R*2'' * Let ''R* = R*' '' if ''has_even_y(R)'', otherwise let ''R* = -R*' '' -* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' -* Let ''mu = KeyAggCoeff(pk1..u, pk*)'' * Let ''P' = point(pk*)''; fail if that fails * Let ''P = P' '' if ''has_even_y(Q)'', otherwise let ''P = -P' '' +* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails * Fail if ''s⋅G ≠ R* + e⋅mu⋅P'' * Return success iff no failure occurred before reaching this point. ==== Partial Signature Aggregation ==== Input: -* The final nonce ''R'' as created during ''Sign'' or ''PartialSigVerify'': a point * The number ''u'' of signatures with ''0 < u < 2^32'' * The partial signatures ''psig1..u'': ''u'' 32-byte arrays +* The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigAgg(R, psig1..u)''''' is defined as: +The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: +* Let ''(_, _, R, _) = GetSessionValues(session_ctx)''; fail if that fails * For ''i = 1 .. u'': ** Let ''si = int(psigi)''; fail if ''si ≥ n''. * Let ''s = s1 + ... + su mod n'' From aee0747e38c87a426145c2646e34e77b09b80801 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 9 Feb 2022 10:18:00 +0000 Subject: [PATCH 163/381] musig-spec: add general description of tweaking --- doc/musig-spec.mediawiki | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 5f0f72ff..1bb8edae 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -28,8 +28,9 @@ This document is licensed under the 2-clause BSD license. * The second unique key in the pubkey list given to ''KeyAgg'' (as well as any keys identical to this key) gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). * The public key inputs are serialized using x-only (32 byte) instead of compressed (33 byte) serialization. The reason for this is that as x-only keys are becoming more common, the full key may not be available. * The public nonces are serialized in compressed format (33 bytes). We accept the small overhead compared to x-only serialization to avoid complicating the specification. +* This specification supports signing for ''tweaked'' aggregate public keys. There are two modes of tweaking. ''Ordinary'' tweaking allows deriving child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot tweak. See section [[#tweaking|Tweaking]] below for details. -=== Specification === +=== Notation === The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure schemeAmong other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.. * Lowercase variables represent integers or byte arrays. @@ -47,6 +48,7 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. ** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. +** The function ''with_even_y(P)'', where ''P'' is a point, returns ''P'' if ''is_infinite(P)'' or ''has_even_y(P)''. Otherwise, ''with_even_y(P)'' returns ''-P''. ** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is a byte that is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. ** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. ** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' @@ -63,6 +65,7 @@ The following conventions are used, with constants as defined for [https://www.s * Other: ** Tuples are written by listing the elements within parentheses and separated by commas. For example, ''(2, 3, 1)'' is a tuple. +=== Specification === ==== Key Sorting ==== @@ -243,6 +246,20 @@ Avoiding reuse also implies that the ''NonceGen'' algorithm must compute unbiase === Remarks on Security and Correctness === +==== Tweaking ==== + +This MuSig specification supports two modes of tweaking that correspond to the following algorithms: + +Input: +* ''P'': a point +* The tweak ''t'': an integer with ''0 ≤ t < n '' + +The algorithm '''''OrdinaryTweak(P, t)''''' is defined as: +* Return ''P + t⋅G'' + +The algorithm '''''XonlyTweak(P, t)''''' is defined as: +* Return ''with_even_y(P) + t⋅G'' + ==== Dealing with Infinity in Nonce Aggregation ==== If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). From 633d01add0f259abe638a9f2763685bbaecd527f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 4 Feb 2022 14:10:09 +0000 Subject: [PATCH 164/381] musig-spec: add x-only and ordinary tweaking to musig --- doc/musig-spec.mediawiki | 70 ++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 1bb8edae..df4c26a0 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -81,18 +81,25 @@ The algorithm '''''KeySort(pk1..u)''''' is defined as: Input: * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -The algorithm '''''KeyAgg(pk1..u)''''' is defined as: -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails. +The algorithm '''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +* Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. * Return ''bytes(Q)''. -The algorithm '''''KeyAggInternal(pk1..u)''''' is defined as: +The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: * For ''i = 1 .. u'': ** Let ''ai = KeyAggCoeff(pk1..u, pki)''. ** Let ''Pi = point(pki)''; fail if that fails. -* Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' -* Fail if ''is_infinite(Q)''. -* Return ''Q''. +* Let ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' +* Fail if ''is_infinite(Q0)''. +* Let ''tacc0 = 0'' +* Let ''gacc0 = 1'' +* For ''i = 1 .. v'': +** Let ''(Qi, gacci, tacci) = Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''; fail if that fails +* Return ''(Qv, gaccv, taccv)''. The algorithm '''''HashKeys(pk1..u)''''' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' @@ -109,6 +116,17 @@ The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as: ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' +The algorithm '''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''' is defined as: +* If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': +** Let ''gi-1 = -1 mod n'' +* Else: let ''gi-1 = 1'' +* Let ''ti = int(tweaki)''; fail if ''t ≥ n'' +* Let ''Qi = gi-1⋅Qi-1 + ti⋅G'' +** Fail if ''is_infinite(Qi)'' +* Let ''gacci = gi-1⋅gacci-1 mod n'' +* Let ''tacci = ti + gi-1⋅tacci-1 mod n'' +* Return ''(Qi, gacci, tacci)'' + ==== Nonce Generation ==== The algorithm '''''NonceGen()''''' is defined as: @@ -137,22 +155,25 @@ The Session Context is a data structure consisting of the following elements: * The aggregate public nonce ''aggnonce'': a 66-byte array * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans * The message ''m'': a 32-byte array -We write "Let ''(aggnonce, u, pk1..u, m) = session_ctx''" to assign names to the elements of a Session Context. +We write "Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx''" to assign names to the elements of a Session Context. The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: -* Let ''(aggnonce, u, pk1..u, m) = session_ctx'' -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails +* Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx'' +* Let ''(Q, gaccv, taccv) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails * Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' * Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails * Let ''R = R1 + b⋅R2'' * Fail if ''is_infinite(R)'' * Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' -* Return ''(Q, b, R, e)'' +* Return ''(Q, gaccv, taccv, b, R, e)'' The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: -* Let ''(_, u, pk1..u, _) = session_ctx'' +* Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' * Return ''KeyAggCoeff(pk1..u, bytes(P))'' ==== Signing ==== @@ -163,7 +184,7 @@ Input: * The ''session_ctx'': a [[#session-context|Session Context]] data structure The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: -* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails +* Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''k'1 = int(secnonce[0:32]), k'2 = int(secnonce[32:64])'' * Fail if ''k'i = 0'' or ''k'i ≥ n'' for ''i = 1..2'' * Let ''k1 = k'1, k2 = k'2 '' if ''has_even_y(R)'', otherwise let ''k1 = n - k'1, k2 = n - k2'' @@ -171,7 +192,9 @@ The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: * Fail if ''d' = 0'' or ''d' ≥ n'' * Let ''P = d'⋅G'' * Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Let ''d = n - d' '' if ''has_even_y(P) `XOR` has_even_y(Q)'', otherwise let ''d = d' '' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' +* Let ''d = gv⋅gaccv⋅gp⋅d' '' * Let ''s = (k1 + b⋅k2 + e⋅mu⋅d) mod n'' * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' @@ -185,12 +208,15 @@ Input: * The number ''u'' of public nonces and public keys with ''0 < u < 2^32'' * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans * The message ''m'': a 32-byte array * The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' -The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)''''' is defined as: +The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''' is defined as: * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails -* Let ''session_ctx = (aggnonce, u, pk1..u, m)'' +* Let ''session_ctx = (aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m)'' * Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. @@ -203,13 +229,14 @@ Input: * The ''session_ctx'': a [[#session-context|Session Context]] data structure The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''' is defined as: -* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails +* Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' * Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' * Let ''R*' = R*1 + b⋅R*2'' * Let ''R* = R*' '' if ''has_even_y(R)'', otherwise let ''R* = -R*' '' -* Let ''P' = point(pk*)''; fail if that fails -* Let ''P = P' '' if ''has_even_y(Q)'', otherwise let ''P = -P' '' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''g' = gv⋅gaccv mod n'' +* Let ''P = g'⋅point(pk*)''; fail if that fails * Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails * Fail if ''s⋅G ≠ R* + e⋅mu⋅P'' * Return success iff no failure occurred before reaching this point. @@ -222,15 +249,16 @@ Input: * The ''session_ctx'': a [[#session-context|Session Context]] data structure The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: -* Let ''(_, _, R, _) = GetSessionValues(session_ctx)''; fail if that fails +* Let ''(Q, _, taccv, _, _, R, e) = GetSessionValues(session_ctx)''; fail if that fails * For ''i = 1 .. u'': ** Let ''si = int(psigi)''; fail if ''si ≥ n''. -* Let ''s = s1 + ... + su mod n'' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''s = s1 + ... + su + e⋅gv⋅taccv mod n'' * Return ''sig = ''bytes(R) || bytes(s)'' === Signing Flow === -Note that this specification unnecessarily recomputes intermediary values (such as the aggregate public key) that can be cached in real implementations. +Note that this specification unnecessarily recomputes intermediary values (such as the aggregate and tweaked public key) that can be cached in real implementations. There are multiple ways to use above algorithms and arrive at a final Schnorr signature. One of them can be described as follows: From 57eb6b41671ccbd11f11a2bfb2a4467c78e85e67 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 9 Feb 2022 09:22:40 +0000 Subject: [PATCH 165/381] musig-spec: move description of secret key negation to spec Also fix bug in description that resulted in a wrong definition of t. And rename keyagg coefficient from 'mu' to 'a' since we don't use the term "musig coefficient" anymore and a is what is used in the paper. --- doc/musig-spec.mediawiki | 98 +++++++++++++++++++++++-- src/modules/musig/session_impl.h | 122 ++++--------------------------- 2 files changed, 107 insertions(+), 113 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index df4c26a0..e2a3364a 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -191,11 +191,11 @@ The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: * Let ''d' = int(sk)'' * Fail if ''d' = 0'' or ''d' ≥ n'' * Let ''P = d'⋅G'' -* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails * Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' -* Let ''d = gv⋅gaccv⋅gp⋅d' '' -* Let ''s = (k1 + b⋅k2 + e⋅mu⋅d) mod n'' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +*
Let ''d = gv⋅gaccv⋅gp⋅d' '' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) +* Let ''s = (k1 + b⋅k2 + e⋅a⋅d) mod n'' * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' * If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. @@ -236,9 +236,9 @@ The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, sess * Let ''R* = R*' '' if ''has_even_y(R)'', otherwise let ''R* = -R*' '' * Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' * Let ''g' = gv⋅gaccv mod n'' -* Let ''P = g'⋅point(pk*)''; fail if that fails -* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Fail if ''s⋅G ≠ R* + e⋅mu⋅P'' +*
Let ''P = g'⋅point(pk*)''; fail if that fails (See [[#negation-of-the-public-key-when-partially-verifying|Negation Of The Public Key When Partially Verifying]]) +* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails +* Fail if ''s⋅G ≠ R* + e⋅a⋅P'' * Return success iff no failure occurred before reaching this point. ==== Partial Signature Aggregation ==== @@ -288,6 +288,90 @@ The algorithm '''''OrdinaryTweak(P, t)''''' is defined as: The algorithm '''''XonlyTweak(P, t)''''' is defined as: * Return ''with_even_y(P) + t⋅G'' +==== Negation Of The Secret Key When Signing ==== + +In order to produce a partial signature for an x-only public key that is an aggregate of ''u'' x-only keys and tweaked ''v'' times (x-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. + + +The following public keys arise as intermediate steps in the MuSig protocol: +• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's x-only public key. Defining ''d'i'' to be the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have + ''Pi = with_even_y(d'i⋅G) ''. +• ''Q0'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as + ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu''. +• ''Qi'' as computed in ''Tweak'' for ''1 ≤ i ≤ v'' is the tweaked public key after the ''i''-th tweaking operation. It holds that + ''Qi = f(i-1) + ti⋅G'' for ''i = 1, ..., v'' where + ''f(i) := with_even_y(Qi)'' if ''is_xonly_ti+1'' and + ''f(i) := Qi'' otherwise. + + +The goal is to produce a partial signature corresponding to the output of ''KeyAgg'', i.e., the final (x-only) public key point after ''v'' tweaking operations ''with_even_y(Qv)''. + + +We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. It holds that + ''Pi = gpi⋅d'i⋅G''. + +For ''0 ≤ i ≤ v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''gi'' to ''-1 mod n'' if and only if ''is_xonly_ti+1'' is true and ''Qi'' has an odd Y coordinate. Therefore, we have + ''f(i) = gi⋅Qi'' for ''0 ≤ i ≤ v - 1''. + +Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv'' such that + ''with_even_y(Qv) = gv⋅Qv''. + + + +So, the (x-only) final public key is + ''with_even_y(Qv) + = gv⋅Qv + = gv⋅(f(v-1) + tv⋅G) + = gv⋅(gv-1⋅(f(v-2) + tv-1⋅G) + tv⋅G) + = gv⋅gv-1⋅f(v-2) + gv⋅(tv + gv-1⋅tv-1)⋅G + = gv⋅gv-1⋅f(v-2) + (sumi=v-1..v ti⋅prodj=i..v gj)⋅G + = gv⋅gv-1⋅...⋅g1⋅f(0) + (sumi=1..v ti⋅prodj=i..v gj)⋅G + = gv⋅...⋅g0⋅Q0 + gv⋅taccv⋅G'' + where ''tacci'' is computed by ''KeyAggInternal'' and ''Tweak'' as follows: + ''tacc0 = 0 + tacci = ti + gi-1⋅tacci-1 for i=1..v mod n'' + for which it holds that ''gv⋅taccv = sumi=1..v ti⋅prodj=i..v gj''. + + + +''KeyAggInternal'' and ''Tweak'' compute + ''gacc0 = 1 + gacci = gi-1⋅gacci-1 for i=1..v mod n'' +So we can rewrite above equation for the final public key as + ''with_even_y(Qv) = gv⋅gaccv⋅Q0 + gv⋅taccv⋅G''. + + + +Then we have + ''with_even_y(Qv) - gv⋅taccv⋅G + = gv⋅gaccv⋅Q0 + = gv⋅gaccv⋅(a1⋅P1 + ... + au⋅Pu) + = gv⋅gaccv⋅(a1⋅gp1⋅d'1⋅G + ... + au⋅gpu⋅d'u⋅G) + = sumi=1..u(gv⋅gaccv⋅gpi⋅ai⋅d'i)*G''. + + +Thus, signer ''i'' multiplies its secret key ''d'i'' with ''gv⋅gaccv⋅gpi'' in the ''[[#Sign negation|Sign]]'' algorithm. + +==== Negation Of The Public Key When Partially Verifying ==== + + +As explained in [[#negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]] the signer uses a possibly negated secret key + ''d = gv⋅gaccv⋅gp⋅d' mod n'' +when producing a partial signature to ensure that the aggregate signature will correspond to an aggregate public key with even Y coordinate. + + + +The ''[[#SigVerify negation|PartialSigVerifyInternal]]'' algorithm is supposed to check + ''s⋅G = R* + e⋅a⋅d⋅G''. + + + +The verifier doesn't have access to ''d⋅G'', but can construct it using the xonly public key ''pk*'' as follows: +''d⋅G + = gv⋅gaccv⋅gp⋅d'⋅G + = gv⋅gaccv⋅point(pk*)'' + + ==== Dealing with Infinity in Nonce Aggregation ==== If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 191158d0..1326c27c 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -513,77 +513,17 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p return 0; } secp256k1_fe_normalize_var(&pk.y); - /* Determine if the secret key sk should be negated before signing. - * - * We use the following notation: - * - |.| is a function that normalizes a point to an even Y by negating - * if necessary, similar to secp256k1_extrakeys_ge_even_y - * - mu[i] is the i-th KeyAgg coefficient - * - t[i] is the i-th tweak - * - * The following public keys arise as intermediate steps: - * - P[i] is the i-th public key with corresponding secret key x[i] - * P[i] := x[i]*G - * - P_agg[0] is the aggregate public key - * P_agg[0] := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| - * - P_agg[i] for 1 <= i <= m is the tweaked public key after the i-th - * tweaking operation. There are two types of tweaking: x-only and ordinary - * "EC" tweaking. We define a boolean predicate xonly(i) that is true if - * the i-th tweaking operation is x-only tweaking and false otherwise - * (ordinary tweaking). - * Let - * P_agg[i] := f(i, P_agg[i-1]) + t[i]*G for i = 1, ..., m - * where f(i, X) := |X| if xonly(i) - * f(i, X) := X otherwise - * - * Note that our goal is to produce a partial signature corresponding to - * the final public key after m tweaking operations P_final = |P_agg[m]|. - * - * Define d[i] for 0 <= i <= n-1 and d_agg[i] for 0 <= i <= m so that: - * - |P[i]| = d[i]*P[i] - * - f(i+1, P_agg[i]) = d_agg[i]*P_agg[i] for 0 <= i <= m - 1 - * - |P_agg[m]| = d_agg[m]*P_agg[m] - * - * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise. - * For 0 <= i <= m-1, d_agg[i] is -1 if and only if xonly(i+1) is true and - * P_agg[i] has an odd Y coordinate. - * - * The (x-only) final public key is P_final = |P_agg[m]| - * = d_agg[m]*P_agg[m] - * = d_agg[m]*(f(m, P_agg[m-1]) + t[m]*G) - * = d_agg[m]*(d_agg[m-1]*(f(m-1, P_agg[m-2]) + t[m-1]*G) + t[m]*G) - * = d_agg[m]*...*d_agg[0]*P_agg[0] + (d_agg[m]*t[m]+...+*d_agg[1]*t[1])*G. - * To simplify the equation let us define - * d_agg := d_agg[m]*...*d_agg[0]. - * t := d_agg[m]*t[m]+...+*d_agg[1]*t[1] if m > 0, otherwise t := 0 - * Then we have - * P_final - t*G - * = d_agg*P_agg[0] - * = d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) - * = d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) - * = sum((d_agg*d[i])*mu[i]*x[i])*G. - * - * Thus whether signer i should use the negated x[i] depends on the product - * d_agg[m]*...*d_agg[1]*d_agg[0]*d[i]. In other words, negate if and only - * if the following holds: - * (P[i] has odd y) XOR (xonly(1) and P_agg[0] has odd y) - * XOR (xonly(2) and P_agg[1] has odd y) - * XOR ... XOR (xonly(m) and P_agg[m-1] has odd y) - * XOR (P_agg[m] has odd y) - * - * Let us now look at how the terms in the equation correspond to the if - * condition below for some values of m: - * m = 0: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg[0] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * cache_i.internal_key_parity = 0 - * m = 1: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) - * xonly(1) and P_agg[0] has odd y = cache_i.internal_key_parity - * P_agg[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * m = 2: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) - * (xonly(1) and P_agg[0] has odd y) - XOR (xonly(2) and P_agg[1] has odd y) = cache_i.internal_key_parity - * P_agg[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * etc. + /* The specification requires that the secret key is multiplied by + * g*gp = g[0]*...g[v]*gp. + * Since all factors are 1 or -1, the key is negated if and only if + * (P[i] has odd y) XOR (is_xonly_t[1] and Q[0] has odd y) + * XOR (is_xonly_t[2] and Q[1] has odd y) + * XOR ... XOR (is_xonly_t[v] and Q[v-1] has odd y) + * XOR (Q[v] has odd y) + * which is equivalent to + * secp256k1_fe_is_odd(&pk.y) + * XOR cache_i.internal_key_parity + * XOR secp256k1_fe_is_odd(&cache_i.pk.y)). */ if ((secp256k1_fe_is_odd(&pk.y) != secp256k1_fe_is_odd(&cache_i.pk.y)) @@ -659,41 +599,11 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - /* When producing a partial signature, signer i uses a possibly - * negated secret key: - * - * sk[i] = (d_agg*d[i])*x[i] - * - * to ensure that the aggregate signature will correspond to - * an aggregate public key with even Y coordinate (see the - * notation and explanation in musig_partial_sign). - * - * We use the following additional notation: - * - e is the (Schnorr signature) challenge - * - r[i] is the i-th signer's secret nonce - * - R[i] = r[i]*G is the i-th signer's public nonce - * - R is the aggregated public nonce - * - d_nonce is chosen so that |R| = d_nonce*R - * - * The i-th partial signature is: - * - * s[i] = d_nonce*r[i] + mu[i]*e*sk[i] - * - * In order to verify this partial signature, we need to check: - * - * s[i]*G = d_nonce*R[i] + mu[i]*e*sk[i]*G - * - * The verifier doesn't have access to sk[i]*G, but can construct - * it using the xonly public key |P[i]| as follows: - * - * sk[i]*G = d_agg*d[i]*x[i]*G - * = d_agg*d[i]*P[i] - * = d_agg*|P[i]| - * - * The if condition below is true whenever d_agg is negative (again, see the - * explanation in musig_partial_sign). In this case, the verifier negates e - * which will have the same end result as negating |P[i]|, since they are - * multiplied later anyway. + /* The specification requires that the public key is multiplied by g which + * is negative if and only if fe_is_odd(&cache_i.pk.y) XOR + * internal_key_parity. Instead of multiplying g with the public key, we + * negate e which will have the same end result, since e and the public key + * are multiplied later anyway. */ if (secp256k1_fe_is_odd(&cache_i.pk.y) != cache_i.internal_key_parity) { From eac0df13799e38a5f2d88b12348170bda276f952 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 15 Mar 2022 22:26:55 +0000 Subject: [PATCH 166/381] musig: mention how keyagg_cache tweak and parity relate to spec Also rename internal_key_parity -> parity_acc because the former is confusing. --- src/modules/musig/keyagg.h | 5 ++++- src/modules/musig/keyagg_impl.h | 6 +++--- src/modules/musig/session_impl.h | 37 +++++++++++++++----------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/modules/musig/keyagg.h b/src/modules/musig/keyagg.h index 69c1f34a..a56dc9ef 100644 --- a/src/modules/musig/keyagg.h +++ b/src/modules/musig/keyagg.h @@ -18,8 +18,11 @@ typedef struct { secp256k1_ge pk; secp256k1_fe second_pk_x; unsigned char pk_hash[32]; + /* tweak is identical to value tacc[v] in the specification. */ secp256k1_scalar tweak; - int internal_key_parity; + /* parity_acc corresponds to gacc[v] in the spec. If gacc[v] is -1, + * parity_acc is 1. Otherwise, parity_acc is 0. */ + int parity_acc; } secp256k1_keyagg_cache_internal; /* Requires that the saved point is not infinity */ diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index c7f18b37..33cdec2a 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -69,7 +69,7 @@ static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, sec ptr += 32; memcpy(ptr, cache_i->pk_hash, 32); ptr += 32; - *ptr = cache_i->internal_key_parity; + *ptr = cache_i->parity_acc; ptr += 1; secp256k1_scalar_get_b32(ptr, &cache_i->tweak); } @@ -84,7 +84,7 @@ static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_k ptr += 32; memcpy(cache_i->pk_hash, ptr, 32); ptr += 32; - cache_i->internal_key_parity = *ptr & 1; + cache_i->parity_acc = *ptr & 1; ptr += 1; secp256k1_scalar_set_b32(&cache_i->tweak, ptr, NULL); return 1; @@ -278,7 +278,7 @@ static int secp256k1_musig_pubkey_tweak_add_internal(const secp256k1_context* ct return 0; } if (xonly && secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { - cache_i.internal_key_parity ^= 1; + cache_i.parity_acc ^= 1; secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak); } secp256k1_scalar_add(&cache_i.tweak, &cache_i.tweak, &tweak); diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 1326c27c..88e27e17 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -513,21 +513,19 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p return 0; } secp256k1_fe_normalize_var(&pk.y); + /* The specification requires that the secret key is multiplied by - * g*gp = g[0]*...g[v]*gp. - * Since all factors are 1 or -1, the key is negated if and only if - * (P[i] has odd y) XOR (is_xonly_t[1] and Q[0] has odd y) - * XOR (is_xonly_t[2] and Q[1] has odd y) - * XOR ... XOR (is_xonly_t[v] and Q[v-1] has odd y) - * XOR (Q[v] has odd y) - * which is equivalent to - * secp256k1_fe_is_odd(&pk.y) - * XOR cache_i.internal_key_parity - * XOR secp256k1_fe_is_odd(&cache_i.pk.y)). + * g[v]*g*gp. All factors are -1 or 1. The value g[v] is -1 iff + * secp256k1_fe_is_odd(&cache_i.pk.y)), g is is -1 iff parity_acc is 1 and + * gp is -1 if secp256k1_fe_is_odd(&pk.y). Therefore, multiplying by + * g[v]*g*gp is equivalent to negating if + * secp256k1_fe_is_odd(&cache_i.pk.y)) + * XOR cache_i.parity_acc + * XOR secp256k1_fe_is_odd(&pk.y). */ - if ((secp256k1_fe_is_odd(&pk.y) - != secp256k1_fe_is_odd(&cache_i.pk.y)) - != cache_i.internal_key_parity) { + if ((secp256k1_fe_is_odd(&cache_i.pk.y) + != cache_i.parity_acc) + != secp256k1_fe_is_odd(&pk.y)) { secp256k1_scalar_negate(&sk, &sk); } @@ -599,14 +597,13 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - /* The specification requires that the public key is multiplied by g which - * is negative if and only if fe_is_odd(&cache_i.pk.y) XOR - * internal_key_parity. Instead of multiplying g with the public key, we - * negate e which will have the same end result, since e and the public key - * are multiplied later anyway. - */ + /* The specification requires that the public key is multiplied by g[v]*g. + * All factors are -1 or 1. The value g[v] is -1 iff + * secp256k1_fe_is_odd(&cache_i.pk.y)) and g is is -1 iff parity_acc is 1. + * Therefore, multiplying by g[v]*g is equivalent to negating if + * fe_is_odd(&cache_i.pk.y) XOR parity_acc. */ if (secp256k1_fe_is_odd(&cache_i.pk.y) - != cache_i.internal_key_parity) { + != cache_i.parity_acc) { secp256k1_scalar_negate(&e, &e); } From ef537b206595173cc8d6945617bba4d41b821ec2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 23 Mar 2022 14:24:03 +0000 Subject: [PATCH 167/381] musig-spec: fix unnecessary O(n^2) KeyAgg runtime --- doc/musig-spec.mediawiki | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index e2a3364a..b41f5c62 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -90,9 +90,10 @@ The algorithm '''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +* Let ''pk2 = GetSecondKey(pk1..u)'' * For ''i = 1 .. u'': -** Let ''ai = KeyAggCoeff(pk1..u, pki)''. ** Let ''Pi = point(pki)''; fail if that fails. +** Let ''ai = KeyAggCoeff'(pk1..u, pki, pk2)''. * Let ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' * Fail if ''is_infinite(Q0)''. * Let ''tacc0 = 0'' @@ -104,15 +105,19 @@ The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xo The algorithm '''''HashKeys(pk1..u)''''' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' -The algorithm '''''IsSecond(pk1..u, pk')''''' is defined as: +The algorithm '''''GetSecondKey(pk1..u)''''' is defined as: * For ''j = 1 .. u'': ** If ''pkj ≠ pk1'': -*** Return ''true'' if ''pkj = pk' '', otherwise return ''false''. -* Return ''false'' +*** Return ''pkj'' +* Return ''bytes(0)'' The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as: -* Let ''L = HashKeys(pk1..u)''. -* If ''IsSecond(pk1..u, pk')'': +* Let ''pk2 = GetSecondKey(pk1..u)'': +* Return ''KeyAggCoeff'(pk1..u, pk', pk2)'' + +The algorithm '''''KeyAggCoeff'(pk1..u, pk', pk2)''''' is defined as: +* Let ''L = HashKeys(pk1..u)'' +* If ''pk' = pk2'': ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' From 686d96222dfb6bb45604b4ac73422039033ee34a Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 22 Mar 2022 21:25:01 +0000 Subject: [PATCH 168/381] musig-spec: various cleanups - add BIP header & abstract - rename MuSig to MuSig2 because some people may want to use the 3-round version - remove applications because we don't need to motivate an informational BIP - x-only -> X-only - remove overly repetetitive "The algorithm [...] is defined as" - move "Remarks" and "Design" out of "Description" section and move "Test vectors and ..." into "Description" section. The idea is that the Description contains everything that is absolutely required to implement the BIP (safely). --- doc/musig-spec.mediawiki | 94 ++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index b41f5c62..43564522 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,25 +1,27 @@
-  Title: MuSig
+  BIP: ?
+  Title: MuSig2
   Author:
   Status: Draft
-  License: BSD-2-Clause
-  Created: 2020-01-19
+  License: BSD-3-Clause
+  Type: Informational
+  Created: 2022-03-22
 
== Introduction == === Abstract === -This document proposes a standard for the MuSig2 protocol that supports ''tweaking'' and outputs [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. +This document proposes a standard for the [https://eprint.iacr.org/2020/1261.pdf MuSig2] protocol. +The standard is compatible with [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. +It also supports ''tweaking'', which allows creating [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot outputs with key and script paths. === Copyright === -This document is licensed under the 2-clause BSD license. +This document is licensed under the 3-clause BSD license. === Motivation === -== Description == - === Design === * The output of the ''KeyAgg'' algorithm depends on the order of the input public keys. @@ -30,9 +32,13 @@ This document is licensed under the 2-clause BSD license. * The public nonces are serialized in compressed format (33 bytes). We accept the small overhead compared to x-only serialization to avoid complicating the specification. * This specification supports signing for ''tweaked'' aggregate public keys. There are two modes of tweaking. ''Ordinary'' tweaking allows deriving child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot tweak. See section [[#tweaking|Tweaking]] below for details. +== Description == + +When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that lead to catastrophic failure. + === Notation === -The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure schemeAmong other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.. +The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme. * Lowercase variables represent integers or byte arrays. ** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''. ** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''. @@ -59,7 +65,7 @@ The following conventions are used, with constants as defined for [https://www.s *** Fail if ''c ≠ y'2 mod p''. *** Let ''y = y' '' if ''y' mod 2 = 0'', otherwise let ''y = p - y' ''. *** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y''. -** The function ''point(x)'', where ''x'' is a 32-byte array ("x-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. +** The function ''point(x)'', where ''x'' is a 32-byte array ("X-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. ** The function ''pointc(x)'', where ''x'' is a 33-byte array (compressed serialization), sets ''P = lift_x(int(x[1:33]))'' and fails if that fails. If ''x[0] = 2'' it returns ''P'' and if ''x[0] = 3'' it returns ''-P''. Otherwise, it fails. ** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. * Other: @@ -73,7 +79,7 @@ Input: * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays -The algorithm '''''KeySort(pk1..u)''''' is defined as: +'''''KeySort(pk1..u)''''': * Return ''pk1..u'' sorted in lexicographical order. ==== Key Aggregation ==== @@ -85,11 +91,11 @@ Input: * The tweaks ''tweak1..v'': ''v'' 32-byte arrays * The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -The algorithm '''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +'''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''': * Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. * Return ''bytes(Q)''. -The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +'''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''': * Let ''pk2 = GetSecondKey(pk1..u)'' * For ''i = 1 .. u'': ** Let ''Pi = point(pki)''; fail if that fails. @@ -102,26 +108,26 @@ The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xo ** Let ''(Qi, gacci, tacci) = Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''; fail if that fails * Return ''(Qv, gaccv, taccv)''. -The algorithm '''''HashKeys(pk1..u)''''' is defined as: +'''''HashKeys(pk1..u)''''': * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' -The algorithm '''''GetSecondKey(pk1..u)''''' is defined as: +'''''GetSecondKey(pk1..u)''''': * For ''j = 1 .. u'': ** If ''pkj ≠ pk1'': *** Return ''pkj'' * Return ''bytes(0)'' -The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as: +'''''KeyAggCoeff(pk1..u, pk')''''': * Let ''pk2 = GetSecondKey(pk1..u)'': * Return ''KeyAggCoeff'(pk1..u, pk', pk2)'' -The algorithm '''''KeyAggCoeff'(pk1..u, pk', pk2)''''' is defined as: +'''''KeyAggCoeff'(pk1..u, pk', pk2)''''': * Let ''L = HashKeys(pk1..u)'' * If ''pk' = pk2'': ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' -The algorithm '''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''' is defined as: +'''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''': * If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': ** Let ''gi-1 = -1 mod n'' * Else: let ''gi-1 = 1'' @@ -134,7 +140,7 @@ The algorithm '''''Tweak(Qi-1, gacci-1, tweaki, ==== Nonce Generation ==== -The algorithm '''''NonceGen()''''' is defined as: +'''''NonceGen()''''': * Generate two random integers ''k1, k2'' in the range ''1...n-1'' * Let ''R*1 = k1⋅G, R*2 = k2⋅G'' * Let ''pubnonce = cbytes(R*1) || cbytes(R*2)'' @@ -146,7 +152,7 @@ The algorithm '''''NonceGen()''''' is defined as: * The number ''u'' of ''pubnonces'' with ''0 < u < 2^32'' * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays -The algorithm '''''NonceAgg(pubnonce1..u)''''' is defined as: +'''''NonceAgg(pubnonce1..u)''''': * For ''i = 1 .. 2'': ** For ''j = 1 .. u'': *** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails @@ -167,7 +173,7 @@ The Session Context is a data structure consisting of the following elements: We write "Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx''" to assign names to the elements of a Session Context. -The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: +'''''GetSessionValues(session_ctx)''''': * Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx'' * Let ''(Q, gaccv, taccv) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails * Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' @@ -177,7 +183,7 @@ The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: * Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' * Return ''(Q, gaccv, taccv, b, R, e)'' -The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: +'''''GetSessionKeyAggCoeff(session_ctx, P)''''': * Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' * Return ''KeyAggCoeff(pk1..u, bytes(P))'' @@ -188,7 +194,7 @@ Input: * The secret key ''sk'': a 32-byte array * The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: +'''''Sign(secnonce, sk, session_ctx)''''': * Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''k'1 = int(secnonce[0:32]), k'2 = int(secnonce[32:64])'' * Fail if ''k'i = 0'' or ''k'i ≥ n'' for ''i = 1..2'' @@ -219,21 +225,19 @@ Input: * The message ''m'': a 32-byte array * The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' -The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''' is defined as: +'''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''': * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails * Let ''session_ctx = (aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m)'' * Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. -===== PartialSigVerifyInternal ===== - Input: * The partial signature ''psig'': a 32-byte array * The public nonce of the signer ''pubnonce'': a 66-byte array * The public key of the signer ''pk*'' (in ''pk1..u'' of the session_ctx''): a 32-byte array * The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''' is defined as: +'''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''': * Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' * Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' @@ -253,7 +257,7 @@ Input: * The partial signatures ''psig1..u'': ''u'' 32-byte arrays * The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: +'''''PartialSigAgg(psig1..u, session_ctx)''''': * Let ''(Q, _, taccv, _, _, R, e) = GetSessionValues(session_ctx)''; fail if that fails * For ''i = 1 .. u'': ** Let ''si = int(psigi)''; fail if ''si ≥ n''. @@ -277,29 +281,34 @@ Otherwise, it is possible to extract the secret signing key from the partial sig An implementation may invalidate the secnonce argument after ''Sign'' to avoid any reuse. Avoiding reuse also implies that the ''NonceGen'' algorithm must compute unbiased, uniformly random values ''k1'' and ''k2''. -=== Remarks on Security and Correctness === +=== Test Vectors and Reference Code === -==== Tweaking ==== +There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. +Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' functions. -This MuSig specification supports two modes of tweaking that correspond to the following algorithms: +== Remarks on Security and Correctness == + +=== Tweaking === + +This MuSig2 specification supports two modes of tweaking that correspond to the following algorithms: Input: * ''P'': a point * The tweak ''t'': an integer with ''0 ≤ t < n '' -The algorithm '''''OrdinaryTweak(P, t)''''' is defined as: +'''''OrdinaryTweak(P, t)''''': * Return ''P + t⋅G'' -The algorithm '''''XonlyTweak(P, t)''''' is defined as: +'''''XonlyTweak(P, t)''''': * Return ''with_even_y(P) + t⋅G'' -==== Negation Of The Secret Key When Signing ==== +=== Negation Of The Secret Key When Signing === -In order to produce a partial signature for an x-only public key that is an aggregate of ''u'' x-only keys and tweaked ''v'' times (x-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. +In order to produce a partial signature for an X-only public key that is an aggregate of ''u'' X-only keys and tweaked ''v'' times (X-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. -The following public keys arise as intermediate steps in the MuSig protocol: -• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's x-only public key. Defining ''d'i'' to be the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have +The following public keys arise as intermediate steps in the MuSig2 protocol: +• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's X-only public key. Defining ''d'i'' to be the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have ''Pi = with_even_y(d'i⋅G) ''. • ''Q0'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu''. @@ -309,7 +318,7 @@ The following public keys arise as intermediate steps in the MuSig protocol: ''f(i) := Qi'' otherwise. -The goal is to produce a partial signature corresponding to the output of ''KeyAgg'', i.e., the final (x-only) public key point after ''v'' tweaking operations ''with_even_y(Qv)''. +The goal is to produce a partial signature corresponding to the output of ''KeyAgg'', i.e., the final (X-only) public key point after ''v'' tweaking operations ''with_even_y(Qv)''. We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. It holds that @@ -323,7 +332,7 @@ Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv -So, the (x-only) final public key is +So, the (X-only) final public key is ''with_even_y(Qv) = gv⋅Qv = gv⋅(f(v-1) + tv⋅G) @@ -377,7 +386,7 @@ The verifier doesn't have access to ''d⋅G'', but can construct it using the xo = gv⋅gaccv⋅point(pk*)'' -==== Dealing with Infinity in Nonce Aggregation ==== +=== Dealing with Infinity in Nonce Aggregation === If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). If we fail here, we will never be able to determine who it is. @@ -397,13 +406,6 @@ Given a successful adversary against the security game (EUF-CMA) for the modifie We conclude that these two modifications preserve the security of the MuSig2* scheme. -== Applications == - -== Test Vectors and Reference Code == - -There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. -Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' functions. - == Footnotes == From 2adb741c45ecc4ef7180009c42f961e4aee4bf5c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 30 Mar 2022 15:06:46 +0000 Subject: [PATCH 169/381] examples: rename example_musig to musig_example for consistency --- .gitignore | 2 +- Makefile.am | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index c32a5ae7..1ec887de 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,4 @@ src/stamp-h1 libsecp256k1.pc contrib/gh-pr-create.sh -example_musig \ No newline at end of file +musig_example \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 61e272e5..0b50f7a8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -175,15 +175,15 @@ endif TESTS += schnorr_example endif if ENABLE_MODULE_MUSIG -noinst_PROGRAMS += example_musig -example_musig_SOURCES = examples/musig.c -example_musig_CPPFLAGS = -I$(top_srcdir)/include -example_musig_LDADD = libsecp256k1.la -example_musig_LDFLAGS = -static +noinst_PROGRAMS += musig_example +musig_example_SOURCES = examples/musig.c +musig_example_CPPFLAGS = -I$(top_srcdir)/include +musig_example_LDADD = libsecp256k1.la +musig_example_LDFLAGS = -static if BUILD_WINDOWS -example_musig_LDFLAGS += -lbcrypt +musig_example_LDFLAGS += -lbcrypt endif -TESTS += example_musig +TESTS += musig_example endif endif From 03bea1e173d84b33ca81bba4c06b876e160dbe65 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 30 Mar 2022 15:18:07 +0000 Subject: [PATCH 170/381] configure: add -zkp modules to dev-mode and remove redundant code --- configure.ac | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/configure.ac b/configure.ac index f23dbd95..66053fc9 100644 --- a/configure.ac +++ b/configure.ac @@ -146,8 +146,8 @@ AC_ARG_ENABLE(module_ecdh, AC_ARG_ENABLE(module_musig, AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), - [enable_module_musig=$enableval], - [enable_module_musig=no]) + [], + [SECP_SET_DEFAULT([enable_module_musig], [no], [yes])]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [], @@ -155,18 +155,18 @@ AC_ARG_ENABLE(module_recovery, AC_ARG_ENABLE(module_generator, AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]), - [enable_module_generator=$enableval], - [enable_module_generator=no]) + [], + [SECP_SET_DEFAULT([enable_module_generator], [no], [yes])]) AC_ARG_ENABLE(module_rangeproof, AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]), - [enable_module_rangeproof=$enableval], - [enable_module_rangeproof=no]) + [], + [SECP_SET_DEFAULT([enable_module_rangeproof], [no], [yes])]) AC_ARG_ENABLE(module_whitelist, AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), - [enable_module_whitelist=$enableval], - [enable_module_whitelist=no]) + [], + [SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])]) AC_ARG_ENABLE(module_extrakeys, AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=no]]), [], @@ -178,13 +178,13 @@ AC_ARG_ENABLE(module_schnorrsig, AC_ARG_ENABLE(module_ecdsa_s2c, AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), - [enable_module_ecdsa_s2c=$enableval], - [enable_module_ecdsa_s2c=no]) + [], + [SECP_SET_DEFAULT([enable_module_ecdsa_s2c], [no], [yes])]) AC_ARG_ENABLE(module_ecdsa-adaptor, AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]), - [enable_module_ecdsa_adaptor=$enableval], - [enable_module_ecdsa_adaptor=no]) + [], + [SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [no], [yes])]) AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], @@ -192,13 +192,13 @@ AC_ARG_ENABLE(external_default_callbacks, AC_ARG_ENABLE(module_surjectionproof, AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), - [enable_module_surjectionproof=$enableval], - [enable_module_surjectionproof=no]) + [], + [SECP_SET_DEFAULT([enable_module_surjectionproof], [no], [yes])]) AC_ARG_ENABLE(reduced_surjection_proof_size, AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), - [use_reduced_surjection_proof_size=$enableval], - [use_reduced_surjection_proof_size=no]) + [], + [SECP_SET_DEFAULT([use_reduced_surjection_proof_size], [no], [no])]) # Test-only override of the (autodetected by the C code) "widemul" setting. # Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default). From 7c5af740fab0d36a54c9e36872a87de2727c49f1 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 30 Mar 2022 18:45:59 +0000 Subject: [PATCH 171/381] ci: fix missing EXPERIMENTAL flags This was introduced when merging upstream PRs. --- .cirrus.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index 7df661ee..60928eb0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,6 +101,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + EXPERIMENTAL: yes ECDSA_S2C: yes RANGEPROOF: yes WHITELIST: yes @@ -191,6 +192,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + EXPERIMENTAL: yes ECDSA_S2C: yes RANGEPROOF: yes WHITELIST: yes @@ -284,6 +286,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + EXPERIMENTAL: yes ECDSA_S2C: yes RANGEPROOF: yes WHITELIST: yes From 802b7daf23528ebf88586d998cd4fc7b0c9c5a22 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 27 Mar 2022 13:33:01 +0000 Subject: [PATCH 172/381] musig-spec: add motivation and design sections --- doc/musig-spec.mediawiki | 56 +++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 43564522..95c862fe 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -22,15 +22,36 @@ This document is licensed under the 3-clause BSD license. === Motivation === +MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create a single Schnorr signature for the aggregate key and a message. +This is more space-efficient and has lower verification costs than each signer providing an individual public key and signature. +Since MuSig2 is not a threshold-signature scheme, the cooperation of ''all'' signers involved in key aggregation is required to produce a signature. + +One of the primary motivations for MuSig2 is the activation of Taproot ([https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]) on the Bitcoin network, which introduced the ability to authorize transactions with Schnorr signatures. +This standard allows the creation of aggregate public keys that can be used in Taproot outputs. +Such outputs are indistinguishable for a blockchain observer from regular, single-signer outputs but are actually controlled by multiple signers. +Moreover, by tweaking an aggregate key, the shared Taproot output can have script spending paths that are hidden unless used. + +There are multi-signature schemes other than MuSig2 that are fully compatible with Schnorr signatures. +MuSig2 stands out by combining the following features: +* '''Simple Key Setup''': Key aggregation is non-interactive and fully compatible with BIP340 public keys. +* '''Two Communication Rounds''': MuSig2 is faster in practice than three-round multi-signature protocols, particularly when signers are connected through high-latency anonymizing links. Moreover, less communication rounds simplifies the specification and reduces the probability that users make security-relevant mistakes. To prove the security of using only two communication rounds, MuSig2 relies on the algebraic one-more discrete logarithm (AOMDL) assumption instead of the discrete logarithm assumption. AOMDL is a falsifiable and weaker variant of the well-studied OMDL problem. +* '''Low complexity''': MuSig2 has a substantially lower computational and implementation complexity than alternative schemes like [https://eprint.iacr.org/2020/1057 MuSig-DN]. However, this comes at the cost of having no ability to generate nonces deterministically and the requirement to securely handle signing state. + === Design === -* The output of the ''KeyAgg'' algorithm depends on the order of the input public keys. -* It is possible to sort the public keys with the ''KeySort'' algorithm before key aggregation to ensure the same output, independent of the (initial) order. -* The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting. -* The second unique key in the pubkey list given to ''KeyAgg'' (as well as any keys identical to this key) gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). -* The public key inputs are serialized using x-only (32 byte) instead of compressed (33 byte) serialization. The reason for this is that as x-only keys are becoming more common, the full key may not be available. -* The public nonces are serialized in compressed format (33 bytes). We accept the small overhead compared to x-only serialization to avoid complicating the specification. -* This specification supports signing for ''tweaked'' aggregate public keys. There are two modes of tweaking. ''Ordinary'' tweaking allows deriving child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot tweak. See section [[#tweaking|Tweaking]] below for details. +* '''Compatibility with BIP340''': The aggregate public key created as part of this MuSig2 specification is a BIP340 X-only public key, and the signature output at the end of the protocol is a BIP340 signature that passes BIP340 verification for the aggregate key and a message. The public keys that are input to the key aggregation algorithm are also X-only public keys. Compared to compressed serialization, this adds complexity to the specification, but as X-only keys are becoming more common, the full key may not be available. +* '''Tweaking for BIP32 derivations and Taproot''': The specification supports tweaking aggregate public keys and signing for tweaked aggregate public keys. We distinguish two modes of tweaking: ''Ordinary'' tweaking can be used to derive child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking, on the other hand, allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] tweak to add script paths to a Taproot output. See section [[#tweaking|Tweaking]] below for details. +* '''Non-interactive signing with preprocessing''': The first communication round, exchanging the nonces, can happen before the message or even the exact set of signers is determined. Therefore, the signers can view it as a preprocessing step. Later, when the parameters of the signing session are chosen, they can send partial signatures without additional interaction. +* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines an algorithm to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a common order of signers. Then, sorting is unnecessary and very slow for a large set of signers compared to the rest of the MuSig2 protocol. In the worst case, sorting algorithms in standard libraries can have quadratic run time, which is undesirable in adversarial settings. Nonetheless, standards using this specification can mandate sorting before aggregation. Note that the key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting. +* '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the security of the scheme. +* '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because it would be impossible to tell if a signer or the aggregator is to blame. +* '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''. +* '''Parameterization of MuSig2 and security''': In this specification, each signer's nonce consists of two elliptic curve points. The [https://eprint.iacr.org/2020/1261 MuSig2 paper] gives distinct security proofs depending on the number of points that constitute a nonce. See section [[#choosing-the-size-of-the-nonce|Choosing the Size of the Nonce]] for a discussion. + +The specification itself is designed such that efficiency and clarity are balanced. +The algorithms, as specified, are not optimal in terms of computation and space. +In particular, some values are recomputed but can be cached in actual implementations (see [[#signing-flow|Signing Flow]]). +Also, the signers' public nonces are serialized in compressed format (33 bytes) instead of the smaller (32 bytes) but more complicated X-only serialization. == Description == @@ -149,6 +170,7 @@ Input: ==== Nonce Aggregation ==== +Input: * The number ''u'' of ''pubnonces'' with ''0 < u < 2^32'' * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays @@ -183,6 +205,7 @@ We write "Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xon * Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' * Return ''(Q, gaccv, taccv, b, R, e)'' + '''''GetSessionKeyAggCoeff(session_ctx, P)''''': * Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' * Return ''KeyAggCoeff(pk1..u, bytes(P))'' @@ -406,6 +429,25 @@ Given a successful adversary against the security game (EUF-CMA) for the modifie We conclude that these two modifications preserve the security of the MuSig2* scheme. + +=== Choosing the Size of the Nonce === + +The [https://eprint.iacr.org/2020/1261 MuSig2 paper] contains two security proofs that apply to different protocol variants. +The first is for a variant where each signer's nonce consists of four elliptic curve points and uses the random oracle model (ROM). +In the second variant, the signers' nonces consist of only two points. +Its proof requires a stronger model, namely the combination of the ROM and the algebraic group model (AGM). +Relying on the stronger model is a legitimate choice for the following reasons: + +First, an approach widely taken is interpreting a Forking Lemma proof in the ROM merely as design justification and ignoring the loss of security due to the Forking Lemma. +If one believes in this approach, then the ROM may not be the optimal model in the first place because some parts of the concrete security bound are arbitrarily ignored. +One may just as well move to the ROM+AGM model, which produces bounds close to the best-known attacks, e.g., for Schnorr signatures. + +Second, as of this writing, there is no instance of a serious protocol with a security proof in the AGM that is not secure in practice. +There are, however, insecure toy schemes with AGM security proofs, but those explicitly violate the requirements of the AGM. +[https://eprint.iacr.org/2022/226.pdf Broken AGM proofs of toy schemes] provide group elements to the adversary without declaring them as group element inputs. +In contrast, in MuSig2, all group elements that arise in the protocol are known to the adversary and declared as group element inputs. +A scheme very similar to MuSig2 and with two-point nonces was independently proven secure in the ROM and AGM by [https://eprint.iacr.org/2020/1245 Alper and Burdges]. + == Footnotes == From eccba5b4e5eb70710f4d34ae89e62abfab542f7c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 31 Mar 2022 13:33:30 +0000 Subject: [PATCH 173/381] examples: relicense musig example to CC0 public domain --- examples/musig.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index fddb1694..1fbd2207 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -1,8 +1,11 @@ -/*********************************************************************** - * Copyright (c) 2018 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ +/************************************************************************* + * Written in 2018 by Jonas Nick * + * 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 MuSig module to create a * 3-of-3 multisignature. Additionally, see the documentation in From 645d9c53c4d9095342cf770c98e3d367f670ae46 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 31 Mar 2022 13:38:30 +0000 Subject: [PATCH 174/381] examples: let musig use random.h instead of /dev/urandom --- examples/musig.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 1fbd2207..3a657410 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -18,6 +18,8 @@ #include #include +#include "random.h" + struct signer_secrets { secp256k1_keypair keypair; secp256k1_musig_secnonce secnonce; @@ -34,20 +36,14 @@ struct signer { /* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) { unsigned char seckey[32]; - FILE *frand = fopen("/dev/urandom", "r"); - if (frand == NULL) { - return 0; - } - do { - if(!fread(seckey, sizeof(seckey), 1, frand)) { - fclose(frand); - return 0; - } - /* The probability that this not a valid secret key is approximately 2^-128 */ - } while (!secp256k1_ec_seckey_verify(ctx, seckey)); - fclose(frand); - if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { - return 0; + while (1) { + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { + break; + } } if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) { return 0; @@ -103,21 +99,14 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st secp256k1_musig_session session; for (i = 0; i < N_SIGNERS; i++) { - FILE *frand; unsigned char seckey[32]; unsigned char session_id[32]; /* Create random session ID. It is absolutely necessary that the session ID * is unique for every call of secp256k1_musig_nonce_gen. Otherwise * it's trivial for an attacker to extract the secret key! */ - frand = fopen("/dev/urandom", "r"); - if(frand == NULL) { + if (!fill_random(session_id, sizeof(session_id))) { return 0; } - if (!fread(session_id, 32, 1, frand)) { - fclose(frand); - return 0; - } - fclose(frand); if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) { return 0; } From 79472c7ee556a7a3d2abc2e1ab077cfe120c78ff Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 31 Mar 2022 17:41:54 +0200 Subject: [PATCH 175/381] configure: Check compile+link when checking existence of functions Undeclared functions are fine in C but linking will fail. --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b382833c..33dda84d 100644 --- a/configure.ac +++ b/configure.ac @@ -261,13 +261,13 @@ else fi AC_MSG_CHECKING([for __builtin_popcount]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])], +AC_LINK_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])], [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_POPCOUNT,1,[Define this symbol if __builtin_popcount is available]) ], [ AC_MSG_RESULT([no]) ]) AC_MSG_CHECKING([for __builtin_clzll]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() { __builtin_clzll(1);}]])], +AC_LINK_IFELSE([AC_LANG_SOURCE([[void myfunc() { __builtin_clzll(1);}]])], [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_CLZLL,1,[Define this symbol if __builtin_clzll is available]) ], [ AC_MSG_RESULT([no]) ]) From 11fb8a664b95803f176262fad952da4e2c686f58 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 31 Mar 2022 21:29:56 +0000 Subject: [PATCH 176/381] musig-spec: expand on signing flow Also move signing flow before specification because it is slightly more natural. --- doc/musig-spec.mediawiki | 69 ++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 95c862fe..1bb75528 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -57,6 +57,57 @@ Also, the signers' public nonces are serialized in compressed format (33 bytes) When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that lead to catastrophic failure. +=== Signing Flow === + +The basic order of operations to create a multi-signature with the specification is as follows: +The signers start by exchanging public keys and computing an aggregate public key using the ''KeyAgg'' algorithm. +When they want to sign a message, each signer starts the signing session by running ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. +Then, the signers broadcast their ''pubnonce'' to each other and run ''NonceAgg'' to compute an aggregate nonce. +At this point, every signer has the required data to sign, which, in the specification, is stored in a data structure called [[#session-context|Session Context]]. +After running ''Sign'' with the secret signing key, the ''secnonce'' and the session context, each signer sends their partial signature to an aggregator node, which produces a final signature using ''PartialSigAgg''. +If all signers behaved honestly, the result passes [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] verification. + +'''IMPORTANT''': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. +Otherwise, extracting the secret signing key from the partial signatures is possible. +To avoid accidental reuse, an implementation may securely erase the ''secnonce'' argument by overwriting it with zeros after ''Sign'' has been run. +A ''secnonce'' consisting of only zeros is invalid for ''Sign'' and will cause it to fail. +The ''NonceGen'' algorithm '''must''' draw unbiased, uniformly random values ''k1'' and ''k2''. +In particular, ''k1'' and ''k2'' must _not_ be derived deterministically from the session parameters (see [[#nonce-generation|Nonce Generation]]). + +The output of ''KeyAgg'' is dependent on the order of the input public keys. +If there is no common order of the signers already, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the same aggregate key is calculated. +Note that public keys are allowed to occur multiple times in the input of ''KeyAgg'' and ''KeySort'', and that it is possible to successfully complete a MuSig2 signing session with duplicated public keys. + +In some applications, it is beneficial to generate and exchange ''pubnonces'' before the message to sign or the final set of signers is known. +After this preprocessing phase, the ''Sign'' algorithm can be run immediately when the message and set of signers is determined. +This way, the final signature is created quicker and with fewer roundtrips. +However, applications that use this method presumably store the nonces for a longer time and must therefore be even more careful not to reuse them. +Moreover, this method prohibits a defense-in-depth measure that strengthens [[#nonce-generation|Nonce Generation]]. + +Instead of every signer broadcasting their ''pubnonce'' to every other signer, the signers can send their ''pubnonce'' to a single aggregator node that runs ''NonceAgg'' and sends the ''aggnonce'' back to the signers. +This technique reduces the overall communication. +The aggregator node does not need to be trusted for the scheme's security to hold. +All the aggregator node can do is prevent the signing session from succeeding by sending out incorrect aggregate nonces. + +If any signer sends an incorrect partial signature, i.e., one that has not then been created with ''Sign'' and the right arguments for the session, the MuSig2 protocol may fail to output a valid Schnorr signature. +This standard provides the method ''PartialSigVerify'' to verify the correctness of partial signatures. +If partial signatures are authenticated, this method can be used to identify disruptive signers and hold them accountable. +Note that partial signatures are ''not'' signatures. +An adversary can forge a partial signature, i.e., create a partial signature without knowing the secret key for the claimed public keyAssume an adversary wants to forge a partial signature for public key ''P''. It joins the signing session pretending to be two different signers, one with public key ''P' and one with another public key. The adversary can then set the second signer's nonce such that it will be able to produce a partial signature for ''P'', but not for the other claimed signer.. +However, if ''PartialSigVerify'' succeeds for all partial signatures then ''PartialSigAgg'' will return a valid Schnorr signature. + +To simplify the specification, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times. +Actual implementations can cache these values. +As a result, the [[#session-context|Session Context]] may look very different in implementations or may not exist at all. + +==== Nonce Generation ==== + +TODO + +==== Tweaking ==== + +TODO + === Notation === The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme. @@ -288,22 +339,6 @@ Input: * Let ''s = s1 + ... + su + e⋅gv⋅taccv mod n'' * Return ''sig = ''bytes(R) || bytes(s)'' -=== Signing Flow === - -Note that this specification unnecessarily recomputes intermediary values (such as the aggregate and tweaked public key) that can be cached in real implementations. - -There are multiple ways to use above algorithms and arrive at a final Schnorr signature. -One of them can be described as follows: -The signers ''1'' to ''n'' each run ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. -Every signer sends its public key and ''pubnonce'' to every other signer and all signers agree on a single message to sign. -Then, the signers run ''NonceAgg'' and ''Sign'' with their secret signing key and ''secnonce''. -They send the resulting partial signature to every other signer and combine them with the ''PartialSigAgg'' algorithm. - -''IMPORTANT'': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. -Otherwise, it is possible to extract the secret signing key from the partial signatures. -An implementation may invalidate the secnonce argument after ''Sign'' to avoid any reuse. -Avoiding reuse also implies that the ''NonceGen'' algorithm must compute unbiased, uniformly random values ''k1'' and ''k2''. - === Test Vectors and Reference Code === There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. @@ -311,7 +346,7 @@ Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' fun == Remarks on Security and Correctness == -=== Tweaking === +=== Tweaking Definition === This MuSig2 specification supports two modes of tweaking that correspond to the following algorithms: From c715407b4f56fb8e788c018c3bd2a4d4df6655bb Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 1 Apr 2022 13:12:28 +0000 Subject: [PATCH 177/381] musig-spec: fix partial sig verification note in intro --- doc/musig-spec.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 1bb75528..a65c5405 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -44,7 +44,7 @@ MuSig2 stands out by combining the following features: * '''Non-interactive signing with preprocessing''': The first communication round, exchanging the nonces, can happen before the message or even the exact set of signers is determined. Therefore, the signers can view it as a preprocessing step. Later, when the parameters of the signing session are chosen, they can send partial signatures without additional interaction. * '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines an algorithm to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a common order of signers. Then, sorting is unnecessary and very slow for a large set of signers compared to the rest of the MuSig2 protocol. In the worst case, sorting algorithms in standard libraries can have quadratic run time, which is undesirable in adversarial settings. Nonetheless, standards using this specification can mandate sorting before aggregation. Note that the key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting. * '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the security of the scheme. -* '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because it would be impossible to tell if a signer or the aggregator is to blame. +* '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because the individual nonce is required for partial verification. * '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''. * '''Parameterization of MuSig2 and security''': In this specification, each signer's nonce consists of two elliptic curve points. The [https://eprint.iacr.org/2020/1261 MuSig2 paper] gives distinct security proofs depending on the number of points that constitute a nonce. See section [[#choosing-the-size-of-the-nonce|Choosing the Size of the Nonce]] for a discussion. From 8d04ac318f2f6f160480faf6aeb843a1cba28db0 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 1 Apr 2022 21:26:00 +0000 Subject: [PATCH 178/381] musig-spec: remove unnecessary and inconsistent input paragraph --- doc/musig-spec.mediawiki | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index a65c5405..c992e677 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -305,12 +305,6 @@ Input: * Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. -Input: -* The partial signature ''psig'': a 32-byte array -* The public nonce of the signer ''pubnonce'': a 66-byte array -* The public key of the signer ''pk*'' (in ''pk1..u'' of the session_ctx''): a 32-byte array -* The ''session_ctx'': a [[#session-context|Session Context]] data structure - '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''': * Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' From 1a086ba9c9143ef572b6f1fa3d7c6b8ca173414e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 1 Apr 2022 21:26:55 +0000 Subject: [PATCH 179/381] musig-spec: add optional arguments to strengthen nonce function This is a defense-in-depth measure that may help if the value is not drawn uniformly at random. The handling of sk is similar to BIP340. --- doc/musig-spec.mediawiki | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index c992e677..4987b0e1 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -78,7 +78,8 @@ The output of ''KeyAgg'' is dependent on the order of the input public keys. If there is no common order of the signers already, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the same aggregate key is calculated. Note that public keys are allowed to occur multiple times in the input of ''KeyAgg'' and ''KeySort'', and that it is possible to successfully complete a MuSig2 signing session with duplicated public keys. -In some applications, it is beneficial to generate and exchange ''pubnonces'' before the message to sign or the final set of signers is known. +In some applications, it is beneficial to generate and exchange ''pubnonces'' before the signer's secret key, the final set of signers, or the message to sign is known. +In this case, only the available arguments are provided to the ''NonceGen'' algorithm. After this preprocessing phase, the ''Sign'' algorithm can be run immediately when the message and set of signers is determined. This way, the final signature is created quicker and with fewer roundtrips. However, applications that use this method presumably store the nonces for a longer time and must therefore be even more careful not to reuse them. @@ -125,6 +126,7 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. ** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. ** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. +** The function ''len(x)'' where ''x'' is a byte array returns the length of the array. ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. ** The function ''with_even_y(P)'', where ''P'' is a point, returns ''P'' if ''is_infinite(P)'' or ''has_even_y(P)''. Otherwise, ''with_even_y(P)'' returns ''-P''. ** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is a byte that is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. @@ -212,8 +214,19 @@ Input: ==== Nonce Generation ==== -'''''NonceGen()''''': -* Generate two random integers ''k1, k2'' in the range ''1...n-1'' +Input: +* The secret signing key ''sk'': a 32-byte array or 0-byte array (optional argument) +* The aggregate public key ''aggpk'': a 32-byte array or 0-byte array (optional argument) +* The message ''m'': a 32-byte array or 0-byte array (optional argument) +* The auxiliary input ''in'': a byte array of length ''≥ 0'' (optional argument) + +'''''NonceGen(sk, aggpk, m, in)''''': +* Let ''rand' '' be a 32-byte array freshly drawn uniformly at random +* If ''len(sk) > 0'': +** Let ''rand'' be the byte-wise xor of ''sk'' and ''hashMuSig/aux(rand')''The random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the secret signing key itself. It is xored with the secret key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key.. +* Else: let ''rand = rand' '' +* Let ''ki = int(hashMuSig/nonce(rand || len(aggpk) || aggpk || i || len(m) || m || len(in) || in)) mod n'' for ''i = 1,2'' +* Fail if ''k1 = 0'' or ''k2 = 0'' * Let ''R*1 = k1⋅G, R*2 = k2⋅G'' * Let ''pubnonce = cbytes(R*1) || cbytes(R*2)'' * Let ''secnonce = bytes(k1) || bytes(k2)'' From a29b961eb75d4bd4c871ee5cc7de861a2b7011aa Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 3 Apr 2022 23:41:50 +0000 Subject: [PATCH 180/381] musig-spec: add acknowledgements and improve abstract --- doc/musig-spec.mediawiki | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 4987b0e1..68d29514 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -14,7 +14,7 @@ This document proposes a standard for the [https://eprint.iacr.org/2020/1261.pdf MuSig2] protocol. The standard is compatible with [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. -It also supports ''tweaking'', which allows creating [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot outputs with key and script paths. +It supports ''tweaking'', which allows deriving [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32] child keys from aggregate keys and creating [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot outputs with key and script paths. === Copyright === @@ -495,3 +495,5 @@ A scheme very similar to MuSig2 and with two-point nonces was independently prov == Acknowledgements == + +We thank Brandon Black, Riccardo Casatta, Russell O'Connor, and Pieter Wuille for their contributions to this document. From e463ea42bb1fe48e30e6d289461cff4fa0935f77 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 3 Apr 2022 23:42:40 +0000 Subject: [PATCH 181/381] musig-spec: mention stateless signing in signing flow --- doc/musig-spec.mediawiki | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 68d29514..4f4149f4 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -90,6 +90,11 @@ This technique reduces the overall communication. The aggregator node does not need to be trusted for the scheme's security to hold. All the aggregator node can do is prevent the signing session from succeeding by sending out incorrect aggregate nonces. +In general, MuSig2 signers are stateful in the sense that they first generate ''secnonce'' and then need to store it until they receive the other signer's ''pubnonces'' or the ''aggnonce''. +However, it is possible for one of the signers to be stateless. +This signer waits until it receives the ''pubnonce'' of all the other signers and until session parameters such as a message to sign, public keys, and tweaks are determined. +Then, the signer can run ''NonceGen'', ''NonceAgg'' and ''Sign'' in sequence and send out its ''pubnonce'' along with its partial signature. + If any signer sends an incorrect partial signature, i.e., one that has not then been created with ''Sign'' and the right arguments for the session, the MuSig2 protocol may fail to output a valid Schnorr signature. This standard provides the method ''PartialSigVerify'' to verify the correctness of partial signatures. If partial signatures are authenticated, this method can be used to identify disruptive signers and hold them accountable. From f56e223a7a79aa52748d4f542ecebc2ce6c537b2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sun, 3 Apr 2022 23:43:05 +0000 Subject: [PATCH 182/381] musig-spec: explain NonceGen and tweaking in signing flow context --- doc/musig-spec.mediawiki | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 4f4149f4..390e2794 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -71,8 +71,6 @@ If all signers behaved honestly, the result passes [https://github.com/bitcoin/b Otherwise, extracting the secret signing key from the partial signatures is possible. To avoid accidental reuse, an implementation may securely erase the ''secnonce'' argument by overwriting it with zeros after ''Sign'' has been run. A ''secnonce'' consisting of only zeros is invalid for ''Sign'' and will cause it to fail. -The ''NonceGen'' algorithm '''must''' draw unbiased, uniformly random values ''k1'' and ''k2''. -In particular, ''k1'' and ''k2'' must _not_ be derived deterministically from the session parameters (see [[#nonce-generation|Nonce Generation]]). The output of ''KeyAgg'' is dependent on the order of the input public keys. If there is no common order of the signers already, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the same aggregate key is calculated. @@ -108,11 +106,43 @@ As a result, the [[#session-context|Session Context]] may look very different in ==== Nonce Generation ==== -TODO +'''IMPORTANT''': ''NonceGen'' must have access to a high-quality random generator to draw an unbiased, uniformly random value ''rand' ''. +Additionally, implementors must avoid modifying the ''NonceGen'' algorithm without being fully aware of the implications. +In contrast to BIP340 signing, the values ''k1'' and ''k2'' must _not_ be derived deterministically from the session parameters because otherwise active attackers can [https://medium.com/blockstream/musig-dn-schnorr-multisignatures-with-verifiably-deterministic-nonces-27424b5df9d6#e3b6 trick the victim into reusing a nonce]. + +The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random. +If the value ''rand' '' would be identical in two ''NonceGen'' invocations, but any optional argument is unequal, the values ''k1'' and ''k2'' are unequal as well (with overwhelming probability). +In this case, accidentally using the same ''secnonce'' for ''Sign'' in both sessions would be avoided. +Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation. +The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs. +However, the protection from the optional arguments should only be viewed as a last resort. +In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker. + +On systems where obtaining uniformly random values is much harder than maintaining a global atomic counter, it can be beneficial to modify ''NonceGen''. +Instead of drawing ''rand' '' uniformly at random, ''rand' '' can be the output of an atomic counter. +With this modification, the secret signing key ''sk'' of the signer generating the nonce is _not_ an optional argument and must be provided to ''NonceGen''. +The counter must never return the same output in two ''NonceGen'' invocations with the same ''sk''. + +It is possible to modify ''NonceGen'' such that the ''secnonce'' of a single signer can be derived deterministically. +For a deterministic nonce generation algorithm ''NonceGen' '', the arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key and the aggregate public key and message of the session. +In addition, ''NonceGen' '' requires the ''pubnonce'' value of _all_ other signers, which can be provided via the ''in'' argument. +Hence, using ''NonceGen' '' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the signer mentioned in the [[#signing-flow|Signing Flow]] section. +Lastly, to make ''NonceGen' '' deterministic, ''rand' '' is removed and ''rand'' is set to ''sk''. +Note that failure to provide the correct arguments to ''NonceGen' '' will allow attackers to extract secret keys. ==== Tweaking ==== -TODO +In addition to public keys, the ''KeyAgg'' algorithm accepts tweaks, which modify the aggregate public key as defined in the [[#tweaking-definition|Tweaking Definition]] subsection. +For example, if ''KeyAgg'' is run with ''v = 2'', ''is_xonly_t1 = false'', ''is_xonly_t2 = true'', then the aggregate key is first ordinarily tweaked with ''tweak1'' and then X-only tweaked with ''tweak2''. + +The purpose of specifying tweaking is to ensure compatibility with existing uses of tweaking, i.e., that the result of signing is a valid signature for the tweaked public key. +The MuSig2 algorithms take arbitrary tweaks as input but accepting arbitrary tweaks may negatively affect the protocol's security. +Instead, signers should obtain the tweaks according to other specifications. +This typically involves deriving the tweaks from a hash of the aggregate public key and some other information. + +Ordinary tweaking can be used to derive child public keys from an aggregate public key using [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. +On the other hand, X-only tweaking is required for Taproot tweaking per [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]. +A Taproot-tweaked public key commits to a ''script path'', allowing users to create transaction outputs that are spendable either with a MuSig2 multi-signature or by providing inputs that satisfy the script path. === Notation === From fd51a6281ec21c9dcb71c13666a2551370e31fd1 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 4 Apr 2022 10:57:30 +0000 Subject: [PATCH 183/381] musig-spec: add authors --- doc/musig-spec.mediawiki | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 390e2794..a1f86280 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,7 +1,9 @@
   BIP: ?
   Title: MuSig2
-  Author:
+  Author: Jonas Nick 
+          Tim Ruffing 
+          Elliott Jin 
   Status: Draft
   License: BSD-3-Clause
   Type: Informational

From d903c09fd2087684281239187346cc8856b4fdca Mon Sep 17 00:00:00 2001
From: Tim Ruffing 
Date: Mon, 4 Apr 2022 19:12:16 +0200
Subject: [PATCH 184/381] musig-spec: Improve writing in Motivation, Design

---
 doc/musig-spec.mediawiki | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki
index a1f86280..0022868f 100644
--- a/doc/musig-spec.mediawiki
+++ b/doc/musig-spec.mediawiki
@@ -24,19 +24,23 @@ This document is licensed under the 3-clause BSD license.
 
 === Motivation ===
 
-MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create a single Schnorr signature for the aggregate key and a message.
-This is more space-efficient and has lower verification costs than each signer providing an individual public key and signature.
-Since MuSig2 is not a threshold-signature scheme, the cooperation of ''all'' signers involved in key aggregation is required to produce a signature.
+MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create ordinary Schnorr signatures valid under the aggregate key. 
+Signing requires interaction between ''all'' signers involved in key aggregation.
+(MuSig2 is a ''n-of-n'' multi-signature scheme and not a ''t-of-n' threshold-signature scheme.) 
 
-One of the primary motivations for MuSig2 is the activation of Taproot ([https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]) on the Bitcoin network, which introduced the ability to authorize transactions with Schnorr signatures.
+The primary motivation for MuSig2 is the activation of Taproot ([https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]) on the Bitcoin network, which introduced the ability to authorize transactions with Schnorr signatures.
 This standard allows the creation of aggregate public keys that can be used in Taproot outputs.
-Such outputs are indistinguishable for a blockchain observer from regular, single-signer outputs but are actually controlled by multiple signers.
-Moreover, by tweaking an aggregate key, the shared Taproot output can have script spending paths that are hidden unless used.
+
+The on-chain footprint of a MuSig2 Taproot output is a single BIP340 public key, and a transaction spending the output only requires a single signature cooperatively produced by all signers. This is '''more compact''' and has '''lower verification cost''' than each signer providing an individual public key and signature, as would be required by an ''n-of-n'' policy implemented using OP_CHECKSIGADD as introduced in ([https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki BIP342]). 
+As a side effect, the number ''n'' of signers is not limited by any consensus rules when using MuSig2.
+
+Moreover, MuSig2 offers a '''higher level of privacy''' than OP_CHECKSIGADD: MuSig2 Taproot outputs are indistinguishable for a blockchain observer from regular, single-signer Taproot outputs even though they are actually controlled by multiple signers. By tweaking an aggregate key, the shared Taproot output can have script spending paths that are hidden unless used.
 
 There are multi-signature schemes other than MuSig2 that are fully compatible with Schnorr signatures.
-MuSig2 stands out by combining the following features:
+The MuSig2 variant in this specification stands out by combining all of the following features:
 * '''Simple Key Setup''': Key aggregation is non-interactive and fully compatible with BIP340 public keys.
-* '''Two Communication Rounds''': MuSig2 is faster in practice than three-round multi-signature protocols, particularly when signers are connected through high-latency anonymizing links. Moreover, less communication rounds simplifies the specification and reduces the probability that users make security-relevant mistakes. To prove the security of using only two communication rounds, MuSig2 relies on the algebraic one-more discrete logarithm (AOMDL) assumption instead of the discrete logarithm assumption. AOMDL is a falsifiable and weaker variant of the well-studied OMDL problem.
+* '''Two Communication Rounds''': MuSig2 is faster in practice than previous three-round multi-signature protocols such as MuSig1, particularly when signers are connected through high-latency anonymous links. Moreover, the need for fewer communication rounds simplifies the specification and reduces the probability that implementations and users make security-relevant mistakes.
+* '''Provable security''': MuSig2 has been [https://eprint.iacr.org/2020/1261.pdf proven existentially unforgeable] under the algebraic one-more discrete logarithm (AOMDL) assumption (instead of the discrete logarithm assumption required for single-signer Schnorr signatures). AOMDL is a falsifiable and weaker variant of the well-studied OMDL problem.
 * '''Low complexity''': MuSig2 has a substantially lower computational and implementation complexity than alternative schemes like [https://eprint.iacr.org/2020/1057 MuSig-DN]. However, this comes at the cost of having no ability to generate nonces deterministically and the requirement to securely handle signing state.
 
 === Design ===
@@ -44,14 +48,14 @@ MuSig2 stands out by combining the following features:
 * '''Compatibility with BIP340''': The aggregate public key created as part of this MuSig2 specification is a BIP340 X-only public key, and the signature output at the end of the protocol is a BIP340 signature that passes BIP340 verification for the aggregate key and a message. The public keys that are input to the key aggregation algorithm are also X-only public keys. Compared to compressed serialization, this adds complexity to the specification, but as X-only keys are becoming more common, the full key may not be available.
 * '''Tweaking for BIP32 derivations and Taproot''': The specification supports tweaking aggregate public keys and signing for tweaked aggregate public keys. We distinguish two modes of tweaking: ''Ordinary'' tweaking can be used to derive child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking, on the other hand, allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] tweak to add script paths to a Taproot output. See section [[#tweaking|Tweaking]] below for details.
 * '''Non-interactive signing with preprocessing''': The first communication round, exchanging the nonces, can happen before the message or even the exact set of signers is determined. Therefore, the signers can view it as a preprocessing step. Later, when the parameters of the signing session are chosen, they can send partial signatures without additional interaction.
-* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines an algorithm to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a common order of signers. Then, sorting is unnecessary and very slow for a large set of signers compared to the rest of the MuSig2 protocol. In the worst case, sorting algorithms in standard libraries can have quadratic run time, which is undesirable in adversarial settings. Nonetheless, standards using this specification can mandate sorting before aggregation. Note that the key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting.
-* '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the security of the scheme.
+* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines an algorithm to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a canonical order of signers. Then, sorting is unnecessary and very slow for a large set of signers compared to the rest of the MuSig2 protocol. In the worst case, sorting algorithms in standard libraries can have quadratic run time, which is undesirable in adversarial settings. Nonetheless, applications using this specification can mandate sorting before aggregation.
+* '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the unforgeability of the scheme.
 * '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because the individual nonce is required for partial verification.
 * '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''.
 * '''Parameterization of MuSig2 and security''': In this specification, each signer's nonce consists of two elliptic curve points. The [https://eprint.iacr.org/2020/1261 MuSig2 paper] gives distinct security proofs depending on the number of points that constitute a nonce. See section [[#choosing-the-size-of-the-nonce|Choosing the Size of the Nonce]] for a discussion.
 
-The specification itself is designed such that efficiency and clarity are balanced.
-The algorithms, as specified, are not optimal in terms of computation and space.
+This specification is written with a focus on clarity.
+As a result, the specified algorithms are not always optimal in terms of computation and space.
 In particular, some values are recomputed but can be cached in actual implementations (see [[#signing-flow|Signing Flow]]).
 Also, the signers' public nonces are serialized in compressed format (33 bytes) instead of the smaller (32 bytes) but more complicated X-only serialization.
 
@@ -236,7 +240,7 @@ Input:
 * Let ''L = HashKeys(pk1..u)''
 * If ''pk' = pk2'':
 ** Return 1
-* Return ''int(hashKeyAgg coefficient(L || pk')) mod n''
+* Return ''int(hashKeyAgg coefficient(L || pk')) mod n''The key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting.
 
 '''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''':
 * If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'':

From 510b61a80304f7e9aabec5f7d9968f94cc0f22e1 Mon Sep 17 00:00:00 2001
From: Jonas Nick 
Date: Mon, 4 Apr 2022 21:38:46 +0000
Subject: [PATCH 185/381] musig: add test vectors for applying multiple tweaks

---
 src/modules/musig/tests_impl.h | 103 ++++++++++++++++++++++++++++-----
 1 file changed, 88 insertions(+), 15 deletions(-)

diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h
index aab68615..3125d1ed 100644
--- a/src/modules/musig/tests_impl.h
+++ b/src/modules/musig/tests_impl.h
@@ -1143,7 +1143,7 @@ void musig_test_vectors_noncegen(void) {
     }
 }
 
-void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char *tweak, int xonly_tweak, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) {
+void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char tweak[][32], const int *is_xonly_t, int n_tweaks, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) {
     secp256k1_keypair signer_keypair;
     secp256k1_musig_secnonce secnonce;
     secp256k1_xonly_pubkey pk[3];
@@ -1163,11 +1163,11 @@ void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache,
         pk_ptr[i] = &pk[i];
     }
     CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, keyagg_cache, pk_ptr, 3) == 1);
-    if (tweak != NULL) {
-        if (xonly_tweak) {
-            CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1);
+    for (i = 0; i < n_tweaks; i++) {
+        if (is_xonly_t[i]) {
+            CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak[i]) == 1);
         } else {
-            CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1);
+            CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, keyagg_cache, tweak[i]) == 1);
         }
     }
     memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4);
@@ -1247,7 +1247,7 @@ void musig_test_vectors_sign(void) {
             0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7,
             0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B,
         };
-        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 0);
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, NULL, pk, 0);
         /* TODO: remove when test vectors are not expected to change anymore */
         /* int k, l; */
         /* printf("const unsigned char sig_expected[32] = {\n"); */
@@ -1276,7 +1276,7 @@ void musig_test_vectors_sign(void) {
             0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35,
             0x7C, 0xEC, 0xA7, 0xC8, 0x42, 0x42, 0x95, 0xEA,
         };
-        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 1);
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, NULL, pk, 1);
         /* Check that the description of the test vector is correct */
         CHECK(musig_test_pk_parity(&keyagg_cache) == 0);
         CHECK(musig_test_is_second_pk(&keyagg_cache, sk));
@@ -1292,7 +1292,7 @@ void musig_test_vectors_sign(void) {
             0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF,
             0xA4, 0xA3, 0xD1, 0xBC, 0xAA, 0xBC, 0x75, 0x17,
         };
-        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 2);
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, NULL, pk, 2);
         /* Check that the description of the test vector is correct */
         CHECK(musig_test_pk_parity(&keyagg_cache) == 1);
         CHECK(fin_nonce_parity == 0);
@@ -1307,13 +1307,14 @@ void musig_test_vectors_sign(void) {
             0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B,
             0x49, 0x13, 0x64, 0x17, 0x75, 0xE1, 0x37, 0x5B,
         };
-        const unsigned char tweak[32] = {
+        const unsigned char tweak[1][32] = {{
             0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF,
             0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D,
             0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79,
             0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB,
-        };
-        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, 1, NULL, pk, 2);
+        }};
+        int is_xonly_t[1] = { 1 };
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 1, NULL, pk, 2);
 
         CHECK(musig_test_pk_parity(&keyagg_cache) == 1);
         CHECK(!musig_test_is_second_pk(&keyagg_cache, sk));
@@ -1328,19 +1329,91 @@ void musig_test_vectors_sign(void) {
             0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD,
             0x7B, 0xC5, 0x99, 0x16, 0x44, 0xE4, 0x4D, 0xDD,
         };
-        const unsigned char tweak[32] = {
+        const unsigned char tweak[1][32] = {{
             0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF,
             0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D,
             0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79,
             0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB,
-        };
-        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, 0, NULL, pk, 2);
+        }};
+        int is_xonly_t[1] = { 0 };
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 1, NULL, pk, 2);
 
         CHECK(musig_test_pk_parity(&keyagg_cache) == 1);
         CHECK(!musig_test_is_second_pk(&keyagg_cache, sk));
         CHECK(fin_nonce_parity == 0);
         CHECK(memcmp(sig, sig_expected, 32) == 0);
     }
+    {
+       /* This is a test that includes an ordinary and an x-only public key tweak. */
+        const unsigned char sig_expected[32] = {
+            0xC3, 0xA8, 0x29, 0xA8, 0x14, 0x80, 0xE3, 0x6E,
+            0xC3, 0xAB, 0x05, 0x29, 0x64, 0x50, 0x9A, 0x94,
+            0xEB, 0xF3, 0x42, 0x10, 0x40, 0x3D, 0x16, 0xB2,
+            0x26, 0xA6, 0xF1, 0x6E, 0xC8, 0x5B, 0x73, 0x57,
+        };
+
+        const unsigned char tweak[2][32] = {
+            {
+                0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF,
+                0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D,
+                0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79,
+                0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB,
+            },
+            {
+                0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A,
+                0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D,
+                0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0,
+                0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55,
+            },
+        };
+        int is_xonly_t[2] = { 0, 1 };
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 2, NULL, pk, 2);
+        CHECK(musig_test_pk_parity(&keyagg_cache) == 0);
+        CHECK(!musig_test_is_second_pk(&keyagg_cache, sk));
+        CHECK(fin_nonce_parity == 0);
+        CHECK(memcmp(sig, sig_expected, 32) == 0);
+    }
+    {
+       /* This is a test with four tweaks: x-only, ordinary, x-only, ordinary. */
+        const unsigned char sig_expected[32] = {
+            0x8C, 0x44, 0x73, 0xC6, 0xA3, 0x82, 0xBD, 0x3C,
+            0x4A, 0xD7, 0xBE, 0x59, 0x81, 0x8D, 0xA5, 0xED,
+            0x7C, 0xF8, 0xCE, 0xC4, 0xBC, 0x21, 0x99, 0x6C,
+            0xFD, 0xA0, 0x8B, 0xB4, 0x31, 0x6B, 0x8B, 0xC7,
+        };
+        const unsigned char tweak[4][32] = {
+            {
+                0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF,
+                0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D,
+                0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79,
+                0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB,
+            },
+            {
+                0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A,
+                0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D,
+                0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0,
+                0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55,
+            },
+            {
+                0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE,
+                0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, 0x45, 0x7E,
+                0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20,
+                0x83, 0x16, 0x26, 0xF2, 0x90, 0xFA, 0x87, 0xE0,
+            },
+            {
+                0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0,
+                0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, 0xBF, 0x99,
+                0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A,
+                0x74, 0xFE, 0xD0, 0xA3, 0xCF, 0x5A, 0xEF, 0x9D,
+            },
+        };
+        int is_xonly_t[4] = { 1, 0, 1, 0 };
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 4, NULL, pk, 2);
+        CHECK(musig_test_pk_parity(&keyagg_cache) == 0);
+        CHECK(!musig_test_is_second_pk(&keyagg_cache, sk));
+        CHECK(fin_nonce_parity == 1);
+        CHECK(memcmp(sig, sig_expected, 32) == 0);
+    }
     {
        /* This is a test that includes an adaptor. */
         const unsigned char sig_expected[32] = {
@@ -1357,7 +1430,7 @@ void musig_test_vectors_sign(void) {
         };
         secp256k1_pubkey pub_adaptor;
         CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor) == 1);
-        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, &pub_adaptor, pk, 2);
+        musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, &pub_adaptor, pk, 2);
 
         CHECK(musig_test_pk_parity(&keyagg_cache) == 1);
         CHECK(!musig_test_is_second_pk(&keyagg_cache, sk));

From 376733b58b282a4985dd78d0125749473f0aeff3 Mon Sep 17 00:00:00 2001
From: Jonas Nick 
Date: Mon, 4 Apr 2022 21:48:38 +0000
Subject: [PATCH 186/381] musig-spec: clarify hashing in noncegen by converting
 ints to bytes

---
 doc/musig-spec.mediawiki | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki
index 0022868f..b103393d 100644
--- a/doc/musig-spec.mediawiki
+++ b/doc/musig-spec.mediawiki
@@ -165,7 +165,7 @@ The following conventions are used, with constants as defined for [https://www.s
 * Functions and operations:
 ** ''||'' refers to byte array concatenation.
 ** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''.
-** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first.
+** The function ''bytes(n, x)'', where ''x'' is an integer, returns the n-byte encoding of ''x'', most significant byte first.
 ** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''.
 ** The function ''len(x)'' where ''x'' is a byte array returns the length of the array.
 ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''.
@@ -230,7 +230,7 @@ Input:
 * For ''j = 1 .. u'':
 ** If ''pkj ≠ pk1'':
 *** Return ''pkj''
-* Return ''bytes(0)''
+* Return ''bytes(32, 0)''
 
 '''''KeyAggCoeff(pk1..u, pk')''''':
 * Let ''pk2 = GetSecondKey(pk1..u)'':
@@ -259,18 +259,18 @@ Input:
 * The secret signing key ''sk'': a 32-byte array or 0-byte array (optional argument)
 * The aggregate public key ''aggpk'': a 32-byte array or 0-byte array (optional argument)
 * The message ''m'': a 32-byte array or 0-byte array (optional argument)
-* The auxiliary input ''in'': a byte array of length ''≥ 0'' (optional argument)
+* The auxiliary input ''in'': a byte array with ''0 ≤ len(in) ≤ 232-1'' (optional argument)
 
 '''''NonceGen(sk, aggpk, m, in)''''':
 * Let ''rand' '' be a 32-byte array freshly drawn uniformly at random
 * If ''len(sk) > 0'':
 ** Let ''rand'' be the byte-wise xor of ''sk'' and ''hashMuSig/aux(rand')''The random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the secret signing key itself. It is xored with the secret key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key..
 * Else: let ''rand = rand' ''
-* Let ''ki = int(hashMuSig/nonce(rand || len(aggpk) || aggpk || i || len(m) || m || len(in) || in)) mod n'' for ''i = 1,2''
+* Let ''ki = int(hashMuSig/nonce(rand || bytes(1, len(aggpk)) || aggpk || bytes(1, i) || bytes(1, len(m)) || m || bytes(4, len(in)) || in)) mod n'' for ''i = 1,2''
 * Fail if ''k1 = 0'' or ''k2 = 0''
 * Let ''R*1 = k1⋅G, R*2 = k2⋅G''
 * Let ''pubnonce = cbytes(R*1) || cbytes(R*2)''
-* Let ''secnonce = bytes(k1) || bytes(k2)''
+* Let ''secnonce = bytes(32, k1) || bytes(32, k2)''
 * Return ''secnonce'' and ''pubnonce''
 
 ==== Nonce Aggregation ====
@@ -335,7 +335,7 @@ Input:
 * Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n''
 * 
Let ''d = gv⋅gaccv⋅gp⋅d' '' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) * Let ''s = (k1 + b⋅k2 + e⋅a⋅d) mod n'' -* Let ''psig = bytes(s)'' +* Let ''psig = bytes(32, s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' * If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. * Return partial signature ''psig'' @@ -385,7 +385,7 @@ Input: ** Let ''si = int(psigi)''; fail if ''si ≥ n''. * Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' * Let ''s = s1 + ... + su + e⋅gv⋅taccv mod n'' -* Return ''sig = ''bytes(R) || bytes(s)'' +* Return ''sig = ''bytes(R) || bytes(32, s)'' === Test Vectors and Reference Code === From b7f8ea2f2a828cb5a6804320a39750a77fffafba Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 4 Apr 2022 22:39:38 +0000 Subject: [PATCH 187/381] musig-spec: address robot-dreams' comments - KeyAggCoeff' -> KeyAggCoeffInternal for consistency - In Sign, add mod n when calculating d - In Tweak, reorder the parameters to (Q, gacc, tacc, tweak, is_xonly) because the first three are "state" arguments - Rename Tweak function to ApplyTweak to avoid confusion with tweak (the vector). This becomes apparent in the python reference code. --- doc/musig-spec.mediawiki | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 0022868f..ef7224d2 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -214,13 +214,13 @@ Input: * Let ''pk2 = GetSecondKey(pk1..u)'' * For ''i = 1 .. u'': ** Let ''Pi = point(pki)''; fail if that fails. -** Let ''ai = KeyAggCoeff'(pk1..u, pki, pk2)''. +** Let ''ai = KeyAggCoeffInternal(pk1..u, pki, pk2)''. * Let ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' * Fail if ''is_infinite(Q0)''. * Let ''tacc0 = 0'' * Let ''gacc0 = 1'' * For ''i = 1 .. v'': -** Let ''(Qi, gacci, tacci) = Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''; fail if that fails +** Let ''(Qi, gacci, tacci) = ApplyTweak(Qi-1, gacci-1, tacci-1, tweaki, is_xonly_ti)''; fail if that fails * Return ''(Qv, gaccv, taccv)''. '''''HashKeys(pk1..u)''''': @@ -234,15 +234,15 @@ Input: '''''KeyAggCoeff(pk1..u, pk')''''': * Let ''pk2 = GetSecondKey(pk1..u)'': -* Return ''KeyAggCoeff'(pk1..u, pk', pk2)'' +* Return ''KeyAggCoeffInternal(pk1..u, pk', pk2)'' -'''''KeyAggCoeff'(pk1..u, pk', pk2)''''': +'''''KeyAggCoeffInternal(pk1..u, pk', pk2)''''': * Let ''L = HashKeys(pk1..u)'' * If ''pk' = pk2'': ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n''The key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting. -'''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''': +'''''ApplyTweak(Qi-1, gacci-1, tacci-1, tweaki, is_xonly_ti)''''': * If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': ** Let ''gi-1 = -1 mod n'' * Else: let ''gi-1 = 1'' @@ -333,7 +333,7 @@ Input: * Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails * Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' * Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' -*
Let ''d = gv⋅gaccv⋅gp⋅d' '' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) +*
Let ''d = gv⋅gaccv⋅gp⋅d' mod n'' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) * Let ''s = (k1 + b⋅k2 + e⋅a⋅d) mod n'' * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' @@ -402,10 +402,10 @@ Input: * ''P'': a point * The tweak ''t'': an integer with ''0 ≤ t < n '' -'''''OrdinaryTweak(P, t)''''': +'''''ApplyOrdinaryTweak(P, t)''''': * Return ''P + t⋅G'' -'''''XonlyTweak(P, t)''''': +'''''ApplyXonlyTweak(P, t)''''': * Return ''with_even_y(P) + t⋅G'' === Negation Of The Secret Key When Signing === From 1b292cdb52844828559a650e9ed70f10160d75ee Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 5 Apr 2022 15:01:09 +0200 Subject: [PATCH 188/381] Improve writing in Signing flow --- doc/musig-spec.mediawiki | 91 +++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 1a3df765..fe8e05dc 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -61,7 +61,7 @@ Also, the signers' public nonces are serialized in compressed format (33 bytes) == Description == -When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that lead to catastrophic failure. +When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that may lead to catastrophic failure. === Signing Flow === @@ -74,20 +74,45 @@ After running ''Sign'' with the secret signing key, the ''secnonce'' and the ses If all signers behaved honestly, the result passes [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] verification. '''IMPORTANT''': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. -Otherwise, extracting the secret signing key from the partial signatures is possible. -To avoid accidental reuse, an implementation may securely erase the ''secnonce'' argument by overwriting it with zeros after ''Sign'' has been run. -A ''secnonce'' consisting of only zeros is invalid for ''Sign'' and will cause it to fail. +Otherwise, it is possible to extract the secret signing key from the two partial signatures output by the two executions of ''Sign''. +To avoid accidental reuse of ''secnonce'', an implementation may securely erase the ''secnonce'' argument by overwriting it with 32 zero bytes after it has been read by ''Sign'. +A ''secnonce'' consisting of only zero bytes is invalid for ''Sign'' and will cause it to fail. + +To simplify the specification, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times. +Actual implementations can cache these values. +As a result, the [[#session-context|Session Context]] may look very different in implementations or may not exist at all. + +==== Public Key Aggregation ==== The output of ''KeyAgg'' is dependent on the order of the input public keys. -If there is no common order of the signers already, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the same aggregate key is calculated. -Note that public keys are allowed to occur multiple times in the input of ''KeyAgg'' and ''KeySort'', and that it is possible to successfully complete a MuSig2 signing session with duplicated public keys. +If the application does not have a canonical order of the signers, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the aggregate key is independent of the order of signers. + +The same public key is allowed to occur more than once in the input of ''KeyAgg'' and ''KeySort''. +This is by design: All algorithms in this specification handle multiple signers who (claim to) have identical public keys properly, +and applications are not required to check for duplicate public keys. +In fact, applications are recommended to omit checks for duplicates public keys in order to simplify error handling. +Moreover, it is often impossible to tell at key aggregation which signer is to blame for the duplicate, i.e., which signer came up with the public key honestly and which disruptive signer copied it. +In constrast, MuSig2 is designed to identify disruptive signers at signing time: any signer who prevents a signing session from completing succesfully by sending incorrect contributions in the session can be identified and hold accountable (see below). + +==== Nonce Generation ==== + +'''IMPORTANT''': ''NonceGen'' must have access to a high-quality random generator to draw an unbiased, uniformly random value ''rand' ''. +In contrast to BIP340 signing, the values ''k1'' and ''k2'' '''must not be derived deterministically''' from the session parameters because otherwise active attackers can [https://medium.com/blockstream/musig-dn-schnorr-multisignatures-with-verifiably-deterministic-nonces-27424b5df9d6#e3b6 trick the victim into reusing a nonce]. + +The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random. +If the value ''rand' '' was identical in two ''NonceGen'' invocations, but any optional argument was different, the ''secnonce'' would still be guaranteed be different as well (with overwhelming probability), and thus accidentally using the same ''secnonce'' for ''Sign'' in both sessions would be avoided. +Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation. +The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs, +e.g., a supposedly unique session id (taken from the application), a session counter wide enough not to repeat in practice, any nonces by other signers (if already known), or the serialization of a data structure containing multiple of the above. +However, the protection provided the optional arguments should only be viewed as a last resort. +In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker. In some applications, it is beneficial to generate and exchange ''pubnonces'' before the signer's secret key, the final set of signers, or the message to sign is known. In this case, only the available arguments are provided to the ''NonceGen'' algorithm. After this preprocessing phase, the ''Sign'' algorithm can be run immediately when the message and set of signers is determined. This way, the final signature is created quicker and with fewer roundtrips. However, applications that use this method presumably store the nonces for a longer time and must therefore be even more careful not to reuse them. -Moreover, this method prohibits a defense-in-depth measure that strengthens [[#nonce-generation|Nonce Generation]]. +Moreover, this method is not compatible with the defense-in-depth mechanism described in the previous paragraph. Instead of every signer broadcasting their ''pubnonce'' to every other signer, the signers can send their ''pubnonce'' to a single aggregator node that runs ''NonceAgg'' and sends the ''aggnonce'' back to the signers. This technique reduces the overall communication. @@ -99,45 +124,15 @@ However, it is possible for one of the signers to be stateless. This signer waits until it receives the ''pubnonce'' of all the other signers and until session parameters such as a message to sign, public keys, and tweaks are determined. Then, the signer can run ''NonceGen'', ''NonceAgg'' and ''Sign'' in sequence and send out its ''pubnonce'' along with its partial signature. +==== Identifiying Disruptive Signers ==== If any signer sends an incorrect partial signature, i.e., one that has not then been created with ''Sign'' and the right arguments for the session, the MuSig2 protocol may fail to output a valid Schnorr signature. This standard provides the method ''PartialSigVerify'' to verify the correctness of partial signatures. -If partial signatures are authenticated, this method can be used to identify disruptive signers and hold them accountable. +If partial signatures are received over authenticated channels, this method can be used to identify disruptive signers and hold them accountable. Note that partial signatures are ''not'' signatures. An adversary can forge a partial signature, i.e., create a partial signature without knowing the secret key for the claimed public keyAssume an adversary wants to forge a partial signature for public key ''P''. It joins the signing session pretending to be two different signers, one with public key ''P' and one with another public key. The adversary can then set the second signer's nonce such that it will be able to produce a partial signature for ''P'', but not for the other claimed signer.. However, if ''PartialSigVerify'' succeeds for all partial signatures then ''PartialSigAgg'' will return a valid Schnorr signature. -To simplify the specification, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times. -Actual implementations can cache these values. -As a result, the [[#session-context|Session Context]] may look very different in implementations or may not exist at all. - -==== Nonce Generation ==== - -'''IMPORTANT''': ''NonceGen'' must have access to a high-quality random generator to draw an unbiased, uniformly random value ''rand' ''. -Additionally, implementors must avoid modifying the ''NonceGen'' algorithm without being fully aware of the implications. -In contrast to BIP340 signing, the values ''k1'' and ''k2'' must _not_ be derived deterministically from the session parameters because otherwise active attackers can [https://medium.com/blockstream/musig-dn-schnorr-multisignatures-with-verifiably-deterministic-nonces-27424b5df9d6#e3b6 trick the victim into reusing a nonce]. - -The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random. -If the value ''rand' '' would be identical in two ''NonceGen'' invocations, but any optional argument is unequal, the values ''k1'' and ''k2'' are unequal as well (with overwhelming probability). -In this case, accidentally using the same ''secnonce'' for ''Sign'' in both sessions would be avoided. -Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation. -The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs. -However, the protection from the optional arguments should only be viewed as a last resort. -In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker. - -On systems where obtaining uniformly random values is much harder than maintaining a global atomic counter, it can be beneficial to modify ''NonceGen''. -Instead of drawing ''rand' '' uniformly at random, ''rand' '' can be the output of an atomic counter. -With this modification, the secret signing key ''sk'' of the signer generating the nonce is _not_ an optional argument and must be provided to ''NonceGen''. -The counter must never return the same output in two ''NonceGen'' invocations with the same ''sk''. - -It is possible to modify ''NonceGen'' such that the ''secnonce'' of a single signer can be derived deterministically. -For a deterministic nonce generation algorithm ''NonceGen' '', the arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key and the aggregate public key and message of the session. -In addition, ''NonceGen' '' requires the ''pubnonce'' value of _all_ other signers, which can be provided via the ''in'' argument. -Hence, using ''NonceGen' '' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the signer mentioned in the [[#signing-flow|Signing Flow]] section. -Lastly, to make ''NonceGen' '' deterministic, ''rand' '' is removed and ''rand'' is set to ''sk''. -Note that failure to provide the correct arguments to ''NonceGen' '' will allow attackers to extract secret keys. - ==== Tweaking ==== - In addition to public keys, the ''KeyAgg'' algorithm accepts tweaks, which modify the aggregate public key as defined in the [[#tweaking-definition|Tweaking Definition]] subsection. For example, if ''KeyAgg'' is run with ''v = 2'', ''is_xonly_t1 = false'', ''is_xonly_t2 = true'', then the aggregate key is first ordinarily tweaked with ''tweak1'' and then X-only tweaked with ''tweak2''. @@ -150,6 +145,24 @@ Ordinary tweaking can be used to derive child public keys from an aggregate publ On the other hand, X-only tweaking is required for Taproot tweaking per [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]. A Taproot-tweaked public key commits to a ''script path'', allowing users to create transaction outputs that are spendable either with a MuSig2 multi-signature or by providing inputs that satisfy the script path. +==== Modifications to Nonce Generation ==== + +Implementors must avoid modifying the ''NonceGen'' algorithm without being fully aware of the implications. +The following two modifications are secure when applied correctly and may be useful in special circumstances, e.g., in very restricted environments where secure randomness is not available. + +First, on systems where obtaining uniformly random values is much harder than maintaining a global atomic counter, it can be beneficial to modify ''NonceGen''. +Instead of drawing ''rand' '' uniformly at random, ''rand' '' can be the value of an atomic counter that is incremented whenever it is read. +With this modification, the secret signing key ''sk'' of the signer generating the nonce is '''not''' an optional argument and must be provided to ''NonceGen''. +The security of the resulting scheme is then depending on the requirement that the counter must never return the same output in two ''NonceGen'' invocations with the same ''sk''. + +Second, if there is unique signer who is supposed to send the ''pubnonce'' last, it is possible to modify nonce generation for this single signer to be deterministic and not require randomness. +To obtain a deterministic nonce generation algorithm ''NonceGenDeterministic'', the algorithm ''NonceGen'' should be modified as follows: The arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key and the aggregate public key and message of the session. +In addition, ''NonceGenDeterministic'' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''in'' argument. +Hence, using ''NonceGenDeterministic'' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the stateless signer described in the [[#nonce-generation|Nonce Generation]] section. +Further inputs can be to added ''in'' as described in the [[#nonce-generation|Nonce Generation]] section. +Lastly, to make ''NonceGenDeterministic'' deterministic, ''rand' '' is removed and ''rand'' is set to ''sk''. +Failure to provide the correct arguments to ''NonceGenDeterministic'' will allow attackers to extract secret keys. + === Notation === The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme. From 0940575215f282456d689e7ce2c83a1c40a9c86b Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Tue, 5 Apr 2022 12:23:36 -0400 Subject: [PATCH 189/381] musig-spec: Clarify negation for signing and verification --- doc/musig-spec.mediawiki | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index fe8e05dc..26c2417f 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -426,27 +426,28 @@ Input: In order to produce a partial signature for an X-only public key that is an aggregate of ''u'' X-only keys and tweaked ''v'' times (X-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. -The following public keys arise as intermediate steps in the MuSig2 protocol: -• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's X-only public key. Defining ''d'i'' to be the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have +The following elliptic curve points arise as intermediate steps in the MuSig2 protocol: +• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's X-only public key. Defining ''d'i'' to be the ''i''-th signer's secret key as an integer, i.e. the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have ''Pi = with_even_y(d'i⋅G) ''. • ''Q0'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu''. • ''Qi'' as computed in ''Tweak'' for ''1 ≤ i ≤ v'' is the tweaked public key after the ''i''-th tweaking operation. It holds that ''Qi = f(i-1) + ti⋅G'' for ''i = 1, ..., v'' where - ''f(i) := with_even_y(Qi)'' if ''is_xonly_ti+1'' and - ''f(i) := Qi'' otherwise. + ''f(i-1) := with_even_y(Qi-1)'' if ''is_xonly_ti'' and + ''f(i-1) := Qi-1'' otherwise. +• ''with_even_y(Qv)'' is the final result of ''KeyAgg''. -The goal is to produce a partial signature corresponding to the output of ''KeyAgg'', i.e., the final (X-only) public key point after ''v'' tweaking operations ''with_even_y(Qv)''. +The signer's goal is to produce a partial signature corresponding to the final result of ''KeyAgg'', i.e. the X-only public key ''with_even_y(Qv)''. -We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. It holds that +We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. Note that ''gpi'' indicates whether the ''i''-th signer needed to negate their secret key to produce an X-only public key. In particular, ''Pi = gpi⋅d'i⋅G''. -For ''0 ≤ i ≤ v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''gi'' to ''-1 mod n'' if and only if ''is_xonly_ti+1'' is true and ''Qi'' has an odd Y coordinate. Therefore, we have +For ''0 ≤ i ≤ v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''gi'' to ''-1 mod n'' if and only if ''is_xonly_ti+1'' is true and ''Qi'' has an odd Y coordinate. In other words, ''gi'' indicates whether ''Qi'' needed to be negated to apply an X-only tweak: ''f(i) = gi⋅Qi'' for ''0 ≤ i ≤ v - 1''. -Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv'' such that +Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv'' depending on whether ''Qv'' needed to be negated to produce the (X-only) final output of ''KeyAgg': ''with_even_y(Qv) = gv⋅Qv''. @@ -483,7 +484,7 @@ Then we have = sumi=1..u(gv⋅gaccv⋅gpi⋅ai⋅d'i)*G''. -Thus, signer ''i'' multiplies its secret key ''d'i'' with ''gv⋅gaccv⋅gpi'' in the ''[[#Sign negation|Sign]]'' algorithm. +Intuitively, ''gacci'' tracks accumulated sign flipping and ''tacci'' tracks the accumulated tweak value after applying the first ''i'' individual tweaks. Additionally, ''gv'' indicates whether ''Qv'' needed to be negated to produce the final X-only result, and ''gpi'' indicates whether ''d'i'' needs to be negated to produce the initial X-only key ''Pi''. Thus, signer ''i'' multiplies its secret key ''d'i'' with ''gv⋅gaccv⋅gpi'' in the ''[[#Sign negation|Sign]]'' algorithm. ==== Negation Of The Public Key When Partially Verifying ==== @@ -503,6 +504,7 @@ The verifier doesn't have access to ''d⋅G'', but can construct it using the xo ''d⋅G = gv⋅gaccv⋅gp⋅d'⋅G = gv⋅gaccv⋅point(pk*)'' +Note that the aggregate public key and list of tweaks are inputs to partial signature verification, so the verifier can also construct ''gv'' and ''gaccv''. === Dealing with Infinity in Nonce Aggregation === From bf615193ce673c29598db9bd4c316d76647b83cb Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 5 Apr 2022 14:51:44 +0000 Subject: [PATCH 190/381] musig-spec: minor fixups --- doc/musig-spec.mediawiki | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index fe8e05dc..bb1a5330 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -75,7 +75,7 @@ If all signers behaved honestly, the result passes [https://github.com/bitcoin/b '''IMPORTANT''': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. Otherwise, it is possible to extract the secret signing key from the two partial signatures output by the two executions of ''Sign''. -To avoid accidental reuse of ''secnonce'', an implementation may securely erase the ''secnonce'' argument by overwriting it with 32 zero bytes after it has been read by ''Sign'. +To avoid accidental reuse of ''secnonce'', an implementation may securely erase the ''secnonce'' argument by overwriting it with 64 zero bytes after it has been read by ''Sign'. A ''secnonce'' consisting of only zero bytes is invalid for ''Sign'' and will cause it to fail. To simplify the specification, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times. @@ -88,11 +88,11 @@ The output of ''KeyAgg'' is dependent on the order of the input public keys. If the application does not have a canonical order of the signers, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the aggregate key is independent of the order of signers. The same public key is allowed to occur more than once in the input of ''KeyAgg'' and ''KeySort''. -This is by design: All algorithms in this specification handle multiple signers who (claim to) have identical public keys properly, +This is by design: All algorithms in this specification handle multiple signers who (claim to) have identical public keys properly, and applications are not required to check for duplicate public keys. -In fact, applications are recommended to omit checks for duplicates public keys in order to simplify error handling. +In fact, applications are recommended to omit checks for duplicate public keys in order to simplify error handling. Moreover, it is often impossible to tell at key aggregation which signer is to blame for the duplicate, i.e., which signer came up with the public key honestly and which disruptive signer copied it. -In constrast, MuSig2 is designed to identify disruptive signers at signing time: any signer who prevents a signing session from completing succesfully by sending incorrect contributions in the session can be identified and hold accountable (see below). +In contrast, MuSig2 is designed to identify disruptive signers at signing time: any signer who prevents a signing session from completing successfully by sending incorrect contributions in the session can be identified and held accountable (see below). ==== Nonce Generation ==== @@ -104,7 +104,7 @@ If the value ''rand' '' was identical in two ''NonceGen'' invocations, but any o Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation. The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs, e.g., a supposedly unique session id (taken from the application), a session counter wide enough not to repeat in practice, any nonces by other signers (if already known), or the serialization of a data structure containing multiple of the above. -However, the protection provided the optional arguments should only be viewed as a last resort. +However, the protection provided by the optional arguments should only be viewed as a last resort. In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker. In some applications, it is beneficial to generate and exchange ''pubnonces'' before the signer's secret key, the final set of signers, or the message to sign is known. @@ -125,6 +125,7 @@ This signer waits until it receives the ''pubnonce'' of all the other signers an Then, the signer can run ''NonceGen'', ''NonceAgg'' and ''Sign'' in sequence and send out its ''pubnonce'' along with its partial signature. ==== Identifiying Disruptive Signers ==== + If any signer sends an incorrect partial signature, i.e., one that has not then been created with ''Sign'' and the right arguments for the session, the MuSig2 protocol may fail to output a valid Schnorr signature. This standard provides the method ''PartialSigVerify'' to verify the correctness of partial signatures. If partial signatures are received over authenticated channels, this method can be used to identify disruptive signers and hold them accountable. @@ -133,6 +134,7 @@ An adversary can forge a partial signature, i.e., create a partial signature wit However, if ''PartialSigVerify'' succeeds for all partial signatures then ''PartialSigAgg'' will return a valid Schnorr signature. ==== Tweaking ==== + In addition to public keys, the ''KeyAgg'' algorithm accepts tweaks, which modify the aggregate public key as defined in the [[#tweaking-definition|Tweaking Definition]] subsection. For example, if ''KeyAgg'' is run with ''v = 2'', ''is_xonly_t1 = false'', ''is_xonly_t2 = true'', then the aggregate key is first ordinarily tweaked with ''tweak1'' and then X-only tweaked with ''tweak2''. @@ -155,13 +157,14 @@ Instead of drawing ''rand' '' uniformly at random, ''rand' '' can be the value o With this modification, the secret signing key ''sk'' of the signer generating the nonce is '''not''' an optional argument and must be provided to ''NonceGen''. The security of the resulting scheme is then depending on the requirement that the counter must never return the same output in two ''NonceGen'' invocations with the same ''sk''. -Second, if there is unique signer who is supposed to send the ''pubnonce'' last, it is possible to modify nonce generation for this single signer to be deterministic and not require randomness. -To obtain a deterministic nonce generation algorithm ''NonceGenDeterministic'', the algorithm ''NonceGen'' should be modified as follows: The arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key and the aggregate public key and message of the session. -In addition, ''NonceGenDeterministic'' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''in'' argument. -Hence, using ''NonceGenDeterministic'' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the stateless signer described in the [[#nonce-generation|Nonce Generation]] section. +Second, if there is a unique signer who is supposed to send the ''pubnonce'' last, it is possible to modify nonce generation for this single signer to not require high-quality randomness. +If randomness is entirely unavailable, nonce generation for this signer can also be made deterministic. +To obtain such a nonce generation algorithm ''NonceGen' '', the algorithm ''NonceGen'' should be modified as follows: The arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key, the aggregate public key, and message of the session, respectively. +In addition, ''NonceGen '' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''in'' argument. +Hence, using ''NonceGen' '' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the stateless signer described in the [[#nonce-generation|Nonce Generation]] section. Further inputs can be to added ''in'' as described in the [[#nonce-generation|Nonce Generation]] section. -Lastly, to make ''NonceGenDeterministic'' deterministic, ''rand' '' is removed and ''rand'' is set to ''sk''. -Failure to provide the correct arguments to ''NonceGenDeterministic'' will allow attackers to extract secret keys. +Lastly, if no randomness, not even low-quality randomness, is available, ''NonceGen' '' can be made deterministic by removing ''rand' '' and setting ''rand'' to ''sk''. +Failure to provide the correct arguments to ''NonceGen' '' will allow attackers to extract secret keys. === Notation === From 67247e53afdf32f414a9fbd0fb008b3935b1e6d9 Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Tue, 5 Apr 2022 15:06:34 -0400 Subject: [PATCH 191/381] musig-spec: More minor cleanup --- doc/musig-spec.mediawiki | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 24ce37fe..dbc941fb 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -24,14 +24,14 @@ This document is licensed under the 3-clause BSD license. === Motivation === -MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create ordinary Schnorr signatures valid under the aggregate key. +MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create ordinary Schnorr signatures valid under the aggregate key. Signing requires interaction between ''all'' signers involved in key aggregation. -(MuSig2 is a ''n-of-n'' multi-signature scheme and not a ''t-of-n' threshold-signature scheme.) +(MuSig2 is a ''n-of-n'' multi-signature scheme and not a ''t-of-n' threshold-signature scheme.) The primary motivation for MuSig2 is the activation of Taproot ([https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]) on the Bitcoin network, which introduced the ability to authorize transactions with Schnorr signatures. This standard allows the creation of aggregate public keys that can be used in Taproot outputs. -The on-chain footprint of a MuSig2 Taproot output is a single BIP340 public key, and a transaction spending the output only requires a single signature cooperatively produced by all signers. This is '''more compact''' and has '''lower verification cost''' than each signer providing an individual public key and signature, as would be required by an ''n-of-n'' policy implemented using OP_CHECKSIGADD as introduced in ([https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki BIP342]). +The on-chain footprint of a MuSig2 Taproot output is a single BIP340 public key, and a transaction spending the output only requires a single signature cooperatively produced by all signers. This is '''more compact''' and has '''lower verification cost''' than each signer providing an individual public key and signature, as would be required by an ''n-of-n'' policy implemented using OP_CHECKSIGADD as introduced in ([https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki BIP342]). As a side effect, the number ''n'' of signers is not limited by any consensus rules when using MuSig2. Moreover, MuSig2 offers a '''higher level of privacy''' than OP_CHECKSIGADD: MuSig2 Taproot outputs are indistinguishable for a blockchain observer from regular, single-signer Taproot outputs even though they are actually controlled by multiple signers. By tweaking an aggregate key, the shared Taproot output can have script spending paths that are hidden unless used. @@ -48,10 +48,10 @@ The MuSig2 variant in this specification stands out by combining all of the foll * '''Compatibility with BIP340''': The aggregate public key created as part of this MuSig2 specification is a BIP340 X-only public key, and the signature output at the end of the protocol is a BIP340 signature that passes BIP340 verification for the aggregate key and a message. The public keys that are input to the key aggregation algorithm are also X-only public keys. Compared to compressed serialization, this adds complexity to the specification, but as X-only keys are becoming more common, the full key may not be available. * '''Tweaking for BIP32 derivations and Taproot''': The specification supports tweaking aggregate public keys and signing for tweaked aggregate public keys. We distinguish two modes of tweaking: ''Ordinary'' tweaking can be used to derive child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking, on the other hand, allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] tweak to add script paths to a Taproot output. See section [[#tweaking|Tweaking]] below for details. * '''Non-interactive signing with preprocessing''': The first communication round, exchanging the nonces, can happen before the message or even the exact set of signers is determined. Therefore, the signers can view it as a preprocessing step. Later, when the parameters of the signing session are chosen, they can send partial signatures without additional interaction. -* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines an algorithm to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a canonical order of signers. Then, sorting is unnecessary and very slow for a large set of signers compared to the rest of the MuSig2 protocol. In the worst case, sorting algorithms in standard libraries can have quadratic run time, which is undesirable in adversarial settings. Nonetheless, applications using this specification can mandate sorting before aggregation. +* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines a function to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a canonical order of signers. Nonetheless, applications using this specification can mandate sorting before aggregationApplications that sort input public keys before aggregation should ensure that the sort implementation is reasonably efficient, and in particular does not degenerate to quadratic runtime on pathological inputs.. * '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the unforgeability of the scheme. * '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because the individual nonce is required for partial verification. -* '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''. +* '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second distinct key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''. * '''Parameterization of MuSig2 and security''': In this specification, each signer's nonce consists of two elliptic curve points. The [https://eprint.iacr.org/2020/1261 MuSig2 paper] gives distinct security proofs depending on the number of points that constitute a nonce. See section [[#choosing-the-size-of-the-nonce|Choosing the Size of the Nonce]] for a discussion. This specification is written with a focus on clarity. From c235e5055f5d76e0cd39dcce3addb8cbd525e1bd Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Tue, 5 Apr 2022 18:18:18 -0400 Subject: [PATCH 192/381] musig-spec: Add naive Python reference implementation --- doc/musig-reference.py | 500 +++++++++++++++++++++++++++++++++++++++ doc/musig-spec.mediawiki | 4 +- 2 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 doc/musig-reference.py diff --git a/doc/musig-reference.py b/doc/musig-reference.py new file mode 100644 index 00000000..f7702492 --- /dev/null +++ b/doc/musig-reference.py @@ -0,0 +1,500 @@ +from collections import namedtuple +from typing import Any, List, Optional, Tuple +import hashlib +import secrets +import time + +# WARNING: Implementers should be aware that some inputs could +# trigger assertion errors, and proceed with caution. For example, +# an assertion error raised in one of the functions below should not +# cause a server process to crash. + +# +# The following helper functions were copied from the BIP-340 reference implementation: +# https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py +# + +p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +# Points are tuples of X and Y coordinates and the point at infinity is +# represented by the None keyword. +G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) + +Point = Tuple[int, int] + +# This implementation can be sped up by storing the midstate after hashing +# tag_hash instead of rehashing it all the time. +def tagged_hash(tag: str, msg: bytes) -> bytes: + tag_hash = hashlib.sha256(tag.encode()).digest() + return hashlib.sha256(tag_hash + tag_hash + msg).digest() + +def is_infinite(P: Optional[Point]) -> bool: + return P is None + +def x(P: Point) -> int: + assert not is_infinite(P) + return P[0] + +def y(P: Point) -> int: + assert not is_infinite(P) + return P[1] + +def point_add(P1: Optional[Point], P2: Optional[Point]) -> Optional[Point]: + if P1 is None: + return P2 + if P2 is None: + return P1 + if (x(P1) == x(P2)) and (y(P1) != y(P2)): + return None + if P1 == P2: + lam = (3 * x(P1) * x(P1) * pow(2 * y(P1), p - 2, p)) % p + else: + lam = ((y(P2) - y(P1)) * pow(x(P2) - x(P1), p - 2, p)) % p + x3 = (lam * lam - x(P1) - x(P2)) % p + return (x3, (lam * (x(P1) - x3) - y(P1)) % p) + +def point_mul(P: Optional[Point], n: int) -> Optional[Point]: + R = None + for i in range(256): + if (n >> i) & 1: + R = point_add(R, P) + P = point_add(P, P) + return R + +def bytes_from_int(x: int) -> bytes: + return x.to_bytes(32, byteorder="big") + +def bytes_from_point(P: Point) -> bytes: + return bytes_from_int(x(P)) + +def lift_x(b: bytes) -> Optional[Point]: + x = int_from_bytes(b) + if x >= p: + return None + y_sq = (pow(x, 3, p) + 7) % p + y = pow(y_sq, (p + 1) // 4, p) + if pow(y, 2, p) != y_sq: + return None + return (x, y if y & 1 == 0 else p-y) + +def int_from_bytes(b: bytes) -> int: + return int.from_bytes(b, byteorder="big") + +def has_even_y(P: Point) -> bool: + assert not is_infinite(P) + return y(P) % 2 == 0 + +def schnorr_verify(msg: bytes, pubkey: bytes, sig: bytes) -> bool: + if len(msg) != 32: + raise ValueError('The message must be a 32-byte array.') + if len(pubkey) != 32: + raise ValueError('The public key must be a 32-byte array.') + if len(sig) != 64: + raise ValueError('The signature must be a 64-byte array.') + P = lift_x(pubkey) + r = int_from_bytes(sig[0:32]) + s = int_from_bytes(sig[32:64]) + if (P is None) or (r >= p) or (s >= n): + return False + e = int_from_bytes(tagged_hash("BIP0340/challenge", sig[0:32] + pubkey + msg)) % n + R = point_add(point_mul(G, s), point_mul(P, n - e)) + if (R is None) or (not has_even_y(R)) or (x(R) != r): + return False + return True + +# +# End of helper functions copied from BIP-340 reference implementation. +# + +infinity = None + +def cbytes(P: Point) -> bytes: + a = b'\x02' if has_even_y(P) else b'\x03' + return a + bytes_from_point(P) + +def point_negate(P: Optional[Point]) -> Optional[Point]: + if P is None: + return P + return (x(P), p - y(P)) + +def pointc(x: bytes) -> Point: + P = lift_x(x[1:33]) + if P is None: + raise ValueError('x is not a valid compressed point.') + if x[0] == 2: + return P + elif x[0] == 3: + P = point_negate(P) + assert P is not None + return P + else: + raise ValueError('x is not a valid compressed point.') + +def key_agg(pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool]) -> bytes: + Q, _, _ = key_agg_internal(pubkeys, tweaks, is_xonly) + return bytes_from_point(Q) + +def key_agg_internal(pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool]) -> Tuple[Point, int, int]: + pk2 = get_second_key(pubkeys) + u = len(pubkeys) + Q = infinity + for i in range(u): + P_i = lift_x(pubkeys[i]) + a_i = key_agg_coeff_internal(pubkeys, pubkeys[i], pk2) + Q = point_add(Q, point_mul(P_i, a_i)) + if Q is None: + raise ValueError('The aggregate public key cannot be infinity.') + gacc = 1 + tacc = 0 + v = len(tweaks) + for i in range(v): + Q, gacc, tacc = apply_tweak(Q, gacc, tacc, tweaks[i], is_xonly[i]) + return Q, gacc, tacc + +def hash_keys(pubkeys: List[bytes]) -> bytes: + return tagged_hash('KeyAgg list', b''.join(pubkeys)) + +def get_second_key(pubkeys: List[bytes]) -> bytes: + u = len(pubkeys) + for j in range(1, u): + if pubkeys[j] != pubkeys[0]: + return pubkeys[j] + return bytes_from_int(0) + +def key_agg_coeff(pubkeys: List[bytes], pk_: bytes) -> int: + pk2 = get_second_key(pubkeys) + return key_agg_coeff_internal(pubkeys, pk_, pk2) + +def key_agg_coeff_internal(pubkeys: List[bytes], pk_: bytes, pk2: bytes) -> int: + L = hash_keys(pubkeys) + if pk_ == pk2: + return 1 + return int_from_bytes(tagged_hash('KeyAgg coefficient', L + pk_)) % n + +def apply_tweak(Q: Point, gacc: int, tacc: int, tweak_i: bytes, is_xonly_i: bool) -> Tuple[Point, int, int]: + if len(tweak_i) != 32: + raise ValueError('The tweak must be a 32-byte array.') + if is_xonly_i and not has_even_y(Q): + g = n - 1 + else: + g = 1 + t_i = int_from_bytes(tweak_i) + if t_i >= n: + raise ValueError('The tweak must be less than n.') + Q_i = point_add(point_mul(Q, g), point_mul(G, t_i)) + if Q_i is None: + raise ValueError('The result of tweaking cannot be infinity.') + gacc_i = g * gacc % n + tacc_i = (t_i + g * tacc) % n + return Q_i, gacc_i, tacc_i + +def bytes_xor(a: bytes, b: bytes) -> bytes: + return bytes(x ^ y for x, y in zip(a, b)) + +def nonce_hash(rand: bytes, aggpk: bytes, i: int, msg: bytes, extra_in: bytes) -> int: + buf = b'' + buf += rand + buf += len(aggpk).to_bytes(1, 'big') + buf += aggpk + buf += i.to_bytes(1, 'big') + buf += len(msg).to_bytes(1, 'big') + buf += msg + buf += len(extra_in).to_bytes(4, 'big') + buf += extra_in + return int_from_bytes(tagged_hash('MuSig/nonce', buf)) + +def nonce_gen(sk: bytes, aggpk: bytes, msg: bytes, extra_in: bytes) -> Tuple[bytes, bytes]: + if len(sk) not in (0, 32): + raise ValueError('The optional byte array sk must have length 0 or 32.') + if len(aggpk) not in (0, 32): + raise ValueError('The optional byte array aggpk must have length 0 or 32.') + if len(msg) not in (0, 32): + raise ValueError('The optional byte array msg must have length 0 or 32.') + rand_ = secrets.token_bytes(32) + if len(sk) > 0: + rand = bytes_xor(sk, tagged_hash('MuSig/aux', rand_)) + else: + rand = rand_ + k_1 = nonce_hash(rand, aggpk, 1, msg, extra_in) + k_2 = nonce_hash(rand, aggpk, 2, msg, extra_in) + # k_1 == 0 or k_2 == 0 cannot occur except with negligible probability. + assert k_1 != 0 + assert k_2 != 0 + R_1_ = point_mul(G, k_1) + R_2_ = point_mul(G, k_2) + assert R_1_ is not None + assert R_2_ is not None + pubnonce = cbytes(R_1_) + cbytes(R_2_) + secnonce = bytes_from_int(k_1) + bytes_from_int(k_2) + return secnonce, pubnonce + +def nonce_agg(pubnonces: List[bytes]) -> bytes: + u = len(pubnonces) + aggnonce = b'' + for i in (1, 2): + R_i_ = infinity + for j in range(u): + R_i_ = point_add(R_i_, pointc(pubnonces[j][(i-1)*33:i*33])) + R_i = R_i_ if not is_infinite(R_i_) else G + assert R_i is not None + aggnonce += cbytes(R_i) + return aggnonce + +SessionContext = namedtuple('SessionContext', ['aggnonce', 'pubkeys', 'tweaks', 'is_xonly', 'msg']) + +def get_session_values(session_ctx: SessionContext) -> tuple[Point, int, int, int, Point, int]: + (aggnonce, pubkeys, tweaks, is_xonly, msg) = session_ctx + Q, gacc_v, tacc_v = key_agg_internal(pubkeys, tweaks, is_xonly) + b = int_from_bytes(tagged_hash('MuSig/noncecoef', aggnonce + bytes_from_point(Q) + msg)) % n + R_1 = pointc(aggnonce[0:33]) + R_2 = pointc(aggnonce[33:66]) + R = point_add(R_1, point_mul(R_2, b)) + # The aggregate public nonce cannot be infinity except with negligible probability. + assert R is not None + e = int_from_bytes(tagged_hash('BIP0340/challenge', bytes_from_point(R) + bytes_from_point(Q) + msg)) % n + return (Q, gacc_v, tacc_v, b, R, e) + +def get_session_key_agg_coeff(session_ctx: SessionContext, P: Point) -> int: + (_, pubkeys, _, _, _) = session_ctx + return key_agg_coeff(pubkeys, bytes_from_point(P)) + +# Callers should overwrite secnonce with zeros after calling sign. +def sign(secnonce: bytes, sk: bytes, session_ctx: SessionContext) -> bytes: + (Q, gacc_v, _, b, R, e) = get_session_values(session_ctx) + k_1_ = int_from_bytes(secnonce[0:32]) + k_2_ = int_from_bytes(secnonce[32:64]) + if not 0 < k_1_ < n: + raise ValueError('first secnonce value is out of range.') + if not 0 < k_2_ < n: + raise ValueError('second secnonce value is out of range.') + k_1 = k_1_ if has_even_y(R) else n - k_1_ + k_2 = k_2_ if has_even_y(R) else n - k_2_ + d_ = int_from_bytes(sk) + if not 0 < d_ < n: + raise ValueError('secret key value is out of range.') + P = point_mul(G, d_) + assert P is not None + a = get_session_key_agg_coeff(session_ctx, P) + gp = 1 if has_even_y(P) else n - 1 + g_v = 1 if has_even_y(Q) else n - 1 + d = g_v * gacc_v * gp * d_ % n + s = (k_1 + b * k_2 + e * a * d) % n + psig = bytes_from_int(s) + R_1_ = point_mul(G, k_1_) + R_2_ = point_mul(G, k_2_) + assert R_1_ is not None + assert R_2_ is not None + pubnonce = cbytes(R_1_) + cbytes(R_2_) + # Optional correctness check. The result of signing should pass signature verification. + assert partial_sig_verify_internal(psig, pubnonce, bytes_from_point(P), session_ctx) + return psig + +def partial_sig_verify(psig: bytes, pubnonces: List[bytes], pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool], msg: bytes, i: int) -> bool: + aggnonce = nonce_agg(pubnonces) + session_ctx = SessionContext(aggnonce, pubkeys, tweaks, is_xonly, msg) + return partial_sig_verify_internal(psig, pubnonces[i], pubkeys[i], session_ctx) + +def partial_sig_verify_internal(psig: bytes, pubnonce: bytes, pk_: bytes, session_ctx: SessionContext) -> bool: + (Q, gacc_v, _, b, R, e) = get_session_values(session_ctx) + s = int_from_bytes(psig) + if s >= n: + return False + R_1_ = pointc(pubnonce[0:33]) + R_2_ = pointc(pubnonce[33:66]) + R__ = point_add(R_1_, point_mul(R_2_, b)) + R_ = R__ if has_even_y(R) else point_negate(R__) + g_v = 1 if has_even_y(Q) else n - 1 + g_ = g_v * gacc_v % n + P = point_mul(lift_x(pk_), g_) + if P is None: + return False + a = get_session_key_agg_coeff(session_ctx, P) + return point_mul(G, s) == point_add(R_, point_mul(P, e * a % n)) + +def partial_sig_agg(psigs: List[bytes], session_ctx: SessionContext) -> Optional[bytes]: + (Q, _, tacc_v, _, R, e) = get_session_values(session_ctx) + s = 0 + u = len(psigs) + for i in range(u): + s_i = int_from_bytes(psigs[i]) + if s_i >= n: + return None + s = (s + s_i) % n + g_v = 1 if has_even_y(Q) else n - 1 + s = (s + e * g_v * tacc_v) % n + return bytes_from_point(R) + bytes_from_int(s) +# +# The following code is only used for testing. +# Test vectors were copied from libsecp256k1-zkp's MuSig test file. +# See `musig_test_vectors_keyagg` and `musig_test_vectors_sign` in +# https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h +# +def fromhex_all(l): + return [bytes.fromhex(l_i) for l_i in l] + +def test_key_agg_vectors(): + X = fromhex_all([ + 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', + 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', + '3590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66', + ]) + + expected = fromhex_all([ + 'E5830140512195D74C8307E39637CBE5FB730EBEAB80EC514CF88A877CEEEE0B', + 'D70CD69A2647F7390973DF48CBFA2CCC407B8B2D60B08C5F1641185C7998A290', + '81A8B093912C9E481408D09776CEFB48AEB8B65481B6BAAFB3C5810106717BEB', + '2EB18851887E7BDC5E830E89B19DDBC28078F1FA88AAD0AD01CA06FE4F80210B', + ]) + + assert key_agg([X[0], X[1], X[2]], [], []) == expected[0] + assert key_agg([X[2], X[1], X[0]], [], []) == expected[1] + assert key_agg([X[0], X[0], X[0]], [], []) == expected[2] + assert key_agg([X[0], X[0], X[1], X[1]], [], []) == expected[3] + +def test_sign_vectors(): + X = fromhex_all([ + 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', + 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', + ]) + + secnonce = bytes.fromhex( + '508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61' + + 'FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7') + + aggnonce = bytes.fromhex( + '028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61' + + '037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9') + + sk = bytes.fromhex('7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671') + msg = bytes.fromhex('F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF') + + expected = fromhex_all([ + '68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B', + '2DF67BFFF18E3DE797E13C6475C963048138DAEC5CB20A357CECA7C8424295EA', + '0D5B651E6DE34A29A12DE7A8B4183B4AE6A7F7FBE15CDCAFA4A3D1BCAABC7517', + ]) + + pk = bytes_from_point(point_mul(G, int_from_bytes(sk))) + + session_ctx = SessionContext(aggnonce, [pk, X[0], X[1]], [], [], msg) + assert sign(secnonce, sk, session_ctx) == expected[0] + # WARNING: An actual implementation should clear the secnonce after use, + # e.g. by setting secnonce = bytes(64) after usage. Reusing the secnonce, as + # we do here for testing purposes, can leak the secret key. + + session_ctx = SessionContext(aggnonce, [X[0], pk, X[1]], [], [], msg) + assert sign(secnonce, sk, session_ctx) == expected[1] + + session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], [], [], msg) + assert sign(secnonce, sk, session_ctx) == expected[2] + +def test_tweak_vectors(): + X = fromhex_all([ + 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', + 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', + ]) + + secnonce = bytes.fromhex( + '508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61' + + 'FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7') + + aggnonce = bytes.fromhex( + '028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61' + + '037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9') + + sk = bytes.fromhex('7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671') + msg = bytes.fromhex('F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF') + + tweaks = fromhex_all([ + 'E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB', + 'AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455', + 'F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0', + '1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D', + ]) + + expected = fromhex_all([ + '5E24C7496B565DEBC3B9639E6F1304A21597F9603D3AB05B4913641775E1375B', + '78408DDCAB4813D1394C97D493EF1084195C1D4B52E63ECD7BC5991644E44DDD', + 'C3A829A81480E36EC3AB052964509A94EBF34210403D16B226A6F16EC85B7357', + '8C4473C6A382BD3C4AD7BE59818DA5ED7CF8CEC4BC21996CFDA08BB4316B8BC7', + ]) + + pk = bytes_from_point(point_mul(G, int_from_bytes(sk))) + + # A single x-only tweak + session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:1], [True], msg) + assert sign(secnonce, sk, session_ctx) == expected[0] + # WARNING: An actual implementation should clear the secnonce after use, + # e.g. by setting secnonce = bytes(64) after usage. Reusing the secnonce, as + # we do here for testing purposes, can leak the secret key. + + # A single ordinary tweak + session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:1], [False], msg) + assert sign(secnonce, sk, session_ctx) == expected[1] + + # An ordinary tweak followed by an x-only tweak + session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:2], [False, True], msg) + assert sign(secnonce, sk, session_ctx) == expected[2] + + # Four tweaks: x-only, ordinary, x-only, ordinary + session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:4], [True, False, True, False], msg) + assert sign(secnonce, sk, session_ctx) == expected[3] + +def test_sign_and_verify_random(iters): + for i in range(iters): + sk_1 = secrets.token_bytes(32) + sk_2 = secrets.token_bytes(32) + pk_1 = bytes_from_point(point_mul(G, int_from_bytes(sk_1))) + pk_2 = bytes_from_point(point_mul(G, int_from_bytes(sk_2))) + pubkeys = [pk_1, pk_2] + + # In this example, the message and aggregate pubkey are known + # before nonce generation, so they can be passed into the nonce + # generation function as a defense-in-depth measure to protect + # against nonce reuse. + # + # If these values are not known when nonce_gen is called, empty + # byte arrays can be passed in for the corresponding arguments + # instead. + msg = secrets.token_bytes(32) + v = secrets.randbelow(4) + tweaks = [secrets.token_bytes(32) for _ in range(v)] + is_xonly = [secrets.choice([False, True]) for _ in range(v)] + aggpk = key_agg(pubkeys, tweaks, is_xonly) + + # Use a non-repeating counter for extra_in + secnonce_1, pubnonce_1 = nonce_gen(sk_1, aggpk, msg, i.to_bytes(4, 'big')) + + # Use a clock for extra_in + t = time.clock_gettime_ns(time.CLOCK_MONOTONIC) + secnonce_2, pubnonce_2 = nonce_gen(sk_2, aggpk, msg, t.to_bytes(8, 'big')) + + pubnonces = [pubnonce_1, pubnonce_2] + aggnonce = nonce_agg(pubnonces) + + session_ctx = SessionContext(aggnonce, pubkeys, tweaks, is_xonly, msg) + psig_1 = sign(secnonce_1, sk_1, session_ctx) + # Clear the secnonce after use + secnonce_1 = bytes(64) + assert partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, msg, 0) + + # Wrong signer index + assert not partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, msg, 1) + + # Wrong message + assert not partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, secrets.token_bytes(32), 0) + + psig_2 = sign(secnonce_2, sk_2, session_ctx) + # Clear the secnonce after use + secnonce_2 = bytes(64) + assert partial_sig_verify(psig_2, pubnonces, pubkeys, tweaks, is_xonly, msg, 1) + + sig = partial_sig_agg([psig_1, psig_2], session_ctx) + assert schnorr_verify(msg, aggpk, sig) + +if __name__ == '__main__': + test_key_agg_vectors() + test_sign_vectors() + test_tweak_vectors() + test_sign_and_verify_random(4) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 24ce37fe..7ce736e9 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -405,8 +405,8 @@ Input: === Test Vectors and Reference Code === -There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. -Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' functions. +We provide a naive, highly inefficient, and non-constant time [[musig-reference.py|pure Python 3 reference implementation of the key aggregation, partial signing, and partial signature verification algorithms, together with some test vectors]]. +The reference implementation is for demonstration purposes only and not to be used in production environments. == Remarks on Security and Correctness == From cc07b8f7a9a7aa3a023f04127cb85c1723dd1bf9 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 5 Apr 2022 22:46:58 +0000 Subject: [PATCH 193/381] musig-spec: remove it --- doc/musig-reference.py | 500 ---------------------------------- doc/musig-spec.mediawiki | 559 +-------------------------------------- 2 files changed, 1 insertion(+), 1058 deletions(-) delete mode 100644 doc/musig-reference.py diff --git a/doc/musig-reference.py b/doc/musig-reference.py deleted file mode 100644 index f7702492..00000000 --- a/doc/musig-reference.py +++ /dev/null @@ -1,500 +0,0 @@ -from collections import namedtuple -from typing import Any, List, Optional, Tuple -import hashlib -import secrets -import time - -# WARNING: Implementers should be aware that some inputs could -# trigger assertion errors, and proceed with caution. For example, -# an assertion error raised in one of the functions below should not -# cause a server process to crash. - -# -# The following helper functions were copied from the BIP-340 reference implementation: -# https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py -# - -p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F -n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - -# Points are tuples of X and Y coordinates and the point at infinity is -# represented by the None keyword. -G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) - -Point = Tuple[int, int] - -# This implementation can be sped up by storing the midstate after hashing -# tag_hash instead of rehashing it all the time. -def tagged_hash(tag: str, msg: bytes) -> bytes: - tag_hash = hashlib.sha256(tag.encode()).digest() - return hashlib.sha256(tag_hash + tag_hash + msg).digest() - -def is_infinite(P: Optional[Point]) -> bool: - return P is None - -def x(P: Point) -> int: - assert not is_infinite(P) - return P[0] - -def y(P: Point) -> int: - assert not is_infinite(P) - return P[1] - -def point_add(P1: Optional[Point], P2: Optional[Point]) -> Optional[Point]: - if P1 is None: - return P2 - if P2 is None: - return P1 - if (x(P1) == x(P2)) and (y(P1) != y(P2)): - return None - if P1 == P2: - lam = (3 * x(P1) * x(P1) * pow(2 * y(P1), p - 2, p)) % p - else: - lam = ((y(P2) - y(P1)) * pow(x(P2) - x(P1), p - 2, p)) % p - x3 = (lam * lam - x(P1) - x(P2)) % p - return (x3, (lam * (x(P1) - x3) - y(P1)) % p) - -def point_mul(P: Optional[Point], n: int) -> Optional[Point]: - R = None - for i in range(256): - if (n >> i) & 1: - R = point_add(R, P) - P = point_add(P, P) - return R - -def bytes_from_int(x: int) -> bytes: - return x.to_bytes(32, byteorder="big") - -def bytes_from_point(P: Point) -> bytes: - return bytes_from_int(x(P)) - -def lift_x(b: bytes) -> Optional[Point]: - x = int_from_bytes(b) - if x >= p: - return None - y_sq = (pow(x, 3, p) + 7) % p - y = pow(y_sq, (p + 1) // 4, p) - if pow(y, 2, p) != y_sq: - return None - return (x, y if y & 1 == 0 else p-y) - -def int_from_bytes(b: bytes) -> int: - return int.from_bytes(b, byteorder="big") - -def has_even_y(P: Point) -> bool: - assert not is_infinite(P) - return y(P) % 2 == 0 - -def schnorr_verify(msg: bytes, pubkey: bytes, sig: bytes) -> bool: - if len(msg) != 32: - raise ValueError('The message must be a 32-byte array.') - if len(pubkey) != 32: - raise ValueError('The public key must be a 32-byte array.') - if len(sig) != 64: - raise ValueError('The signature must be a 64-byte array.') - P = lift_x(pubkey) - r = int_from_bytes(sig[0:32]) - s = int_from_bytes(sig[32:64]) - if (P is None) or (r >= p) or (s >= n): - return False - e = int_from_bytes(tagged_hash("BIP0340/challenge", sig[0:32] + pubkey + msg)) % n - R = point_add(point_mul(G, s), point_mul(P, n - e)) - if (R is None) or (not has_even_y(R)) or (x(R) != r): - return False - return True - -# -# End of helper functions copied from BIP-340 reference implementation. -# - -infinity = None - -def cbytes(P: Point) -> bytes: - a = b'\x02' if has_even_y(P) else b'\x03' - return a + bytes_from_point(P) - -def point_negate(P: Optional[Point]) -> Optional[Point]: - if P is None: - return P - return (x(P), p - y(P)) - -def pointc(x: bytes) -> Point: - P = lift_x(x[1:33]) - if P is None: - raise ValueError('x is not a valid compressed point.') - if x[0] == 2: - return P - elif x[0] == 3: - P = point_negate(P) - assert P is not None - return P - else: - raise ValueError('x is not a valid compressed point.') - -def key_agg(pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool]) -> bytes: - Q, _, _ = key_agg_internal(pubkeys, tweaks, is_xonly) - return bytes_from_point(Q) - -def key_agg_internal(pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool]) -> Tuple[Point, int, int]: - pk2 = get_second_key(pubkeys) - u = len(pubkeys) - Q = infinity - for i in range(u): - P_i = lift_x(pubkeys[i]) - a_i = key_agg_coeff_internal(pubkeys, pubkeys[i], pk2) - Q = point_add(Q, point_mul(P_i, a_i)) - if Q is None: - raise ValueError('The aggregate public key cannot be infinity.') - gacc = 1 - tacc = 0 - v = len(tweaks) - for i in range(v): - Q, gacc, tacc = apply_tweak(Q, gacc, tacc, tweaks[i], is_xonly[i]) - return Q, gacc, tacc - -def hash_keys(pubkeys: List[bytes]) -> bytes: - return tagged_hash('KeyAgg list', b''.join(pubkeys)) - -def get_second_key(pubkeys: List[bytes]) -> bytes: - u = len(pubkeys) - for j in range(1, u): - if pubkeys[j] != pubkeys[0]: - return pubkeys[j] - return bytes_from_int(0) - -def key_agg_coeff(pubkeys: List[bytes], pk_: bytes) -> int: - pk2 = get_second_key(pubkeys) - return key_agg_coeff_internal(pubkeys, pk_, pk2) - -def key_agg_coeff_internal(pubkeys: List[bytes], pk_: bytes, pk2: bytes) -> int: - L = hash_keys(pubkeys) - if pk_ == pk2: - return 1 - return int_from_bytes(tagged_hash('KeyAgg coefficient', L + pk_)) % n - -def apply_tweak(Q: Point, gacc: int, tacc: int, tweak_i: bytes, is_xonly_i: bool) -> Tuple[Point, int, int]: - if len(tweak_i) != 32: - raise ValueError('The tweak must be a 32-byte array.') - if is_xonly_i and not has_even_y(Q): - g = n - 1 - else: - g = 1 - t_i = int_from_bytes(tweak_i) - if t_i >= n: - raise ValueError('The tweak must be less than n.') - Q_i = point_add(point_mul(Q, g), point_mul(G, t_i)) - if Q_i is None: - raise ValueError('The result of tweaking cannot be infinity.') - gacc_i = g * gacc % n - tacc_i = (t_i + g * tacc) % n - return Q_i, gacc_i, tacc_i - -def bytes_xor(a: bytes, b: bytes) -> bytes: - return bytes(x ^ y for x, y in zip(a, b)) - -def nonce_hash(rand: bytes, aggpk: bytes, i: int, msg: bytes, extra_in: bytes) -> int: - buf = b'' - buf += rand - buf += len(aggpk).to_bytes(1, 'big') - buf += aggpk - buf += i.to_bytes(1, 'big') - buf += len(msg).to_bytes(1, 'big') - buf += msg - buf += len(extra_in).to_bytes(4, 'big') - buf += extra_in - return int_from_bytes(tagged_hash('MuSig/nonce', buf)) - -def nonce_gen(sk: bytes, aggpk: bytes, msg: bytes, extra_in: bytes) -> Tuple[bytes, bytes]: - if len(sk) not in (0, 32): - raise ValueError('The optional byte array sk must have length 0 or 32.') - if len(aggpk) not in (0, 32): - raise ValueError('The optional byte array aggpk must have length 0 or 32.') - if len(msg) not in (0, 32): - raise ValueError('The optional byte array msg must have length 0 or 32.') - rand_ = secrets.token_bytes(32) - if len(sk) > 0: - rand = bytes_xor(sk, tagged_hash('MuSig/aux', rand_)) - else: - rand = rand_ - k_1 = nonce_hash(rand, aggpk, 1, msg, extra_in) - k_2 = nonce_hash(rand, aggpk, 2, msg, extra_in) - # k_1 == 0 or k_2 == 0 cannot occur except with negligible probability. - assert k_1 != 0 - assert k_2 != 0 - R_1_ = point_mul(G, k_1) - R_2_ = point_mul(G, k_2) - assert R_1_ is not None - assert R_2_ is not None - pubnonce = cbytes(R_1_) + cbytes(R_2_) - secnonce = bytes_from_int(k_1) + bytes_from_int(k_2) - return secnonce, pubnonce - -def nonce_agg(pubnonces: List[bytes]) -> bytes: - u = len(pubnonces) - aggnonce = b'' - for i in (1, 2): - R_i_ = infinity - for j in range(u): - R_i_ = point_add(R_i_, pointc(pubnonces[j][(i-1)*33:i*33])) - R_i = R_i_ if not is_infinite(R_i_) else G - assert R_i is not None - aggnonce += cbytes(R_i) - return aggnonce - -SessionContext = namedtuple('SessionContext', ['aggnonce', 'pubkeys', 'tweaks', 'is_xonly', 'msg']) - -def get_session_values(session_ctx: SessionContext) -> tuple[Point, int, int, int, Point, int]: - (aggnonce, pubkeys, tweaks, is_xonly, msg) = session_ctx - Q, gacc_v, tacc_v = key_agg_internal(pubkeys, tweaks, is_xonly) - b = int_from_bytes(tagged_hash('MuSig/noncecoef', aggnonce + bytes_from_point(Q) + msg)) % n - R_1 = pointc(aggnonce[0:33]) - R_2 = pointc(aggnonce[33:66]) - R = point_add(R_1, point_mul(R_2, b)) - # The aggregate public nonce cannot be infinity except with negligible probability. - assert R is not None - e = int_from_bytes(tagged_hash('BIP0340/challenge', bytes_from_point(R) + bytes_from_point(Q) + msg)) % n - return (Q, gacc_v, tacc_v, b, R, e) - -def get_session_key_agg_coeff(session_ctx: SessionContext, P: Point) -> int: - (_, pubkeys, _, _, _) = session_ctx - return key_agg_coeff(pubkeys, bytes_from_point(P)) - -# Callers should overwrite secnonce with zeros after calling sign. -def sign(secnonce: bytes, sk: bytes, session_ctx: SessionContext) -> bytes: - (Q, gacc_v, _, b, R, e) = get_session_values(session_ctx) - k_1_ = int_from_bytes(secnonce[0:32]) - k_2_ = int_from_bytes(secnonce[32:64]) - if not 0 < k_1_ < n: - raise ValueError('first secnonce value is out of range.') - if not 0 < k_2_ < n: - raise ValueError('second secnonce value is out of range.') - k_1 = k_1_ if has_even_y(R) else n - k_1_ - k_2 = k_2_ if has_even_y(R) else n - k_2_ - d_ = int_from_bytes(sk) - if not 0 < d_ < n: - raise ValueError('secret key value is out of range.') - P = point_mul(G, d_) - assert P is not None - a = get_session_key_agg_coeff(session_ctx, P) - gp = 1 if has_even_y(P) else n - 1 - g_v = 1 if has_even_y(Q) else n - 1 - d = g_v * gacc_v * gp * d_ % n - s = (k_1 + b * k_2 + e * a * d) % n - psig = bytes_from_int(s) - R_1_ = point_mul(G, k_1_) - R_2_ = point_mul(G, k_2_) - assert R_1_ is not None - assert R_2_ is not None - pubnonce = cbytes(R_1_) + cbytes(R_2_) - # Optional correctness check. The result of signing should pass signature verification. - assert partial_sig_verify_internal(psig, pubnonce, bytes_from_point(P), session_ctx) - return psig - -def partial_sig_verify(psig: bytes, pubnonces: List[bytes], pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool], msg: bytes, i: int) -> bool: - aggnonce = nonce_agg(pubnonces) - session_ctx = SessionContext(aggnonce, pubkeys, tweaks, is_xonly, msg) - return partial_sig_verify_internal(psig, pubnonces[i], pubkeys[i], session_ctx) - -def partial_sig_verify_internal(psig: bytes, pubnonce: bytes, pk_: bytes, session_ctx: SessionContext) -> bool: - (Q, gacc_v, _, b, R, e) = get_session_values(session_ctx) - s = int_from_bytes(psig) - if s >= n: - return False - R_1_ = pointc(pubnonce[0:33]) - R_2_ = pointc(pubnonce[33:66]) - R__ = point_add(R_1_, point_mul(R_2_, b)) - R_ = R__ if has_even_y(R) else point_negate(R__) - g_v = 1 if has_even_y(Q) else n - 1 - g_ = g_v * gacc_v % n - P = point_mul(lift_x(pk_), g_) - if P is None: - return False - a = get_session_key_agg_coeff(session_ctx, P) - return point_mul(G, s) == point_add(R_, point_mul(P, e * a % n)) - -def partial_sig_agg(psigs: List[bytes], session_ctx: SessionContext) -> Optional[bytes]: - (Q, _, tacc_v, _, R, e) = get_session_values(session_ctx) - s = 0 - u = len(psigs) - for i in range(u): - s_i = int_from_bytes(psigs[i]) - if s_i >= n: - return None - s = (s + s_i) % n - g_v = 1 if has_even_y(Q) else n - 1 - s = (s + e * g_v * tacc_v) % n - return bytes_from_point(R) + bytes_from_int(s) -# -# The following code is only used for testing. -# Test vectors were copied from libsecp256k1-zkp's MuSig test file. -# See `musig_test_vectors_keyagg` and `musig_test_vectors_sign` in -# https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h -# -def fromhex_all(l): - return [bytes.fromhex(l_i) for l_i in l] - -def test_key_agg_vectors(): - X = fromhex_all([ - 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', - 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', - '3590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66', - ]) - - expected = fromhex_all([ - 'E5830140512195D74C8307E39637CBE5FB730EBEAB80EC514CF88A877CEEEE0B', - 'D70CD69A2647F7390973DF48CBFA2CCC407B8B2D60B08C5F1641185C7998A290', - '81A8B093912C9E481408D09776CEFB48AEB8B65481B6BAAFB3C5810106717BEB', - '2EB18851887E7BDC5E830E89B19DDBC28078F1FA88AAD0AD01CA06FE4F80210B', - ]) - - assert key_agg([X[0], X[1], X[2]], [], []) == expected[0] - assert key_agg([X[2], X[1], X[0]], [], []) == expected[1] - assert key_agg([X[0], X[0], X[0]], [], []) == expected[2] - assert key_agg([X[0], X[0], X[1], X[1]], [], []) == expected[3] - -def test_sign_vectors(): - X = fromhex_all([ - 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', - 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', - ]) - - secnonce = bytes.fromhex( - '508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61' + - 'FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7') - - aggnonce = bytes.fromhex( - '028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61' + - '037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9') - - sk = bytes.fromhex('7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671') - msg = bytes.fromhex('F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF') - - expected = fromhex_all([ - '68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B', - '2DF67BFFF18E3DE797E13C6475C963048138DAEC5CB20A357CECA7C8424295EA', - '0D5B651E6DE34A29A12DE7A8B4183B4AE6A7F7FBE15CDCAFA4A3D1BCAABC7517', - ]) - - pk = bytes_from_point(point_mul(G, int_from_bytes(sk))) - - session_ctx = SessionContext(aggnonce, [pk, X[0], X[1]], [], [], msg) - assert sign(secnonce, sk, session_ctx) == expected[0] - # WARNING: An actual implementation should clear the secnonce after use, - # e.g. by setting secnonce = bytes(64) after usage. Reusing the secnonce, as - # we do here for testing purposes, can leak the secret key. - - session_ctx = SessionContext(aggnonce, [X[0], pk, X[1]], [], [], msg) - assert sign(secnonce, sk, session_ctx) == expected[1] - - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], [], [], msg) - assert sign(secnonce, sk, session_ctx) == expected[2] - -def test_tweak_vectors(): - X = fromhex_all([ - 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', - 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', - ]) - - secnonce = bytes.fromhex( - '508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61' + - 'FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7') - - aggnonce = bytes.fromhex( - '028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61' + - '037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9') - - sk = bytes.fromhex('7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671') - msg = bytes.fromhex('F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF') - - tweaks = fromhex_all([ - 'E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB', - 'AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455', - 'F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0', - '1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D', - ]) - - expected = fromhex_all([ - '5E24C7496B565DEBC3B9639E6F1304A21597F9603D3AB05B4913641775E1375B', - '78408DDCAB4813D1394C97D493EF1084195C1D4B52E63ECD7BC5991644E44DDD', - 'C3A829A81480E36EC3AB052964509A94EBF34210403D16B226A6F16EC85B7357', - '8C4473C6A382BD3C4AD7BE59818DA5ED7CF8CEC4BC21996CFDA08BB4316B8BC7', - ]) - - pk = bytes_from_point(point_mul(G, int_from_bytes(sk))) - - # A single x-only tweak - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:1], [True], msg) - assert sign(secnonce, sk, session_ctx) == expected[0] - # WARNING: An actual implementation should clear the secnonce after use, - # e.g. by setting secnonce = bytes(64) after usage. Reusing the secnonce, as - # we do here for testing purposes, can leak the secret key. - - # A single ordinary tweak - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:1], [False], msg) - assert sign(secnonce, sk, session_ctx) == expected[1] - - # An ordinary tweak followed by an x-only tweak - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:2], [False, True], msg) - assert sign(secnonce, sk, session_ctx) == expected[2] - - # Four tweaks: x-only, ordinary, x-only, ordinary - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:4], [True, False, True, False], msg) - assert sign(secnonce, sk, session_ctx) == expected[3] - -def test_sign_and_verify_random(iters): - for i in range(iters): - sk_1 = secrets.token_bytes(32) - sk_2 = secrets.token_bytes(32) - pk_1 = bytes_from_point(point_mul(G, int_from_bytes(sk_1))) - pk_2 = bytes_from_point(point_mul(G, int_from_bytes(sk_2))) - pubkeys = [pk_1, pk_2] - - # In this example, the message and aggregate pubkey are known - # before nonce generation, so they can be passed into the nonce - # generation function as a defense-in-depth measure to protect - # against nonce reuse. - # - # If these values are not known when nonce_gen is called, empty - # byte arrays can be passed in for the corresponding arguments - # instead. - msg = secrets.token_bytes(32) - v = secrets.randbelow(4) - tweaks = [secrets.token_bytes(32) for _ in range(v)] - is_xonly = [secrets.choice([False, True]) for _ in range(v)] - aggpk = key_agg(pubkeys, tweaks, is_xonly) - - # Use a non-repeating counter for extra_in - secnonce_1, pubnonce_1 = nonce_gen(sk_1, aggpk, msg, i.to_bytes(4, 'big')) - - # Use a clock for extra_in - t = time.clock_gettime_ns(time.CLOCK_MONOTONIC) - secnonce_2, pubnonce_2 = nonce_gen(sk_2, aggpk, msg, t.to_bytes(8, 'big')) - - pubnonces = [pubnonce_1, pubnonce_2] - aggnonce = nonce_agg(pubnonces) - - session_ctx = SessionContext(aggnonce, pubkeys, tweaks, is_xonly, msg) - psig_1 = sign(secnonce_1, sk_1, session_ctx) - # Clear the secnonce after use - secnonce_1 = bytes(64) - assert partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, msg, 0) - - # Wrong signer index - assert not partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, msg, 1) - - # Wrong message - assert not partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, secrets.token_bytes(32), 0) - - psig_2 = sign(secnonce_2, sk_2, session_ctx) - # Clear the secnonce after use - secnonce_2 = bytes(64) - assert partial_sig_verify(psig_2, pubnonces, pubkeys, tweaks, is_xonly, msg, 1) - - sig = partial_sig_agg([psig_1, psig_2], session_ctx) - assert schnorr_verify(msg, aggpk, sig) - -if __name__ == '__main__': - test_key_agg_vectors() - test_sign_vectors() - test_tweak_vectors() - test_sign_and_verify_random(4) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index a714e7f4..017a0c3e 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,558 +1 @@ -
-  BIP: ?
-  Title: MuSig2
-  Author: Jonas Nick 
-          Tim Ruffing 
-          Elliott Jin 
-  Status: Draft
-  License: BSD-3-Clause
-  Type: Informational
-  Created: 2022-03-22
-
- -== Introduction == - -=== Abstract === - -This document proposes a standard for the [https://eprint.iacr.org/2020/1261.pdf MuSig2] protocol. -The standard is compatible with [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. -It supports ''tweaking'', which allows deriving [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32] child keys from aggregate keys and creating [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot outputs with key and script paths. - -=== Copyright === - -This document is licensed under the 3-clause BSD license. - -=== Motivation === - -MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create ordinary Schnorr signatures valid under the aggregate key. -Signing requires interaction between ''all'' signers involved in key aggregation. -(MuSig2 is a ''n-of-n'' multi-signature scheme and not a ''t-of-n' threshold-signature scheme.) - -The primary motivation for MuSig2 is the activation of Taproot ([https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]) on the Bitcoin network, which introduced the ability to authorize transactions with Schnorr signatures. -This standard allows the creation of aggregate public keys that can be used in Taproot outputs. - -The on-chain footprint of a MuSig2 Taproot output is a single BIP340 public key, and a transaction spending the output only requires a single signature cooperatively produced by all signers. This is '''more compact''' and has '''lower verification cost''' than each signer providing an individual public key and signature, as would be required by an ''n-of-n'' policy implemented using OP_CHECKSIGADD as introduced in ([https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki BIP342]). -As a side effect, the number ''n'' of signers is not limited by any consensus rules when using MuSig2. - -Moreover, MuSig2 offers a '''higher level of privacy''' than OP_CHECKSIGADD: MuSig2 Taproot outputs are indistinguishable for a blockchain observer from regular, single-signer Taproot outputs even though they are actually controlled by multiple signers. By tweaking an aggregate key, the shared Taproot output can have script spending paths that are hidden unless used. - -There are multi-signature schemes other than MuSig2 that are fully compatible with Schnorr signatures. -The MuSig2 variant in this specification stands out by combining all of the following features: -* '''Simple Key Setup''': Key aggregation is non-interactive and fully compatible with BIP340 public keys. -* '''Two Communication Rounds''': MuSig2 is faster in practice than previous three-round multi-signature protocols such as MuSig1, particularly when signers are connected through high-latency anonymous links. Moreover, the need for fewer communication rounds simplifies the specification and reduces the probability that implementations and users make security-relevant mistakes. -* '''Provable security''': MuSig2 has been [https://eprint.iacr.org/2020/1261.pdf proven existentially unforgeable] under the algebraic one-more discrete logarithm (AOMDL) assumption (instead of the discrete logarithm assumption required for single-signer Schnorr signatures). AOMDL is a falsifiable and weaker variant of the well-studied OMDL problem. -* '''Low complexity''': MuSig2 has a substantially lower computational and implementation complexity than alternative schemes like [https://eprint.iacr.org/2020/1057 MuSig-DN]. However, this comes at the cost of having no ability to generate nonces deterministically and the requirement to securely handle signing state. - -=== Design === - -* '''Compatibility with BIP340''': The aggregate public key created as part of this MuSig2 specification is a BIP340 X-only public key, and the signature output at the end of the protocol is a BIP340 signature that passes BIP340 verification for the aggregate key and a message. The public keys that are input to the key aggregation algorithm are also X-only public keys. Compared to compressed serialization, this adds complexity to the specification, but as X-only keys are becoming more common, the full key may not be available. -* '''Tweaking for BIP32 derivations and Taproot''': The specification supports tweaking aggregate public keys and signing for tweaked aggregate public keys. We distinguish two modes of tweaking: ''Ordinary'' tweaking can be used to derive child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking, on the other hand, allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] tweak to add script paths to a Taproot output. See section [[#tweaking|Tweaking]] below for details. -* '''Non-interactive signing with preprocessing''': The first communication round, exchanging the nonces, can happen before the message or even the exact set of signers is determined. Therefore, the signers can view it as a preprocessing step. Later, when the parameters of the signing session are chosen, they can send partial signatures without additional interaction. -* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines a function to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a canonical order of signers. Nonetheless, applications using this specification can mandate sorting before aggregationApplications that sort input public keys before aggregation should ensure that the sort implementation is reasonably efficient, and in particular does not degenerate to quadratic runtime on pathological inputs.. -* '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the unforgeability of the scheme. -* '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because the individual nonce is required for partial verification. -* '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second distinct key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''. -* '''Parameterization of MuSig2 and security''': In this specification, each signer's nonce consists of two elliptic curve points. The [https://eprint.iacr.org/2020/1261 MuSig2 paper] gives distinct security proofs depending on the number of points that constitute a nonce. See section [[#choosing-the-size-of-the-nonce|Choosing the Size of the Nonce]] for a discussion. - -This specification is written with a focus on clarity. -As a result, the specified algorithms are not always optimal in terms of computation and space. -In particular, some values are recomputed but can be cached in actual implementations (see [[#signing-flow|Signing Flow]]). -Also, the signers' public nonces are serialized in compressed format (33 bytes) instead of the smaller (32 bytes) but more complicated X-only serialization. - -== Description == - -When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that may lead to catastrophic failure. - -=== Signing Flow === - -The basic order of operations to create a multi-signature with the specification is as follows: -The signers start by exchanging public keys and computing an aggregate public key using the ''KeyAgg'' algorithm. -When they want to sign a message, each signer starts the signing session by running ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. -Then, the signers broadcast their ''pubnonce'' to each other and run ''NonceAgg'' to compute an aggregate nonce. -At this point, every signer has the required data to sign, which, in the specification, is stored in a data structure called [[#session-context|Session Context]]. -After running ''Sign'' with the secret signing key, the ''secnonce'' and the session context, each signer sends their partial signature to an aggregator node, which produces a final signature using ''PartialSigAgg''. -If all signers behaved honestly, the result passes [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] verification. - -'''IMPORTANT''': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. -Otherwise, it is possible to extract the secret signing key from the two partial signatures output by the two executions of ''Sign''. -To avoid accidental reuse of ''secnonce'', an implementation may securely erase the ''secnonce'' argument by overwriting it with 64 zero bytes after it has been read by ''Sign'. -A ''secnonce'' consisting of only zero bytes is invalid for ''Sign'' and will cause it to fail. - -To simplify the specification, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times. -Actual implementations can cache these values. -As a result, the [[#session-context|Session Context]] may look very different in implementations or may not exist at all. - -==== Public Key Aggregation ==== - -The output of ''KeyAgg'' is dependent on the order of the input public keys. -If the application does not have a canonical order of the signers, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the aggregate key is independent of the order of signers. - -The same public key is allowed to occur more than once in the input of ''KeyAgg'' and ''KeySort''. -This is by design: All algorithms in this specification handle multiple signers who (claim to) have identical public keys properly, -and applications are not required to check for duplicate public keys. -In fact, applications are recommended to omit checks for duplicate public keys in order to simplify error handling. -Moreover, it is often impossible to tell at key aggregation which signer is to blame for the duplicate, i.e., which signer came up with the public key honestly and which disruptive signer copied it. -In contrast, MuSig2 is designed to identify disruptive signers at signing time: any signer who prevents a signing session from completing successfully by sending incorrect contributions in the session can be identified and held accountable (see below). - -==== Nonce Generation ==== - -'''IMPORTANT''': ''NonceGen'' must have access to a high-quality random generator to draw an unbiased, uniformly random value ''rand' ''. -In contrast to BIP340 signing, the values ''k1'' and ''k2'' '''must not be derived deterministically''' from the session parameters because otherwise active attackers can [https://medium.com/blockstream/musig-dn-schnorr-multisignatures-with-verifiably-deterministic-nonces-27424b5df9d6#e3b6 trick the victim into reusing a nonce]. - -The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random. -If the value ''rand' '' was identical in two ''NonceGen'' invocations, but any optional argument was different, the ''secnonce'' would still be guaranteed be different as well (with overwhelming probability), and thus accidentally using the same ''secnonce'' for ''Sign'' in both sessions would be avoided. -Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation. -The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs, -e.g., a supposedly unique session id (taken from the application), a session counter wide enough not to repeat in practice, any nonces by other signers (if already known), or the serialization of a data structure containing multiple of the above. -However, the protection provided by the optional arguments should only be viewed as a last resort. -In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker. - -In some applications, it is beneficial to generate and exchange ''pubnonces'' before the signer's secret key, the final set of signers, or the message to sign is known. -In this case, only the available arguments are provided to the ''NonceGen'' algorithm. -After this preprocessing phase, the ''Sign'' algorithm can be run immediately when the message and set of signers is determined. -This way, the final signature is created quicker and with fewer roundtrips. -However, applications that use this method presumably store the nonces for a longer time and must therefore be even more careful not to reuse them. -Moreover, this method is not compatible with the defense-in-depth mechanism described in the previous paragraph. - -Instead of every signer broadcasting their ''pubnonce'' to every other signer, the signers can send their ''pubnonce'' to a single aggregator node that runs ''NonceAgg'' and sends the ''aggnonce'' back to the signers. -This technique reduces the overall communication. -The aggregator node does not need to be trusted for the scheme's security to hold. -All the aggregator node can do is prevent the signing session from succeeding by sending out incorrect aggregate nonces. - -In general, MuSig2 signers are stateful in the sense that they first generate ''secnonce'' and then need to store it until they receive the other signer's ''pubnonces'' or the ''aggnonce''. -However, it is possible for one of the signers to be stateless. -This signer waits until it receives the ''pubnonce'' of all the other signers and until session parameters such as a message to sign, public keys, and tweaks are determined. -Then, the signer can run ''NonceGen'', ''NonceAgg'' and ''Sign'' in sequence and send out its ''pubnonce'' along with its partial signature. - -==== Identifiying Disruptive Signers ==== - -If any signer sends an incorrect partial signature, i.e., one that has not then been created with ''Sign'' and the right arguments for the session, the MuSig2 protocol may fail to output a valid Schnorr signature. -This standard provides the method ''PartialSigVerify'' to verify the correctness of partial signatures. -If partial signatures are received over authenticated channels, this method can be used to identify disruptive signers and hold them accountable. -Note that partial signatures are ''not'' signatures. -An adversary can forge a partial signature, i.e., create a partial signature without knowing the secret key for the claimed public keyAssume an adversary wants to forge a partial signature for public key ''P''. It joins the signing session pretending to be two different signers, one with public key ''P' and one with another public key. The adversary can then set the second signer's nonce such that it will be able to produce a partial signature for ''P'', but not for the other claimed signer.. -However, if ''PartialSigVerify'' succeeds for all partial signatures then ''PartialSigAgg'' will return a valid Schnorr signature. - -==== Tweaking ==== - -In addition to public keys, the ''KeyAgg'' algorithm accepts tweaks, which modify the aggregate public key as defined in the [[#tweaking-definition|Tweaking Definition]] subsection. -For example, if ''KeyAgg'' is run with ''v = 2'', ''is_xonly_t1 = false'', ''is_xonly_t2 = true'', then the aggregate key is first ordinarily tweaked with ''tweak1'' and then X-only tweaked with ''tweak2''. - -The purpose of specifying tweaking is to ensure compatibility with existing uses of tweaking, i.e., that the result of signing is a valid signature for the tweaked public key. -The MuSig2 algorithms take arbitrary tweaks as input but accepting arbitrary tweaks may negatively affect the protocol's security. -Instead, signers should obtain the tweaks according to other specifications. -This typically involves deriving the tweaks from a hash of the aggregate public key and some other information. - -Ordinary tweaking can be used to derive child public keys from an aggregate public key using [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. -On the other hand, X-only tweaking is required for Taproot tweaking per [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]. -A Taproot-tweaked public key commits to a ''script path'', allowing users to create transaction outputs that are spendable either with a MuSig2 multi-signature or by providing inputs that satisfy the script path. - -==== Modifications to Nonce Generation ==== - -Implementors must avoid modifying the ''NonceGen'' algorithm without being fully aware of the implications. -The following two modifications are secure when applied correctly and may be useful in special circumstances, e.g., in very restricted environments where secure randomness is not available. - -First, on systems where obtaining uniformly random values is much harder than maintaining a global atomic counter, it can be beneficial to modify ''NonceGen''. -Instead of drawing ''rand' '' uniformly at random, ''rand' '' can be the value of an atomic counter that is incremented whenever it is read. -With this modification, the secret signing key ''sk'' of the signer generating the nonce is '''not''' an optional argument and must be provided to ''NonceGen''. -The security of the resulting scheme is then depending on the requirement that the counter must never return the same output in two ''NonceGen'' invocations with the same ''sk''. - -Second, if there is a unique signer who is supposed to send the ''pubnonce'' last, it is possible to modify nonce generation for this single signer to not require high-quality randomness. -If randomness is entirely unavailable, nonce generation for this signer can also be made deterministic. -To obtain such a nonce generation algorithm ''NonceGen' '', the algorithm ''NonceGen'' should be modified as follows: The arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key, the aggregate public key, and message of the session, respectively. -In addition, ''NonceGen '' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''in'' argument. -Hence, using ''NonceGen' '' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the stateless signer described in the [[#nonce-generation|Nonce Generation]] section. -Further inputs can be to added ''in'' as described in the [[#nonce-generation|Nonce Generation]] section. -Lastly, if no randomness, not even low-quality randomness, is available, ''NonceGen' '' can be made deterministic by removing ''rand' '' and setting ''rand'' to ''sk''. -Failure to provide the correct arguments to ''NonceGen' '' will allow attackers to extract secret keys. - -=== Notation === - -The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme. -* Lowercase variables represent integers or byte arrays. -** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''. -** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''. -* Uppercase variables refer to points on the curve with equation ''y2 = x3 + 7'' over the integers modulo ''p''. -** ''is_infinite(P)'' returns whether or not ''P'' is the point at infinity. -** ''x(P)'' and ''y(P)'' are integers in the range ''0..p-1'' and refer to the X and Y coordinates of a point ''P'' (assuming it is not infinity). -** The constant ''G'' refers to the base point, for which ''x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'' and ''y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8''. -** Addition of points refers to the usual [https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law elliptic curve group operation]. -** [https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication Multiplication (⋅) of an integer and a point] refers to the repeated application of the group operation. -* Functions and operations: -** ''||'' refers to byte array concatenation. -** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. -** The function ''bytes(n, x)'', where ''x'' is an integer, returns the n-byte encoding of ''x'', most significant byte first. -** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. -** The function ''len(x)'' where ''x'' is a byte array returns the length of the array. -** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. -** The function ''with_even_y(P)'', where ''P'' is a point, returns ''P'' if ''is_infinite(P)'' or ''has_even_y(P)''. Otherwise, ''with_even_y(P)'' returns ''-P''. -** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is a byte that is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. -** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. -** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' - Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: -*** Fail if ''x > p-1''. -*** Let ''c = x3 + 7 mod p''. -*** Let ''y' = c(p+1)/4 mod p''. -*** Fail if ''c ≠ y'2 mod p''. -*** Let ''y = y' '' if ''y' mod 2 = 0'', otherwise let ''y = p - y' ''. -*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y''. -** The function ''point(x)'', where ''x'' is a 32-byte array ("X-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. -** The function ''pointc(x)'', where ''x'' is a 33-byte array (compressed serialization), sets ''P = lift_x(int(x[1:33]))'' and fails if that fails. If ''x[0] = 2'' it returns ''P'' and if ''x[0] = 3'' it returns ''-P''. Otherwise, it fails. -** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. -* Other: -** Tuples are written by listing the elements within parentheses and separated by commas. For example, ''(2, 3, 1)'' is a tuple. - -=== Specification === - -==== Key Sorting ==== - -Input: -* The number ''u'' of public keys with ''0 < u < 2^32'' -* The public keys ''pk1..u'': ''u'' 32-byte arrays - -'''''KeySort(pk1..u)''''': -* Return ''pk1..u'' sorted in lexicographical order. - -==== Key Aggregation ==== - -Input: -* The number ''u'' of public keys with ''0 < u < 2^32'' -* The public keys ''pk1..u'': ''u'' 32-byte arrays -* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' -* The tweaks ''tweak1..v'': ''v'' 32-byte arrays -* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans - -'''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''': -* Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. -* Return ''bytes(Q)''. - -'''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''': -* Let ''pk2 = GetSecondKey(pk1..u)'' -* For ''i = 1 .. u'': -** Let ''Pi = point(pki)''; fail if that fails. -** Let ''ai = KeyAggCoeffInternal(pk1..u, pki, pk2)''. -* Let ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' -* Fail if ''is_infinite(Q0)''. -* Let ''tacc0 = 0'' -* Let ''gacc0 = 1'' -* For ''i = 1 .. v'': -** Let ''(Qi, gacci, tacci) = ApplyTweak(Qi-1, gacci-1, tacci-1, tweaki, is_xonly_ti)''; fail if that fails -* Return ''(Qv, gaccv, taccv)''. - -'''''HashKeys(pk1..u)''''': -* Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' - -'''''GetSecondKey(pk1..u)''''': -* For ''j = 1 .. u'': -** If ''pkj ≠ pk1'': -*** Return ''pkj'' -* Return ''bytes(32, 0)'' - -'''''KeyAggCoeff(pk1..u, pk')''''': -* Let ''pk2 = GetSecondKey(pk1..u)'': -* Return ''KeyAggCoeffInternal(pk1..u, pk', pk2)'' - -'''''KeyAggCoeffInternal(pk1..u, pk', pk2)''''': -* Let ''L = HashKeys(pk1..u)'' -* If ''pk' = pk2'': -** Return 1 -* Return ''int(hashKeyAgg coefficient(L || pk')) mod n''The key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting. - -'''''ApplyTweak(Qi-1, gacci-1, tacci-1, tweaki, is_xonly_ti)''''': -* If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': -** Let ''gi-1 = -1 mod n'' -* Else: let ''gi-1 = 1'' -* Let ''ti = int(tweaki)''; fail if ''t ≥ n'' -* Let ''Qi = gi-1⋅Qi-1 + ti⋅G'' -** Fail if ''is_infinite(Qi)'' -* Let ''gacci = gi-1⋅gacci-1 mod n'' -* Let ''tacci = ti + gi-1⋅tacci-1 mod n'' -* Return ''(Qi, gacci, tacci)'' - -==== Nonce Generation ==== - -Input: -* The secret signing key ''sk'': a 32-byte array or 0-byte array (optional argument) -* The aggregate public key ''aggpk'': a 32-byte array or 0-byte array (optional argument) -* The message ''m'': a 32-byte array or 0-byte array (optional argument) -* The auxiliary input ''in'': a byte array with ''0 ≤ len(in) ≤ 232-1'' (optional argument) - -'''''NonceGen(sk, aggpk, m, in)''''': -* Let ''rand' '' be a 32-byte array freshly drawn uniformly at random -* If ''len(sk) > 0'': -** Let ''rand'' be the byte-wise xor of ''sk'' and ''hashMuSig/aux(rand')''The random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the secret signing key itself. It is xored with the secret key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key.. -* Else: let ''rand = rand' '' -* Let ''ki = int(hashMuSig/nonce(rand || bytes(1, len(aggpk)) || aggpk || bytes(1, i) || bytes(1, len(m)) || m || bytes(4, len(in)) || in)) mod n'' for ''i = 1,2'' -* Fail if ''k1 = 0'' or ''k2 = 0'' -* Let ''R*1 = k1⋅G, R*2 = k2⋅G'' -* Let ''pubnonce = cbytes(R*1) || cbytes(R*2)'' -* Let ''secnonce = bytes(32, k1) || bytes(32, k2)'' -* Return ''secnonce'' and ''pubnonce'' - -==== Nonce Aggregation ==== - -Input: -* The number ''u'' of ''pubnonces'' with ''0 < u < 2^32'' -* The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays - -'''''NonceAgg(pubnonce1..u)''''': -* For ''i = 1 .. 2'': -** For ''j = 1 .. u'': -*** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails -** Let ''R'i = Ri,1 + Ri,2 + ... + Ri,u'' -**
Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' (see [[#dealing-with-infinity-in-nonce-aggregation|Dealing with Infinity in Nonce Aggregation]]) -* Return ''aggnonce = cbytes(R1) || cbytes(R2)'' - -==== Session Context ==== - -The Session Context is a data structure consisting of the following elements: -* The aggregate public nonce ''aggnonce'': a 66-byte array -* The number ''u'' of public keys with ''0 < u < 2^32'' -* The public keys ''pk1..u'': ''u'' 32-byte arrays -* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' -* The tweaks ''tweak1..v'': ''v'' 32-byte arrays -* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -* The message ''m'': a 32-byte array - -We write "Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx''" to assign names to the elements of a Session Context. - -'''''GetSessionValues(session_ctx)''''': -* Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx'' -* Let ''(Q, gaccv, taccv) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails -* Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' -* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails -* Let ''R = R1 + b⋅R2'' -* Fail if ''is_infinite(R)'' -* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' -* Return ''(Q, gaccv, taccv, b, R, e)'' - - -'''''GetSessionKeyAggCoeff(session_ctx, P)''''': -* Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' -* Return ''KeyAggCoeff(pk1..u, bytes(P))'' - -==== Signing ==== - -Input: -* The secret nonce ''secnonce'' that has never been used as input to ''Sign'' before: a 64-byte array -* The secret key ''sk'': a 32-byte array -* The ''session_ctx'': a [[#session-context|Session Context]] data structure - -'''''Sign(secnonce, sk, session_ctx)''''': -* Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails -* Let ''k'1 = int(secnonce[0:32]), k'2 = int(secnonce[32:64])'' -* Fail if ''k'i = 0'' or ''k'i ≥ n'' for ''i = 1..2'' -* Let ''k1 = k'1, k2 = k'2 '' if ''has_even_y(R)'', otherwise let ''k1 = n - k'1, k2 = n - k2'' -* Let ''d' = int(sk)'' -* Fail if ''d' = 0'' or ''d' ≥ n'' -* Let ''P = d'⋅G'' -* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' -* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' -*
Let ''d = gv⋅gaccv⋅gp⋅d' mod n'' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) -* Let ''s = (k1 + b⋅k2 + e⋅a⋅d) mod n'' -* Let ''psig = bytes(32, s)'' -* Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' -* If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. -* Return partial signature ''psig'' - -==== Partial Signature Verification ==== - -Input: -* The partial signature ''psig'': a 32-byte array -* The number ''u'' of public nonces and public keys with ''0 < u < 2^32'' -* The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays -* The public keys ''pk1..u'': ''u'' 32-byte arrays -* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' -* The tweaks ''tweak1..v'': ''v'' 32-byte arrays -* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -* The message ''m'': a 32-byte array -* The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' - -'''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''': -* Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails -* Let ''session_ctx = (aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m)'' -* Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' -* Return success iff no failure occurred before reaching this point. - -'''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''': -* Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails -* Let ''s = int(psig)''; fail if ''s ≥ n'' -* Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' -* Let ''R*' = R*1 + b⋅R*2'' -* Let ''R* = R*' '' if ''has_even_y(R)'', otherwise let ''R* = -R*' '' -* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' -* Let ''g' = gv⋅gaccv mod n'' -*
Let ''P = g'⋅point(pk*)''; fail if that fails (See [[#negation-of-the-public-key-when-partially-verifying|Negation Of The Public Key When Partially Verifying]]) -* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Fail if ''s⋅G ≠ R* + e⋅a⋅P'' -* Return success iff no failure occurred before reaching this point. - -==== Partial Signature Aggregation ==== - -Input: -* The number ''u'' of signatures with ''0 < u < 2^32'' -* The partial signatures ''psig1..u'': ''u'' 32-byte arrays -* The ''session_ctx'': a [[#session-context|Session Context]] data structure - -'''''PartialSigAgg(psig1..u, session_ctx)''''': -* Let ''(Q, _, taccv, _, _, R, e) = GetSessionValues(session_ctx)''; fail if that fails -* For ''i = 1 .. u'': -** Let ''si = int(psigi)''; fail if ''si ≥ n''. -* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' -* Let ''s = s1 + ... + su + e⋅gv⋅taccv mod n'' -* Return ''sig = ''bytes(R) || bytes(32, s)'' - -=== Test Vectors and Reference Code === - -We provide a naive, highly inefficient, and non-constant time [[musig-reference.py|pure Python 3 reference implementation of the key aggregation, partial signing, and partial signature verification algorithms, together with some test vectors]]. -The reference implementation is for demonstration purposes only and not to be used in production environments. - -== Remarks on Security and Correctness == - -=== Tweaking Definition === - -This MuSig2 specification supports two modes of tweaking that correspond to the following algorithms: - -Input: -* ''P'': a point -* The tweak ''t'': an integer with ''0 ≤ t < n '' - -'''''ApplyOrdinaryTweak(P, t)''''': -* Return ''P + t⋅G'' - -'''''ApplyXonlyTweak(P, t)''''': -* Return ''with_even_y(P) + t⋅G'' - -=== Negation Of The Secret Key When Signing === - -In order to produce a partial signature for an X-only public key that is an aggregate of ''u'' X-only keys and tweaked ''v'' times (X-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. - - -The following elliptic curve points arise as intermediate steps in the MuSig2 protocol: -• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's X-only public key. Defining ''d'i'' to be the ''i''-th signer's secret key as an integer, i.e. the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have - ''Pi = with_even_y(d'i⋅G) ''. -• ''Q0'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as - ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu''. -• ''Qi'' as computed in ''Tweak'' for ''1 ≤ i ≤ v'' is the tweaked public key after the ''i''-th tweaking operation. It holds that - ''Qi = f(i-1) + ti⋅G'' for ''i = 1, ..., v'' where - ''f(i-1) := with_even_y(Qi-1)'' if ''is_xonly_ti'' and - ''f(i-1) := Qi-1'' otherwise. -• ''with_even_y(Qv)'' is the final result of ''KeyAgg''. - - -The signer's goal is to produce a partial signature corresponding to the final result of ''KeyAgg'', i.e. the X-only public key ''with_even_y(Qv)''. - - -We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. Note that ''gpi'' indicates whether the ''i''-th signer needed to negate their secret key to produce an X-only public key. In particular, - ''Pi = gpi⋅d'i⋅G''. - -For ''0 ≤ i ≤ v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''gi'' to ''-1 mod n'' if and only if ''is_xonly_ti+1'' is true and ''Qi'' has an odd Y coordinate. In other words, ''gi'' indicates whether ''Qi'' needed to be negated to apply an X-only tweak: - ''f(i) = gi⋅Qi'' for ''0 ≤ i ≤ v - 1''. - -Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv'' depending on whether ''Qv'' needed to be negated to produce the (X-only) final output of ''KeyAgg': - ''with_even_y(Qv) = gv⋅Qv''. - - - -So, the (X-only) final public key is - ''with_even_y(Qv) - = gv⋅Qv - = gv⋅(f(v-1) + tv⋅G) - = gv⋅(gv-1⋅(f(v-2) + tv-1⋅G) + tv⋅G) - = gv⋅gv-1⋅f(v-2) + gv⋅(tv + gv-1⋅tv-1)⋅G - = gv⋅gv-1⋅f(v-2) + (sumi=v-1..v ti⋅prodj=i..v gj)⋅G - = gv⋅gv-1⋅...⋅g1⋅f(0) + (sumi=1..v ti⋅prodj=i..v gj)⋅G - = gv⋅...⋅g0⋅Q0 + gv⋅taccv⋅G'' - where ''tacci'' is computed by ''KeyAggInternal'' and ''Tweak'' as follows: - ''tacc0 = 0 - tacci = ti + gi-1⋅tacci-1 for i=1..v mod n'' - for which it holds that ''gv⋅taccv = sumi=1..v ti⋅prodj=i..v gj''. - - - -''KeyAggInternal'' and ''Tweak'' compute - ''gacc0 = 1 - gacci = gi-1⋅gacci-1 for i=1..v mod n'' -So we can rewrite above equation for the final public key as - ''with_even_y(Qv) = gv⋅gaccv⋅Q0 + gv⋅taccv⋅G''. - - - -Then we have - ''with_even_y(Qv) - gv⋅taccv⋅G - = gv⋅gaccv⋅Q0 - = gv⋅gaccv⋅(a1⋅P1 + ... + au⋅Pu) - = gv⋅gaccv⋅(a1⋅gp1⋅d'1⋅G + ... + au⋅gpu⋅d'u⋅G) - = sumi=1..u(gv⋅gaccv⋅gpi⋅ai⋅d'i)*G''. - - -Intuitively, ''gacci'' tracks accumulated sign flipping and ''tacci'' tracks the accumulated tweak value after applying the first ''i'' individual tweaks. Additionally, ''gv'' indicates whether ''Qv'' needed to be negated to produce the final X-only result, and ''gpi'' indicates whether ''d'i'' needs to be negated to produce the initial X-only key ''Pi''. Thus, signer ''i'' multiplies its secret key ''d'i'' with ''gv⋅gaccv⋅gpi'' in the ''[[#Sign negation|Sign]]'' algorithm. - -==== Negation Of The Public Key When Partially Verifying ==== - - -As explained in [[#negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]] the signer uses a possibly negated secret key - ''d = gv⋅gaccv⋅gp⋅d' mod n'' -when producing a partial signature to ensure that the aggregate signature will correspond to an aggregate public key with even Y coordinate. - - - -The ''[[#SigVerify negation|PartialSigVerifyInternal]]'' algorithm is supposed to check - ''s⋅G = R* + e⋅a⋅d⋅G''. - - - -The verifier doesn't have access to ''d⋅G'', but can construct it using the xonly public key ''pk*'' as follows: -''d⋅G - = gv⋅gaccv⋅gp⋅d'⋅G - = gv⋅gaccv⋅point(pk*)'' -Note that the aggregate public key and list of tweaks are inputs to partial signature verification, so the verifier can also construct ''gv'' and ''gaccv''. - - -=== Dealing with Infinity in Nonce Aggregation === - -If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). -If we fail here, we will never be able to determine who it is. -Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. - -However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. -Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: -* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). -* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). - -The entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). -In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. - -The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". -This modification only depends on the publicly available ''aggnonce''. -Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. - -We conclude that these two modifications preserve the security of the MuSig2* scheme. - - -=== Choosing the Size of the Nonce === - -The [https://eprint.iacr.org/2020/1261 MuSig2 paper] contains two security proofs that apply to different protocol variants. -The first is for a variant where each signer's nonce consists of four elliptic curve points and uses the random oracle model (ROM). -In the second variant, the signers' nonces consist of only two points. -Its proof requires a stronger model, namely the combination of the ROM and the algebraic group model (AGM). -Relying on the stronger model is a legitimate choice for the following reasons: - -First, an approach widely taken is interpreting a Forking Lemma proof in the ROM merely as design justification and ignoring the loss of security due to the Forking Lemma. -If one believes in this approach, then the ROM may not be the optimal model in the first place because some parts of the concrete security bound are arbitrarily ignored. -One may just as well move to the ROM+AGM model, which produces bounds close to the best-known attacks, e.g., for Schnorr signatures. - -Second, as of this writing, there is no instance of a serious protocol with a security proof in the AGM that is not secure in practice. -There are, however, insecure toy schemes with AGM security proofs, but those explicitly violate the requirements of the AGM. -[https://eprint.iacr.org/2022/226.pdf Broken AGM proofs of toy schemes] provide group elements to the adversary without declaring them as group element inputs. -In contrast, in MuSig2, all group elements that arise in the protocol are known to the adversary and declared as group element inputs. -A scheme very similar to MuSig2 and with two-point nonces was independently proven secure in the ROM and AGM by [https://eprint.iacr.org/2020/1245 Alper and Burdges]. - -== Footnotes == - - - -== Acknowledgements == - -We thank Brandon Black, Riccardo Casatta, Russell O'Connor, and Pieter Wuille for their contributions to this document. +This document was moved to [https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki]. \ No newline at end of file From db648478c3cc600dbe42c5badb45a1e1fa217f13 Mon Sep 17 00:00:00 2001 From: Jon Griffiths Date: Mon, 18 Jul 2022 12:29:45 +1200 Subject: [PATCH 194/381] extrakeys: rename swap/swap64 to fix OpenBSD 7.1 compilation OpenBSD defines swap64 in . --- src/modules/extrakeys/hsort_impl.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/extrakeys/hsort_impl.h b/src/modules/extrakeys/hsort_impl.h index fcc4b10b..a5a94023 100644 --- a/src/modules/extrakeys/hsort_impl.h +++ b/src/modules/extrakeys/hsort_impl.h @@ -23,7 +23,7 @@ static SECP256K1_INLINE size_t child2(size_t i) { return child1(i)+1; } -static SECP256K1_INLINE void swap64(unsigned char *a, size_t i, size_t j, size_t stride) { +static SECP256K1_INLINE void heap_swap64(unsigned char *a, size_t i, size_t j, size_t stride) { unsigned char tmp[64]; VERIFY_CHECK(stride <= 64); memcpy(tmp, a + i*stride, stride); @@ -31,12 +31,12 @@ static SECP256K1_INLINE void swap64(unsigned char *a, size_t i, size_t j, size_t memcpy(a + j*stride, tmp, stride); } -static SECP256K1_INLINE void swap(unsigned char *a, size_t i, size_t j, size_t stride) { +static SECP256K1_INLINE void heap_swap(unsigned char *a, size_t i, size_t j, size_t stride) { while (64 < stride) { - swap64(a + (stride - 64), i, j, 64); + heap_swap64(a + (stride - 64), i, j, 64); stride -= 64; } - swap64(a, i, j, stride); + heap_swap64(a, i, j, stride); } static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_size, size_t stride, @@ -71,7 +71,7 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s if (child2(i) < heap_size && 0 <= cmp(a + child2(i)*stride, a + child1(i)*stride, cmp_data)) { if (0 < cmp(a + child2(i)*stride, a + i*stride, cmp_data)) { - swap(a, i, child2(i), stride); + heap_swap(a, i, child2(i), stride); i = child2(i); } else { /* At this point we have [child2(i)] >= [child1(i)] and we have @@ -80,7 +80,7 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s return; } } else if (0 < cmp(a + child1(i)*stride, a + i*stride, cmp_data)) { - swap(a, i, child1(i), stride); + heap_swap(a, i, child1(i), stride); i = child1(i); } else { return; @@ -106,7 +106,7 @@ static void secp256k1_hsort(void *ptr, size_t count, size_t size, } for(i = count; 1 < i; --i) { /* Extract the largest value from the heap */ - swap(ptr, 0, i-1, size); + heap_swap(ptr, 0, i-1, size); /* Repair the heap condition */ heap_down(ptr, 0, i-1, size, cmp, cmp_data); From 4ff6e4274d49cb95ab246b599b274104baf83f9f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 26 Jul 2022 17:09:36 +0000 Subject: [PATCH 195/381] surjectionproof: add test for existing behavior on input=output proofs --- src/modules/surjection/tests_impl.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 89ba4e1a..70955760 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -524,6 +524,33 @@ void test_bad_parse(void) { CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0); } +void test_input_eq_output(void) { + secp256k1_surjectionproof proof; + secp256k1_fixed_asset_tag fixed_tag; + secp256k1_generator ephemeral_tag; + unsigned char blinding_key[32]; + unsigned char entropy[32]; + size_t input_index; + + secp256k1_testrand256(fixed_tag.data); + secp256k1_testrand256(blinding_key); + secp256k1_testrand256(entropy); + + CHECK(secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, &fixed_tag, 1, 1, &fixed_tag, 100, entropy) == 1); + CHECK(input_index == 0); + + /* Generation should fail */ + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_tag, fixed_tag.data, blinding_key)); + CHECK(!secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); + + /* It succeeds when the blinding factor is 0... (will fix this in the next commit) */ + memset(blinding_key, 0, 32); + CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_tag, fixed_tag.data, blinding_key)); + CHECK(secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); + /* ...but verification doesn't */ + CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag)); +} + void test_fixed_vectors(void) { const unsigned char tag0_ser[] = { 0x0a, @@ -672,6 +699,7 @@ void test_fixed_vectors(void) { void run_surjection_tests(void) { test_surjectionproof_api(); + test_input_eq_output(); test_fixed_vectors(); test_input_selection(0); From bf18ff5a8c6295cb7db6e2989aefd6a78df7720f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 26 Jul 2022 17:14:49 +0000 Subject: [PATCH 196/381] surjectionproof: fix generation to fail when any input == the output Verification will fail in this case, so don't "succeed" in generating a bad proof. --- src/modules/surjection/main_impl.h | 12 ++++++++---- src/modules/surjection/tests_impl.h | 6 ++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index b8f145e5..343b86ae 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -307,10 +307,14 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s if (overflow) { return 0; } - /* The only time the input may equal the output is if neither one was blinded in the first place, - * i.e. both blinding keys are zero. Otherwise this is a privacy leak. */ - if (secp256k1_scalar_eq(&tmps, &blinding_key) && !secp256k1_scalar_is_zero(&blinding_key)) { - return 0; + /* If any input tag is equal to an output tag, verification will fail, because our ring + * signature logic would receive a zero-key, which is illegal. This is unfortunate but + * it is deployed on Liquid and cannot be fixed without a hardfork. We should review + * this at the same time that we relax the max-256-inputs rule. */ + for (i = 0; i < n_ephemeral_input_tags; i++) { + if (memcmp(ephemeral_input_tags[i].data, ephemeral_output_tag->data, sizeof(ephemeral_output_tag->data)) == 0) { + return 0; + } } secp256k1_scalar_negate(&tmps, &tmps); secp256k1_scalar_add(&blinding_key, &blinding_key, &tmps); diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 70955760..ac7c269b 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -543,12 +543,10 @@ void test_input_eq_output(void) { CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_tag, fixed_tag.data, blinding_key)); CHECK(!secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); - /* It succeeds when the blinding factor is 0... (will fix this in the next commit) */ + /* ...even when the blinding key is zero */ memset(blinding_key, 0, 32); CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_tag, fixed_tag.data, blinding_key)); - CHECK(secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); - /* ...but verification doesn't */ - CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag)); + CHECK(!secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key)); } void test_fixed_vectors(void) { From d1175d265d514bd0c22faaf262d7df362f33af89 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 29 Jul 2022 21:04:04 +0000 Subject: [PATCH 197/381] surjectionproof: use secp256k1_memcmp_var rather than bare memcmp Co-authored-by: Tim Ruffing --- src/modules/surjection/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 343b86ae..d74f2a48 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -312,7 +312,7 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s * it is deployed on Liquid and cannot be fixed without a hardfork. We should review * this at the same time that we relax the max-256-inputs rule. */ for (i = 0; i < n_ephemeral_input_tags; i++) { - if (memcmp(ephemeral_input_tags[i].data, ephemeral_output_tag->data, sizeof(ephemeral_output_tag->data)) == 0) { + if (secp256k1_memcmp_var(ephemeral_input_tags[i].data, ephemeral_output_tag->data, sizeof(ephemeral_output_tag->data)) == 0) { return 0; } } From 347f96d94a6c2840e045510672549897be743101 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 4 Aug 2022 20:47:19 +0000 Subject: [PATCH 198/381] fix include paths in all the -zkp modules This is causing out-of-tree build failures in Elements. --- src/modules/ecdsa_adaptor/main_impl.h | 4 ++-- src/modules/ecdsa_adaptor/tests_impl.h | 2 +- src/modules/ecdsa_s2c/main_impl.h | 4 ++-- src/modules/ecdsa_s2c/tests_impl.h | 2 +- src/modules/generator/main_impl.h | 8 ++++---- src/modules/generator/tests_impl.h | 10 +++++----- src/modules/rangeproof/borromean.h | 10 +++++----- src/modules/rangeproof/borromean_impl.h | 14 +++++++------- src/modules/rangeproof/main_impl.h | 8 ++++---- src/modules/rangeproof/pedersen.h | 6 +++--- src/modules/rangeproof/pedersen_impl.h | 14 +++++++------- src/modules/rangeproof/rangeproof.h | 8 ++++---- src/modules/rangeproof/rangeproof_impl.h | 17 ++++++++--------- src/modules/rangeproof/tests_impl.h | 10 +++++----- src/modules/surjection/main_impl.h | 10 +++++----- src/modules/surjection/surjection.h | 4 ++-- src/modules/surjection/surjection_impl.h | 8 ++++---- src/modules/surjection/tests_impl.h | 10 +++++----- src/modules/whitelist/main_impl.h | 4 ++-- src/modules/whitelist/tests_impl.h | 2 +- 20 files changed, 77 insertions(+), 78 deletions(-) diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index c6d211e1..1e3997bd 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H #define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H -#include "include/secp256k1_ecdsa_adaptor.h" -#include "modules/ecdsa_adaptor/dleq_impl.h" +#include "../../../include/secp256k1_ecdsa_adaptor.h" +#include "dleq_impl.h" /* (R, R', s', dleq_proof) */ static int secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index 37547ace..d0f0eb5a 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -1,7 +1,7 @@ #ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H #define SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H -#include "include/secp256k1_ecdsa_adaptor.h" +#include "../../../include/secp256k1_ecdsa_adaptor.h" void rand_scalar(secp256k1_scalar *scalar) { unsigned char buf32[32]; diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index a8272720..95cb088d 100644 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_MODULE_ECDSA_S2C_MAIN_H #define SECP256K1_MODULE_ECDSA_S2C_MAIN_H -#include "include/secp256k1.h" -#include "include/secp256k1_ecdsa_s2c.h" +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ecdsa_s2c.h" static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) { secp256k1_pubkey_save((secp256k1_pubkey*) opening, ge); diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index 1868c76b..95b4d95e 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_ECDSA_S2C_TESTS_H #define SECP256K1_MODULE_ECDSA_S2C_TESTS_H -#include "include/secp256k1_ecdsa_s2c.h" +#include "../../../include/secp256k1_ecdsa_s2c.h" static void test_ecdsa_s2c_tagged_hash(void) { unsigned char tag_data[14] = "s2c/ecdsa/data"; diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 9217169c..c915c791 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -9,10 +9,10 @@ #include -#include "field.h" -#include "group.h" -#include "hash.h" -#include "scalar.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../scalar.h" static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { int succeed; diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 068c5f39..cc43912a 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -10,12 +10,12 @@ #include #include -#include "group.h" -#include "scalar.h" -#include "testrand.h" -#include "util.h" +#include "../../group.h" +#include "../../scalar.h" +#include "../../testrand.h" +#include "../../util.h" -#include "include/secp256k1_generator.h" +#include "../../../include/secp256k1_generator.h" void test_generator_api(void) { unsigned char key[32]; diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h index efd4da16..b9a762bf 100644 --- a/src/modules/rangeproof/borromean.h +++ b/src/modules/rangeproof/borromean.h @@ -8,11 +8,11 @@ #ifndef _SECP256K1_BORROMEAN_H_ #define _SECP256K1_BORROMEAN_H_ -#include "scalar.h" -#include "field.h" -#include "group.h" -#include "ecmult.h" -#include "ecmult_gen.h" +#include "../../scalar.h" +#include "../../field.h" +#include "../../group.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e0, const secp256k1_scalar *s, const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen); diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 0b8dcd47..c0a5c332 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -8,13 +8,13 @@ #ifndef _SECP256K1_BORROMEAN_IMPL_H_ #define _SECP256K1_BORROMEAN_IMPL_H_ -#include "scalar.h" -#include "field.h" -#include "group.h" -#include "hash.h" -#include "eckey.h" -#include "ecmult.h" -#include "ecmult_gen.h" +#include "../../scalar.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../eckey.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" #include "borromean.h" #include diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 9129e4c3..b1c36cdd 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -7,11 +7,11 @@ #ifndef SECP256K1_MODULE_RANGEPROOF_MAIN #define SECP256K1_MODULE_RANGEPROOF_MAIN -#include "group.h" +#include "../../group.h" -#include "modules/rangeproof/pedersen_impl.h" -#include "modules/rangeproof/borromean_impl.h" -#include "modules/rangeproof/rangeproof_impl.h" +#include "pedersen_impl.h" +#include "borromean_impl.h" +#include "rangeproof_impl.h" /** Alternative generator for secp256k1. * This is the sha256 of 'g' after standard encoding (without compression), diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h index 14d9920e..ce42d521 100644 --- a/src/modules/rangeproof/pedersen.h +++ b/src/modules/rangeproof/pedersen.h @@ -7,9 +7,9 @@ #ifndef _SECP256K1_PEDERSEN_H_ #define _SECP256K1_PEDERSEN_H_ -#include "ecmult_gen.h" -#include "group.h" -#include "scalar.h" +#include "../../ecmult_gen.h" +#include "../../group.h" +#include "../../scalar.h" #include diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/rangeproof/pedersen_impl.h index 69f22e38..6ebffc4f 100644 --- a/src/modules/rangeproof/pedersen_impl.h +++ b/src/modules/rangeproof/pedersen_impl.h @@ -9,13 +9,13 @@ #include -#include "eckey.h" -#include "ecmult_const.h" -#include "ecmult_gen.h" -#include "group.h" -#include "field.h" -#include "scalar.h" -#include "util.h" +#include "../../eckey.h" +#include "../../ecmult_const.h" +#include "../../ecmult_gen.h" +#include "../../group.h" +#include "../../field.h" +#include "../../scalar.h" +#include "../../util.h" static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t value) { unsigned char data[32]; diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index fd8cf950..5aadce0e 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -7,10 +7,10 @@ #ifndef _SECP256K1_RANGEPROOF_H_ #define _SECP256K1_RANGEPROOF_H_ -#include "scalar.h" -#include "group.h" -#include "ecmult.h" -#include "ecmult_gen.h" +#include "../../scalar.h" +#include "../../group.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 184b7988..75c06a14 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -7,16 +7,15 @@ #ifndef _SECP256K1_RANGEPROOF_IMPL_H_ #define _SECP256K1_RANGEPROOF_IMPL_H_ -#include "eckey.h" -#include "scalar.h" -#include "group.h" -#include "rangeproof.h" -#include "hash_impl.h" -#include "pedersen_impl.h" -#include "util.h" +#include "../../eckey.h" +#include "../../scalar.h" +#include "../../group.h" +#include "../../hash_impl.h" +#include "../../util.h" -#include "modules/rangeproof/pedersen.h" -#include "modules/rangeproof/borromean.h" +#include "pedersen.h" +#include "rangeproof.h" +#include "borromean.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 41c4d616..47c5f3a7 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -9,12 +9,12 @@ #include -#include "group.h" -#include "scalar.h" -#include "testrand.h" -#include "util.h" +#include "../../group.h" +#include "../../scalar.h" +#include "../../testrand.h" +#include "../../util.h" -#include "include/secp256k1_rangeproof.h" +#include "../../../include/secp256k1_rangeproof.h" static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *sttc, const int32_t *ecount) { secp256k1_pedersen_commitment commit; diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index d74f2a48..15de0d0d 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -13,11 +13,11 @@ #include "libsecp256k1-config.h" #endif -#include "include/secp256k1_rangeproof.h" -#include "include/secp256k1_surjectionproof.h" -#include "modules/rangeproof/borromean.h" -#include "modules/surjection/surjection_impl.h" -#include "hash.h" +#include "../../../include/secp256k1_rangeproof.h" +#include "../../../include/secp256k1_surjectionproof.h" +#include "../rangeproof/borromean.h" +#include "surjection_impl.h" +#include "../../hash.h" #ifdef USE_REDUCED_SURJECTION_PROOF_SIZE #undef SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS diff --git a/src/modules/surjection/surjection.h b/src/modules/surjection/surjection.h index 20ac4931..55d320a0 100644 --- a/src/modules/surjection/surjection.h +++ b/src/modules/surjection/surjection.h @@ -7,8 +7,8 @@ #ifndef _SECP256K1_SURJECTION_H_ #define _SECP256K1_SURJECTION_H_ -#include "group.h" -#include "scalar.h" +#include "../../group.h" +#include "../../scalar.h" SECP256K1_INLINE static int secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag); diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index f3652567..90649e97 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -10,10 +10,10 @@ #include #include -#include "eckey.h" -#include "group.h" -#include "scalar.h" -#include "hash.h" +#include "../../eckey.h" +#include "../../group.h" +#include "../../scalar.h" +#include "../../hash.h" SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, const secp256k1_generator *ephemeral_input_tags, size_t n_input_tags, const secp256k1_generator *ephemeral_output_tag) { /* compute message */ diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index ac7c269b..a00f6ad2 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -7,11 +7,11 @@ #ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS #define SECP256K1_MODULE_SURJECTIONPROOF_TESTS -#include "testrand.h" -#include "group.h" -#include "include/secp256k1_generator.h" -#include "include/secp256k1_rangeproof.h" -#include "include/secp256k1_surjectionproof.h" +#include "../../testrand.h" +#include "../../group.h" +#include "../../../include/secp256k1_generator.h" +#include "../../../include/secp256k1_rangeproof.h" +#include "../../../include/secp256k1_surjectionproof.h" static void test_surjectionproof_api(void) { unsigned char seed[32]; diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index a37e16ff..9a50ce7f 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_MODULE_WHITELIST_MAIN #define SECP256K1_MODULE_WHITELIST_MAIN -#include "include/secp256k1_whitelist.h" -#include "modules/whitelist/whitelist_impl.h" +#include "../../../include/secp256k1_whitelist.h" +#include "whitelist_impl.h" #define MAX_KEYS SECP256K1_WHITELIST_MAX_N_KEYS /* shorter alias */ diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 10e8693e..184fb032 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_WHITELIST_TESTS #define SECP256K1_MODULE_WHITELIST_TESTS -#include "include/secp256k1_whitelist.h" +#include "../../../include/secp256k1_whitelist.h" void test_whitelist_end_to_end_internal(const unsigned char *summed_seckey, const unsigned char *online_seckey, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey, const size_t signer_i, const size_t n_keys) { unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; From 1493113e61eb593a18b8e2328dbe9bc1b82f68d5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 9 Aug 2022 19:17:30 +0000 Subject: [PATCH 199/381] build: automatically enable module dependencies --- configure.ac | 50 ++++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/configure.ac b/configure.ac index d79fe6e6..8be0dd85 100644 --- a/configure.ac +++ b/configure.ac @@ -385,6 +385,10 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" ### Handle module options ### +# Besides testing whether modules are enabled, the following code also enables +# module dependencies. The order of the tests matters: the dependency must be +# tested first. + if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi @@ -398,30 +402,30 @@ if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi -if test x"$enable_module_generator" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_GENERATOR, 1, [Define this symbol to enable the NUMS generator module]) -fi - -if test x"$enable_module_rangeproof" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) -fi - if test x"$enable_module_whitelist" = x"yes"; then + enable_module_rangeproof=yes AC_DEFINE(ENABLE_MODULE_WHITELIST, 1, [Define this symbol to enable the key whitelisting module]) fi if test x"$enable_module_surjectionproof" = x"yes"; then + enable_module_rangeproof=yes AC_DEFINE(ENABLE_MODULE_SURJECTIONPROOF, 1, [Define this symbol to enable the surjection proof module]) fi -# Test if extrakeys is set _after_ the MuSig module to allow the MuSig -# module to set enable_module_schnorrsig=yes + +if test x"$enable_module_rangeproof" = x"yes"; then + enable_module_generator=yes + AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) +fi + +if test x"$enable_module_generator" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_GENERATOR, 1, [Define this symbol to enable the NUMS generator module]) +fi + if test x"$enable_module_schnorrsig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) enable_module_extrakeys=yes fi -# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig -# module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) fi @@ -458,28 +462,6 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_s2c]) AC_MSG_NOTICE([Building ECDSA adaptor signatures module: $enable_module_ecdsa_adaptor]) AC_MSG_NOTICE([******]) - - - if test x"$enable_module_schnorrsig" != x"yes"; then - if test x"$enable_module_musig" = x"yes"; then - AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) - fi - fi - - if test x"$enable_module_generator" != x"yes"; then - if test x"$enable_module_rangeproof" = x"yes"; then - AC_MSG_ERROR([Rangeproof module requires the generator module. Use --enable-module-generator to allow.]) - fi - fi - - if test x"$enable_module_rangeproof" != x"yes"; then - if test x"$enable_module_whitelist" = x"yes"; then - AC_MSG_ERROR([Whitelist module requires the rangeproof module. Use --enable-module-rangeproof to allow.]) - fi - if test x"$enable_module_surjectionproof" = x"yes"; then - AC_MSG_ERROR([Surjection proof module requires the rangeproof module. Use --enable-module-rangeproof to allow.]) - fi - fi else if test x"$enable_module_musig" = x"yes"; then AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) From 58ab152bb4b6c8b4ab17061e90d61fcbc1be9e6c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 10 Aug 2022 09:04:47 +0000 Subject: [PATCH 200/381] build: move all output concerning enabled modules at single place --- configure.ac | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 8be0dd85..ed33fb58 100644 --- a/configure.ac +++ b/configure.ac @@ -454,13 +454,6 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([******]) AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) - AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator]) - AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) - AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) - AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) - AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) - AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_s2c]) - AC_MSG_NOTICE([Building ECDSA adaptor signatures module: $enable_module_ecdsa_adaptor]) AC_MSG_NOTICE([******]) else if test x"$enable_module_musig" = x"yes"; then @@ -537,6 +530,10 @@ 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 generator = $enable_module_generator" +echo " module rangeproof = $enable_module_rangeproof" +echo " module surjectionproof = $enable_module_surjectionproof" +echo " module whitelist = $enable_module_whitelist" echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" From 171b294a1c7a736c1b93fa194e3af90b625259fa Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 10 Aug 2022 09:20:26 +0000 Subject: [PATCH 201/381] build: improve error message if --enable-experimental is missed --- configure.ac | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index ed33fb58..3ab35ed8 100644 --- a/configure.ac +++ b/configure.ac @@ -456,6 +456,22 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([******]) else + # The order of the following tests matters. If the user enables a dependent + # module (which automatically enables the module dependencies) we want to + # print an error for the dependent module, not the module dependency. Hence, + # we first test dependent modules. + if test x"$enable_module_whitelist" = x"yes"; then + AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_surjectionproof" = x"yes"; then + AC_MSG_ERROR([Surjection proof module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_rangeproof" = x"yes"; then + AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$enable_module_generator" = x"yes"; then + AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_musig" = x"yes"; then AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) fi @@ -468,18 +484,6 @@ else if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi - if test x"$enable_module_generator" = x"yes"; then - AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.]) - fi - if test x"$enable_module_rangeproof" = x"yes"; then - AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.]) - fi - if test x"$enable_module_whitelist" = x"yes"; then - AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) - fi - if test x"$enable_module_surjectionproof" = x"yes"; then - AC_MSG_ERROR([Surjection proof module is experimental. Use --enable-experimental to allow.]) - fi fi ### From 92820d944b52d923dad57b7d5bae5fec48f28ddd Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 5 Aug 2022 21:05:09 +0000 Subject: [PATCH 202/381] rangeproof: add a test for all-zero blinding factors --- include/secp256k1_rangeproof.h | 3 +- src/modules/rangeproof/tests_impl.h | 53 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index d4f35de7..d179b36f 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -227,7 +227,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * proof: pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL) * min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount. * commit: the commitment being proved. - * blind: 32-byte blinding factor used by commit. + * blind: 32-byte blinding factor used by commit. The blinding factor may be all-zeros as long as min_bits is set to 3 or greater. + * This is a side-effect of the underlying crypto, not a deliberate API choice, but it may be useful when balancing CT transactions. * nonce: 32-byte secret nonce used to initialize the proof (value can be reverse-engineered out of the proof if this secret is known.) * exp: Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18. * (-1 is a special case that makes the value public. 0 is the most private.) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 47c5f3a7..67378cc4 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -547,6 +547,58 @@ static void test_rangeproof(void) { } } +static void test_rangeproof_null_blinder(void) { + unsigned char proof[5134]; + const unsigned char blind[32] = { 0 }; + const uint64_t v = 1111; + uint64_t minv, maxv; + secp256k1_pedersen_commitment commit; + size_t len; + + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); + + /* Try a 32-bit proof; should work */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 1, &commit, blind, commit.data, 0, 32, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(minv == 1); + CHECK(maxv == 1ULL << 32); + + /* Try a 3-bit proof; should work */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v - 1, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(minv == 1110); + CHECK(maxv == 1117); + + /* But a 2-bits will not because then it does not have any subcommitments (which rerandomize + * the blinding factors that get passed into the borromean logic ... passing 0s will fail) */ + len = 5134; + CHECK(!secp256k1_rangeproof_sign(ctx, proof, &len, v - 1, &commit, blind, commit.data, 0, 2, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + + /* Rewinding with 3-bits works */ + { + uint64_t value_out; + unsigned char msg[128]; + unsigned char msg_out[128]; + unsigned char blind_out[32]; + size_t msg_len = sizeof(msg); + + len = 1000; + secp256k1_testrand256(msg); + secp256k1_testrand256(&msg[32]); + secp256k1_testrand256(&msg[64]); + secp256k1_testrand256(&msg[96]); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, 0, 3, v, msg, sizeof(msg), NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blind_out, &value_out, msg_out, &msg_len, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h) != 0); + CHECK(memcmp(blind, blind_out, sizeof(blind)) == 0); + CHECK(memcmp(msg, msg_out, sizeof(msg)) == 0); + CHECK(value_out == v); + CHECK(minv == v); + CHECK(maxv == v + 7); + } +} + #define MAX_N_GENS 30 void test_multiple_generators(void) { const size_t n_inputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1; @@ -705,6 +757,7 @@ void run_rangeproof_tests(void) { test_borromean(); } test_rangeproof(); + test_rangeproof_null_blinder(); test_multiple_generators(); } From 5a40f3d99bbd879391a3fb3c038a6d49ec01bc03 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 10 Aug 2022 22:14:31 +0000 Subject: [PATCH 203/381] replace memcmp with secp256k1_memcmp_var throughout the codebase memcmp only appears in -zkp-specific modules. Fix those. --- src/modules/ecdsa_s2c/tests_impl.h | 8 +++--- src/modules/generator/tests_impl.h | 8 +++--- src/modules/musig/tests_impl.h | 36 ++++++++++++------------ src/modules/rangeproof/borromean_impl.h | 2 +- src/modules/rangeproof/rangeproof_impl.h | 2 +- src/modules/rangeproof/tests_impl.h | 18 ++++++------ src/modules/surjection/main_impl.h | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index 95b4d95e..8f50206d 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -78,7 +78,7 @@ void run_s2c_opening_test(void) { * points' x-coordinates are uniformly random */ if (secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1) { CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1); - CHECK(memcmp(output, input, sizeof(output)) == 0); + CHECK(secp256k1_memcmp_var(output, input, sizeof(output)) == 0); } secp256k1_testrand256(&input[1]); /* Set pubkey oddness tag to first bit of input[1] */ @@ -255,7 +255,7 @@ static void test_ecdsa_s2c_fixed_vectors(void) { secp256k1_ecdsa_signature signature; CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, opening_ser, &s2c_opening) == 1); - CHECK(memcmp(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0); + CHECK(secp256k1_memcmp_var(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0); CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, test->s2c_data, &s2c_opening) == 1); } } @@ -331,7 +331,7 @@ static void test_ecdsa_anti_exfil_signer_commit(void) { const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i]; CHECK(secp256k1_ecdsa_anti_exfil_signer_commit(ctx, &s2c_opening, message, privkey, test->s2c_data) == 1); CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, buf, &s2c_opening) == 1); - CHECK(memcmp(test->expected_s2c_exfil_opening, buf, sizeof(buf)) == 0); + CHECK(secp256k1_memcmp_var(test->expected_s2c_exfil_opening, buf, sizeof(buf)) == 0); } } @@ -397,7 +397,7 @@ static void test_ecdsa_anti_exfil(void) { CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1); CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0); CHECK(secp256k1_anti_exfil_host_verify(ctx, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1); - CHECK(memcmp(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0); + CHECK(secp256k1_memcmp_var(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0); } } diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index cc43912a..9f36f83d 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -134,7 +134,7 @@ void test_shallue_van_de_woestijne(void) { shallue_van_de_woestijne(&ge, &fe); secp256k1_ge_to_storage(&ges, &ge); - CHECK(memcmp(&ges, &results[i * 2 + s - 2], sizeof(secp256k1_ge_storage)) == 0); + CHECK(secp256k1_memcmp_var(&ges, &results[i * 2 + s - 2], sizeof(secp256k1_ge_storage)) == 0); } } } @@ -188,11 +188,11 @@ void test_generator_generate(void) { CHECK(secp256k1_generator_generate_blinded(ctx, &gen, v, s)); secp256k1_generator_load(&ge, &gen); secp256k1_ge_to_storage(&ges, &ge); - CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); + CHECK(secp256k1_memcmp_var(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); CHECK(secp256k1_generator_generate(ctx, &gen, v)); secp256k1_generator_load(&ge, &gen); secp256k1_ge_to_storage(&ges, &ge); - CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); + CHECK(secp256k1_memcmp_var(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0); } /* There is no range restriction on the value, but the blinder must be a @@ -215,7 +215,7 @@ void test_generator_fixed_vector(void) { CHECK(secp256k1_generator_parse(ctx, &parse, two_g)); CHECK(secp256k1_generator_serialize(ctx, result, &parse)); - CHECK(memcmp(two_g, result, 33) == 0); + CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); result[0] = 0x0a; CHECK(secp256k1_generator_parse(ctx, &parse, result)); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 3125d1ed..9660227e 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -360,7 +360,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_pubnonce tmp; CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, &pubnonce[0]) == 1); CHECK(secp256k1_musig_pubnonce_parse(none, &tmp, pubnonce_ser) == 1); - CHECK(memcmp(&tmp, &pubnonce[0], sizeof(tmp)) == 0); + CHECK(secp256k1_memcmp_var(&tmp, &pubnonce[0], sizeof(tmp)) == 0); } /** Receive nonces and aggregate **/ @@ -414,7 +414,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_aggnonce tmp; CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, &aggnonce) == 1); CHECK(secp256k1_musig_aggnonce_parse(none, &tmp, aggnonce_ser) == 1); - CHECK(memcmp(&tmp, &aggnonce, sizeof(tmp)) == 0); + CHECK(secp256k1_memcmp_var(&tmp, &aggnonce, sizeof(tmp)) == 0); } /** Process nonces **/ @@ -444,7 +444,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1); /* The secnonce is set to 0 and subsequent signing attempts fail */ - CHECK(memcmp(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0); + CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0); CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 0); CHECK(ecount == 1); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); @@ -496,7 +496,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_partial_sig tmp; CHECK(secp256k1_musig_partial_sig_serialize(none, buf, &partial_sig[0]) == 1); CHECK(secp256k1_musig_partial_sig_parse(none, &tmp, buf) == 1); - CHECK(memcmp(&tmp, &partial_sig[0], sizeof(tmp)) == 0); + CHECK(secp256k1_memcmp_var(&tmp, &partial_sig[0], sizeof(tmp)) == 0); } /** Partial signature verification */ @@ -582,10 +582,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Secret adaptor can be extracted from signature */ ecount = 0; CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1); - CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); + CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) == 0); /* wrong nonce parity */ CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1); - CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) != 0); + CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) != 0); CHECK(secp256k1_musig_extract_adaptor(none, NULL, final_sig, pre_sig, 0) == 0); CHECK(ecount == 1); CHECK(secp256k1_musig_extract_adaptor(none, sec_adaptor1, NULL, pre_sig, 0) == 0); @@ -764,7 +764,7 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig_a[1], &secnonce_a[1], &keypair_a[1], &keyagg_cache_a, &session_a) == 1); CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig_a, &session_a, partial_sig_a_ptr, 2) == 1); CHECK(secp256k1_musig_extract_adaptor(ctx, sec_adaptor_extracted, final_sig_b, pre_sig_b, nonce_parity_b) == 1); - CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ + CHECK(secp256k1_memcmp_var(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ CHECK(secp256k1_musig_adapt(ctx, final_sig_a, pre_sig_a, sec_adaptor_extracted, nonce_parity_a) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1); } @@ -794,7 +794,7 @@ void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, secp256k1_sha256_write(sha_tagged, buf, 32); secp256k1_sha256_finalize(&sha, buf); secp256k1_sha256_finalize(sha_tagged, buf2); - CHECK(memcmp(buf, buf2, 32) == 0); + CHECK(secp256k1_memcmp_var(buf, buf2, 32) == 0); } /* Checks that the initialized tagged hashes initialized have the expected @@ -904,7 +904,7 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { } else { secp256k1_pubkey tmp_key = P[i-1]; CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &tmp_key, tweak)); - CHECK(memcmp(&tmp_key, &P[i], sizeof(tmp_key)) == 0); + CHECK(secp256k1_memcmp_var(&tmp_key, &P[i], sizeof(tmp_key)) == 0); } /* Test signing for P[i] */ musig_tweak_test_helper(&P_xonly[i], sk[0], sk[1], &keyagg_cache); @@ -1138,7 +1138,7 @@ void musig_test_vectors_noncegen(void) { for (j = 0; j < 2; j++) { unsigned char k32[32]; secp256k1_scalar_get_b32(k32, &k[i][j]); - CHECK(memcmp(k32, k32_expected[i][j], 32) == 0); + CHECK(secp256k1_memcmp_var(k32, k32_expected[i][j], 32) == 0); } } } @@ -1264,7 +1264,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 1); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test where the aggregate public key point has an _even_ y @@ -1281,7 +1281,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 0); CHECK(musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 0); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test where the parity of aggregate public key point (1) is unequal to the @@ -1297,7 +1297,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(fin_nonce_parity == 0); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test that includes an xonly public key tweak. */ @@ -1319,7 +1319,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 1); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test that includes an ordinary public key tweak. */ @@ -1341,7 +1341,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 0); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test that includes an ordinary and an x-only public key tweak. */ @@ -1371,7 +1371,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 0); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 0); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test with four tweaks: x-only, ordinary, x-only, ordinary. */ @@ -1412,7 +1412,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 0); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 1); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } { /* This is a test that includes an adaptor. */ @@ -1435,7 +1435,7 @@ void musig_test_vectors_sign(void) { CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 1); - CHECK(memcmp(sig, sig_expected, 32) == 0); + CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); } } diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index c0a5c332..f8ee11a4 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -105,7 +105,7 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e } secp256k1_sha256_write(&sha256_e0, m, mlen); secp256k1_sha256_finalize(&sha256_e0, tmp); - return memcmp(e0, tmp, 32) == 0; + return secp256k1_memcmp_var(e0, tmp, 32) == 0; } int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 75c06a14..fbf32b29 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -401,7 +401,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * idx = npub + rsizes[rings - 1] - 1 - j; secp256k1_scalar_get_b32(tmp, &s[idx]); secp256k1_rangeproof_ch32xor(tmp, &prep[idx * 32]); - if ((tmp[0] & 128) && (memcmp(&tmp[16], &tmp[24], 8) == 0) && (memcmp(&tmp[8], &tmp[16], 8) == 0)) { + if ((tmp[0] & 128) && (secp256k1_memcmp_var(&tmp[16], &tmp[24], 8) == 0) && (secp256k1_memcmp_var(&tmp[8], &tmp[16], 8) == 0)) { value = 0; for (i = 0; i < 8; i++) { value = (value << 8) + tmp[24 + i]; diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 67378cc4..03d80314 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -196,7 +196,7 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(max_value >= val); CHECK(value_out == val); CHECK(message_len == sizeof(message_out)); - CHECK(memcmp(message, message_out, sizeof(message_out)) == 0); + CHECK(secp256k1_memcmp_var(message, message_out, sizeof(message_out)) == 0); CHECK(secp256k1_rangeproof_rewind(both, NULL, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0); CHECK(*ecount == 21); /* blindout may be NULL */ @@ -434,13 +434,13 @@ static void test_rangeproof(void) { mlen = 4096; CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); if (input_message != NULL) { - CHECK(memcmp(message, input_message, input_message_len) == 0); + CHECK(secp256k1_memcmp_var(message, input_message, input_message_len) == 0); } for (j = input_message_len; j < mlen; j++) { CHECK(message[j] == 0); } CHECK(mlen <= 4096); - CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv <= v); CHECK(maxv >= v); @@ -448,7 +448,7 @@ static void test_rangeproof(void) { CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 73); CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); - CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); CHECK(maxv == v); @@ -460,7 +460,7 @@ static void test_rangeproof(void) { CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h)); CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); - CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); CHECK(maxv == v); @@ -527,7 +527,7 @@ static void test_rangeproof(void) { CHECK(message[j] == 0); } CHECK(mlen <= 4096); - CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); CHECK(minv <= v); CHECK(maxv >= v); @@ -591,8 +591,8 @@ static void test_rangeproof_null_blinder(void) { secp256k1_testrand256(&msg[96]); CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, 0, 3, v, msg, sizeof(msg), NULL, 0, secp256k1_generator_h)); CHECK(secp256k1_rangeproof_rewind(ctx, blind_out, &value_out, msg_out, &msg_len, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h) != 0); - CHECK(memcmp(blind, blind_out, sizeof(blind)) == 0); - CHECK(memcmp(msg, msg_out, sizeof(msg)) == 0); + CHECK(secp256k1_memcmp_var(blind, blind_out, sizeof(blind)) == 0); + CHECK(secp256k1_memcmp_var(msg, msg_out, sizeof(msg)) == 0); CHECK(value_out == v); CHECK(minv == v); CHECK(maxv == v + 7); @@ -737,7 +737,7 @@ void test_pedersen_commitment_fixed_vector(void) { CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); - CHECK(memcmp(two_g, result, 33) == 0); + CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); result[0] = 0x08; CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 15de0d0d..dcd4d6e0 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -243,7 +243,7 @@ int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1 while (1) { size_t next_input_index; next_input_index = secp256k1_surjectionproof_csprng_next(&csprng, n_input_tags); - if (memcmp(&fixed_input_tags[next_input_index], fixed_output_tag, sizeof(*fixed_output_tag)) == 0) { + if (secp256k1_memcmp_var(&fixed_input_tags[next_input_index], fixed_output_tag, sizeof(*fixed_output_tag)) == 0) { *input_index = next_input_index; has_output_tag = 1; } From 5ac8fb035e8f0ad012d9f54b49e77b5d1f5e713a Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 9 Aug 2022 20:05:42 +0000 Subject: [PATCH 204/381] surjectionproof: make sure that n_used_pubkeys > 0 in generate If the proof was generated with surjectionproof_initialize (as mandated by the API docs), then n_used_pubkeys can never be 0. Without this commit, compilers will (rightfully) warn that borromean_s[ring_input_index] is not initialized in surjectionproof_generate. Therefore, this commit makes sure that n_used_pubkeys is greater than 0 which ensures that the array is initialized at ring_input_index. --- src/modules/surjection/main_impl.h | 6 +++++- src/modules/surjection/tests_impl.h | 30 +++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 15de0d0d..231dca17 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -298,6 +298,10 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s CHECK(proof->initialized == 1); #endif + n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof); + /* This must be true if the proof was created with surjectionproof_initialize */ + ARG_CHECK(n_used_pubkeys > 0); + /* Compute secret key */ secp256k1_scalar_set_b32(&tmps, input_blinding_key, &overflow); if (overflow) { @@ -321,7 +325,7 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s /* Compute public keys */ n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof); - n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof); + if (n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) { return 0; } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index a00f6ad2..a792bb5f 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -173,31 +173,45 @@ static void test_surjectionproof_api(void) { CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, NULL) == 0); CHECK(ecount == 16); + /* Test how surjectionproof_generate fails when the proof was not created + * with surjectionproof_initialize */ + ecount = 0; + CHECK(secp256k1_surjectionproof_generate(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 1); + { + secp256k1_surjectionproof tmp_proof = proof; + tmp_proof.n_inputs = 0; + CHECK(secp256k1_surjectionproof_generate(sign, &tmp_proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); + } + CHECK(ecount == 1); + + CHECK(secp256k1_surjectionproof_generate(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 1); + /* Check serialize */ + ecount = 0; serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0); - CHECK(ecount == 16); + CHECK(ecount == 0); serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, NULL, &serialized_len, &proof) == 0); - CHECK(ecount == 17); + CHECK(ecount == 1); serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, NULL, &proof) == 0); - CHECK(ecount == 18); + CHECK(ecount == 2); serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, NULL) == 0); - CHECK(ecount == 19); + CHECK(ecount == 3); serialized_len = sizeof(serialized_proof); CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0); /* Check parse */ CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, serialized_len) != 0); - CHECK(ecount == 19); + CHECK(ecount == 3); CHECK(secp256k1_surjectionproof_parse(none, NULL, serialized_proof, serialized_len) == 0); - CHECK(ecount == 20); + CHECK(ecount == 4); CHECK(secp256k1_surjectionproof_parse(none, &proof, NULL, serialized_len) == 0); - CHECK(ecount == 21); + CHECK(ecount == 5); CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, 0) == 0); - CHECK(ecount == 21); + CHECK(ecount == 5); secp256k1_context_destroy(none); secp256k1_context_destroy(sign); From f1410cb67a2de45f8b1b1c14862656c0ae09ff87 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 13 Aug 2022 00:37:20 +0000 Subject: [PATCH 205/381] rangeproof: add secp256k1_rangeproof_max_size function to estimate rangeproof size Provides a method that will give an upper bound on the size of a rangeproof, given an upper bound on the value to be passed in and an upper bound on the min_bits parameter. There is a lot of design freedom here since the actual size of the rangeproof depends on every parameter passed to rangeproof_sign, including the value to be proven, often in quite intricate ways. For the sake of simplicity we assume a nonzero `min_value` and that `exp` will be 0 (the default, and size-maximizing, choice), and provide an exact value for a proof of the given value and min_bits. --- include/secp256k1_rangeproof.h | 27 +++++++++++++++++++++++++++ src/modules/rangeproof/main_impl.h | 12 ++++++++++++ src/modules/rangeproof/tests_impl.h | 11 +++++++++++ 3 files changed, 50 insertions(+) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index d179b36f..23e511ed 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -287,6 +287,33 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( size_t plen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +/** Returns an upper bound on the size of a rangeproof with the given parameters + * + * An actual rangeproof may be smaller, for example if the actual value + * is less than both the provided `max_value` and 2^`min_bits`, or if + * the `exp` parameter to `secp256k1_rangeproof_sign` is set such that + * the proven range is compressed. In particular this function will always + * overestimate the size of single-value proofs. Also, if `min_value` + * is set to 0 in the proof, the result will usually, but not always, + * be 8 bytes smaller than if a nonzero value had been passed. + * + * The goal of this function is to provide a useful upper bound for + * memory allocation or fee estimation purposes, without requiring + * too many parameters be fixed in advance. + * + * To obtain the size of largest possible proof, set `max_value` to + * `UINT64_MAX` (and `min_bits` to any valid value such as 0). + * + * In: ctx: pointer to a context object + * max_value: the maximum value that might be passed for `value` for the proof. + * min_bits: the value that will be passed as `min_bits` for the proof. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT size_t secp256k1_rangeproof_max_size( + const secp256k1_context* ctx, + uint64_t max_value, + int min_bits +) SECP256K1_ARG_NONNULL(1); + # ifdef __cplusplus } # endif diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index b1c36cdd..432f4b95 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -304,4 +304,16 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp); } +size_t secp256k1_rangeproof_max_size(const secp256k1_context* ctx, uint64_t max_value, int min_bits) { + const int val_mantissa = max_value > 0 ? 64 - secp256k1_clz64_var(max_value) : 1; + const int mantissa = min_bits > val_mantissa ? min_bits : val_mantissa; + const size_t rings = (mantissa + 1) / 2; + const size_t npubs = rings * 4 - 2 * (mantissa % 2); + + VERIFY_CHECK(ctx != NULL); + (void) ctx; + + return 10 + 32 * (npubs + rings - 1) + 32 + ((rings - 1 + 7) / 8); +} + #endif diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 03d80314..27703800 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -225,6 +225,11 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); CHECK(*ecount == 29); } + + /* This constant is hardcoded in these tests and elsewhere, so we + * consider it to be part of the API and test it here. */ + CHECK(secp256k1_rangeproof_max_size(none, 0, 64) == 5134); + CHECK(secp256k1_rangeproof_max_size(none, UINT64_MAX, 0) == 5134); } static void test_api(void) { @@ -431,6 +436,7 @@ static void test_rangeproof(void) { len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); + CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0)); mlen = 4096; CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); if (input_message != NULL) { @@ -447,6 +453,7 @@ static void test_rangeproof(void) { len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 73); + CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0)); CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0); CHECK(vout == v); @@ -457,6 +464,7 @@ static void test_rangeproof(void) { len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), secp256k1_generator_h)); CHECK(len <= 73); + CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0)); CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h)); CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); @@ -472,6 +480,7 @@ static void test_rangeproof(void) { for (i = 0; i < 19; i++) { len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0)); CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); CHECK(minv <= v); @@ -487,6 +496,7 @@ static void test_rangeproof(void) { len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); + CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 3)); /* Test if trailing bytes are rejected. */ proof[len] = v; CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len + 1, NULL, 0, secp256k1_generator_h)); @@ -521,6 +531,7 @@ static void test_rangeproof(void) { } CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); + CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, min_bits)); mlen = 4096; CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); for (j = 0; j < mlen; j++) { From 310e5170619b03977d471e9f7461bbaf5b607ca9 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Oct 2021 00:16:57 +0000 Subject: [PATCH 206/381] rangeproof: add a bunch more testing Add two new fixed rangeproof vectors; check that various extracted values are correct; add a test for creating and verifying single-value proofs. --- src/modules/rangeproof/tests_impl.h | 269 +++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 8 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 27703800..b60126ef 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -610,8 +610,96 @@ static void test_rangeproof_null_blinder(void) { } } +static void test_single_value_proof(uint64_t val) { + unsigned char proof[5000]; + secp256k1_pedersen_commitment commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + unsigned char nonce[32]; + const unsigned char message[1] = " "; /* no message will fit into a single-value proof */ + unsigned char message_out[sizeof(proof)] = { 0 }; + size_t plen = sizeof(proof); + uint64_t min_val_out = 0; + uint64_t max_val_out = 0; + + uint64_t val_out = 0; + size_t m_len_out = 0; + + secp256k1_testrand256(blind); + secp256k1_testrand256(nonce); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, secp256k1_generator_h)); + + CHECK(secp256k1_rangeproof_sign( + ctx, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + message, sizeof(message), /* Will cause this to fail */ + NULL, 0, + secp256k1_generator_h + ) == 0); + + plen = sizeof(proof); + CHECK(secp256k1_rangeproof_sign( + ctx, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + NULL, 0, + NULL, 0, + secp256k1_generator_h + ) == 1); + + /* Different proof sizes are unfortunate but is caused by `min_value` of + * zero being special-cased and encoded more efficiently. */ + if (val == 0) { + CHECK(plen == 65); + } else { + CHECK(plen == 73); + } + + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + ) == 1); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + + memset(message_out, 0, sizeof(message_out)); + m_len_out = sizeof(message_out); + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind_out, &val_out, + message_out, &m_len_out, + nonce, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + )); + CHECK(val_out == val); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + CHECK(m_len_out == 0); + CHECK(secp256k1_memcmp_var(blind, blind_out, 32) == 0); + for (m_len_out = 0; m_len_out < sizeof(message_out); m_len_out++) { + CHECK(message_out[m_len_out] == 0); + } +} + #define MAX_N_GENS 30 -void test_multiple_generators(void) { +static void test_multiple_generators(void) { const size_t n_inputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1; const size_t n_outputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1; const size_t n_generators = n_inputs + n_outputs; @@ -673,7 +761,18 @@ void test_multiple_generators(void) { } void test_rangeproof_fixed_vectors(void) { - const unsigned char vector_1[] = { + size_t i; + unsigned char blind[32]; + uint64_t value; + uint64_t min_value; + uint64_t max_value; + secp256k1_pedersen_commitment pc; + unsigned char message[4000] = {0}; + size_t m_len = sizeof(message); + + /* Vector 1: no message */ +{ + static const unsigned char vector_1[] = { 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x02, 0x2a, 0x5c, 0x42, 0x0e, 0x1d, 0x51, 0xe1, 0xb7, 0xf3, 0x69, 0x04, 0xb5, 0xbb, 0x9b, 0x41, 0x66, 0x14, 0xf3, 0x64, 0x42, 0x26, 0xe3, 0xa7, 0x6a, 0x06, 0xbb, 0xa8, 0x5a, 0x49, 0x6f, 0x19, 0x76, 0xfb, 0xe5, 0x75, 0x77, 0x88, @@ -716,25 +815,174 @@ void test_rangeproof_fixed_vectors(void) { 0xa6, 0x45, 0xf6, 0xce, 0xcf, 0x48, 0xf6, 0x1e, 0x3d, 0xd2, 0xcf, 0xcb, 0x3a, 0xcd, 0xbb, 0x92, 0x29, 0x24, 0x16, 0x7f, 0x8a, 0xa8, 0x5c, 0x0c, 0x45, 0x71, 0x33 }; - const unsigned char commit_1[] = { + static const unsigned char commit_1[] = { 0x08, 0xf5, 0x1e, 0x0d, 0xc5, 0x86, 0x78, 0x51, 0xa9, 0x00, 0x00, 0xef, 0x4d, 0xe2, 0x94, 0x60, 0x89, 0x83, 0x04, 0xb4, 0x0e, 0x90, 0x10, 0x05, 0x1c, 0x7f, 0xd7, 0x33, 0x92, 0x1f, 0xe7, 0x74, 0x59 }; - uint64_t min_value_1; - uint64_t max_value_1; - secp256k1_pedersen_commitment pc; + static const unsigned char blind_1[] = { + 0x98, 0x44, 0xfc, 0x7a, 0x64, 0xa9, 0xca, 0xdf, 0xf3, 0x2f, 0x9f, 0x02, 0xba, 0x46, 0xc7, 0xd9, + 0x77, 0x47, 0xa4, 0xd3, 0x53, 0x17, 0xc6, 0x44, 0x30, 0x73, 0x84, 0xeb, 0x1f, 0xbe, 0xa1, 0xfb + }; CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_1)); - CHECK(secp256k1_rangeproof_verify( ctx, - &min_value_1, &max_value_1, + &min_value, &max_value, &pc, vector_1, sizeof(vector_1), NULL, 0, secp256k1_generator_h )); + CHECK(min_value == 86); + CHECK(max_value == 25586); + + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind, &value, + message, &m_len, + pc.data, + &min_value, &max_value, + &pc, + vector_1, sizeof(vector_1), + NULL, 0, + secp256k1_generator_h + )); + + CHECK(secp256k1_memcmp_var(blind, blind_1, 32) == 0); + CHECK(value == 86); + CHECK(min_value == 86); + CHECK(max_value == 25586); + CHECK(m_len == 448); /* length of the sidechannel in the proof */ + for (i = 0; i < m_len; i++) { + /* No message encoded in this vector */ + CHECK(message[i] == 0); + } +} + + /* Vector 2: embedded message */ +{ + static const unsigned char vector_2[] = { + 0x40, 0x03, 0x00, 0x90, 0x1a, 0x61, 0x64, 0xbb, 0x85, 0x1a, 0x78, 0x35, 0x1e, 0xe0, 0xd5, 0x96, + 0x71, 0x0f, 0x18, 0x8e, 0xf3, 0x33, 0xf0, 0x75, 0xfe, 0xd6, 0xc6, 0x11, 0x6b, 0x42, 0x89, 0xea, + 0xa2, 0x0c, 0x89, 0x25, 0x37, 0x81, 0x10, 0xf9, 0xf0, 0x9b, 0xda, 0x68, 0x2a, 0xd9, 0x2e, 0x0c, + 0x45, 0x17, 0x54, 0x6d, 0x02, 0xd2, 0x21, 0x5d, 0xbc, 0x10, 0xf8, 0x8f, 0xf1, 0x92, 0x40, 0xa9, + 0xc7, 0x24, 0x00, 0x1b, 0xc8, 0x75, 0x0f, 0xf6, 0x8f, 0x93, 0x8b, 0x78, 0x62, 0x73, 0x3c, 0x86, + 0x4b, 0x61, 0x7c, 0x0f, 0xc6, 0x41, 0xc9, 0xb3, 0xc1, 0x30, 0x7f, 0xd4, 0xee, 0x9f, 0x37, 0x08, + 0x9b, 0x64, 0x23, 0xd5, 0xe6, 0x1a, 0x03, 0x54, 0x74, 0x9b, 0x0b, 0xae, 0x6f, 0x2b, 0x1e, 0xf5, + 0x40, 0x44, 0xaa, 0x12, 0xe8, 0xbd, 0xe0, 0xa6, 0x85, 0x89, 0xf1, 0xa9, 0xd0, 0x3f, 0x2e, 0xc6, + 0x1f, 0x11, 0xf5, 0x44, 0x69, 0x99, 0x31, 0x10, 0x2e, 0x64, 0xc6, 0x44, 0xdb, 0x47, 0x06, 0x6d, + 0xd5, 0xf2, 0x8d, 0x19, 0x00, 0x39, 0xb8, 0xca, 0xda, 0x5c, 0x1d, 0x83, 0xbd, 0xa3, 0x6d, 0xbf, + 0x97, 0xdd, 0x83, 0x86, 0xc9, 0x56, 0xe2, 0xbb, 0x37, 0x4b, 0x2d, 0xb5, 0x9d, 0xf2, 0x7a, 0x6a, + 0x25, 0x47, 0xfa, 0x03, 0x05, 0xc5, 0xda, 0x73, 0xe1, 0x96, 0x15, 0x21, 0x23, 0xe5, 0xef, 0x55, + 0x36, 0xdd, 0xf1, 0xb1, 0x3f, 0x33, 0x1a, 0x91, 0x6c, 0x73, 0x64, 0xd3, 0x88, 0xe7, 0xc6, 0xc9, + 0x04, 0x29, 0xae, 0x55, 0x27, 0xa0, 0x80, 0x60, 0xaf, 0x0c, 0x09, 0x2f, 0xc8, 0x1b, 0xe6, 0x16, + 0x9e, 0xed, 0x29, 0xc7, 0x93, 0xce, 0xc7, 0x0d, 0xdf, 0x1f, 0x28, 0xba, 0xf3, 0x38, 0xc3, 0xaa, + 0x99, 0xd9, 0x21, 0x41, 0xb8, 0x10, 0xa5, 0x48, 0x37, 0xec, 0x60, 0xda, 0x64, 0x5a, 0x73, 0x55, + 0xd7, 0xff, 0x23, 0xfa, 0xf6, 0xc6, 0xf4, 0xe2, 0xca, 0x99, 0x2f, 0x30, 0x36, 0x48, 0x73, 0x8b, + 0x57, 0xa6, 0x62, 0x12, 0xa3, 0xe7, 0x5c, 0xa8, 0xd1, 0xe6, 0x85, 0x05, 0x59, 0xfe, 0x2b, 0x44, + 0xe4, 0x73, 0x1c, 0xc3, 0x56, 0x32, 0x07, 0x65, 0x4a, 0x58, 0xaf, 0x2b, 0x3f, 0x36, 0xca, 0xb4, + 0x1d, 0x5c, 0x2a, 0x46, 0x1f, 0xf7, 0x63, 0x59, 0x4f, 0x2b, 0xd0, 0xf6, 0xfc, 0xcf, 0x04, 0x09, + 0xb7, 0x65, 0x1b + }; + static const unsigned char commit_2[] = { + 0x09, + 0x25, 0xa4, 0xbd, 0xc4, 0x57, 0x69, 0xeb, 0x4f, 0x34, 0x0f, 0xea, 0xb8, 0xe4, 0x72, 0x04, 0x54, + 0x06, 0xe5, 0xd6, 0x85, 0x15, 0x42, 0xea, 0x6e, 0x1d, 0x11, 0x11, 0x9c, 0x56, 0xf8, 0x10, 0x45 + }; + static const unsigned char blind_2[] = { + 0xdc, 0x79, 0x07, 0x89, 0x2d, 0xc4, 0xe3, 0x76, 0xf9, 0x13, 0x38, 0xd6, 0x4b, 0x46, 0xed, 0x9d, + 0x9b, 0xf6, 0x70, 0x3d, 0x04, 0xcf, 0x96, 0x8c, 0xfd, 0xb5, 0xff, 0x0a, 0x06, 0xc7, 0x08, 0x8b + }; + static const unsigned char message_2[] = "When I see my own likeness in the depths of someone else's consciousness, I always experience a moment of panic."; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_2)); + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_value, &max_value, + &pc, + vector_2, sizeof(vector_2), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == 0); + CHECK(max_value == 15); + + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind, &value, + message, &m_len, + pc.data, + &min_value, &max_value, + &pc, + vector_2, sizeof(vector_2), + NULL, 0, + secp256k1_generator_h + )); + + CHECK(secp256k1_memcmp_var(blind, blind_2, 32) == 0); + CHECK(value == 11); + CHECK(min_value == 0); + CHECK(max_value == 15); + CHECK(m_len == 192); /* length of the sidechannel in the proof */ + CHECK(secp256k1_memcmp_var(message, message_2, sizeof(message_2)) == 0); + for (i = sizeof(message_2); i < m_len; i++) { + CHECK(message[i] == 0); + } +} + + /* Vector 3: single-value proof of UINT64_MAX */ +{ + static const unsigned char vector_3[] = { + 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0x7d, 0x0b, 0x79, 0x0e, 0xaf, 0x41, + 0xa5, 0x8e, 0x9b, 0x0c, 0x5b, 0xa3, 0xee, 0x7d, 0xfd, 0x3d, 0x6b, 0xf3, 0xac, 0x04, 0x8a, 0x43, + 0x75, 0xb0, 0xb7, 0x0e, 0x92, 0xd7, 0xdf, 0xf0, 0x76, 0xc4, 0xa5, 0xb6, 0x2f, 0xf1, 0xb5, 0xfb, + 0xb4, 0xb6, 0x29, 0xea, 0x34, 0x9b, 0x16, 0x30, 0x0d, 0x06, 0xf1, 0xb4, 0x3f, 0x0d, 0x73, 0x59, + 0x75, 0xbf, 0x5d, 0x19, 0x59, 0xef, 0x11, 0xf0, 0xbf + }; + static const unsigned char commit_3[] = { + 0x08, + 0xc7, 0xea, 0x40, 0x7d, 0x26, 0x38, 0xa2, 0x99, 0xb9, 0x40, 0x22, 0x78, 0x17, 0x57, 0x65, 0xb3, + 0x36, 0x82, 0x18, 0x42, 0xc5, 0x57, 0x04, 0x5e, 0x58, 0x5e, 0xf6, 0x40, 0x8b, 0x24, 0x73, 0x10 + }; + static const unsigned char nonce_3[] = { + 0x84, 0x50, 0x94, 0x69, 0xa3, 0x4b, 0x6c, 0x62, 0x1a, 0xc7, 0xe2, 0x0e, 0x07, 0x9a, 0x6f, 0x85, + 0x5f, 0x26, 0x50, 0xcd, 0x88, 0x5a, 0x9f, 0xaa, 0x23, 0x5e, 0x0a, 0xe0, 0x7e, 0xc5, 0xe9, 0xf1 + }; + static const unsigned char blind_3[] = { + 0x68, 0x89, 0x47, 0x8c, 0x77, 0xec, 0xcc, 0x2b, 0x65, 0x01, 0x78, 0x6b, 0x06, 0x8b, 0x38, 0x94, + 0xc0, 0x6b, 0x9b, 0x4c, 0x02, 0xa6, 0xc8, 0xf6, 0xc0, 0x34, 0xea, 0x35, 0x57, 0xf4, 0xe1, 0x37 + }; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_3)); + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_value, &max_value, + &pc, + vector_3, sizeof(vector_3), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == UINT64_MAX); + CHECK(max_value == UINT64_MAX); + + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind, &value, + message, &m_len, + nonce_3, + &min_value, &max_value, + &pc, + vector_3, sizeof(vector_3), + NULL, 0, + secp256k1_generator_h + )); + CHECK(secp256k1_memcmp_var(blind, blind_3, 32) == 0); + CHECK(value == UINT64_MAX); + CHECK(min_value == UINT64_MAX); + CHECK(max_value == UINT64_MAX); + CHECK(m_len == 0); +} } void test_pedersen_commitment_fixed_vector(void) { @@ -759,6 +1007,11 @@ void test_pedersen_commitment_fixed_vector(void) { void run_rangeproof_tests(void) { int i; test_api(); + + test_single_value_proof(0); + test_single_value_proof(12345678); + test_single_value_proof(UINT64_MAX); + test_rangeproof_fixed_vectors(); test_pedersen_commitment_fixed_vector(); for (i = 0; i < count / 2 + 1; i++) { From 34876ecb5facfc274580c0549df14e6664c03d82 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 12 Jan 2022 12:23:41 +0000 Subject: [PATCH 207/381] rangeproof: add more static test vectors Fixes #42 --- include/secp256k1_rangeproof.h | 9 + src/modules/rangeproof/tests_impl.h | 526 ++++++++++++++++++++++++++++ 2 files changed, 535 insertions(+) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 23e511ed..9bb01454 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -10,6 +10,15 @@ extern "C" { #include +/** Length of a message that can be embedded into a maximally-sized rangeproof + * + * It is not be possible to fit a message of this size into a non-maximally-sized + * rangeproof, but it is guaranteed that any embeddable message can fit into an + * array of this size. This constant is intended to be used for memory allocations + * and sanity checks. + */ +#define SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN 3968 + /** Opaque data structure that stores a Pedersen commitment * * The exact representation of data inside is implementation defined and not diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index b60126ef..411a7f98 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -985,6 +985,531 @@ void test_rangeproof_fixed_vectors(void) { } } +static void print_vector_helper(unsigned char *buf, size_t buf_len) { + size_t j; + printf(" "); + for (j = 0; j < buf_len; j++) { + printf("0x%02x", buf[j]); + if (j == buf_len-1) { + printf(",\n"); + } else if ((j+1) % 16 != 0) { + printf(", "); + } else { + printf(",\n"); + printf(" "); + } + } + printf("};\n"); +} + +static void print_vector(int i, unsigned char *proof, size_t p_len, secp256k1_pedersen_commitment *commit) { + unsigned char commit_output[33]; + + printf("unsigned char vector_%d[] = {\n", i); + print_vector_helper(proof, p_len); + + CHECK(secp256k1_pedersen_commitment_serialize(ctx, commit_output, commit)); + printf("unsigned char commit_%d[] = {\n", i); + print_vector_helper(commit_output, sizeof(commit_output)); +} + + +/* Use same nonce and blinding value for all "reproducible" test vectors */ +static unsigned char vector_blind[] = { + 0x48, 0x26, 0xad, 0x41, 0x37, 0x4c, 0x25, 0x62, 0x52, 0x14, 0x78, 0x82, 0x89, 0x9c, 0x86, 0x27, + 0xa1, 0x19, 0xf6, 0xe1, 0xfa, 0x44, 0xe4, 0x29, 0x08, 0xa7, 0xb3, 0x45, 0xad, 0x35, 0xb2, 0xd9, +}; +static unsigned char vector_nonce[] = { + 0xc8, 0x5c, 0x7e, 0x6c, 0xa1, 0xfa, 0x11, 0x35, 0xc7, 0x45, 0x24, 0x8a, 0xb5, 0x28, 0x6d, 0x1a, + 0x88, 0x00, 0xff, 0xca, 0x96, 0x0f, 0xc7, 0x77, 0xa5, 0x96, 0x7a, 0x5e, 0xf8, 0x88, 0x2d, 0xd4, +}; + +/* Maximum length of a message that can be embedded into a rangeproof */ +void test_rangeproof_fixed_vectors_reproducible_helper(unsigned char *vector, size_t vector_len, unsigned char *commit, uint64_t *value_r, uint64_t *min_value_r, uint64_t *max_value_r, unsigned char *message_r, size_t *m_len_r) { + secp256k1_pedersen_commitment pc; + unsigned char blind_r[32]; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit)); + CHECK(secp256k1_rangeproof_verify( + ctx, + min_value_r, max_value_r, + &pc, + vector, vector_len, + NULL, 0, + secp256k1_generator_h + )); + + *m_len_r = SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN; + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind_r, value_r, + message_r, m_len_r, + vector_nonce, + min_value_r, max_value_r, + &pc, + vector, vector_len, + NULL, 0, + secp256k1_generator_h + )); + CHECK(secp256k1_memcmp_var(blind_r, vector_blind, sizeof(vector_blind)) == 0); +} + +void test_rangeproof_fixed_vectors_reproducible(void) { + uint64_t value_r; + uint64_t min_value_r; + uint64_t max_value_r; + unsigned char message[SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN], message_r[SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN]; + size_t m_len_r; + memset(message, 0xFF, sizeof(message)); + + /* Test maximum values for value, min_bits, m_len and exp */ + { + uint64_t value = UINT64_MAX; + uint64_t min_value = 0; + size_t m_len = sizeof(message); /* maximum message length */ + + /* Uncomment this to recreate test vector */ + /* int min_bits = 64; */ + /* int exp = 18; */ + /* unsigned char proof[5126]; */ + /* size_t p_len = sizeof(proof); */ + /* secp256k1_pedersen_commitment pc; */ + /* CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); */ + /* CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); */ + /* CHECK(p_len == sizeof(proof)); */ + /* print_vector(0, proof, p_len, &pc); */ + + unsigned char vector_0[] = { + 0x40, 0x3f, 0xd1, 0x77, 0x65, 0x05, 0x87, 0x88, 0xd0, 0x3d, 0xb2, 0x24, 0x60, 0x7a, 0x08, 0x76, + 0xf8, 0x9f, 0x5a, 0x00, 0x73, 0x32, 0x6b, 0x5b, 0x0b, 0x59, 0xda, 0xa0, 0x6d, 0x2b, 0x66, 0xb8, + 0xfa, 0xa4, 0x8c, 0xf9, 0x78, 0x5e, 0xe3, 0xc7, 0x30, 0xea, 0xb4, 0x31, 0x77, 0x3a, 0xe4, 0xe3, + 0xf0, 0x76, 0x15, 0x21, 0x07, 0xb3, 0x6e, 0x84, 0x36, 0xdb, 0x45, 0xe6, 0x2b, 0x14, 0x50, 0xf3, + 0x53, 0x5d, 0x79, 0xf8, 0x6d, 0xc4, 0x99, 0x36, 0xa4, 0x7c, 0xc1, 0x14, 0x90, 0x99, 0xa8, 0x4b, + 0xf0, 0x01, 0x9f, 0xe7, 0xd4, 0xf9, 0xf1, 0x74, 0xb0, 0x7f, 0xf5, 0x90, 0x8d, 0x27, 0x9e, 0x61, + 0x9e, 0xc5, 0xd0, 0xa6, 0x32, 0xe8, 0x64, 0x4a, 0x02, 0x8b, 0xbf, 0xf7, 0xb8, 0x31, 0xa3, 0x4d, + 0x99, 0xbe, 0x12, 0x77, 0x4b, 0x07, 0x4a, 0xef, 0x75, 0xb4, 0xb3, 0x6e, 0x96, 0x95, 0xff, 0xe9, + 0xf7, 0xfc, 0x27, 0x17, 0x62, 0xfa, 0x99, 0xed, 0x00, 0x3c, 0xdd, 0xaa, 0xae, 0x9e, 0x80, 0xc1, + 0x29, 0x73, 0x4d, 0xfc, 0x41, 0xe6, 0xb4, 0x21, 0xe2, 0x62, 0x78, 0xf5, 0x46, 0xef, 0xcd, 0xcf, + 0x15, 0x2a, 0x05, 0x80, 0xb9, 0x95, 0xaa, 0xa5, 0xe9, 0x69, 0x5f, 0xfd, 0x58, 0x12, 0x00, 0x51, + 0xc4, 0x8f, 0xa2, 0xce, 0x03, 0x7f, 0x16, 0x19, 0xb1, 0x77, 0xc2, 0x98, 0xbe, 0xaa, 0x18, 0x6a, + 0x80, 0x0b, 0x4a, 0x81, 0x85, 0xc0, 0xc2, 0x62, 0xb3, 0xec, 0xae, 0xe7, 0x95, 0xbf, 0xd3, 0xe0, + 0xcd, 0xa3, 0xdd, 0x02, 0x70, 0x98, 0x6c, 0xf3, 0x4b, 0x43, 0xec, 0x8d, 0x07, 0xf4, 0x3e, 0xb0, + 0x00, 0x7c, 0xb7, 0x1a, 0x85, 0x9a, 0x94, 0xe8, 0x57, 0xc9, 0x7e, 0x24, 0xb4, 0x7a, 0x84, 0x17, + 0x08, 0xe6, 0xae, 0x91, 0x14, 0xcb, 0x94, 0xf3, 0xe9, 0x13, 0x25, 0x35, 0x54, 0xbf, 0x22, 0xe9, + 0xab, 0x8e, 0xa4, 0xa1, 0x18, 0x78, 0x6c, 0xee, 0x13, 0x26, 0xfc, 0x79, 0xf1, 0xe5, 0x51, 0x1e, + 0x0c, 0xac, 0xa5, 0xef, 0x0d, 0xee, 0xe8, 0x5f, 0x93, 0xa4, 0x88, 0x4a, 0x32, 0x95, 0x8a, 0x61, + 0x76, 0xd8, 0xac, 0x2d, 0x36, 0x9a, 0x6b, 0xa4, 0x7c, 0x30, 0xa3, 0x09, 0x38, 0xbb, 0xbc, 0x51, + 0x1a, 0x10, 0xae, 0x9e, 0x18, 0x9f, 0xd8, 0xc8, 0xce, 0xfa, 0x63, 0xab, 0x28, 0xc9, 0x76, 0x28, + 0x32, 0x61, 0x39, 0x83, 0x99, 0x0a, 0x41, 0xc0, 0x55, 0x1c, 0x65, 0x6c, 0xcf, 0xc3, 0x72, 0x47, + 0xe7, 0xb1, 0x99, 0xb5, 0x04, 0x44, 0xb9, 0xde, 0x4b, 0x83, 0x37, 0x66, 0xb2, 0xee, 0x9f, 0x07, + 0xf1, 0x4f, 0x4d, 0x59, 0xee, 0x37, 0x79, 0x47, 0x0e, 0x31, 0x70, 0x3a, 0xfa, 0xe0, 0xa1, 0xef, + 0xa2, 0x1f, 0xeb, 0xe8, 0xd7, 0x4f, 0xcb, 0xc2, 0xce, 0xdc, 0x82, 0xa6, 0x36, 0xed, 0x1d, 0xdd, + 0xa6, 0x40, 0x10, 0x38, 0x4f, 0x28, 0x90, 0xc3, 0xe3, 0xb6, 0xa4, 0x74, 0xbb, 0x56, 0x23, 0x01, + 0x3a, 0xb6, 0xb1, 0xad, 0x94, 0x4b, 0x52, 0x42, 0x0a, 0x9d, 0xd6, 0x89, 0xdd, 0xa7, 0x0f, 0x66, + 0xdb, 0x4e, 0x5b, 0xa4, 0xc2, 0x11, 0xd7, 0xd5, 0xf7, 0x0a, 0xf1, 0xc8, 0x35, 0x16, 0xc0, 0x7d, + 0x29, 0x5d, 0x5c, 0x62, 0x6b, 0xe0, 0x1b, 0x74, 0x8d, 0x14, 0x9e, 0x08, 0xb6, 0x18, 0x0d, 0x2b, + 0x3a, 0xfb, 0x22, 0x9e, 0xd6, 0x77, 0x05, 0x1b, 0xd4, 0x5d, 0x25, 0x27, 0x97, 0x40, 0x93, 0x58, + 0x35, 0xad, 0xc5, 0x19, 0x96, 0x62, 0xbb, 0x10, 0x2d, 0x4e, 0x24, 0x62, 0xc0, 0x1a, 0xe6, 0x12, + 0x84, 0xac, 0x1b, 0x5e, 0x25, 0xa9, 0xc7, 0x5d, 0xa0, 0x34, 0x85, 0x70, 0xf1, 0x08, 0xd8, 0xf9, + 0x1c, 0x1b, 0x71, 0xcc, 0x72, 0xec, 0xce, 0x30, 0x91, 0x67, 0xac, 0xe5, 0x4e, 0x51, 0xa6, 0x47, + 0x74, 0x09, 0x19, 0xee, 0x9d, 0x2d, 0x3f, 0xf2, 0x49, 0x5a, 0xf0, 0x6c, 0x8f, 0xe9, 0x1f, 0x10, + 0xcc, 0x32, 0x2f, 0x8d, 0x3e, 0xfa, 0xba, 0x0c, 0x37, 0x51, 0xe2, 0x3e, 0xcc, 0xdd, 0x81, 0xd5, + 0xef, 0xef, 0x56, 0x3a, 0xd2, 0x96, 0x10, 0xfb, 0x19, 0xce, 0x90, 0xbf, 0x82, 0x11, 0xf6, 0xe6, + 0x28, 0x7e, 0x4d, 0x16, 0x61, 0x87, 0xdd, 0xc5, 0x61, 0x5d, 0x85, 0xa9, 0x02, 0xea, 0xdf, 0x7a, + 0x2c, 0x92, 0xdd, 0xc3, 0xa3, 0xc9, 0xec, 0xd9, 0xc0, 0x54, 0xbd, 0xf2, 0x77, 0x00, 0x5e, 0x18, + 0x8c, 0x0a, 0x63, 0xdf, 0x8f, 0xf9, 0x1e, 0x45, 0x1c, 0xd1, 0xf7, 0x11, 0x1e, 0xbd, 0x20, 0x30, + 0x1c, 0x27, 0x45, 0xfe, 0xb7, 0xe6, 0x93, 0xae, 0x2a, 0xc1, 0xdc, 0xe2, 0xf6, 0xd1, 0x54, 0x42, + 0xa3, 0x26, 0x79, 0xd6, 0xb3, 0x5c, 0xb0, 0xd2, 0x97, 0x3e, 0xc0, 0x0e, 0xce, 0xf4, 0x82, 0x42, + 0x99, 0x49, 0xb7, 0xad, 0x33, 0xd6, 0x67, 0x81, 0xaf, 0x40, 0xa2, 0xbd, 0x71, 0x7e, 0x82, 0x66, + 0x8d, 0x97, 0x3b, 0x9d, 0x30, 0xe8, 0x4b, 0x4d, 0xcd, 0xf0, 0x1e, 0xfb, 0x33, 0xbd, 0xcd, 0xb2, + 0xca, 0x7e, 0x5d, 0xa2, 0xe5, 0x6a, 0xa1, 0xc0, 0xbd, 0xae, 0x8c, 0x65, 0x93, 0x9d, 0x53, 0x6f, + 0x8d, 0xce, 0x4f, 0xc2, 0xb1, 0x54, 0x7d, 0x7a, 0x59, 0x08, 0xc0, 0xaa, 0xfc, 0x12, 0xa9, 0x41, + 0x9b, 0x11, 0x3c, 0x24, 0x3a, 0x89, 0x8f, 0x2f, 0x5a, 0x49, 0x44, 0x64, 0x83, 0x17, 0xbe, 0xa2, + 0xff, 0x6f, 0xcb, 0x65, 0x04, 0x6f, 0x75, 0x4c, 0x5d, 0x15, 0x06, 0x7b, 0x3b, 0x3e, 0xc1, 0xed, + 0xcb, 0xdc, 0xde, 0x04, 0x03, 0x30, 0x74, 0xc2, 0x6b, 0x50, 0x53, 0x6b, 0x8c, 0xcd, 0x1b, 0xe7, + 0xd5, 0xf8, 0x42, 0xe5, 0x07, 0x16, 0xc3, 0x70, 0x83, 0x49, 0x25, 0xa4, 0x1e, 0x61, 0xa8, 0xc4, + 0x13, 0x82, 0xfe, 0x56, 0xd7, 0x04, 0xbe, 0x4d, 0x59, 0x05, 0xb8, 0x35, 0xcc, 0xd2, 0xaf, 0x40, + 0x24, 0xf3, 0xbc, 0x48, 0x58, 0x0e, 0x01, 0xe6, 0x47, 0x19, 0x9d, 0xb6, 0xe3, 0x6f, 0x17, 0x5e, + 0xab, 0x6c, 0xa3, 0x5a, 0x9a, 0xdd, 0x95, 0x2a, 0x18, 0x22, 0x42, 0x36, 0x07, 0xa0, 0x47, 0xb1, + 0x2e, 0x8f, 0xc8, 0x78, 0xcc, 0x7d, 0x16, 0x61, 0x5f, 0x34, 0x66, 0xee, 0x01, 0x17, 0x60, 0xa3, + 0x3f, 0x4d, 0xb1, 0xcc, 0xcc, 0x13, 0x99, 0x51, 0x3e, 0x78, 0x69, 0x7b, 0x83, 0x49, 0x5f, 0xf3, + 0x89, 0xa9, 0x9e, 0x24, 0x18, 0x08, 0x4d, 0xdb, 0x8a, 0xb1, 0xd8, 0xd7, 0xae, 0x30, 0x82, 0x4d, + 0x3d, 0x4f, 0xce, 0xbe, 0x17, 0xe5, 0x47, 0x5d, 0xa6, 0x03, 0x8c, 0xae, 0xe7, 0xa2, 0x63, 0xf3, + 0xe8, 0x88, 0x21, 0xf4, 0xfd, 0xa9, 0x32, 0x15, 0x93, 0x0c, 0xbe, 0x61, 0xe8, 0x35, 0x6c, 0xb5, + 0xc9, 0xa9, 0xec, 0x1c, 0x7f, 0x34, 0x5b, 0xb0, 0x80, 0x6d, 0x0a, 0x52, 0x87, 0x74, 0x12, 0x90, + 0x3a, 0xf7, 0x40, 0x41, 0xe1, 0x62, 0xa5, 0xb7, 0xf0, 0x5d, 0x45, 0x3e, 0x55, 0x1a, 0x30, 0xec, + 0x5d, 0x52, 0x00, 0x76, 0x38, 0x10, 0x0d, 0xf0, 0x2f, 0x7f, 0xf2, 0x3e, 0x34, 0x1f, 0x1a, 0xd9, + 0xf8, 0xb9, 0x86, 0xf9, 0xdc, 0x05, 0xe0, 0xcf, 0x28, 0x49, 0xfd, 0x21, 0x64, 0xf6, 0xa1, 0xc4, + 0xf7, 0xce, 0x91, 0xb2, 0x15, 0xdf, 0x82, 0x39, 0x30, 0x60, 0xf3, 0xd1, 0xa6, 0x18, 0xc4, 0x3b, + 0xf7, 0xd2, 0x64, 0xe8, 0xab, 0x67, 0x23, 0xb8, 0x2c, 0x57, 0x84, 0x17, 0xc0, 0x2c, 0x21, 0xfc, + 0x55, 0x8b, 0xb6, 0x06, 0xbf, 0x79, 0x7e, 0x29, 0x5a, 0xfb, 0x5c, 0xa5, 0x5a, 0xe4, 0x46, 0xac, + 0x16, 0x8e, 0xf4, 0x03, 0xb7, 0xbb, 0xb0, 0x7b, 0xbf, 0xd3, 0x84, 0xbe, 0xb5, 0x6a, 0xc8, 0x28, + 0xe8, 0x2c, 0x2d, 0x0b, 0x7d, 0x0a, 0x65, 0xd3, 0xee, 0x54, 0x8c, 0xbf, 0xd9, 0xda, 0x84, 0x21, + 0x80, 0x07, 0x68, 0x09, 0x75, 0xbd, 0xa8, 0xd0, 0xbf, 0xa0, 0xf3, 0xc7, 0xc5, 0xb5, 0xf2, 0xf8, + 0xf1, 0x74, 0x6e, 0x7d, 0xad, 0x80, 0xbd, 0x87, 0xe9, 0x83, 0x2e, 0xda, 0x61, 0x28, 0x03, 0x74, + 0xe5, 0x45, 0x74, 0x0d, 0x1f, 0x47, 0x46, 0x10, 0x7f, 0xef, 0x12, 0x9f, 0x78, 0xec, 0x03, 0xed, + 0x22, 0x86, 0x6f, 0x1a, 0x31, 0x14, 0xf4, 0x3a, 0x7f, 0xef, 0x98, 0x3e, 0x64, 0x86, 0xb9, 0x0e, + 0x5b, 0x0d, 0x55, 0xba, 0xcc, 0x6d, 0x04, 0xc7, 0x9c, 0x1e, 0xd4, 0xf7, 0xf0, 0x60, 0x6d, 0x54, + 0x75, 0x70, 0x3b, 0x99, 0xd3, 0x01, 0x9f, 0x34, 0x44, 0x98, 0xab, 0xd1, 0x6b, 0xc8, 0xaa, 0xfd, + 0xd9, 0x5a, 0xd3, 0xee, 0x5d, 0x2c, 0x54, 0x38, 0x06, 0x1f, 0x93, 0xb3, 0x5c, 0x87, 0x10, 0x5e, + 0xcb, 0xcd, 0xd3, 0x4a, 0x89, 0xd2, 0x0d, 0xac, 0xeb, 0x42, 0x67, 0x2b, 0xd1, 0x75, 0x12, 0x58, + 0xdd, 0x19, 0xe3, 0x21, 0x33, 0x75, 0xf6, 0x51, 0x25, 0xeb, 0xa6, 0x43, 0x44, 0x82, 0x64, 0xae, + 0xb8, 0x97, 0x01, 0xff, 0x17, 0x8f, 0xce, 0x96, 0xc8, 0xc3, 0x86, 0xa0, 0x05, 0xb2, 0x2c, 0x33, + 0x01, 0x27, 0x25, 0x84, 0x83, 0x85, 0x33, 0xe2, 0xd0, 0xc5, 0x65, 0x89, 0x85, 0x45, 0x81, 0x3f, + 0x2d, 0xb4, 0xb1, 0x8b, 0x1d, 0x04, 0x5d, 0x4c, 0xd8, 0x46, 0x8a, 0x04, 0x3a, 0x3b, 0xa7, 0x76, + 0x47, 0x5e, 0xcc, 0xc0, 0x16, 0xb2, 0x3a, 0x38, 0x9a, 0x6a, 0x50, 0x3a, 0x8b, 0x82, 0xb7, 0x6b, + 0xf2, 0x60, 0x53, 0x4e, 0xdf, 0x8a, 0x02, 0x9f, 0xc6, 0x27, 0x4f, 0xf5, 0x2a, 0xf1, 0xf1, 0x2f, + 0x4a, 0xaa, 0xc7, 0x94, 0xc0, 0xdc, 0xdb, 0x8c, 0x41, 0xd9, 0x16, 0x13, 0xa2, 0xae, 0x37, 0x2a, + 0x7e, 0x26, 0x6f, 0xdf, 0x46, 0x07, 0x74, 0x88, 0x62, 0xab, 0x28, 0x64, 0x12, 0x7c, 0xca, 0xd5, + 0xbb, 0x6f, 0x7f, 0x3b, 0x44, 0x99, 0x20, 0x93, 0x9a, 0xa0, 0xac, 0x17, 0xed, 0x82, 0xf9, 0x43, + 0xc2, 0x98, 0x6b, 0xcf, 0x54, 0x91, 0xfe, 0x3c, 0x9e, 0xfa, 0x7b, 0x57, 0x38, 0xe2, 0x64, 0x58, + 0x9c, 0xe0, 0x41, 0x95, 0x8a, 0xa0, 0xa3, 0x3d, 0x7b, 0x3e, 0x99, 0xea, 0xc7, 0xec, 0x82, 0xc8, + 0xa8, 0xae, 0xbd, 0xf9, 0x5c, 0x7e, 0xa2, 0x20, 0x78, 0xce, 0x4a, 0x6c, 0x74, 0x2a, 0xe7, 0x31, + 0xdb, 0xc1, 0x02, 0x49, 0x4b, 0x83, 0x0b, 0x0e, 0x7e, 0xeb, 0x69, 0x59, 0xf9, 0x3c, 0x13, 0x47, + 0xaf, 0xbd, 0x58, 0xec, 0x7f, 0xae, 0x7e, 0x4b, 0xf3, 0x3d, 0x18, 0xbf, 0xb0, 0x79, 0x92, 0x59, + 0x9e, 0x5f, 0x03, 0x30, 0x15, 0xba, 0xec, 0xd1, 0xaf, 0x2e, 0xf7, 0x88, 0xde, 0x50, 0xae, 0x9e, + 0x59, 0x14, 0xf3, 0xa5, 0x78, 0x25, 0xd7, 0xd9, 0x1a, 0x33, 0x81, 0x29, 0x8b, 0x93, 0xf6, 0xfa, + 0x90, 0x3d, 0x13, 0xaa, 0x0d, 0xa7, 0x8e, 0x79, 0xc0, 0x36, 0x45, 0x29, 0xa5, 0xf1, 0xfe, 0x92, + 0x1b, 0x57, 0x42, 0x58, 0xe0, 0x85, 0x15, 0xa2, 0xc9, 0xb1, 0x50, 0x08, 0x6a, 0x02, 0x46, 0xc6, + 0x1d, 0xd0, 0xf0, 0xb4, 0x5a, 0xcc, 0xd5, 0x54, 0x9d, 0xab, 0x13, 0x47, 0x9f, 0x82, 0x60, 0x9b, + 0x11, 0x64, 0x35, 0xfa, 0xef, 0x89, 0xb4, 0x87, 0x43, 0x48, 0x6e, 0x78, 0x64, 0x01, 0xbe, 0x09, + 0xd1, 0xd0, 0x40, 0xdf, 0x77, 0x6b, 0xee, 0x92, 0xc5, 0xff, 0x77, 0xcf, 0x20, 0x95, 0x36, 0x78, + 0x35, 0x1f, 0x1e, 0xaf, 0x4b, 0xa7, 0x66, 0x71, 0x9d, 0x5e, 0xb7, 0xd9, 0x70, 0x6e, 0xaa, 0x35, + 0x49, 0x3c, 0x9a, 0x23, 0x53, 0xaf, 0x3e, 0x9d, 0x60, 0x72, 0xb5, 0x27, 0x33, 0x80, 0x33, 0xd2, + 0x11, 0x4b, 0xff, 0xfb, 0x53, 0xab, 0x14, 0x4c, 0xe4, 0xe7, 0xbc, 0x2f, 0x5c, 0xd8, 0xbf, 0x81, + 0x5c, 0xf7, 0x4d, 0x5d, 0xb8, 0x84, 0x62, 0xf3, 0xd2, 0x0a, 0x53, 0x66, 0xd3, 0x13, 0xff, 0xb0, + 0xeb, 0x4b, 0x1f, 0x10, 0x1d, 0xa9, 0xba, 0x5c, 0xad, 0xe2, 0x52, 0x91, 0xae, 0xbe, 0x5d, 0x05, + 0x54, 0x6d, 0x72, 0x1e, 0xc1, 0x14, 0xb5, 0x9b, 0x22, 0x3f, 0x78, 0x73, 0x5e, 0x99, 0x50, 0xde, + 0xa8, 0x41, 0x31, 0xd0, 0x44, 0xf3, 0x2f, 0x31, 0xd9, 0x0b, 0x72, 0x1a, 0xd3, 0x70, 0xbe, 0x84, + 0xfc, 0xe1, 0xed, 0x15, 0xb9, 0xe9, 0x69, 0xe2, 0xbe, 0x50, 0xa2, 0xda, 0x4e, 0x7a, 0x83, 0xc5, + 0x56, 0xea, 0xaf, 0x2c, 0xc9, 0x8f, 0xcc, 0x83, 0x1a, 0xa5, 0x0e, 0x74, 0xaa, 0x64, 0x96, 0xb5, + 0x5a, 0x4a, 0x72, 0xad, 0x86, 0x5a, 0xb8, 0x5a, 0x04, 0x0d, 0x68, 0x21, 0x63, 0x23, 0x7b, 0x17, + 0x9d, 0xfd, 0x1b, 0x3f, 0xac, 0x80, 0x98, 0x97, 0xb5, 0xd4, 0xb7, 0x08, 0x50, 0xf4, 0x18, 0xf9, + 0x16, 0xb1, 0x57, 0xfa, 0xff, 0xa8, 0x02, 0x6e, 0x62, 0x7b, 0x15, 0x8d, 0xc0, 0x17, 0xdd, 0xa3, + 0x45, 0x98, 0x5a, 0xa0, 0xda, 0xe2, 0xd1, 0x17, 0x91, 0x3d, 0xda, 0x18, 0x16, 0x19, 0x3a, 0xfb, + 0xfd, 0x44, 0x72, 0x03, 0x97, 0x72, 0x1d, 0xbf, 0x11, 0xca, 0x95, 0x05, 0x6a, 0x9e, 0x41, 0x3d, + 0x85, 0xb4, 0xd9, 0xb3, 0x88, 0xd4, 0xd9, 0xfb, 0x1c, 0xd2, 0x35, 0x31, 0x12, 0xcc, 0xf3, 0x03, + 0x50, 0x8a, 0xfb, 0x0e, 0x72, 0xeb, 0x86, 0x65, 0xd5, 0x96, 0x0f, 0x53, 0x1e, 0x13, 0x99, 0x91, + 0xa7, 0x70, 0xab, 0x4c, 0xa6, 0x1a, 0xb7, 0x0d, 0x71, 0x0e, 0xb6, 0x17, 0x85, 0xdf, 0x9b, 0x74, + 0x19, 0x4a, 0xc7, 0x55, 0x0e, 0xe9, 0x22, 0x6b, 0x8e, 0xa8, 0x5e, 0x45, 0xc4, 0x0f, 0x36, 0xbb, + 0x21, 0xda, 0x97, 0xcf, 0xed, 0x41, 0xb5, 0x00, 0x26, 0xb1, 0x70, 0x43, 0x5c, 0x60, 0x59, 0x23, + 0x19, 0xc5, 0xdb, 0x49, 0xef, 0xdd, 0x5d, 0x19, 0x5f, 0x58, 0xaa, 0x20, 0xd9, 0x09, 0x17, 0x09, + 0xc7, 0x5f, 0xb9, 0x65, 0x8f, 0x0a, 0xe8, 0x4d, 0x7d, 0x60, 0x88, 0x7a, 0x53, 0x9e, 0xf1, 0x25, + 0xcd, 0xa1, 0x3e, 0xc6, 0xc5, 0x86, 0xf2, 0xee, 0x60, 0x0f, 0x11, 0x3e, 0xe3, 0x90, 0x4d, 0xff, + 0x49, 0x0b, 0x2f, 0x85, 0x7f, 0x18, 0x53, 0x4e, 0xe2, 0x5c, 0x06, 0x61, 0x51, 0x08, 0xca, 0x55, + 0x80, 0x83, 0xa4, 0x80, 0x05, 0x26, 0xe7, 0x29, 0xdb, 0xab, 0x94, 0x66, 0xe9, 0xbf, 0xf8, 0xd2, + 0x79, 0x71, 0x61, 0x05, 0x33, 0xc8, 0x6b, 0x5c, 0x62, 0x01, 0x7f, 0x82, 0xef, 0x5f, 0xa3, 0xf6, + 0x29, 0x86, 0xb6, 0x0a, 0xa5, 0xed, 0x3a, 0xae, 0x34, 0x12, 0xba, 0xb0, 0x80, 0xd4, 0x53, 0x79, + 0x57, 0x7d, 0xa2, 0x38, 0xfd, 0x39, 0xc1, 0xaf, 0x07, 0x8d, 0x23, 0x6b, 0xcd, 0x97, 0xe0, 0xf6, + 0x83, 0x74, 0x7c, 0x3c, 0x95, 0xdb, 0xb1, 0xf0, 0xf7, 0x9e, 0xe9, 0xe4, 0x68, 0x67, 0x18, 0x0e, + 0x00, 0x12, 0x70, 0x79, 0xc2, 0xfe, 0xde, 0x00, 0x12, 0x87, 0x00, 0xd3, 0x8a, 0xc5, 0x1b, 0xb4, + 0xd7, 0x46, 0x1d, 0x73, 0x5c, 0x51, 0x93, 0x19, 0x83, 0x1b, 0x28, 0xb9, 0x70, 0x87, 0x20, 0x34, + 0xb6, 0x7e, 0xb9, 0x1f, 0x9d, 0x15, 0xdb, 0xd7, 0xfb, 0x30, 0x92, 0xe8, 0x21, 0x6c, 0x5c, 0xdb, + 0xaf, 0x5c, 0xd5, 0xa5, 0x76, 0x4b, 0x5f, 0xc3, 0xa8, 0x73, 0x9a, 0x42, 0x04, 0x21, 0xf1, 0x49, + 0xbc, 0x47, 0xa0, 0xa7, 0x4a, 0xda, 0x16, 0x34, 0xcd, 0x62, 0xe1, 0xe0, 0x9a, 0x35, 0xf2, 0xf9, + 0xb2, 0x3c, 0xcf, 0xa6, 0x4f, 0xd7, 0x23, 0xc2, 0xd2, 0xa5, 0x5a, 0xf9, 0xfd, 0x7d, 0x25, 0x84, + 0xda, 0xda, 0x66, 0xb9, 0x99, 0x7f, 0xec, 0xfc, 0x82, 0x7c, 0xea, 0x6b, 0x23, 0x89, 0x6e, 0xab, + 0xe9, 0x43, 0xcb, 0x88, 0xcd, 0x18, 0x6e, 0xae, 0x2d, 0xf5, 0xc7, 0xe3, 0x92, 0x55, 0x0b, 0xc0, + 0x4d, 0xc8, 0xee, 0x4b, 0x7e, 0xfe, 0x84, 0x8c, 0x32, 0x33, 0xc8, 0xf1, 0xfb, 0xd8, 0x11, 0x22, + 0xd9, 0x0d, 0x2b, 0x16, 0x19, 0xba, 0x65, 0xe4, 0x99, 0x26, 0x7b, 0xa7, 0x04, 0x11, 0xfc, 0xb8, + 0x05, 0x48, 0x36, 0x43, 0x82, 0x14, 0xb7, 0x31, 0x50, 0xfd, 0x38, 0x89, 0xc1, 0x36, 0xa1, 0xd5, + 0x8e, 0x52, 0x76, 0x99, 0xc8, 0x38, 0x49, 0xb4, 0x94, 0x02, 0x96, 0x35, 0x8c, 0xc1, 0x9c, 0x7c, + 0x2b, 0xbe, 0x73, 0x62, 0x0a, 0xd3, 0x57, 0x3d, 0xdb, 0x81, 0x14, 0x7c, 0xd0, 0x4a, 0xe5, 0x2f, + 0x63, 0xbd, 0xac, 0xcf, 0x83, 0x10, 0xfd, 0x06, 0x54, 0xc0, 0x5c, 0xba, 0x96, 0x72, 0x0b, 0xcf, + 0x0a, 0x74, 0xe2, 0xbf, 0xbc, 0x1c, 0xc6, 0xd8, 0x9e, 0x7f, 0x5f, 0xbb, 0x00, 0xfe, 0x2a, 0xbd, + 0x36, 0x02, 0x56, 0x5b, 0xa2, 0x30, 0x75, 0x44, 0x62, 0xf8, 0x22, 0x24, 0x14, 0x04, 0x30, 0x26, + 0xe5, 0xb4, 0x06, 0x3d, 0xfe, 0x5c, 0x3a, 0xc7, 0xd8, 0x1d, 0x1b, 0xc9, 0x99, 0xbb, 0xa5, 0x2c, + 0x92, 0x3b, 0xaa, 0x92, 0xf2, 0x12, 0x59, 0xc1, 0xd7, 0xec, 0xae, 0x89, 0x45, 0xfb, 0xe6, 0x15, + 0xa7, 0xe8, 0xad, 0x26, 0xf9, 0xb3, 0xe0, 0xd5, 0x57, 0xab, 0x4c, 0xab, 0xda, 0xe0, 0xc2, 0x9d, + 0xb1, 0x12, 0x6f, 0xc9, 0x84, 0x2b, 0x66, 0x89, 0x05, 0x37, 0x2a, 0x6b, 0x8d, 0xe8, 0x21, 0xa4, + 0xbb, 0x28, 0xc4, 0xb4, 0xa6, 0x93, 0xc2, 0xc9, 0x54, 0x39, 0x38, 0x84, 0xae, 0x70, 0x9b, 0xcf, + 0xc5, 0xc5, 0x3c, 0x56, 0x21, 0xb4, 0x95, 0xb0, 0xa7, 0x2a, 0x30, 0xd8, 0xcb, 0x18, 0x22, 0x31, + 0x59, 0x01, 0x62, 0x43, 0xe2, 0x65, 0xb9, 0xf3, 0xc6, 0x5c, 0x9c, 0xe1, 0xea, 0x48, 0x6f, 0x10, + 0xc2, 0x27, 0x3a, 0xd6, 0xd1, 0x15, 0xeb, 0x7f, 0xc9, 0x2b, 0x21, 0x25, 0xae, 0x91, 0x34, 0xd0, + 0x6b, 0xfe, 0xe3, 0x79, 0x52, 0xb9, 0xb2, 0x17, 0xd6, 0x6b, 0xf0, 0xfa, 0x3f, 0x15, 0xb5, 0x74, + 0x10, 0xf9, 0xd9, 0xb0, 0xc5, 0xdb, 0x72, 0x1a, 0x76, 0xeb, 0x41, 0x6f, 0xb5, 0x9b, 0x8d, 0xb9, + 0x8f, 0x75, 0x6d, 0xc8, 0x25, 0xfa, 0xee, 0xdb, 0x1c, 0x3c, 0x01, 0x80, 0x38, 0x5b, 0x83, 0x01, + 0xc0, 0x02, 0xa1, 0x1c, 0x71, 0xef, 0xbc, 0x58, 0xa5, 0xf6, 0x49, 0xb7, 0xef, 0x9c, 0xa7, 0x7d, + 0x39, 0xcc, 0x2a, 0x0b, 0xdb, 0x78, 0xb7, 0x5d, 0x22, 0x36, 0xb1, 0x36, 0x72, 0x56, 0x62, 0x19, + 0xf1, 0x5c, 0x0a, 0x63, 0x02, 0x6a, 0x59, 0x8e, 0x24, 0xb6, 0x32, 0x21, 0x11, 0x96, 0xd2, 0x8c, + 0xf1, 0xaf, 0x84, 0x3f, 0xf5, 0x98, 0x0d, 0x22, 0x14, 0x15, 0xda, 0x9f, 0x44, 0x0b, 0x08, 0x33, + 0x13, 0x11, 0x53, 0x78, 0xe1, 0xf1, 0x4b, 0x40, 0x1c, 0x43, 0x73, 0x5d, 0x86, 0x06, 0xe1, 0x02, + 0x79, 0x3f, 0x68, 0x00, 0xb7, 0xb0, 0xcb, 0x3b, 0x2e, 0x12, 0x4e, 0x81, 0x77, 0x2a, 0xc5, 0x74, + 0xc4, 0xd9, 0xa0, 0x1e, 0xa3, 0x74, 0x17, 0x61, 0x15, 0xaf, 0xa7, 0xb4, 0x25, 0x61, 0x85, 0x60, + 0x70, 0x85, 0xfb, 0x43, 0xb9, 0x43, 0x4f, 0x87, 0x44, 0x1a, 0x7b, 0xee, 0x68, 0xbb, 0x74, 0x99, + 0xff, 0x81, 0x22, 0xb4, 0x8d, 0x94, 0xf0, 0x4a, 0x3e, 0x28, 0xf5, 0xbd, 0x5c, 0xa1, 0x7f, 0x5c, + 0xea, 0xe6, 0xba, 0xf2, 0x5d, 0x85, 0xf2, 0xed, 0xd7, 0xfd, 0x93, 0xf0, 0xbb, 0xba, 0x86, 0x23, + 0xb5, 0xee, 0xb5, 0x5b, 0xd9, 0x91, 0xbf, 0xdc, 0xe3, 0xdc, 0xbe, 0x82, 0x92, 0xce, 0xb4, 0x0f, + 0xbf, 0xf1, 0x80, 0x0f, 0xe8, 0xc3, 0x04, 0x49, 0x3e, 0x95, 0x72, 0xf8, 0x36, 0x44, 0xce, 0xf4, + 0x3a, 0x6a, 0xeb, 0xdd, 0x88, 0x28, 0x8d, 0xf3, 0x04, 0x65, 0xd8, 0xfd, 0x9d, 0xb1, 0x22, 0xbd, + 0x44, 0xe7, 0xcf, 0x0f, 0x2f, 0xfc, 0xb6, 0xd6, 0x60, 0x7a, 0x6e, 0x6a, 0x93, 0x83, 0xf1, 0x86, + 0xf4, 0xf1, 0x23, 0xb2, 0xfd, 0x4a, 0xb6, 0xcf, 0x97, 0x70, 0xae, 0xa9, 0x44, 0x94, 0x7a, 0x4e, + 0xf9, 0x1e, 0xf8, 0xdc, 0x1f, 0xf0, 0x06, 0x16, 0xdf, 0x69, 0xde, 0x70, 0xea, 0x64, 0x98, 0xe6, + 0x1f, 0x9f, 0x3a, 0xcf, 0xf6, 0xb6, 0x10, 0xb1, 0x3c, 0xd1, 0x76, 0x67, 0x6c, 0xaf, 0x3c, 0x0a, + 0xc3, 0x46, 0x31, 0x89, 0xf7, 0x7a, 0x0f, 0x9b, 0x7d, 0xd8, 0x9a, 0x81, 0x57, 0x0c, 0x37, 0xda, + 0xf7, 0xc3, 0xe5, 0x4b, 0x14, 0xe9, 0x0e, 0x8c, 0x27, 0xdc, 0x68, 0xdb, 0xd8, 0xdb, 0x8b, 0xee, + 0x4e, 0x9f, 0x93, 0x18, 0x4c, 0x75, 0xec, 0x41, 0x6f, 0xbe, 0x89, 0xe3, 0x77, 0x64, 0xbd, 0x22, + 0xa1, 0x0a, 0x16, 0x93, 0xbc, 0x7a, 0xec, 0x6e, 0x3c, 0x2d, 0x97, 0x4a, 0xb1, 0x7d, 0xb6, 0xfc, + 0x01, 0x8b, 0x87, 0x2b, 0x60, 0xe4, 0x53, 0xde, 0x72, 0xef, 0xa5, 0xdf, 0xeb, 0x2d, 0x3c, 0x01, + 0xdc, 0xfd, 0xd8, 0x3b, 0x2a, 0x16, 0x74, 0x1b, 0x26, 0x40, 0x5c, 0x4d, 0x11, 0x7f, 0xa8, 0x3f, + 0xc9, 0xe8, 0x8f, 0x6b, 0xe4, 0x96, 0x3f, 0x2b, 0x4e, 0xfa, 0x1f, 0xa5, 0x73, 0x85, 0xbb, 0xe5, + 0x6f, 0xd7, 0x0c, 0xf4, 0x46, 0x81, 0xfd, 0x0d, 0x0f, 0x70, 0x3c, 0x51, 0x46, 0x47, 0x5d, 0x37, + 0x02, 0x94, 0x52, 0x44, 0x4b, 0xac, 0x30, 0xc3, 0x72, 0xd1, 0x50, 0x5e, 0x29, 0xa2, 0x4a, 0xfb, + 0xbe, 0x8e, 0x60, 0x16, 0xb0, 0xb1, 0x82, 0xd8, 0x29, 0xf5, 0x3c, 0x72, 0x3c, 0x79, 0x9d, 0xa8, + 0x64, 0x15, 0x57, 0x7a, 0x10, 0x0f, 0xb4, 0xc0, 0x02, 0xdb, 0xc6, 0x6c, 0x37, 0xa1, 0xf3, 0x52, + 0x5c, 0xb5, 0xcd, 0x42, 0xf1, 0x43, 0x31, 0xfe, 0x0b, 0xaa, 0xcb, 0xd5, 0x0a, 0xf4, 0x69, 0x64, + 0xf5, 0x98, 0x41, 0x53, 0xed, 0x04, 0x27, 0x56, 0x4a, 0xe9, 0x4c, 0x96, 0x64, 0x29, 0xff, 0xe2, + 0xe1, 0x79, 0xf5, 0x78, 0x69, 0x43, 0xe6, 0x4e, 0x4e, 0xa8, 0x8a, 0x50, 0x15, 0x37, 0x44, 0x73, + 0x41, 0xe8, 0x21, 0xe4, 0x0a, 0x2e, 0x43, 0x2d, 0x4b, 0xd1, 0xfe, 0xf5, 0xc5, 0x30, 0x31, 0x39, + 0xfa, 0x53, 0x7a, 0x3c, 0xd5, 0x63, 0x7a, 0x5c, 0xff, 0x18, 0xf8, 0x79, 0x84, 0x73, 0x84, 0x55, + 0x61, 0x17, 0x2f, 0xad, 0x34, 0xd3, 0xf2, 0xf3, 0x2b, 0xe6, 0xe3, 0xc2, 0x0e, 0x2b, 0x28, 0xc4, + 0x92, 0xd0, 0xd3, 0x14, 0x06, 0x58, 0xbb, 0x2f, 0xdf, 0xe6, 0x3d, 0x0d, 0x6b, 0x71, 0x40, 0xc2, + 0xbf, 0x6b, 0x41, 0xae, 0x85, 0x39, 0x1c, 0xdf, 0x02, 0xf3, 0x6e, 0x85, 0x15, 0x1d, 0xb8, 0xe4, + 0xbe, 0x32, 0x77, 0x70, 0x22, 0x5b, 0xe7, 0x03, 0xd6, 0x1c, 0xab, 0x90, 0x9b, 0x0d, 0x03, 0x40, + 0x0f, 0x3d, 0x82, 0x9b, 0xb8, 0x32, 0x03, 0x99, 0xee, 0xe4, 0xa9, 0x63, 0x35, 0xec, 0xe2, 0x29, + 0xb3, 0xda, 0x4c, 0x3c, 0x4c, 0x6b, 0x4f, 0x67, 0x0a, 0x8e, 0x35, 0xe2, 0xe5, 0x3d, 0x97, 0xe6, + 0xec, 0xb2, 0x99, 0xa2, 0x7d, 0x1c, 0xf0, 0xb0, 0x6d, 0xc7, 0x14, 0xe6, 0xb1, 0x77, 0x75, 0x2d, + 0x73, 0x0e, 0xa6, 0x3b, 0x4b, 0x90, 0xac, 0xe4, 0x34, 0x60, 0x6d, 0x03, 0xba, 0xb2, 0xb4, 0xb8, + 0x11, 0xe1, 0x1c, 0xb2, 0x27, 0xbb, 0x39, 0x61, 0x47, 0x53, 0x3e, 0xb0, 0x5b, 0x0d, 0x5a, 0xc9, + 0x36, 0xd5, 0xd3, 0x99, 0xa8, 0xdb, 0x74, 0x32, 0x5b, 0x05, 0x95, 0x0d, 0x2f, 0x19, 0xa7, 0x99, + 0x2e, 0x57, 0x19, 0xde, 0x4b, 0xb9, 0x08, 0x57, 0xdc, 0x37, 0x15, 0x7d, 0x1f, 0xe5, 0x12, 0x33, + 0x91, 0xa1, 0xdd, 0xe3, 0x30, 0x2b, 0x84, 0x4c, 0xe9, 0xbd, 0x0c, 0x8b, 0x6d, 0xe4, 0x75, 0x12, + 0x41, 0x98, 0x4b, 0xab, 0x25, 0xda, 0x4b, 0xc0, 0x92, 0xb1, 0x4a, 0x32, 0x06, 0x5a, 0xac, 0x93, + 0xf2, 0x6f, 0x01, 0x4d, 0xc1, 0xac, 0xf2, 0x37, 0x8b, 0x4e, 0x03, 0xbb, 0xd0, 0x06, 0x4f, 0xfa, + 0x7d, 0xb5, 0xdc, 0x1e, 0xf3, 0x2d, 0x05, 0x29, 0x1b, 0x7e, 0x6e, 0x0e, 0x26, 0x3c, 0x11, 0x5c, + 0xaf, 0x97, 0x55, 0x5a, 0xc9, 0x4f, 0x75, 0xb5, 0x24, 0x72, 0xa7, 0x07, 0x0b, 0x02, 0xd9, 0xa2, + 0xa3, 0xb0, 0xde, 0x30, 0x22, 0xfc, 0x54, 0x14, 0xfa, 0x7b, 0x19, 0x58, 0x19, 0x59, 0x9f, 0x7d, + 0x8a, 0x63, 0xd7, 0x27, 0x66, 0xe0, 0x52, 0x05, 0x3d, 0x4e, 0x14, 0xad, 0xf5, 0xaa, 0xc5, 0x79, + 0x1a, 0x02, 0x78, 0x03, 0x2c, 0xe4, 0x86, 0x14, 0x51, 0xbc, 0xc7, 0x31, 0x58, 0x04, 0x39, 0xe9, + 0xac, 0xae, 0xaf, 0x74, 0x97, 0xef, 0x9a, 0x5a, 0x97, 0x7c, 0xf5, 0xcd, 0xa1, 0x19, 0xd6, 0x31, + 0xc2, 0x0b, 0x5e, 0x66, 0x9a, 0x7f, 0x96, 0x09, 0x59, 0xc5, 0x17, 0x5c, 0x39, 0x66, 0xa7, 0x29, + 0x31, 0xb9, 0x6d, 0xad, 0x4e, 0xc1, 0x90, 0xb9, 0x94, 0x02, 0xc3, 0xe0, 0x69, 0xb8, 0x44, 0x6b, + 0xa3, 0xa6, 0xab, 0x7e, 0xfe, 0xfa, 0x8f, 0x89, 0x60, 0xf5, 0x7e, 0x68, 0x62, 0xcf, 0x58, 0xa2, + 0x96, 0xca, 0xe5, 0x36, 0x23, 0xd9, 0x88, 0x6e, 0x8b, 0x17, 0x7e, 0xf3, 0xe5, 0xd7, 0x63, 0xb0, + 0x96, 0x82, 0x18, 0xf3, 0x10, 0x13, 0x09, 0xce, 0x7b, 0xe3, 0xd8, 0xb6, 0x5b, 0x6f, 0x0e, 0x13, + 0x1b, 0xd3, 0x49, 0x57, 0x8b, 0xa1, 0xf8, 0xbd, 0xda, 0xed, 0x0d, 0x9c, 0x6c, 0x32, 0x61, 0x03, + 0xdd, 0x39, 0x63, 0x9c, 0x6f, 0xe3, 0xe5, 0xc1, 0xd6, 0xdb, 0x26, 0x1d, 0xa2, 0x60, 0x7e, 0x7b, + 0xb2, 0x51, 0x80, 0x8f, 0x78, 0x89, 0x43, 0xaa, 0x9d, 0x76, 0xef, 0x1e, 0xdb, 0xd0, 0x51, 0xb4, + 0x8e, 0xed, 0x14, 0x42, 0xfb, 0x63, 0x5d, 0x21, 0x99, 0x30, 0x99, 0x05, 0x53, 0x25, 0xe8, 0xec, + 0xe7, 0xf1, 0x40, 0x8c, 0xba, 0xb6, 0x6a, 0xe0, 0xc7, 0x73, 0x68, 0x6c, 0x15, 0x4b, 0x42, 0xe3, + 0xda, 0x97, 0x0e, 0x40, 0xc5, 0xff, 0x98, 0x2b, 0xf2, 0x19, 0xca, 0x3d, 0x49, 0x72, 0x7a, 0x96, + 0xd5, 0x16, 0xd4, 0x2a, 0xf4, 0x5d, 0x1d, 0xd2, 0x1d, 0x22, 0x60, 0xe0, 0x18, 0xf7, 0x38, 0x04, + 0x01, 0x94, 0x45, 0xb2, 0x5f, 0xc0, 0x10, 0xce, 0x65, 0x62, 0x33, 0x61, 0x6d, 0x65, 0x15, 0x7d, + 0x39, 0xc0, 0xcf, 0xcb, 0x4c, 0xa2, 0x23, 0x1c, 0xa8, 0x42, 0x46, 0x99, 0x81, 0xdc, 0x28, 0xad, + 0xd6, 0xcc, 0x92, 0xdd, 0x06, 0x58, 0x02, 0x0c, 0xe1, 0x67, 0xf2, 0x4f, 0x7a, 0xe3, 0xde, 0xb9, + 0x65, 0x81, 0xb4, 0x59, 0x13, 0xfc, 0xa4, 0x12, 0x13, 0x41, 0x9f, 0xd2, 0xe2, 0x9e, 0xea, 0x87, + 0x6a, 0x95, 0xba, 0xae, 0x27, 0x4d, 0x12, 0xc4, 0x58, 0x2a, 0x4c, 0x18, 0x68, 0x6b, 0x3b, 0xd6, + 0xfa, 0xc5, 0x47, 0x0c, 0xfe, 0x40, 0x66, 0x5b, 0x0e, 0xe9, 0xe9, 0x01, 0x4f, 0x1c, 0x48, 0x52, + 0x19, 0xc8, 0x9d, 0xa3, 0xbe, 0x81, 0x04, 0xa1, 0xd8, 0x4e, 0x6f, 0x6e, 0x80, 0xcd, 0xf7, 0x38, + 0x63, 0x16, 0x1f, 0x27, 0x4e, 0x5e, 0x4a, 0xcb, 0xc8, 0x15, 0x62, 0x15, 0x9e, 0x4e, 0xbc, 0xfc, + 0xa3, 0x26, 0x85, 0x54, 0xe0, 0x56, 0xb9, 0xef, 0xc0, 0x6c, 0x42, 0x87, 0x35, 0x9a, 0x28, 0xc1, + 0x90, 0x2c, 0xa2, 0xc6, 0xbe, 0x9e, 0xbb, 0x11, 0x1b, 0x88, 0xa7, 0xea, 0x03, 0x82, 0x8b, 0xf5, + 0xb5, 0x72, 0x76, 0xb8, 0x30, 0x2d, 0x07, 0xb3, 0x2f, 0x35, 0x48, 0xf5, 0xc5, 0x6c, 0xd3, 0x30, + 0x98, 0x55, 0xe1, 0x68, 0x0d, 0x47, 0xcf, 0xbe, 0x29, 0x8f, 0xcf, 0x48, 0x7b, 0x79, 0x2d, 0xd8, + 0xbf, 0x52, 0xce, 0x1d, 0xdc, 0xb2, 0x22, 0xe6, 0x86, 0x30, 0x98, 0x50, 0xf4, 0x56, 0x53, 0x49, + 0x38, 0x5f, 0x6d, 0xce, 0xe9, 0x35, 0x2f, 0xcf, 0x44, 0x8a, 0x96, 0xe5, 0x0a, 0xd8, 0x62, 0x64, + 0x1b, 0xfe, 0x38, 0x83, 0x36, 0x60, 0xd4, 0x31, 0xa4, 0x63, 0x4a, 0xd7, 0xa0, 0x9b, 0x1a, 0xca, + 0x84, 0xaa, 0x30, 0xba, 0xea, 0xcc, 0x48, 0xb2, 0xb0, 0x51, 0xc5, 0x0e, 0xd6, 0x3b, 0x1b, 0xab, + 0x20, 0x4a, 0xd0, 0xd9, 0xc8, 0xf3, 0x19, 0x63, 0x07, 0x10, 0x7c, 0xcd, 0xa6, 0x5a, 0xa5, 0xcd, + 0x3b, 0x8e, 0x09, 0x7f, 0xf0, 0x54, 0xba, 0xc8, 0x2a, 0xbb, 0x1d, 0x0d, 0x76, 0xdd, 0xb1, 0x47, + 0xed, 0xb9, 0x41, 0x25, 0x43, 0x97, 0x2d, 0x27, 0xc0, 0x89, 0x81, 0x69, 0xfb, 0x02, 0x21, 0xd3, + 0xc6, 0x59, 0x65, 0x11, 0x42, 0xbc, 0x88, 0x98, 0xa0, 0x0a, 0xa1, 0xdc, 0xb1, 0xb7, 0x03, 0xc1, + 0x64, 0x8f, 0x95, 0xb3, 0x37, 0x67, 0x87, 0x5f, 0xe5, 0xf1, 0x93, 0x27, 0xe7, 0x12, 0x62, 0x5e, + 0x87, 0x52, 0x4b, 0x44, 0x5f, 0xe5, 0x45, 0x58, 0xc0, 0xfb, 0x06, 0xbc, 0x8a, 0xe0, 0xd4, 0xe5, + 0x9f, 0xf3, 0x2c, 0x20, 0x30, 0x64, 0xfe, 0x46, 0xf5, 0xf3, 0xda, 0x53, 0x42, 0x8f, 0x99, 0x7d, + 0xfb, 0x47, 0xb7, 0x4a, 0xbe, 0x66, 0xf6, 0xe1, 0x2e, 0x62, 0x87, 0x26, 0xce, 0x02, 0x9a, 0xe2, + 0xeb, 0xca, 0x64, 0xc4, 0xaa, 0x0d, 0xbc, 0x9b, 0x1e, 0x32, 0xc3, 0xf8, 0xec, 0xaf, 0x1b, 0xdb, + 0xff, 0x0c, 0x71, 0x69, 0xc6, 0xb2, 0xc0, 0x3a, 0x66, 0xee, 0xd2, 0xc9, 0x16, 0x4d, 0xaf, 0x24, + 0xfa, 0x65, 0x18, 0x75, 0xfb, 0x45, 0x25, 0xa6, 0x26, 0xfb, 0x66, 0x2a, 0x15, 0x9c, 0x85, 0xf4, + 0xda, 0x1d, 0x4f, 0x3b, 0xe2, 0x4d, 0x23, 0xeb, 0x8c, 0x57, 0xb1, 0xb5, 0x4c, 0x90, 0x23, 0x7f, + 0x01, 0xfa, 0x06, 0x9d, 0x35, 0x4c, 0x6a, 0xb5, 0x79, 0xb9, 0x00, 0x17, 0x46, 0x46, 0x54, 0x45, + 0x60, 0xed, 0xe8, 0x86, 0xc3, 0xde, 0x65, 0xdf, 0x5b, 0x40, 0x97, 0xcd, 0x77, 0x25, 0x41, 0xcd, + 0xf2, 0x89, 0xbf, 0xfe, 0x55, 0xa1, 0xf9, 0xf5, 0x7a, 0x18, 0x2d, 0x6c, 0xec, 0x28, 0xed, 0xd1, + 0x44, 0xa1, 0x98, 0x77, 0x73, 0x6f, 0xc0, 0x4c, 0x7f, 0x05, 0xc0, 0x9c, 0x19, 0xed, 0x90, 0x3c, + 0x56, 0xb6, 0xf4, 0x86, 0x2f, 0x60, 0xb3, 0x1b, 0x95, 0x58, 0x26, 0x40, 0xc2, 0xc0, 0xda, 0x24, + 0x26, 0xcb, 0x06, 0x26, 0x53, 0x30, 0x12, 0x35, 0x0b, 0xe5, 0x39, 0x80, 0x3a, 0xf7, 0xd5, 0xae, + 0xb8, 0xcb, 0xea, 0xf6, 0x4c, 0x3d, 0x81, 0xb4, 0x1f, 0x97, 0x88, 0x2c, 0x9f, 0x9c, 0x70, 0x70, + 0xd5, 0x34, 0xe8, 0x65, 0xb7, 0xdb, 0xb3, 0x33, 0x26, 0xcf, 0x95, 0xa3, 0x31, 0x6a, 0x90, 0x9f, + 0xde, 0xd3, 0x17, 0x8a, 0x24, 0xdc, 0xe1, 0x57, 0xb8, 0x28, 0x70, 0x79, 0x52, 0xd7, 0x9d, 0x6b, + 0x99, 0xbb, 0xf7, 0x14, 0x5e, 0xc0, 0x05, 0xff, 0xbc, 0x82, 0xc2, 0x20, 0x57, 0x6c, 0xbe, 0xbe, + 0x71, 0xf7, 0xdb, 0x24, 0xc3, 0x33, 0xe6, 0x26, 0x50, 0x9a, 0xfc, 0xd6, 0x35, 0xb7, 0x42, 0xc4, + 0x63, 0x22, 0x3d, 0x4c, 0x52, 0x1b, 0xf3, 0x6a, 0x38, 0x5c, 0x9b, 0xf5, 0x9b, 0xfb, 0x5f, 0x0b, + 0x37, 0x9a, 0xf2, 0x25, 0xd3, 0xd9, 0xa7, 0xc5, 0x02, 0x0f, 0x86, 0xaa, 0xee, 0x71, 0x49, 0xa2, + 0x22, 0xc2, 0x9d, 0x92, 0xc2, 0x3e, 0x8f, 0x26, 0x7f, 0x5c, 0x23, 0x62, 0xe4, 0xb5, 0xf5, 0x9e, + 0xea, 0x2f, 0xbc, 0xe8, 0x4b, 0x4d, 0xd1, 0xbd, 0x2e, 0x39, 0x04, 0x56, 0xfc, 0x0f, 0xd3, 0xd6, + 0x10, 0x16, 0xe5, 0x02, 0x11, 0x5c, 0xbc, 0x66, 0x90, 0xf1, 0xb7, 0xf6, 0x4f, 0x56, 0x0f, 0x87, + 0x2c, 0xa8, 0xb6, 0xa9, 0x30, 0xf6, 0x17, 0x1b, 0xda, 0x2c, 0x2a, 0x75, 0x09, 0xcc, 0x32, 0xe2, + 0x77, 0xc8, 0xd8, 0x98, 0x7b, 0xd4, 0x8a, 0x73, 0xda, 0xe2, 0x76, 0x78, 0x04, 0x82, 0xab, 0x11, + 0x71, 0xe3, 0x73, 0x79, 0x5e, 0xdb, 0x86, 0x79, 0x52, 0x1d, 0x28, 0xc1, 0x87, 0xa8, 0x2f, 0xca, + 0xbc, 0xb9, 0xba, 0x58, 0x4e, 0xb3, 0x89, 0x4d, 0x74, 0x88, 0x37, 0x36, 0xbb, 0x1d, 0x1d, 0xe8, + 0xc9, 0xd4, 0xe8, 0xa4, 0x31, 0x17, 0x56, 0xf4, 0x72, 0xc0, 0x00, 0x58, 0x1c, 0x76, 0xab, 0x2d, + 0x75, 0xe6, 0x90, 0x85, 0xbf, 0xdc, 0x57, 0x2f, 0xbe, 0xb1, 0xa9, 0x3c, 0xd1, 0xf6, 0x58, 0x80, + 0xcf, 0x43, 0x60, 0xb4, 0x1b, 0x82, 0x02, 0x90, 0x3c, 0xad, 0xfc, 0xef, 0xc4, 0xce, 0x9a, 0xec, + 0xea, 0x26, 0x57, 0xb9, 0x7e, 0xf7, 0x69, 0xa3, 0x06, 0x52, 0xc2, 0x51, 0x83, 0x8f, 0xf4, 0x0d, + 0xb7, 0xa3, 0xfc, 0xae, 0x29, 0x81, 0xcc, 0xdc, 0xbb, 0x2d, 0xed, 0x38, 0xf4, 0x40, 0x7a, 0x16, + 0x56, 0x11, 0x7f, 0x33, 0x50, 0x44, 0xc0, 0xb9, 0x77, 0x66, 0x6b, 0xce, 0x18, 0xd6, 0xdf, 0x39, + 0x9e, 0xa6, 0xa1, 0xbd, 0xed, 0x0e, 0xd7, 0x14, 0xe1, 0xb9, 0x6a, 0x51, 0x4b, 0x5c, 0x00, 0xdc, + 0xd7, 0xdb, 0x76, 0x69, 0xef, 0xfa, 0x2e, 0xcf, 0x4f, 0xa8, 0x2e, 0x59, 0x26, 0xa0, 0x55, 0x7e, + 0x9b, 0x55, 0xfc, 0x7c, 0x32, 0xff, 0xbf, 0x08, 0x90, 0xb5, 0x7a, 0x4f, 0xf2, 0x26, 0x05, 0x85, + 0xf6, 0x56, 0x7b, 0x56, 0x41, 0xdc, 0x95, 0xcf, 0x03, 0x45, 0xef, 0x78, 0x34, 0x30, 0x61, 0x9a, + 0x44, 0x8f, 0xbc, 0xb5, 0x5c, 0xf4, 0x64, 0x2c, 0x13, 0x21, 0x3c, 0xa5, 0x1b, 0xe7, 0x9c, 0x60, + 0x9b, 0x49, 0xe1, 0x34, 0x8a, 0xef, 0x72, 0xa7, 0xa9, 0x71, 0x6f, 0x32, 0x52, 0x64, 0x0a, 0xa0, + 0x4a, 0xf3, 0xa2, 0xee, 0x16, 0x1a, 0xbe, 0x93, 0x0a, 0xa2, 0xd8, 0xb0, 0x82, 0x3b, 0x4d, 0x7d, + 0x24, 0xe2, 0x09, 0xef, 0x6b, 0xdd, 0x92, 0x00, 0x6f, 0x76, 0xd1, 0x41, 0xde, 0xdb, 0xab, 0x6c, + 0x39, 0x4d, 0x26, 0x54, 0xb4, 0x56, 0xce, 0x4c, 0x19, 0x95, 0x2a, 0x51, 0xa9, 0xf1, 0xad, 0x58, + 0xf8, 0x6d, 0xeb, 0x32, 0xc2, 0x30, 0x03, 0x15, 0x0e, 0x9d, 0x76, 0x74, 0x5d, 0xe0, 0x62, 0xd9, + 0xc3, 0x83, 0xe4, 0xd5, 0x33, 0x1d, 0x7b, 0xfe, 0x6f, 0x16, 0x5c, 0xaf, 0x34, 0xe9, 0x2c, 0x20, + 0xca, 0x99, 0xac, 0x57, 0x47, 0x81, 0x0d, 0x57, 0x65, 0x33, 0x1a, 0x5b, 0x0c, 0xaa, 0x13, 0x00, + 0xf9, 0x3d, 0x02, 0x2c, 0xc1, 0x23, 0x7d, 0x55, 0x47, 0xdb, 0x8b, 0xcb, 0x50, 0xc1, 0xd2, 0x95, + 0xff, 0x0b, 0x8a, 0xb3, 0x38, 0x43, 0x51, 0xe1, 0x3b, 0x3b, 0xc3, 0x4b, 0x5e, 0xc9, 0xac, 0x0f, + 0xb3, 0x81, 0x32, 0xf1, 0x2e, 0xa2, 0x51, 0xea, 0xb2, 0x85, 0x1a, 0x48, 0xee, 0x35, 0xa0, 0x86, + 0x05, 0x14, 0x05, 0xc3, 0xf5, 0xe2, 0xa1, 0xdf, 0x47, 0xb0, 0xe2, 0x04, 0x21, 0x85, 0xbd, 0x0b, + 0x00, 0x98, 0xe4, 0xdb, 0xe1, 0x7a, 0xf7, 0xfd, 0x7a, 0x92, 0x45, 0x15, 0x57, 0xd7, 0xcf, 0x18, + 0xb7, 0xa3, 0xf2, 0xd5, 0xcf, 0x18, 0x7a, 0xe5, 0xa4, 0x4b, 0x0c, 0xe1, 0x66, 0xd9, 0x1b, 0x3f, + 0x60, 0x47, 0x90, 0xd1, 0xe9, 0xb8, 0xf5, 0xa8, 0x36, 0x1f, 0xab, 0x7d, 0x85, 0x9a, 0x57, 0xc6, + 0xdb, 0x9c, 0xf8, 0x90, 0xbf, 0xc7, 0xb7, 0xf5, 0xa3, 0x1d, 0x69, 0x6d, 0xe8, 0xda, 0x3d, 0xa6, + 0xaa, 0x38, 0x55, 0x30, 0x9d, 0x7a, 0xc0, 0x6d, 0x42, 0xeb, 0xc6, 0x97, 0xf3, 0x84, 0xa1, 0x0a, + 0x4c, 0x78, 0xc3, 0xdb, 0x9f, 0x29, 0x85, 0x08, 0x4d, 0x8f, 0xbe, 0x8e, 0xba, 0x68, 0x1a, 0x97, + 0x74, 0xa7, 0xd2, 0xec, 0xb9, 0x8b, 0xbf, 0x8c, 0xd7, 0xb6, 0x2e, 0x3d, 0x3b, 0x7d, 0xf8, 0x84, + 0x2b, 0x30, 0xe6, 0x40, 0x13, 0x53, 0xbe, 0x47, 0xd9, 0x14, 0x16, 0xdf, 0x05, 0xeb, 0xa3, 0x6c, + 0x74, 0xd4, 0xba, 0x8d, 0xf5, 0x80, 0xab, 0xc8, 0x40, 0x3c, 0xd5, 0x00, 0x47, 0x86, 0x66, 0x73, + 0x6b, 0x36, 0xc5, 0xa2, 0x81, 0x92, 0xd8, 0xfd, 0xde, 0x61, 0x34, 0xc3, 0x34, 0xaa, 0x1a, 0x40, + 0x3c, 0x95, 0x8f, 0x75, 0x2f, 0xc6, 0x22, 0xbb, 0x45, 0xfa, 0x68, 0x10, 0xd7, 0x22, 0x09, 0x59, + 0x36, 0x25, 0x45, 0x06, 0x9b, 0xa2, 0x33, 0xf9, 0x34, 0x63, 0xe2, 0x2b, 0x18, 0xa7, 0xbe, 0x25, + 0xf2, 0xe6, 0x9d, 0x99, 0x97, 0xb1, 0x0d, 0x64, 0x3a, 0x53, 0xcb, 0xe5, 0x73, 0xc6, 0x47, 0x3c, + 0x76, 0x87, 0xae, 0x74, 0x1f, 0x4f, 0x84, 0x2e, 0x4f, 0x10, 0xda, 0x4e, 0x32, 0x40, 0x71, 0xc4, + 0xd9, 0xac, 0x85, 0x4c, 0x6e, 0x10, 0x37, 0x66, 0xcd, 0x49, 0x83, 0x20, 0xa7, 0xe7, 0x47, 0x70, + 0xaf, 0x38, 0x6c, 0x95, 0x32, 0x6e, 0x7f, 0x21, 0x9e, 0x2b, 0xbd, 0x09, 0x6a, 0xe0, 0xd9, 0xdf, + 0x27, 0x1e, 0x41, 0x0b, 0x1a, 0xc3, 0x6c, 0x83, 0x9f, 0x1a, 0x57, 0x5e, 0x94, 0x72, 0xc5, 0x8d, + 0x9f, 0x61, 0xe4, 0x47, 0x56, 0xb1, 0x80, 0x32, 0x3c, 0x23, 0x4c, 0x21, 0x0d, 0xdd, 0x4e, 0x5f, + 0x61, 0x8a, 0xcf, 0xee, 0x59, 0x87, 0x36, 0xe4, 0x0a, 0x24, 0x7c, 0x03, 0xda, 0x64, 0x76, 0x3c, + 0x80, 0x04, 0x3c, 0x89, 0x91, 0x9e, 0x56, 0xba, 0x66, 0x98, 0xb2, 0xfc, 0x8d, 0x81, 0xdf, 0xf4, + 0x3c, 0x0c, 0x0c, 0x03, 0xee, 0xd9, 0xb4, 0xb0, 0x0a, 0xcf, 0x6d, 0x0b, 0xf6, 0xe7, 0xa6, 0x21, + 0x1c, 0xe7, 0x9f, 0xa5, 0x74, 0xea, 0x18, 0x3f, 0xf7, 0x2c, 0x3c, 0x09, 0x53, 0xd6, 0xcc, 0x71, + 0xd7, 0x07, 0x9d, 0x3d, 0x59, 0xb4, 0xec, 0x86, 0xe9, 0x8b, 0xa0, 0x14, 0x99, 0xf8, 0xa6, 0x9b, + 0x59, 0xe2, 0x6e, 0x73, 0x78, 0xe0, 0xf3, 0xcb, 0xce, 0x06, 0xd0, 0x1b, 0x70, 0xd8, 0x15, 0xc2, + 0xbf, 0x04, 0xe8, 0xcb, 0x31, 0x1d, 0x04, 0x9f, 0x9d, 0xf2, 0xa1, 0x60, 0x1f, 0x63, 0x49, 0x64, + 0x56, 0x3e, 0xa1, 0x64, 0xf2, 0xb0, 0xaa, 0xdc, 0x5f, 0xa3, 0x3b, 0x8d, 0x16, 0x07, 0xa1, 0xf3, + 0xec, 0xc5, 0x7f, 0xe2, 0x1c, 0xeb, 0xb7, 0x81, 0xd3, 0xdf, 0x5f, 0xee, 0xa0, 0xe1, 0x82, 0x25, + 0x7a, 0xe2, 0x3f, 0xce, 0x3b, 0x89, 0x1f, 0xbe, 0x73, 0x9e, 0xe4, 0x46, 0x11, 0x39, 0xfc, 0x6b, + 0xe6, 0x99, 0xd6, 0x98, 0x9f, 0x8f, 0x19, 0x41, 0x90, 0x5d, 0xf1, 0x85, 0x94, 0xe7, 0x13, 0x91, + 0xe3, 0x01, 0xfd, 0x41, 0x29, 0x1d, 0xcb, 0x11, 0x13, 0xcd, 0x4c, 0x92, 0x6c, 0x15, 0x7c, 0xd1, + 0xbc, 0x50, 0x68, 0x4c, 0x46, 0xe3, 0x0f, 0x25, 0xd5, 0x6c, 0x3b, 0x53, 0x0b, 0x2f, 0x1d, 0xd2, + 0x52, 0xca, 0x97, 0x29, 0x5c, 0xdf, 0x24, 0xa8, 0xc6, 0xbe, 0xd1, 0xc1, 0x14, 0x20, 0x24, 0x4f, + 0xfa, 0xd4, 0xe5, 0xb0, 0x93, 0x45, 0xe1, 0xc9, 0xf2, 0xbe, 0x0d, 0xc2, 0xd1, 0x4c, 0xab, 0x2f, + 0x85, 0xc1, 0x0b, 0x51, 0x40, 0x7c, 0xdf, 0x7f, 0x74, 0xa1, 0x3d, 0xb8, 0x4d, 0x4f, 0x26, 0x24, + 0x11, 0x61, 0x64, 0xd3, 0x0e, 0x5b, 0x29, 0x87, 0x05, 0xfe, 0xb9, 0x78, 0xde, 0xad, 0xf9, 0xe8, + 0xc4, 0x47, 0x4d, 0xfb, 0xa8, 0x54, 0x99, 0x50, 0x1d, 0xe7, 0xaf, 0x1a, 0x12, 0x20, 0x8d, 0xcd, + 0xde, 0x8e, 0xf8, 0x26, 0x65, 0x8b, + }; + unsigned char commit_0[] = { + 0x09, 0x9e, 0x56, 0x8d, 0x5b, 0x9d, 0x2a, 0xd6, 0x1f, 0xe0, 0x81, 0x21, 0xcc, 0x15, 0xb3, 0x66, + 0x6d, 0xb4, 0xbb, 0xac, 0xdd, 0x28, 0x08, 0xab, 0x21, 0x6e, 0x35, 0xac, 0xa7, 0xe0, 0x0a, 0xa8, + 0xef, + }; + + test_rangeproof_fixed_vectors_reproducible_helper(vector_0, sizeof(vector_0), commit_0, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); + CHECK(value_r == value); + CHECK(m_len_r == m_len); + CHECK(secp256k1_memcmp_var(message_r, message, m_len_r) == 0); + CHECK(min_value_r == min_value); + CHECK(max_value_r == UINT64_MAX); + memset(message_r, 0, sizeof(message_r)); + } + + /* Test min_bits = 3 */ + { + uint64_t value = 13; + size_t m_len = 128; /* maximum message length with min_bits = 3 */ + + /* Uncomment this to recreate test vector */ + /* uint64_t min_value = 1; */ + /* int min_bits = 3; */ + /* int exp = 1; */ + /* unsigned char proof[267]; */ + /* size_t p_len = sizeof(proof); */ + /* secp256k1_pedersen_commitment pc; */ + /* CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); */ + /* CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); */ + /* CHECK(p_len == sizeof(proof)); */ + /* print_vector(1, proof, p_len, &pc); */ + + unsigned char vector_1[] = { + 0x61, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0xcb, 0xdc, 0xbe, 0x42, 0xe6, + 0x44, 0x1e, 0xc4, 0x63, 0x9d, 0xb1, 0x93, 0x7b, 0x49, 0xdc, 0xd5, 0x6e, 0x55, 0xdd, 0x3b, 0x1e, + 0x41, 0x1c, 0x0e, 0xd7, 0x47, 0xd7, 0xf0, 0x26, 0xf7, 0xe4, 0x36, 0xbd, 0x51, 0xb9, 0x77, 0x90, + 0x33, 0xdd, 0x64, 0xe7, 0x47, 0x38, 0x49, 0x29, 0x12, 0xa8, 0x12, 0x79, 0xbc, 0x62, 0xea, 0xf9, + 0xb5, 0x51, 0x8f, 0x51, 0xea, 0x28, 0x5d, 0x30, 0x9f, 0x30, 0xd5, 0x93, 0x31, 0x56, 0x61, 0x01, + 0xd7, 0x7f, 0xa4, 0xec, 0xfc, 0xe5, 0x83, 0x52, 0x5a, 0xe0, 0x80, 0x76, 0x40, 0xb8, 0x8d, 0x67, + 0x23, 0x46, 0x8c, 0xb8, 0x74, 0x2a, 0x20, 0x12, 0x86, 0x4d, 0xd8, 0x8c, 0x23, 0x73, 0x2f, 0xbe, + 0x99, 0xa5, 0xd5, 0x8c, 0x11, 0xc7, 0xb2, 0xf9, 0xd3, 0x7c, 0x88, 0x16, 0x4d, 0x21, 0x80, 0x10, + 0x70, 0xfc, 0x1f, 0x9b, 0x0b, 0x5e, 0xbe, 0xe3, 0x65, 0xe2, 0x4f, 0xbd, 0x1d, 0xb0, 0x64, 0x0a, + 0xc5, 0xe0, 0x94, 0x8b, 0x49, 0xf7, 0xc4, 0x88, 0x5e, 0xc0, 0x2d, 0xbb, 0x98, 0x60, 0x5f, 0xd2, + 0x7a, 0x9a, 0xff, 0x9e, 0x1c, 0x1f, 0x45, 0x34, 0x08, 0x96, 0xa9, 0xd3, 0xa5, 0x4d, 0x95, 0x9c, + 0x1f, 0xe6, 0xe5, 0xdc, 0x32, 0xbb, 0x18, 0x4a, 0x76, 0x22, 0xe9, 0x75, 0x1f, 0x45, 0x6b, 0x81, + 0x70, 0x4a, 0xc8, 0x00, 0x72, 0x7a, 0xc8, 0xee, 0xed, 0xc5, 0x19, 0x8f, 0xec, 0x7b, 0x4b, 0xfd, + 0x7f, 0xc8, 0x51, 0xda, 0x28, 0x0e, 0x95, 0xd3, 0xc6, 0xc1, 0x29, 0x28, 0x3f, 0xd8, 0x3d, 0x41, + 0xde, 0xdf, 0xfc, 0x2b, 0x71, 0x3a, 0xdb, 0x78, 0xa4, 0x0e, 0x50, 0xb8, 0xf9, 0xae, 0xdb, 0x7b, + 0xb1, 0x31, 0x81, 0xc2, 0xf2, 0xb5, 0x01, 0x64, 0x8e, 0x86, 0xe2, 0x8b, 0x67, 0x13, 0xec, 0x7e, + 0xf5, 0xad, 0x9d, 0x57, 0x2b, 0x5d, 0x0c, 0x94, 0xa9, 0x89, 0x92, + }; + unsigned char commit_1[] = { + 0x09, 0xe5, 0xb3, 0x27, 0x82, 0x88, 0xeb, 0x21, 0xcd, 0xb2, 0x56, 0x37, 0x61, 0x84, 0xce, 0xc1, + 0x66, 0x16, 0x2e, 0x44, 0xc8, 0x65, 0x8e, 0xe6, 0x3a, 0x1a, 0x57, 0x2c, 0xb9, 0x6c, 0x07, 0x85, + 0xf0, + }; + + test_rangeproof_fixed_vectors_reproducible_helper(vector_1, sizeof(vector_1), commit_1, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); + CHECK(value_r == value); + CHECK(m_len_r == m_len); + CHECK(secp256k1_memcmp_var(message_r, message, m_len_r) == 0); + CHECK(min_value_r == 3); + CHECK(max_value_r == 73); + memset(message_r, 0, sizeof(message_r)); + } + + /* Test large min_value */ + { + uint64_t value = INT64_MAX; + size_t m_len = 0; /* maximum message length with min_bits = 3 */ + + /* Uncomment this to recreate test vector */ + /* uint64_t min_value = INT64_MAX-1; */ + /* int min_bits = 1; */ + /* int exp = 0; */ + /* unsigned char proof[106]; */ + /* size_t p_len = sizeof(proof); */ + /* secp256k1_pedersen_commitment pc; */ + /* CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); */ + /* CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); */ + /* CHECK(p_len == sizeof(proof)); */ + /* print_vector(2, proof, p_len, &pc); */ + + unsigned char vector_2[] = { + 0x60, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x81, 0xd8, 0x21, 0x12, 0x4d, 0xa4, + 0x84, 0xdd, 0x2c, 0xd1, 0x04, 0xe7, 0x08, 0x9a, 0xd3, 0x6f, 0xa5, 0xd8, 0xfc, 0x52, 0x4c, 0xba, + 0xf0, 0x83, 0xeb, 0x76, 0x9f, 0x1c, 0x03, 0xe3, 0xcf, 0x23, 0x1e, 0x40, 0x18, 0xc6, 0x6d, 0xf9, + 0x25, 0x56, 0x80, 0x3c, 0x83, 0xdd, 0x58, 0x36, 0x43, 0xe3, 0x56, 0xa0, 0xb7, 0xf0, 0x0e, 0xf9, + 0xe2, 0x8b, 0x82, 0x5a, 0x77, 0xa7, 0xbe, 0x36, 0x98, 0x10, 0x99, 0x2e, 0xaa, 0x21, 0x24, 0xe6, + 0x78, 0xa8, 0xcc, 0xc7, 0x06, 0x1c, 0x06, 0xb0, 0x03, 0x87, 0x86, 0x89, 0xce, 0x85, 0x88, 0xea, + 0xa1, 0x9d, 0x4d, 0xfd, 0x8d, 0x65, 0xbd, 0xa9, 0xd0, 0x0f, + }; + unsigned char commit_2[] = { + 0x09, 0x2a, 0x74, 0xa1, 0x9c, 0xee, 0xcb, 0x6a, 0xd1, 0xa7, 0x97, 0xbe, 0x97, 0xe7, 0xb6, 0x37, + 0x90, 0x96, 0xc2, 0x5a, 0xe5, 0xfc, 0xed, 0x91, 0xff, 0x4c, 0x67, 0x07, 0x96, 0x1d, 0x2a, 0xb3, + 0x70, + }; + + test_rangeproof_fixed_vectors_reproducible_helper(vector_2, sizeof(vector_2), commit_2, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); + CHECK(value_r == value); + CHECK(m_len_r == m_len); + CHECK(secp256k1_memcmp_var(message_r, message, m_len_r) == 0); + CHECK(min_value_r == INT64_MAX-1); + CHECK(max_value_r == INT64_MAX); + memset(message_r, 0, sizeof(message_r)); + } +} + void test_pedersen_commitment_fixed_vector(void) { const unsigned char two_g[33] = { 0x09, @@ -1013,6 +1538,7 @@ void run_rangeproof_tests(void) { test_single_value_proof(UINT64_MAX); test_rangeproof_fixed_vectors(); + test_rangeproof_fixed_vectors_reproducible(); test_pedersen_commitment_fixed_vector(); for (i = 0; i < count / 2 + 1; i++) { test_pedersen(); From 6b6ced9839f2576898ba334e7ed2d550a2062b32 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 20 Aug 2022 16:06:46 +0000 Subject: [PATCH 208/381] rangeproof: add more max_size tests --- src/modules/rangeproof/tests_impl.h | 75 +++++++++++++++++------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 411a7f98..9c920734 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -655,6 +655,7 @@ static void test_single_value_proof(uint64_t val) { NULL, 0, secp256k1_generator_h ) == 1); + CHECK(plen <= secp256k1_rangeproof_max_size(ctx, val, 0)); /* Different proof sizes are unfortunate but is caused by `min_value` of * zero being special-cased and encoded more efficiently. */ @@ -1068,16 +1069,11 @@ void test_rangeproof_fixed_vectors_reproducible(void) { uint64_t min_value = 0; size_t m_len = sizeof(message); /* maximum message length */ - /* Uncomment this to recreate test vector */ - /* int min_bits = 64; */ - /* int exp = 18; */ - /* unsigned char proof[5126]; */ - /* size_t p_len = sizeof(proof); */ - /* secp256k1_pedersen_commitment pc; */ - /* CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); */ - /* CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); */ - /* CHECK(p_len == sizeof(proof)); */ - /* print_vector(0, proof, p_len, &pc); */ + int min_bits = 64; + int exp = 18; + unsigned char proof[5126]; + size_t p_len = sizeof(proof); + secp256k1_pedersen_commitment pc; unsigned char vector_0[] = { 0x40, 0x3f, 0xd1, 0x77, 0x65, 0x05, 0x87, 0x88, 0xd0, 0x3d, 0xb2, 0x24, 0x60, 0x7a, 0x08, 0x76, @@ -1408,6 +1404,15 @@ void test_rangeproof_fixed_vectors_reproducible(void) { 0xef, }; + CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); + CHECK(p_len <= secp256k1_rangeproof_max_size(ctx, value, min_bits)); + CHECK(p_len == sizeof(proof)); + /* Uncomment the next line to print the test vector */ + /* print_vector(0, proof, p_len, &pc); */ + CHECK(p_len == sizeof(vector_0)); + CHECK(secp256k1_memcmp_var(proof, vector_0, p_len) == 0); + test_rangeproof_fixed_vectors_reproducible_helper(vector_0, sizeof(vector_0), commit_0, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); CHECK(value_r == value); CHECK(m_len_r == m_len); @@ -1422,17 +1427,12 @@ void test_rangeproof_fixed_vectors_reproducible(void) { uint64_t value = 13; size_t m_len = 128; /* maximum message length with min_bits = 3 */ - /* Uncomment this to recreate test vector */ - /* uint64_t min_value = 1; */ - /* int min_bits = 3; */ - /* int exp = 1; */ - /* unsigned char proof[267]; */ - /* size_t p_len = sizeof(proof); */ - /* secp256k1_pedersen_commitment pc; */ - /* CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); */ - /* CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); */ - /* CHECK(p_len == sizeof(proof)); */ - /* print_vector(1, proof, p_len, &pc); */ + uint64_t min_value = 1; + int min_bits = 3; + int exp = 1; + unsigned char proof[267]; + size_t p_len = sizeof(proof); + secp256k1_pedersen_commitment pc; unsigned char vector_1[] = { 0x61, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0xcb, 0xdc, 0xbe, 0x42, 0xe6, @@ -1458,6 +1458,14 @@ void test_rangeproof_fixed_vectors_reproducible(void) { 0x66, 0x16, 0x2e, 0x44, 0xc8, 0x65, 0x8e, 0xe6, 0x3a, 0x1a, 0x57, 0x2c, 0xb9, 0x6c, 0x07, 0x85, 0xf0, }; + CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); + CHECK(p_len <= secp256k1_rangeproof_max_size(ctx, value, min_bits)); + CHECK(p_len == sizeof(proof)); + /* Uncomment the next line to print the test vector */ + /* print_vector(1, proof, p_len, &pc); */ + CHECK(p_len == sizeof(vector_1)); + CHECK(secp256k1_memcmp_var(proof, vector_1, p_len) == 0); test_rangeproof_fixed_vectors_reproducible_helper(vector_1, sizeof(vector_1), commit_1, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); CHECK(value_r == value); @@ -1474,16 +1482,12 @@ void test_rangeproof_fixed_vectors_reproducible(void) { size_t m_len = 0; /* maximum message length with min_bits = 3 */ /* Uncomment this to recreate test vector */ - /* uint64_t min_value = INT64_MAX-1; */ - /* int min_bits = 1; */ - /* int exp = 0; */ - /* unsigned char proof[106]; */ - /* size_t p_len = sizeof(proof); */ - /* secp256k1_pedersen_commitment pc; */ - /* CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); */ - /* CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); */ - /* CHECK(p_len == sizeof(proof)); */ - /* print_vector(2, proof, p_len, &pc); */ + uint64_t min_value = INT64_MAX-1; + int min_bits = 1; + int exp = 0; + unsigned char proof[106]; + size_t p_len = sizeof(proof); + secp256k1_pedersen_commitment pc; unsigned char vector_2[] = { 0x60, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x81, 0xd8, 0x21, 0x12, 0x4d, 0xa4, @@ -1500,6 +1504,15 @@ void test_rangeproof_fixed_vectors_reproducible(void) { 0x70, }; + CHECK(secp256k1_pedersen_commit(ctx, &pc, vector_blind, value, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &p_len, min_value, &pc, vector_blind, vector_nonce, exp, min_bits, value, message, m_len, NULL, 0, secp256k1_generator_h)); + CHECK(p_len <= secp256k1_rangeproof_max_size(ctx, value, min_bits)); + CHECK(p_len == sizeof(proof)); + /* Uncomment the next line to print the test vector */ + /* print_vector(2, proof, p_len, &pc); */ + CHECK(p_len == sizeof(vector_2)); + CHECK(secp256k1_memcmp_var(proof, vector_2, p_len) == 0); + test_rangeproof_fixed_vectors_reproducible_helper(vector_2, sizeof(vector_2), commit_2, &value_r, &min_value_r, &max_value_r, message_r, &m_len_r); CHECK(value_r == value); CHECK(m_len_r == m_len); From b7607f93f23a1a342b4fba552598e2a578f50527 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Thu, 1 Sep 2022 22:38:03 -0700 Subject: [PATCH 209/381] Fix reference to xonly_tweak_add --- src/modules/musig/musig.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index 814cdc74..b15cb235 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -32,7 +32,7 @@ Essentially, the protocol proceeds in the following steps: 1. Generate a keypair with `secp256k1_keypair_create` and obtain the xonly public key with `secp256k1_keypair_xonly_pub`. 2. Call `secp256k1_musig_pubkey_agg` with the xonly pubkeys of all participants. -3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_tweak_add`. +3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add`. 4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers. 5. Someone (not necessarily the signer) aggregates the public nonce with `secp256k1_musig_nonce_agg` and sends it to the signers. 6. Process the aggregate nonce with `secp256k1_musig_nonce_process`. From d26100cab266b08fd131503ba8e37d9bf091adbb Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Thu, 1 Sep 2022 22:39:22 -0700 Subject: [PATCH 210/381] Exclude nonce_process from pre-processing steps --- src/modules/musig/musig.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index b15cb235..e9d5332b 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -42,10 +42,10 @@ Essentially, the protocol proceeds in the following steps: The aggregate signature can be verified with `secp256k1_schnorrsig_verify`. -Note that steps 1 to 6 can happen before the message to be signed is known to the signers. +Note that steps 1 to 5 can happen before the message to be signed is known to the signers. Therefore, the communication round to exchange nonces can be viewed as a pre-processing step that is run whenever convenient to the signers. This disables some of the defense-in-depth measures that may protect against API misuse in some cases. -Similarly, the API supports an alternative protocol flow where generating the aggregate key (steps 1 to 3) is allowed to happen after exchanging nonces (steps 4 to 6). +Similarly, the API supports an alternative protocol flow where generating the aggregate key (steps 1 to 3) is allowed to happen after exchanging nonces (steps 4 to 5). # Verification From dd83e72d52da0873e0c1a64c5554efa9000a3454 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Thu, 1 Sep 2022 22:39:34 -0700 Subject: [PATCH 211/381] Add ordinary tweak info --- src/modules/musig/musig.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index e9d5332b..5c9b8d78 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -23,7 +23,7 @@ Therefore, users of the musig module must take great care to make sure of the fo # Key Aggregation and (Taproot) Tweaking Given a set of public keys, the aggregate public key is computed with `secp256k1_musig_pubkey_agg`. -A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly_pubkey_tweak_add`. +A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly_pubkey_tweak_add` and an ordinary tweak can be added with `secp256k1_ec_pubkey_tweak_add`. # Signing @@ -32,7 +32,7 @@ Essentially, the protocol proceeds in the following steps: 1. Generate a keypair with `secp256k1_keypair_create` and obtain the xonly public key with `secp256k1_keypair_xonly_pub`. 2. Call `secp256k1_musig_pubkey_agg` with the xonly pubkeys of all participants. -3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add`. +3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and an ordinary tweak with `secp256k1_musig_pubkey_ec_tweak_add`. 4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers. 5. Someone (not necessarily the signer) aggregates the public nonce with `secp256k1_musig_nonce_agg` and sends it to the signers. 6. Process the aggregate nonce with `secp256k1_musig_nonce_process`. From 3b2c675955874a077482f6c8983970194fcbc3ed Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 14 Nov 2022 17:57:38 -0500 Subject: [PATCH 212/381] Update macOS image for CI --- .cirrus.yml | 54 ++++++++++------------------------------------------- 1 file changed, 10 insertions(+), 44 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 60928eb0..d9d98d43 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -119,63 +119,29 @@ task: << : *CAT_LOGS task: - name: "x86_64: macOS Catalina" + name: "arm64: macOS Ventura" macos_instance: - image: catalina-base + image: ghcr.io/cirruslabs/macos-ventura-base:latest # tasks with valgrind enabled take about 90 minutes timeout_in: 120m env: HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 - # Cirrus gives us a fixed number of 12 virtual CPUs. Not that we even have that many jobs at the moment... - MAKEFLAGS: -j13 + # Cirrus gives us a fixed number of 4 virtual CPUs. Not that we even have that many jobs at the moment... + MAKEFLAGS: -j5 matrix: << : *ENV_MATRIX + env: + ASM: no + WITH_VALGRIND: no + CTIMETEST: no matrix: - env: - CC: gcc-9 + CC: gcc - env: CC: clang - # Update Command Line Tools - # Uncomment this if the Command Line Tools on the CirrusCI macOS image are too old to brew valgrind. - # See https://apple.stackexchange.com/a/195963 for the implementation. - ## update_clt_script: - ## - system_profiler SPSoftwareDataType - ## - touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress - ## - |- - ## PROD=$(softwareupdate -l | grep "*.*Command Line" | tail -n 1 | awk -F"*" '{print $2}' | sed -e 's/^ *//' | sed 's/Label: //g' | tr -d '\n') - ## # For debugging - ## - softwareupdate -l && echo "PROD: $PROD" - ## - softwareupdate -i "$PROD" --verbose - ## - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress - ## - brew_valgrind_pre_script: - # Retry a few times because this tends to fail randomly. - - for i in {1..5}; do brew update && break || sleep 15; done - - brew config - - brew tap LouisBrunner/valgrind - # Fetch valgrind source but don't build it yet. - - brew fetch --HEAD LouisBrunner/valgrind/valgrind - brew_valgrind_cache: - # This is $(brew --cellar valgrind) but command substition does not work here. - folder: /usr/local/Cellar/valgrind - # Rebuild cache if ... - fingerprint_script: - # ... macOS version changes: - - sw_vers - # ... brew changes: - - brew config - # ... valgrind changes: - - git -C "$(brew --cache)/valgrind--git" rev-parse HEAD - populate_script: - # If there's no hit in the cache, build and install valgrind. - - brew install --HEAD LouisBrunner/valgrind/valgrind - brew_valgrind_post_script: - # If we have restored valgrind from the cache, tell brew to create symlink to the PATH. - # If we haven't restored from cached (and just run brew install), this is a no-op. - - brew link valgrind brew_script: - - brew install automake libtool gcc@9 + - brew install automake libtool gcc << : *MERGE_BASE test_script: - ./ci/cirrus.sh From e04c660b11cb744b8fc0611117c0d6c1a3e1e3dd Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 20 Jan 2023 17:09:18 +0100 Subject: [PATCH 213/381] sync-upstream: Fix $REPRODUCE_COMMAND for "select" --- contrib/sync-upstream.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 687420d8..f4ccc449 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -68,7 +68,7 @@ case $1 in shift setup COMMITS=$* - REPRODUCE_COMMAND="$0 $@" + REPRODUCE_COMMAND="$0 select $@" ;; help) help @@ -88,7 +88,7 @@ done # Remove trailing "," TITLE=${TITLE%?} -BODY=$(printf "%s\n\n%s" "$BODY" "This PR can be recreated with \`$REPRODUCE_COMMAND\`.") +BODY=$(printf "%s\n\n%s" "$BODY" "This PR can be recreated with \`$REPRODUCE_COMMAND\`.") echo "-----------------------------------" echo "$TITLE" From d800dd55db28a710bb510a2a5fc33519d355a91c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 10 Jan 2023 14:03:28 +0000 Subject: [PATCH 214/381] musig: remove test vectors These vectors are superseded by test vectors in BIP MuSig2 which will be added in a later commit. --- src/modules/musig/tests_impl.h | 531 --------------------------------- 1 file changed, 531 deletions(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 9660227e..ddc6910f 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -911,534 +911,6 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { } } -void musig_test_vectors_keyagg_helper(const unsigned char **pk_ser, int n_pks, const unsigned char *agg_pk_expected, int has_second_pk, int second_pk_idx) { - secp256k1_xonly_pubkey *pk = malloc(n_pks * sizeof(*pk)); - const secp256k1_xonly_pubkey **pk_ptr = malloc(n_pks * sizeof(*pk_ptr)); - secp256k1_keyagg_cache_internal cache_i; - secp256k1_xonly_pubkey agg_pk; - unsigned char agg_pk_ser[32]; - secp256k1_musig_keyagg_cache keyagg_cache; - int i; - - for (i = 0; i < n_pks; i++) { - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i]) == 1); - pk_ptr[i] = &pk[i]; - } - - CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &keyagg_cache, pk_ptr, n_pks) == 1); - CHECK(secp256k1_keyagg_cache_load(ctx, &cache_i, &keyagg_cache) == 1); - CHECK(secp256k1_fe_is_zero(&cache_i.second_pk_x) == !has_second_pk); - if (!secp256k1_fe_is_zero(&cache_i.second_pk_x)) { - secp256k1_ge pk_pt; - CHECK(secp256k1_xonly_pubkey_load(ctx, &pk_pt, &pk[second_pk_idx]) == 1); - CHECK(secp256k1_fe_equal_var(&pk_pt.x, &cache_i.second_pk_x) == 1); - } - CHECK(secp256k1_xonly_pubkey_serialize(ctx, agg_pk_ser, &agg_pk) == 1); - /* TODO: remove when test vectors are not expected to change anymore */ - /* int k, l; */ - /* printf("const unsigned char agg_pk_expected[32] = {\n"); */ - /* for (k = 0; k < 4; k++) { */ - /* printf(" "); */ - /* for (l = 0; l < 8; l++) { */ - /* printf("0x%02X, ", agg_pk_ser[k*8+l]); */ - /* } */ - /* printf("\n"); */ - /* } */ - /* printf("};\n"); */ - CHECK(secp256k1_memcmp_var(agg_pk_ser, agg_pk_expected, sizeof(agg_pk_ser)) == 0); - free(pk); - free(pk_ptr); -} - -/* Test vector public keys */ -const unsigned char vec_pk[3][32] = { - /* X1 */ - { - 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, - 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, - 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, - 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 - }, - /* X2 */ - { - 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, - 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, - 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, - 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 - }, - /* X3 */ - { - 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, - 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, - 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, - 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 - } -}; - -void musig_test_vectors_keyagg(void) { - size_t i; - const unsigned char *pk[4]; - const unsigned char agg_pk_expected[4][32] = { - { /* 0 */ - 0xE5, 0x83, 0x01, 0x40, 0x51, 0x21, 0x95, 0xD7, - 0x4C, 0x83, 0x07, 0xE3, 0x96, 0x37, 0xCB, 0xE5, - 0xFB, 0x73, 0x0E, 0xBE, 0xAB, 0x80, 0xEC, 0x51, - 0x4C, 0xF8, 0x8A, 0x87, 0x7C, 0xEE, 0xEE, 0x0B, - }, - { /* 1 */ - 0xD7, 0x0C, 0xD6, 0x9A, 0x26, 0x47, 0xF7, 0x39, - 0x09, 0x73, 0xDF, 0x48, 0xCB, 0xFA, 0x2C, 0xCC, - 0x40, 0x7B, 0x8B, 0x2D, 0x60, 0xB0, 0x8C, 0x5F, - 0x16, 0x41, 0x18, 0x5C, 0x79, 0x98, 0xA2, 0x90, - }, - { /* 2 */ - 0x81, 0xA8, 0xB0, 0x93, 0x91, 0x2C, 0x9E, 0x48, - 0x14, 0x08, 0xD0, 0x97, 0x76, 0xCE, 0xFB, 0x48, - 0xAE, 0xB8, 0xB6, 0x54, 0x81, 0xB6, 0xBA, 0xAF, - 0xB3, 0xC5, 0x81, 0x01, 0x06, 0x71, 0x7B, 0xEB, - }, - { /* 3 */ - 0x2E, 0xB1, 0x88, 0x51, 0x88, 0x7E, 0x7B, 0xDC, - 0x5E, 0x83, 0x0E, 0x89, 0xB1, 0x9D, 0xDB, 0xC2, - 0x80, 0x78, 0xF1, 0xFA, 0x88, 0xAA, 0xD0, 0xAD, - 0x01, 0xCA, 0x06, 0xFE, 0x4F, 0x80, 0x21, 0x0B, - }, - }; - - for (i = 0; i < sizeof(agg_pk_expected)/sizeof(agg_pk_expected[0]); i++) { - size_t n_pks; - int has_second_pk; - int second_pk_idx; - switch (i) { - case 0: - /* [X1, X2, X3] */ - n_pks = 3; - pk[0] = vec_pk[0]; - pk[1] = vec_pk[1]; - pk[2] = vec_pk[2]; - has_second_pk = 1; - second_pk_idx = 1; - break; - case 1: - /* [X3, X2, X1] */ - n_pks = 3; - pk[2] = vec_pk[0]; - pk[1] = vec_pk[1]; - pk[0] = vec_pk[2]; - has_second_pk = 1; - second_pk_idx = 1; - break; - case 2: - /* [X1, X1, X1] */ - n_pks = 3; - pk[0] = vec_pk[0]; - pk[1] = vec_pk[0]; - pk[2] = vec_pk[0]; - has_second_pk = 0; - second_pk_idx = 0; /* unchecked */ - break; - case 3: - /* [X1, X1, X2, X2] */ - n_pks = 4; - pk[0] = vec_pk[0]; - pk[1] = vec_pk[0]; - pk[2] = vec_pk[1]; - pk[3] = vec_pk[1]; - has_second_pk = 1; - second_pk_idx = 2; /* second_pk_idx = 3 is equally valid */ - break; - default: - CHECK(0); - } - musig_test_vectors_keyagg_helper(pk, n_pks, agg_pk_expected[i], has_second_pk, second_pk_idx); - } -} - -void musig_test_vectors_noncegen(void) { - enum { N = 3 }; - secp256k1_scalar k[N][2]; - const unsigned char k32_expected[N][2][32] = { - { - { - 0x8D, 0xD0, 0x99, 0x51, 0x79, 0x50, 0x5E, 0xB1, - 0x27, 0x3A, 0x07, 0x11, 0x58, 0x23, 0xC8, 0x6E, - 0xF7, 0x14, 0x39, 0x0F, 0xDE, 0x2D, 0xEE, 0xB6, - 0xF9, 0x31, 0x6A, 0xEE, 0xBE, 0x5C, 0x71, 0xFC, - }, - { - 0x73, 0x29, 0x2E, 0x47, 0x11, 0x34, 0x7D, 0xD3, - 0x9E, 0x36, 0x05, 0xEE, 0xD6, 0x45, 0x65, 0x49, - 0xB3, 0x0F, 0x3B, 0xC7, 0x16, 0x22, 0x5A, 0x18, - 0x65, 0xBA, 0xE1, 0xD9, 0x84, 0xEF, 0xF8, 0x9D, - }, - }, - /* msg32 is NULL */ - { - { - 0x67, 0x02, 0x5A, 0xF2, 0xA3, 0x56, 0x0B, 0xFC, - 0x1D, 0x95, 0xBD, 0xA6, 0xB2, 0x0B, 0x21, 0x50, - 0x97, 0x63, 0xDB, 0x17, 0x3B, 0xD9, 0x37, 0x30, - 0x17, 0x24, 0x66, 0xEC, 0xAF, 0xA2, 0x60, 0x3B, - }, - { - 0x0B, 0x1D, 0x9E, 0x8F, 0x43, 0xBD, 0xAE, 0x69, - 0x99, 0x6E, 0x0E, 0x3A, 0xBC, 0x30, 0x06, 0x4C, - 0x52, 0x37, 0x3E, 0x05, 0x3E, 0x70, 0xC6, 0xD6, - 0x18, 0x4B, 0xFA, 0xDA, 0xE0, 0xF0, 0xE2, 0xD9, - }, - }, - /* All fields except session_id are NULL */ - { - { - 0xA6, 0xC3, 0x24, 0xC7, 0xE8, 0xD1, 0x8A, 0xAA, - 0x59, 0xD7, 0xB4, 0x74, 0xDD, 0x73, 0x82, 0x6D, - 0x7E, 0x74, 0x91, 0x3F, 0x9B, 0x36, 0x12, 0xE4, - 0x4F, 0x28, 0x6E, 0x07, 0x54, 0x14, 0x58, 0x21, - }, - { - 0x4E, 0x75, 0xD3, 0x81, 0xCD, 0xB7, 0x3C, 0x68, - 0xA0, 0x7E, 0x64, 0x15, 0xE0, 0x0E, 0x89, 0x32, - 0x44, 0x21, 0x87, 0x4F, 0x4E, 0x03, 0xE8, 0x67, - 0x73, 0x4E, 0x33, 0x20, 0xCE, 0x24, 0xBA, 0x8E, - }, - }, - }; - unsigned char args[5][32]; - int i, j; - - for (i = 0; i < 5; i++) { - memset(args[i], i, sizeof(args[i])); - } - - secp256k1_nonce_function_musig(k[0], args[0], args[1], args[2], args[3], args[4]); - secp256k1_nonce_function_musig(k[1], args[0], NULL, args[2], args[3], args[4]); - secp256k1_nonce_function_musig(k[2], args[0], NULL, NULL, NULL, NULL); - /* TODO: remove when test vectors are not expected to change anymore */ - /* int t, u; */ - /* printf("const unsigned char k32_expected[N][2][32] = {\n"); */ - /* for (i = 0; i < N; i++) { */ - /* printf(" {\n"); */ - /* for (j = 0; j < 2; j++) { */ - /* unsigned char k32[32]; */ - /* secp256k1_scalar_get_b32(k32, &k[i][j]); */ - /* printf(" {\n"); */ - /* for (t = 0; t < 4; t++) { */ - /* printf(" "); */ - /* for (u = 0; u < 8; u++) { */ - /* printf("0x%02X, ", k32[t*8+u]); */ - /* } */ - /* printf("\n"); */ - /* } */ - /* printf(" },\n"); */ - /* } */ - /* printf(" },\n"); */ - /* } */ - /* printf("};\n"); */ - for (i = 0; i < N; i++) { - for (j = 0; j < 2; j++) { - unsigned char k32[32]; - secp256k1_scalar_get_b32(k32, &k[i][j]); - CHECK(secp256k1_memcmp_var(k32, k32_expected[i][j], 32) == 0); - } - } -} - -void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char tweak[][32], const int *is_xonly_t, int n_tweaks, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) { - secp256k1_keypair signer_keypair; - secp256k1_musig_secnonce secnonce; - secp256k1_xonly_pubkey pk[3]; - const secp256k1_xonly_pubkey *pk_ptr[3]; - secp256k1_xonly_pubkey agg_pk; - secp256k1_musig_session session; - secp256k1_musig_aggnonce agg_pubnonce; - secp256k1_musig_partial_sig partial_sig; - int i; - - CHECK(create_keypair_and_pk(&signer_keypair, &pk[signer_pos], sk) == 1); - for (i = 0; i < 3; i++) { - if (i != signer_pos) { - int offset = i < signer_pos ? 0 : -1; - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i + offset]) == 1); - } - pk_ptr[i] = &pk[i]; - } - CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, keyagg_cache, pk_ptr, 3) == 1); - for (i = 0; i < n_tweaks; i++) { - if (is_xonly_t[i]) { - CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak[i]) == 1); - } else { - CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, keyagg_cache, tweak[i]) == 1); - } - } - memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); - memcpy(&secnonce.data[4], secnonce_bytes, sizeof(secnonce.data) - 4); - CHECK(secp256k1_musig_aggnonce_parse(ctx, &agg_pubnonce, agg_pubnonce_ser) == 1); - CHECK(secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg, keyagg_cache, adaptor) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &signer_keypair, keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_nonce_parity(ctx, fin_nonce_parity, &session) == 1); - memcpy(sig, &partial_sig.data[4], 32); -} - -int musig_test_pk_parity(const secp256k1_musig_keyagg_cache *keyagg_cache) { - secp256k1_keyagg_cache_internal cache_i; - CHECK(secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache) == 1); - return secp256k1_fe_is_odd(&cache_i.pk.y); -} - -int musig_test_is_second_pk(const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *sk) { - secp256k1_ge pkp; - secp256k1_xonly_pubkey pk; - secp256k1_keyagg_cache_internal cache_i; - CHECK(create_keypair_and_pk(NULL, &pk, sk)); - CHECK(secp256k1_xonly_pubkey_load(ctx, &pkp, &pk)); - CHECK(secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)); - return secp256k1_fe_equal_var(&cache_i.second_pk_x, &pkp.x); -} - -/* TODO: Add test vectors for failed signing */ -void musig_test_vectors_sign(void) { - unsigned char sig[32]; - secp256k1_musig_keyagg_cache keyagg_cache; - int fin_nonce_parity; - const unsigned char secnonce[64] = { - 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, - 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, - 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, - 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, - 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, - 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, - 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, - 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, - }; - /* The nonces are already aggregated */ - const unsigned char agg_pubnonce[66] = { - 0x02, - 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, - 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, - 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, - 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, - 0x03, - 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, - 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, - 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, - 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9, - }; - const unsigned char sk[32] = { - 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, - 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, - 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, - 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71, - }; - const unsigned char msg[32] = { - 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, - 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, - 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, - 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF, - }; - const unsigned char *pk[2] = { vec_pk[0], vec_pk[1] }; - - { - /* This is a test where the combined public key point has an _odd_ y - * coordinate, the signer _is not_ the second pubkey in the list and the - * nonce parity is 1. */ - const unsigned char sig_expected[32] = { - 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, - 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, - 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, - 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B, - }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, NULL, pk, 0); - /* TODO: remove when test vectors are not expected to change anymore */ - /* int k, l; */ - /* printf("const unsigned char sig_expected[32] = {\n"); */ - /* for (k = 0; k < 4; k++) { */ - /* printf(" "); */ - /* for (l = 0; l < 8; l++) { */ - /* printf("0x%02X, ", sig[k*8+l]); */ - /* } */ - /* printf("\n"); */ - /* } */ - /* printf("};\n"); */ - - /* Check that the description of the test vector is correct */ - CHECK(musig_test_pk_parity(&keyagg_cache) == 1); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 1); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test where the aggregate public key point has an _even_ y - * coordinate, the signer _is_ the second pubkey in the list and the - * nonce parity is 0. */ - const unsigned char sig_expected[32] = { - 0x2D, 0xF6, 0x7B, 0xFF, 0xF1, 0x8E, 0x3D, 0xE7, - 0x97, 0xE1, 0x3C, 0x64, 0x75, 0xC9, 0x63, 0x04, - 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, - 0x7C, 0xEC, 0xA7, 0xC8, 0x42, 0x42, 0x95, 0xEA, - }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, NULL, pk, 1); - /* Check that the description of the test vector is correct */ - CHECK(musig_test_pk_parity(&keyagg_cache) == 0); - CHECK(musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 0); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test where the parity of aggregate public key point (1) is unequal to the - * nonce parity (0). */ - const unsigned char sig_expected[32] = { - 0x0D, 0x5B, 0x65, 0x1E, 0x6D, 0xE3, 0x4A, 0x29, - 0xA1, 0x2D, 0xE7, 0xA8, 0xB4, 0x18, 0x3B, 0x4A, - 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, - 0xA4, 0xA3, 0xD1, 0xBC, 0xAA, 0xBC, 0x75, 0x17, - }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, NULL, pk, 2); - /* Check that the description of the test vector is correct */ - CHECK(musig_test_pk_parity(&keyagg_cache) == 1); - CHECK(fin_nonce_parity == 0); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test that includes an xonly public key tweak. */ - const unsigned char sig_expected[32] = { - 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, - 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, 0x04, 0xA2, - 0x15, 0x97, 0xF9, 0x60, 0x3D, 0x3A, 0xB0, 0x5B, - 0x49, 0x13, 0x64, 0x17, 0x75, 0xE1, 0x37, 0x5B, - }; - const unsigned char tweak[1][32] = {{ - 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, - 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, - 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, - 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, - }}; - int is_xonly_t[1] = { 1 }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 1, NULL, pk, 2); - - CHECK(musig_test_pk_parity(&keyagg_cache) == 1); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 1); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test that includes an ordinary public key tweak. */ - const unsigned char sig_expected[32] = { - 0x78, 0x40, 0x8D, 0xDC, 0xAB, 0x48, 0x13, 0xD1, - 0x39, 0x4C, 0x97, 0xD4, 0x93, 0xEF, 0x10, 0x84, - 0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD, - 0x7B, 0xC5, 0x99, 0x16, 0x44, 0xE4, 0x4D, 0xDD, - }; - const unsigned char tweak[1][32] = {{ - 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, - 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, - 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, - 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, - }}; - int is_xonly_t[1] = { 0 }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 1, NULL, pk, 2); - - CHECK(musig_test_pk_parity(&keyagg_cache) == 1); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 0); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test that includes an ordinary and an x-only public key tweak. */ - const unsigned char sig_expected[32] = { - 0xC3, 0xA8, 0x29, 0xA8, 0x14, 0x80, 0xE3, 0x6E, - 0xC3, 0xAB, 0x05, 0x29, 0x64, 0x50, 0x9A, 0x94, - 0xEB, 0xF3, 0x42, 0x10, 0x40, 0x3D, 0x16, 0xB2, - 0x26, 0xA6, 0xF1, 0x6E, 0xC8, 0x5B, 0x73, 0x57, - }; - - const unsigned char tweak[2][32] = { - { - 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, - 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, - 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, - 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, - }, - { - 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, - 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D, - 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, - 0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55, - }, - }; - int is_xonly_t[2] = { 0, 1 }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 2, NULL, pk, 2); - CHECK(musig_test_pk_parity(&keyagg_cache) == 0); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 0); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test with four tweaks: x-only, ordinary, x-only, ordinary. */ - const unsigned char sig_expected[32] = { - 0x8C, 0x44, 0x73, 0xC6, 0xA3, 0x82, 0xBD, 0x3C, - 0x4A, 0xD7, 0xBE, 0x59, 0x81, 0x8D, 0xA5, 0xED, - 0x7C, 0xF8, 0xCE, 0xC4, 0xBC, 0x21, 0x99, 0x6C, - 0xFD, 0xA0, 0x8B, 0xB4, 0x31, 0x6B, 0x8B, 0xC7, - }; - const unsigned char tweak[4][32] = { - { - 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, - 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, - 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, - 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, - }, - { - 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, - 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D, - 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, - 0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55, - }, - { - 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, - 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, 0x45, 0x7E, - 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, - 0x83, 0x16, 0x26, 0xF2, 0x90, 0xFA, 0x87, 0xE0, - }, - { - 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, - 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, 0xBF, 0x99, - 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, - 0x74, 0xFE, 0xD0, 0xA3, 0xCF, 0x5A, 0xEF, 0x9D, - }, - }; - int is_xonly_t[4] = { 1, 0, 1, 0 }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, is_xonly_t, 4, NULL, pk, 2); - CHECK(musig_test_pk_parity(&keyagg_cache) == 0); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 1); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } - { - /* This is a test that includes an adaptor. */ - const unsigned char sig_expected[32] = { - 0xD7, 0x67, 0xD0, 0x7D, 0x9A, 0xB8, 0x19, 0x8C, - 0x9F, 0x64, 0xE3, 0xFD, 0x9F, 0x7B, 0x8B, 0xAA, - 0xC6, 0x05, 0xF1, 0x8D, 0xFF, 0x18, 0x95, 0x24, - 0x2D, 0x93, 0x95, 0xD9, 0xC8, 0xE6, 0xDD, 0x7C, - }; - const unsigned char sec_adaptor[32] = { - 0xD5, 0x6A, 0xD1, 0x85, 0x00, 0xF2, 0xD7, 0x8A, - 0xB9, 0x54, 0x80, 0x53, 0x76, 0xF3, 0x9D, 0x1B, - 0x6D, 0x62, 0x04, 0x95, 0x12, 0x39, 0x04, 0x6D, - 0x99, 0x3A, 0x9C, 0x31, 0xE0, 0xF4, 0x78, 0x71, - }; - secp256k1_pubkey pub_adaptor; - CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor) == 1); - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, 0, &pub_adaptor, pk, 2); - - CHECK(musig_test_pk_parity(&keyagg_cache) == 1); - CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); - CHECK(fin_nonce_parity == 1); - CHECK(secp256k1_memcmp_var(sig, sig_expected, 32) == 0); - } -} - void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); @@ -1455,9 +927,6 @@ void run_musig_tests(void) { musig_tweak_test(scratch); } sha256_tag_test(); - musig_test_vectors_keyagg(); - musig_test_vectors_noncegen(); - musig_test_vectors_sign(); secp256k1_scratch_space_destroy(ctx, scratch); } From 206017d67d9bb8b21d5cc924ba53e1618274774c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 19 Apr 2022 12:43:11 +0000 Subject: [PATCH 215/381] musig: update to BIP v0.3 (NonceGen) - 0.3.0: Hash i - 1 instead of i in NonceGen - 0.2.0: Change order of arguments in NonceGen hash function --- src/modules/musig/session_impl.h | 64 +++++++++++++++++++------------- src/modules/musig/tests_impl.h | 7 ++-- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 88e27e17..b0fd1c99 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -226,42 +226,54 @@ static int secp256k1_xonly_ge_serialize(unsigned char *output32, secp256k1_ge *g return 1; } +/* Write optional inputs into the hash */ +static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data32) { + /* The spec requires length prefix to be 4 bytes for `extra_in`, 1 byte + * otherwise */ + VERIFY_CHECK(prefix_size == 4 || prefix_size == 1); + if (prefix_size == 4) { + /* Four byte big-endian value, pad first three bytes with 0 */ + unsigned char zero[3] = {0}; + secp256k1_sha256_write(sha, zero, 3); + } + if (data32 != NULL) { + unsigned char len = 32; + secp256k1_sha256_write(sha, &len, 1); + secp256k1_sha256_write(sha, data32, 32); + } else { + unsigned char len = 0; + secp256k1_sha256_write(sha, &len, 1); + } +} + static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *msg32, const unsigned char *key32, const unsigned char *agg_pk32, const unsigned char *extra_input32) { secp256k1_sha256 sha; - unsigned char seed[32]; + unsigned char rand[32]; unsigned char i; - enum { n_extra_in = 4 }; - const unsigned char *extra_in[n_extra_in]; - /* TODO: this doesn't have the same sidechannel resistance as the BIP340 - * nonce function because the seckey feeds directly into SHA. */ + if (key32 != NULL) { + secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/aux", sizeof("MuSig/aux") - 1); + secp256k1_sha256_write(&sha, session_id, 32); + secp256k1_sha256_finalize(&sha, rand); + for (i = 0; i < 32; i++) { + rand[i] ^= key32[i]; + } + } else { + memcpy(rand, session_id, sizeof(rand)); + } /* Subtract one from `sizeof` to avoid hashing the implicit null byte */ secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/nonce", sizeof("MuSig/nonce") - 1); - secp256k1_sha256_write(&sha, session_id, 32); - extra_in[0] = msg32; - extra_in[1] = key32; - extra_in[2] = agg_pk32; - extra_in[3] = extra_input32; - for (i = 0; i < n_extra_in; i++) { - unsigned char len; - if (extra_in[i] != NULL) { - len = 32; - secp256k1_sha256_write(&sha, &len, 1); - secp256k1_sha256_write(&sha, extra_in[i], 32); - } else { - len = 0; - secp256k1_sha256_write(&sha, &len, 1); - } - } - secp256k1_sha256_finalize(&sha, seed); + secp256k1_sha256_write(&sha, rand, sizeof(rand)); + secp256k1_nonce_function_musig_helper(&sha, 1, agg_pk32); + secp256k1_nonce_function_musig_helper(&sha, 1, msg32); + secp256k1_nonce_function_musig_helper(&sha, 4, extra_input32); for (i = 0; i < 2; i++) { unsigned char buf[32]; - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, seed, 32); - secp256k1_sha256_write(&sha, &i, sizeof(i)); - secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256 sha_tmp = sha; + secp256k1_sha256_write(&sha_tmp, &i, 1); + secp256k1_sha256_finalize(&sha_tmp, buf); secp256k1_scalar_set_b32(&k[i], buf, NULL); } } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ddc6910f..7ca23b6c 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -656,10 +656,11 @@ void musig_nonce_test(void) { secp256k1_nonce_function_musig(k[2], args[0], args[1], NULL, args[3], args[4]); secp256k1_nonce_function_musig(k[3], args[0], args[1], args[2], NULL, args[4]); secp256k1_nonce_function_musig(k[4], args[0], args[1], args[2], args[3], NULL); - for (i = 0; i < 4; i++) { + for (i = 0; i < 5; i++) { + CHECK(!secp256k1_scalar_eq(&k[i][0], &k[i][1])); for (j = i+1; j < 5; j++) { - CHECK(secp256k1_scalar_eq(&k[i][0], &k[j][0]) == 0); - CHECK(secp256k1_scalar_eq(&k[i][1], &k[j][1]) == 0); + CHECK(!secp256k1_scalar_eq(&k[i][0], &k[j][0])); + CHECK(!secp256k1_scalar_eq(&k[i][1], &k[j][1])); } } } From cbe2815633411479e8305deb8b69bce94df723af Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 20 Dec 2022 13:13:54 +0000 Subject: [PATCH 216/381] musig: update to BIP v0.4 "Allow the output of NonceAgg to be inf" --- src/modules/musig/session_impl.h | 126 +++++++++++++++++++++++++------ src/modules/musig/tests_impl.h | 9 +-- 2 files changed, 106 insertions(+), 29 deletions(-) diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index b0fd1c99..559680c8 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -20,6 +20,25 @@ #include "../../scalar.h" #include "../../util.h" +/* point_save_ext and point_load_ext are identical to point_save and point_load + * except that they allow saving and loading the point at infinity */ +static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) { + if (secp256k1_ge_is_infinity(ge)) { + memset(data, 0, 64); + } else { + secp256k1_point_save(data, ge); + } +} + +static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data) { + unsigned char zeros[64] = { 0 }; + if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) { + secp256k1_ge_set_infinity(ge); + } else { + secp256k1_point_load(ge, data); + } +} + static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 }; static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, secp256k1_scalar *k) { @@ -52,8 +71,8 @@ static void secp256k1_musig_secnonce_invalidate(const secp256k1_context* ctx, se static const unsigned char secp256k1_musig_pubnonce_magic[4] = { 0xf5, 0x7a, 0x3d, 0xa0 }; -/* Requires that none of the provided group elements is infinity. Works for both - * musig_pubnonce and musig_aggnonce. */ +/* 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, secp256k1_ge* ge) { int i; memcpy(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4); @@ -62,8 +81,8 @@ static void secp256k1_musig_pubnonce_save(secp256k1_musig_pubnonce* nonce, secp2 } } -/* Works for both musig_pubnonce and musig_aggnonce. Returns 1 unless the nonce - * wasn't properly initialized */ +/* 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* ge, const secp256k1_musig_pubnonce* nonce) { int i; @@ -74,12 +93,24 @@ static int secp256k1_musig_pubnonce_load(const secp256k1_context* ctx, secp256k1 return 1; } +static const unsigned char secp256k1_musig_aggnonce_magic[4] = { 0xa8, 0xb7, 0xe4, 0x67 }; + static void secp256k1_musig_aggnonce_save(secp256k1_musig_aggnonce* nonce, secp256k1_ge* ge) { - secp256k1_musig_pubnonce_save((secp256k1_musig_pubnonce *) nonce, ge); + int i; + memcpy(&nonce->data[0], secp256k1_musig_aggnonce_magic, 4); + for (i = 0; i < 2; i++) { + secp256k1_point_save_ext(&nonce->data[4 + 64*i], &ge[i]); + } } static int secp256k1_musig_aggnonce_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_musig_aggnonce* nonce) { - return secp256k1_musig_pubnonce_load(ctx, ge, (secp256k1_musig_pubnonce *) nonce); + int i; + + ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_aggnonce_magic, 4) == 0); + for (i = 0; i < 2; i++) { + secp256k1_point_load_ext(&ge[i], &nonce->data[4 + 64*i]); + } + return 1; } static const unsigned char secp256k1_musig_session_cache_magic[4] = { 0x9d, 0xed, 0xe9, 0x17 }; @@ -180,17 +211,69 @@ int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig return 0; } } - /* The group elements can not be infinity because they were just parsed */ secp256k1_musig_pubnonce_save(nonce, ge); return 1; } +/* Outputs 33 zero bytes if the given group element is the point at infinity and + * otherwise outputs the compressed serialization */ +static void secp256k1_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { + if (secp256k1_ge_is_infinity(ge)) { + memset(out33, 0, 33); + } else { + int ret; + size_t size = 33; + ret = secp256k1_eckey_pubkey_serialize(ge, out33, &size, 1); + /* Serialize must succeed because the point is not at infinity */ + VERIFY_CHECK(ret && size == 33); + } +} + +/* Outputs the point at infinity if the given byte array is all zero, otherwise + * attempts to parse compressed point serialization. */ +static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { + unsigned char zeros[33] = { 0 }; + + if (memcmp(in33, zeros, sizeof(zeros)) == 0) { + secp256k1_ge_set_infinity(ge); + return 1; + } + return secp256k1_eckey_pubkey_parse(ge, in33, 33); +} + int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_aggnonce* nonce) { - return secp256k1_musig_pubnonce_serialize(ctx, out66, (secp256k1_musig_pubnonce*) nonce); + secp256k1_ge ge[2]; + int i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out66 != NULL); + memset(out66, 0, 66); + ARG_CHECK(nonce != NULL); + + if (!secp256k1_musig_aggnonce_load(ctx, ge, nonce)) { + return 0; + } + for (i = 0; i < 2; i++) { + secp256k1_ge_serialize_ext(&out66[33*i], &ge[i]); + } + return 1; } int secp256k1_musig_aggnonce_parse(const secp256k1_context* ctx, secp256k1_musig_aggnonce* nonce, const unsigned char *in66) { - return secp256k1_musig_pubnonce_parse(ctx, (secp256k1_musig_pubnonce*) nonce, in66); + secp256k1_ge ge[2]; + int i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(in66 != NULL); + + for (i = 0; i < 2; i++) { + if (!secp256k1_ge_parse_ext(&ge[i], &in66[33*i])) { + return 0; + } + } + secp256k1_musig_aggnonce_save(nonce, ge); + return 1; } int secp256k1_musig_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_sig* sig) { @@ -373,12 +456,7 @@ int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggn return 0; } for (i = 0; i < 2; i++) { - if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { - /* Set to G according to the specification */ - aggnonce_pt[i] = secp256k1_ge_const_g; - } else { - secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); - } + secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); } secp256k1_musig_aggnonce_save(aggnonce, aggnonce_pt); return 1; @@ -392,11 +470,7 @@ static int secp256k1_musig_compute_noncehash(unsigned char *noncehash, secp256k1 secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/noncecoef", sizeof("MuSig/noncecoef") - 1); for (i = 0; i < 2; i++) { - size_t size; - if (!secp256k1_eckey_pubkey_serialize(&aggnonce[i], buf, &size, 1)) { - return 0; - } - VERIFY_CHECK(size == sizeof(buf)); + secp256k1_ge_serialize_ext(buf, &aggnonce[i]); secp256k1_sha256_write(&sha, buf, sizeof(buf)); } secp256k1_sha256_write(&sha, agg_pk32, 32); @@ -410,6 +484,7 @@ static int secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigne secp256k1_ge fin_nonce_pt; secp256k1_gej fin_nonce_ptj; secp256k1_ge aggnonce[2]; + int ret; secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); @@ -418,13 +493,16 @@ static int secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigne } /* fin_nonce = aggnonce[0] + b*aggnonce[1] */ secp256k1_scalar_set_b32(b, noncehash, NULL); + secp256k1_gej_set_infinity(&fin_nonce_ptj); secp256k1_ecmult(&fin_nonce_ptj, &aggnoncej[1], b, NULL); - secp256k1_gej_add_ge(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0]); + secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0], NULL); secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj); - if (!secp256k1_xonly_ge_serialize(fin_nonce, &fin_nonce_pt)) { - /* unreachable with overwhelming probability */ - return 0; + if (secp256k1_ge_is_infinity(&fin_nonce_pt)) { + fin_nonce_pt = secp256k1_ge_const_g; } + ret = secp256k1_xonly_ge_serialize(fin_nonce, &fin_nonce_pt); + /* Can't fail since fin_nonce_pt is not infinity */ + VERIFY_CHECK(ret); secp256k1_fe_normalize_var(&fin_nonce_pt.y); *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); return 1; diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 7ca23b6c..b18a6610 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -376,11 +376,11 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 4); CHECK(secp256k1_musig_nonce_agg(none, &aggnonce, inf_pubnonce_ptr, 2) == 1); { - /* Check that the aggnonce is set to G */ + /* Check that the aggnonce encodes two points at infinity */ secp256k1_ge aggnonce_pt[2]; - secp256k1_musig_pubnonce_load(ctx, aggnonce_pt, (secp256k1_musig_pubnonce*)&aggnonce); + secp256k1_musig_aggnonce_load(ctx, aggnonce_pt, &aggnonce); for (i = 0; i < 2; i++) { - ge_equals_ge(&aggnonce_pt[i], &secp256k1_ge_const_g); + secp256k1_ge_is_infinity(&aggnonce_pt[i]); } } CHECK(ecount == 4); @@ -405,8 +405,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 1); CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, zeros68) == 0); - CHECK(ecount == 2); + CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, zeros68) == 1); CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, aggnonce_ser) == 1); { From 87373f51451bed948340d6885111d04051cbfc02 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 27 Aug 2022 14:55:38 +0000 Subject: [PATCH 217/381] MOVE ONLY: move Pedersen commitment stuff to generator module from rangeproof module You can verify this commit with `git diff --color-moved=zebra` --- include/secp256k1_generator.h | 148 ++++++++++++ include/secp256k1_rangeproof.h | 148 ------------ src/modules/generator/Makefile.am.include | 2 + src/modules/generator/main_impl.h | 219 +++++++++++++++++ .../{rangeproof => generator}/pedersen.h | 0 .../{rangeproof => generator}/pedersen_impl.h | 0 src/modules/generator/tests_impl.h | 163 +++++++++++++ src/modules/rangeproof/Makefile.am.include | 2 - src/modules/rangeproof/main_impl.h | 222 +----------------- src/modules/rangeproof/rangeproof_impl.h | 6 +- src/modules/rangeproof/tests_impl.h | 142 ----------- src/secp256k1.c | 2 - 12 files changed, 538 insertions(+), 516 deletions(-) rename src/modules/{rangeproof => generator}/pedersen.h (100%) rename src/modules/{rangeproof => generator}/pedersen_impl.h (100%) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index cb55af91..5479fc81 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -21,6 +21,11 @@ typedef struct { unsigned char data[64]; } secp256k1_generator; +/** + * Static constant generator 'h' maintained for historical reasons. + */ +SECP256K1_API extern const secp256k1_generator *secp256k1_generator_h; + /** Parse a 33-byte generator byte sequence into a generator object. * * Returns: 1 if input contains a valid generator. @@ -86,6 +91,149 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blin const unsigned char *blind32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Opaque data structure that stores a Pedersen commitment + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_pedersen_commitment_serialize and + * secp256k1_pedersen_commitment_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pedersen_commitment; + +/** Parse a 33-byte commitment into a commitment object. + * + * Returns: 1 if input contains a valid commitment. + * Args: ctx: a secp256k1 context object. + * Out: commit: pointer to the output commitment object + * In: input: pointer to a 33-byte serialized commitment key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( + const secp256k1_context* ctx, + secp256k1_pedersen_commitment* commit, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a commitment object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 33-byte byte array + * In: commit: a pointer to a secp256k1_pedersen_commitment containing an + * initialized commitment + */ +SECP256K1_API int secp256k1_pedersen_commitment_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_pedersen_commitment* commit +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a pedersen commitment. + * Returns 1: Commitment successfully created. + * 0: Error. The blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127) or results in the + * point at infinity. Retry with a different factor. + * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) + * blind: pointer to a 32-byte blinding factor (cannot be NULL) + * value: unsigned 64-bit integer value to commit to. + * gen: additional generator 'h' + * Out: commit: pointer to the commitment (cannot be NULL) + * + * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( + const secp256k1_context* ctx, + secp256k1_pedersen_commitment *commit, + const unsigned char *blind, + uint64_t value, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Computes the sum of multiple positive and negative blinding factors. + * Returns 1: Sum successfully computed. + * 0: Error. A blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127). Retry with + * different factors. + * In: ctx: pointer to a context object (cannot be NULL) + * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) + * n: number of factors pointed to by blinds. + * npositive: how many of the initial factors should be treated with a positive sign. + * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( + const secp256k1_context* ctx, + unsigned char *blind_out, + const unsigned char * const *blinds, + size_t n, + size_t npositive +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a tally of pedersen commitments + * Returns 1: commitments successfully sum to zero. + * 0: Commitments do not sum to zero or other error. + * In: ctx: pointer to a context object (cannot be NULL) + * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) + * pcnt: number of commitments pointed to by commits. + * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) + * ncnt: number of commitments pointed to by ncommits. + * + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. + * + * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator + * A all blinding factors and all values must sum to zero. + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( + const secp256k1_context* ctx, + const secp256k1_pedersen_commitment * const* commits, + size_t pcnt, + const secp256k1_pedersen_commitment * const* ncommits, + size_t ncnt +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Sets the final Pedersen blinding factor correctly when the generators themselves + * have blinding factors. + * + * Consider a generator of the form A' = A + rG, where A is the "real" generator + * but A' is the generator provided to verifiers. Then a Pedersen commitment + * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') + * to sum to zero for multiple commitments, we take three arrays consisting of + * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s + * and `blinding_factor`s, and sum them. + * + * The function then subtracts the sum of all (vr + r') from the last element + * of the `blinding_factor` array, setting the total sum to zero. + * + * Returns 1: Blinding factor successfully computed. + * 0: Error. A blinding_factor or generator_blind are larger than the group + * order (probability for random 32 byte number < 2^-127). Retry with + * different values. + * + * In: ctx: pointer to a context object + * value: array of asset values, `v` in the above paragraph. + * May not be NULL unless `n_total` is 0. + * generator_blind: array of asset blinding factors, `r` in the above paragraph + * May not be NULL unless `n_total` is 0. + * n_total: Total size of the above arrays + * n_inputs: How many of the initial array elements represent commitments that + * will be negated in the final sum + * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph + * May not be NULL unless `n_total` is 0. + * the last value will be modified to get the total sum to zero. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( + const secp256k1_context* ctx, + const uint64_t *value, + const unsigned char* const* generator_blind, + unsigned char* const* blinding_factor, + size_t n_total, + size_t n_inputs +); + # ifdef __cplusplus } # endif diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 9bb01454..2d86ab06 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -19,154 +19,6 @@ extern "C" { */ #define SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN 3968 -/** Opaque data structure that stores a Pedersen commitment - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, or - * comparison, use secp256k1_pedersen_commitment_serialize and - * secp256k1_pedersen_commitment_parse. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_pedersen_commitment; - -/** - * Static constant generator 'h' maintained for historical reasons. - */ -SECP256K1_API extern const secp256k1_generator *secp256k1_generator_h; - -/** Parse a 33-byte commitment into a commitment object. - * - * Returns: 1 if input contains a valid commitment. - * Args: ctx: a secp256k1 context object. - * Out: commit: pointer to the output commitment object - * In: input: pointer to a 33-byte serialized commitment key - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( - const secp256k1_context* ctx, - secp256k1_pedersen_commitment* commit, - const unsigned char *input -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize a commitment object into a serialized byte sequence. - * - * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 33-byte byte array - * In: commit: a pointer to a secp256k1_pedersen_commitment containing an - * initialized commitment - */ -SECP256K1_API int secp256k1_pedersen_commitment_serialize( - const secp256k1_context* ctx, - unsigned char *output, - const secp256k1_pedersen_commitment* commit -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Generate a pedersen commitment. - * Returns 1: Commitment successfully created. - * 0: Error. The blinding factor is larger than the group order - * (probability for random 32 byte number < 2^-127) or results in the - * point at infinity. Retry with a different factor. - * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) - * blind: pointer to a 32-byte blinding factor (cannot be NULL) - * value: unsigned 64-bit integer value to commit to. - * gen: additional generator 'h' - * Out: commit: pointer to the commitment (cannot be NULL) - * - * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( - const secp256k1_context* ctx, - secp256k1_pedersen_commitment *commit, - const unsigned char *blind, - uint64_t value, - const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); - -/** Computes the sum of multiple positive and negative blinding factors. - * Returns 1: Sum successfully computed. - * 0: Error. A blinding factor is larger than the group order - * (probability for random 32 byte number < 2^-127). Retry with - * different factors. - * In: ctx: pointer to a context object (cannot be NULL) - * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) - * n: number of factors pointed to by blinds. - * npositive: how many of the initial factors should be treated with a positive sign. - * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( - const secp256k1_context* ctx, - unsigned char *blind_out, - const unsigned char * const *blinds, - size_t n, - size_t npositive -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Verify a tally of pedersen commitments - * Returns 1: commitments successfully sum to zero. - * 0: Commitments do not sum to zero or other error. - * In: ctx: pointer to a context object (cannot be NULL) - * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) - * pcnt: number of commitments pointed to by commits. - * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) - * ncnt: number of commitments pointed to by ncommits. - * - * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. - * - * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, - * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator - * A all blinding factors and all values must sum to zero. - * - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( - const secp256k1_context* ctx, - const secp256k1_pedersen_commitment * const* commits, - size_t pcnt, - const secp256k1_pedersen_commitment * const* ncommits, - size_t ncnt -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Sets the final Pedersen blinding factor correctly when the generators themselves - * have blinding factors. - * - * Consider a generator of the form A' = A + rG, where A is the "real" generator - * but A' is the generator provided to verifiers. Then a Pedersen commitment - * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') - * to sum to zero for multiple commitments, we take three arrays consisting of - * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s - * and `blinding_factor`s, and sum them. - * - * The function then subtracts the sum of all (vr + r') from the last element - * of the `blinding_factor` array, setting the total sum to zero. - * - * Returns 1: Blinding factor successfully computed. - * 0: Error. A blinding_factor or generator_blind are larger than the group - * order (probability for random 32 byte number < 2^-127). Retry with - * different values. - * - * In: ctx: pointer to a context object - * value: array of asset values, `v` in the above paragraph. - * May not be NULL unless `n_total` is 0. - * generator_blind: array of asset blinding factors, `r` in the above paragraph - * May not be NULL unless `n_total` is 0. - * n_total: Total size of the above arrays - * n_inputs: How many of the initial array elements represent commitments that - * will be negated in the final sum - * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph - * May not be NULL unless `n_total` is 0. - * the last value will be modified to get the total sum to zero. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( - const secp256k1_context* ctx, - const uint64_t *value, - const unsigned char* const* generator_blind, - unsigned char* const* blinding_factor, - size_t n_total, - size_t n_inputs -); - /** Verify a proof that a committed value is within a range. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. * 0: Proof failed or other error. diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include index 69933e99..4119966c 100644 --- a/src/modules/generator/Makefile.am.include +++ b/src/modules/generator/Makefile.am.include @@ -1,4 +1,6 @@ include_HEADERS += include/secp256k1_generator.h +noinst_HEADERS += src/modules/generator/pedersen.h +noinst_HEADERS += src/modules/generator/pedersen_impl.h noinst_HEADERS += src/modules/generator/main_impl.h noinst_HEADERS += src/modules/generator/tests_impl.h if USE_BENCHMARK diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index c915c791..c9f6ec8b 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -14,6 +14,29 @@ #include "../../hash.h" #include "../../scalar.h" +#include "modules/generator/pedersen_impl.h" + +/** Alternative generator for secp256k1. + * This is the sha256 of 'g' after standard encoding (without compression), + * which happens to be a point on the curve. More precisely, the generator is + * derived by running the following script with the sage mathematics software. + + import hashlib + F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' + H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) + print('%x %x' % H.xy()) + */ +static const secp256k1_generator secp256k1_generator_h_internal = {{ + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, + 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, + 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 +}}; + +const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; + + static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { int succeed; succeed = secp256k1_fe_set_b32(&ge->x, &gen->data[0]); @@ -219,4 +242,200 @@ int secp256k1_generator_generate_blinded(const secp256k1_context* ctx, secp256k1 return secp256k1_generator_generate_internal(ctx, gen, key32, blind32); } +static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &commit->data[1]); + secp256k1_ge_set_xquad(ge, &fe); + if (commit->data[0] & 1) { + secp256k1_ge_neg(ge, ge); + } +} + +static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { + secp256k1_fe_normalize(&ge->x); + secp256k1_fe_get_b32(&commit->data[1], &ge->x); + commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y); +} + +int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(input != NULL); + (void) ctx; + + if ((input[0] & 0xFE) != 8 || + !secp256k1_fe_set_b32(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { + return 0; + } + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_pedersen_commitment_save(commit, &ge); + return 1; +} + +int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(commit != NULL); + + secp256k1_pedersen_commitment_load(&ge, commit); + + output[0] = 9 ^ secp256k1_fe_is_quad_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); + return 1; +} + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { + secp256k1_ge genp; + secp256k1_gej rj; + secp256k1_ge r; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(gen != NULL); + secp256k1_generator_load(&genp, gen); + secp256k1_scalar_set_b32(&sec, blind, &overflow); + if (!overflow) { + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); + if (!secp256k1_gej_is_infinity(&rj)) { + secp256k1_ge_set_gej(&r, &rj); + secp256k1_pedersen_commitment_save(commit, &r); + ret = 1; + } + secp256k1_gej_clear(&rj); + secp256k1_ge_clear(&r); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest + * negative, then calculates an additional blinding value that adds to zero. + */ +int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { + secp256k1_scalar acc; + secp256k1_scalar x; + size_t i; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(blind_out != NULL); + ARG_CHECK(blinds != NULL); + ARG_CHECK(npositive <= n); + (void) ctx; + secp256k1_scalar_set_int(&acc, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&x, blinds[i], &overflow); + if (overflow) { + return 0; + } + if (i >= npositive) { + secp256k1_scalar_negate(&x, &x); + } + secp256k1_scalar_add(&acc, &acc, &x); + } + secp256k1_scalar_get_b32(blind_out, &acc); + secp256k1_scalar_clear(&acc); + secp256k1_scalar_clear(&x); + return 1; +} + +/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { + secp256k1_gej accj; + secp256k1_ge add; + size_t i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(!pcnt || (commits != NULL)); + ARG_CHECK(!ncnt || (ncommits != NULL)); + (void) ctx; + secp256k1_gej_set_infinity(&accj); + for (i = 0; i < ncnt; i++) { + secp256k1_pedersen_commitment_load(&add, ncommits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + secp256k1_gej_neg(&accj, &accj); + for (i = 0; i < pcnt; i++) { + secp256k1_pedersen_commitment_load(&add, commits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + return secp256k1_gej_is_infinity(&accj); +} + +int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { + secp256k1_scalar sum; + secp256k1_scalar tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(n_total == 0 || value != NULL); + ARG_CHECK(n_total == 0 || generator_blind != NULL); + ARG_CHECK(n_total == 0 || blinding_factor != NULL); + ARG_CHECK(n_total > n_inputs); + (void) ctx; + + if (n_total == 0) { + return 1; + } + + secp256k1_scalar_set_int(&sum, 0); + + /* Here, n_total > 0. Thus the loop runs at least once. + Thus we may use a do-while loop, which checks the loop + condition only at the end. + + The do-while loop helps GCC prove that the loop runs at least + once and suppresses a -Wmaybe-uninitialized warning. */ + i = 0; + do { + int overflow = 0; + secp256k1_scalar addend; + secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ + + secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ + + secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ + secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ + secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ + secp256k1_scalar_clear(&addend); + + i++; + } while (i < n_total); + + /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ + secp256k1_scalar_negate(&sum, &sum); + secp256k1_scalar_add(&tmp, &tmp, &sum); + secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); + + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&sum); + return 1; +} + #endif diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/generator/pedersen.h similarity index 100% rename from src/modules/rangeproof/pedersen.h rename to src/modules/generator/pedersen.h diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/generator/pedersen_impl.h similarity index 100% rename from src/modules/rangeproof/pedersen_impl.h rename to src/modules/generator/pedersen_impl.h diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 9f36f83d..b49ecae9 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -223,11 +223,174 @@ void test_generator_fixed_vector(void) { CHECK(!secp256k1_generator_parse(ctx, &parse, result)); } +static void test_pedersen_api(void) { + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); + secp256k1_pedersen_commitment commit; + const secp256k1_pedersen_commitment *commit_ptr = &commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + const unsigned char *blind_ptr = blind; + unsigned char *blind_out_ptr = blind_out; + uint64_t val = secp256k1_testrand32(); + int32_t ecount = 0; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); + + secp256k1_testrand256(blind); + CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(ecount == 0); + CHECK(secp256k1_pedersen_commit(sttc, &commit, blind, val, secp256k1_generator_h) == 0); + CHECK(ecount == 1); + + CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); + CHECK(ecount == 4); + + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0); + CHECK(ecount == 4); + CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); + CHECK(ecount == 7); + + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0); + CHECK(ecount == 7); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); + CHECK(ecount == 9); + + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); + CHECK(ecount == 9); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); + CHECK(ecount == 14); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(sttc); +} + +static void test_pedersen(void) { + secp256k1_pedersen_commitment commits[19]; + const secp256k1_pedersen_commitment *cptr[19]; + unsigned char blinds[32*19]; + const unsigned char *bptr[19]; + secp256k1_scalar s; + uint64_t values[19]; + int64_t totalv; + int i; + int inputs; + int outputs; + int total; + inputs = (secp256k1_testrand32() & 7) + 1; + outputs = (secp256k1_testrand32() & 7) + 2; + total = inputs + outputs; + for (i = 0; i < 19; i++) { + cptr[i] = &commits[i]; + bptr[i] = &blinds[i * 32]; + } + totalv = 0; + for (i = 0; i < inputs; i++) { + values[i] = secp256k1_testrandi64(0, INT64_MAX - totalv); + totalv += values[i]; + } + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_testrandi64(0, totalv); + totalv -= values[i + inputs]; + } + values[total - 1] = totalv; + + for (i = 0; i < total - 1; i++) { + random_scalar_order(&s); + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); + for (i = 0; i < total; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); + if (inputs > 0 && values[0] > 0) { + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs)); + } + random_scalar_order(&s); + for (i = 0; i < 4; i++) { + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + values[0] = INT64_MAX; + values[1] = 0; + values[2] = 1; + for (i = 0; i < 3; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); +} + +void test_pedersen_commitment_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x09, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); + CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); + CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); + + result[0] = 0x08; + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); + result[0] = 0x0c; + CHECK(!secp256k1_pedersen_commitment_parse(ctx, &parse, result)); +} + + void run_generator_tests(void) { + int i; + test_shallue_van_de_woestijne(); test_generator_fixed_vector(); test_generator_api(); test_generator_generate(); + test_pedersen_api(); + test_pedersen_commitment_fixed_vector(); + for (i = 0; i < count / 2 + 1; i++) { + test_pedersen(); + } } #endif diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include index ff8b8d38..5272f229 100644 --- a/src/modules/rangeproof/Makefile.am.include +++ b/src/modules/rangeproof/Makefile.am.include @@ -1,7 +1,5 @@ include_HEADERS += include/secp256k1_rangeproof.h noinst_HEADERS += src/modules/rangeproof/main_impl.h -noinst_HEADERS += src/modules/rangeproof/pedersen.h -noinst_HEADERS += src/modules/rangeproof/pedersen_impl.h noinst_HEADERS += src/modules/rangeproof/borromean.h noinst_HEADERS += src/modules/rangeproof/borromean_impl.h noinst_HEADERS += src/modules/rangeproof/rangeproof.h diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 432f4b95..b1af2a5e 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -9,225 +9,9 @@ #include "../../group.h" -#include "pedersen_impl.h" -#include "borromean_impl.h" -#include "rangeproof_impl.h" - -/** Alternative generator for secp256k1. - * This is the sha256 of 'g' after standard encoding (without compression), - * which happens to be a point on the curve. More precisely, the generator is - * derived by running the following script with the sage mathematics software. - - import hashlib - F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) - G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' - H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) - print('%x %x' % H.xy()) - */ -static const secp256k1_generator secp256k1_generator_h_internal = {{ - 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, - 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, - 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, - 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 -}}; - -const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; - -static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { - secp256k1_fe fe; - secp256k1_fe_set_b32(&fe, &commit->data[1]); - secp256k1_ge_set_xquad(ge, &fe); - if (commit->data[0] & 1) { - secp256k1_ge_neg(ge, ge); - } -} - -static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { - secp256k1_fe_normalize(&ge->x); - secp256k1_fe_get_b32(&commit->data[1], &ge->x); - commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y); -} - -int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { - secp256k1_fe x; - secp256k1_ge ge; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(commit != NULL); - ARG_CHECK(input != NULL); - (void) ctx; - - if ((input[0] & 0xFE) != 8 || - !secp256k1_fe_set_b32(&x, &input[1]) || - !secp256k1_ge_set_xquad(&ge, &x)) { - return 0; - } - if (input[0] & 1) { - secp256k1_ge_neg(&ge, &ge); - } - secp256k1_pedersen_commitment_save(commit, &ge); - return 1; -} - -int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { - secp256k1_ge ge; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output != NULL); - ARG_CHECK(commit != NULL); - - secp256k1_pedersen_commitment_load(&ge, commit); - - output[0] = 9 ^ secp256k1_fe_is_quad_var(&ge.y); - secp256k1_fe_normalize_var(&ge.x); - secp256k1_fe_get_b32(&output[1], &ge.x); - return 1; -} - -/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ -int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { - secp256k1_ge genp; - secp256k1_gej rj; - secp256k1_ge r; - secp256k1_scalar sec; - int overflow; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(commit != NULL); - ARG_CHECK(blind != NULL); - ARG_CHECK(gen != NULL); - secp256k1_generator_load(&genp, gen); - secp256k1_scalar_set_b32(&sec, blind, &overflow); - if (!overflow) { - secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); - if (!secp256k1_gej_is_infinity(&rj)) { - secp256k1_ge_set_gej(&r, &rj); - secp256k1_pedersen_commitment_save(commit, &r); - ret = 1; - } - secp256k1_gej_clear(&rj); - secp256k1_ge_clear(&r); - } - secp256k1_scalar_clear(&sec); - return ret; -} - -/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest - * negative, then calculates an additional blinding value that adds to zero. - */ -int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { - secp256k1_scalar acc; - secp256k1_scalar x; - size_t i; - int overflow; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(blind_out != NULL); - ARG_CHECK(blinds != NULL); - ARG_CHECK(npositive <= n); - (void) ctx; - secp256k1_scalar_set_int(&acc, 0); - for (i = 0; i < n; i++) { - secp256k1_scalar_set_b32(&x, blinds[i], &overflow); - if (overflow) { - return 0; - } - if (i >= npositive) { - secp256k1_scalar_negate(&x, &x); - } - secp256k1_scalar_add(&acc, &acc, &x); - } - secp256k1_scalar_get_b32(blind_out, &acc); - secp256k1_scalar_clear(&acc); - secp256k1_scalar_clear(&x); - return 1; -} - -/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ -int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { - secp256k1_gej accj; - secp256k1_ge add; - size_t i; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(!pcnt || (commits != NULL)); - ARG_CHECK(!ncnt || (ncommits != NULL)); - (void) ctx; - secp256k1_gej_set_infinity(&accj); - for (i = 0; i < ncnt; i++) { - secp256k1_pedersen_commitment_load(&add, ncommits[i]); - secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); - } - secp256k1_gej_neg(&accj, &accj); - for (i = 0; i < pcnt; i++) { - secp256k1_pedersen_commitment_load(&add, commits[i]); - secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); - } - return secp256k1_gej_is_infinity(&accj); -} - -int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { - secp256k1_scalar sum; - secp256k1_scalar tmp; - size_t i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(n_total == 0 || value != NULL); - ARG_CHECK(n_total == 0 || generator_blind != NULL); - ARG_CHECK(n_total == 0 || blinding_factor != NULL); - ARG_CHECK(n_total > n_inputs); - (void) ctx; - - if (n_total == 0) { - return 1; - } - - secp256k1_scalar_set_int(&sum, 0); - - /* Here, n_total > 0. Thus the loop runs at least once. - Thus we may use a do-while loop, which checks the loop - condition only at the end. - - The do-while loop helps GCC prove that the loop runs at least - once and suppresses a -Wmaybe-uninitialized warning. */ - i = 0; - do { - int overflow = 0; - secp256k1_scalar addend; - secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ - - secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); - if (overflow == 1) { - secp256k1_scalar_clear(&tmp); - secp256k1_scalar_clear(&addend); - secp256k1_scalar_clear(&sum); - return 0; - } - secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ - - secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); - if (overflow == 1) { - secp256k1_scalar_clear(&tmp); - secp256k1_scalar_clear(&addend); - secp256k1_scalar_clear(&sum); - return 0; - } - secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ - secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ - secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ - secp256k1_scalar_clear(&addend); - - i++; - } while (i < n_total); - - /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ - secp256k1_scalar_negate(&sum, &sum); - secp256k1_scalar_add(&tmp, &tmp, &sum); - secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); - - secp256k1_scalar_clear(&tmp); - secp256k1_scalar_clear(&sum); - return 1; -} +#include "modules/generator/main_impl.h" +#include "modules/rangeproof/borromean_impl.h" +#include "modules/rangeproof/rangeproof_impl.h" int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index fbf32b29..dd79b6ad 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -13,9 +13,9 @@ #include "../../hash_impl.h" #include "../../util.h" -#include "pedersen.h" -#include "rangeproof.h" -#include "borromean.h" +#include "modules/generator/pedersen.h" +#include "modules/rangeproof/borromean.h" +#include "modules/rangeproof/rangeproof.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 9c920734..61d0492e 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -16,66 +16,6 @@ #include "../../../include/secp256k1_rangeproof.h" -static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *sttc, const int32_t *ecount) { - secp256k1_pedersen_commitment commit; - const secp256k1_pedersen_commitment *commit_ptr = &commit; - unsigned char blind[32]; - unsigned char blind_out[32]; - const unsigned char *blind_ptr = blind; - unsigned char *blind_out_ptr = blind_out; - uint64_t val = secp256k1_testrand32(); - - secp256k1_testrand256(blind); - CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(*ecount == 0); - CHECK(secp256k1_pedersen_commit(sttc, &commit, blind, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 1); - - CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 2); - CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 3); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); - CHECK(*ecount == 4); - - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0); - CHECK(*ecount == 4); - CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); - CHECK(*ecount == 5); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); - CHECK(*ecount == 6); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); - CHECK(*ecount == 7); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); - CHECK(*ecount == 7); - - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0); - CHECK(*ecount == 7); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); - CHECK(*ecount == 8); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); - CHECK(*ecount == 9); - - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); - CHECK(*ecount == 9); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); - CHECK(*ecount == 10); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); - CHECK(*ecount == 11); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); - CHECK(*ecount == 12); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); - CHECK(*ecount == 13); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); - CHECK(*ecount == 14); -} - static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, const int32_t *ecount) { unsigned char proof[5134]; unsigned char blind[32]; @@ -253,8 +193,6 @@ static void test_api(void) { secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); for (i = 0; i < count; i++) { - ecount = 0; - test_pedersen_api(none, sign, vrfy, sttc, &ecount); ecount = 0; test_rangeproof_api(none, sign, vrfy, both, sttc, &ecount); } @@ -266,63 +204,6 @@ static void test_api(void) { secp256k1_context_destroy(sttc); } -static void test_pedersen(void) { - secp256k1_pedersen_commitment commits[19]; - const secp256k1_pedersen_commitment *cptr[19]; - unsigned char blinds[32*19]; - const unsigned char *bptr[19]; - secp256k1_scalar s; - uint64_t values[19]; - int64_t totalv; - int i; - int inputs; - int outputs; - int total; - inputs = (secp256k1_testrand32() & 7) + 1; - outputs = (secp256k1_testrand32() & 7) + 2; - total = inputs + outputs; - for (i = 0; i < 19; i++) { - cptr[i] = &commits[i]; - bptr[i] = &blinds[i * 32]; - } - totalv = 0; - for (i = 0; i < inputs; i++) { - values[i] = secp256k1_testrandi64(0, INT64_MAX - totalv); - totalv += values[i]; - } - for (i = 0; i < outputs - 1; i++) { - values[i + inputs] = secp256k1_testrandi64(0, totalv); - totalv -= values[i + inputs]; - } - values[total - 1] = totalv; - - for (i = 0; i < total - 1; i++) { - random_scalar_order(&s); - secp256k1_scalar_get_b32(&blinds[i * 32], &s); - } - CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); - for (i = 0; i < total; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); - } - CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); - if (inputs > 0 && values[0] > 0) { - CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs)); - } - random_scalar_order(&s); - for (i = 0; i < 4; i++) { - secp256k1_scalar_get_b32(&blinds[i * 32], &s); - } - values[0] = INT64_MAX; - values[1] = 0; - values[2] = 1; - for (i = 0; i < 3; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); - } - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); -} - static void test_borromean(void) { unsigned char e0[32]; secp256k1_scalar s[64]; @@ -1523,25 +1404,6 @@ void test_rangeproof_fixed_vectors_reproducible(void) { } } -void test_pedersen_commitment_fixed_vector(void) { - const unsigned char two_g[33] = { - 0x09, - 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, - 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 - }; - unsigned char result[33]; - secp256k1_pedersen_commitment parse; - - CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); - CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); - CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); - - result[0] = 0x08; - CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); - result[0] = 0x0c; - CHECK(!secp256k1_pedersen_commitment_parse(ctx, &parse, result)); -} - void run_rangeproof_tests(void) { int i; test_api(); @@ -1552,10 +1414,6 @@ void run_rangeproof_tests(void) { test_rangeproof_fixed_vectors(); test_rangeproof_fixed_vectors_reproducible(); - test_pedersen_commitment_fixed_vector(); - for (i = 0; i < count / 2 + 1; i++) { - test_pedersen(); - } for (i = 0; i < count / 2 + 1; i++) { test_borromean(); } diff --git a/src/secp256k1.c b/src/secp256k1.c index 6c686e0b..bf6a1c61 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -38,8 +38,6 @@ #ifdef ENABLE_MODULE_RANGEPROOF # include "include/secp256k1_rangeproof.h" -# include "modules/rangeproof/pedersen.h" -# include "modules/rangeproof/rangeproof.h" #endif #ifdef ENABLE_MODULE_ECDSA_S2C From 0a6006989f6215a45e982cd696339c503ddfc325 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 12 Jul 2022 18:51:48 +0000 Subject: [PATCH 218/381] Revert "Remove unused scalar_sqr" This reverts commit 5437e7bdfbffddf69fdf7b4af7e997c78f5dafbf. --- src/bench_internal.c | 10 +++ src/scalar.h | 3 + src/scalar_4x64_impl.h | 166 +++++++++++++++++++++++++++++++++++++++++ src/scalar_8x32_impl.h | 89 ++++++++++++++++++++++ src/scalar_low_impl.h | 4 + src/tests.c | 14 ++++ 6 files changed, 286 insertions(+) diff --git a/src/bench_internal.c b/src/bench_internal.c index 61400519..898c7801 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -98,6 +98,15 @@ void bench_scalar_negate(void* arg, int iters) { } } +void bench_scalar_sqr(void* arg, int iters) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]); + } +} + void bench_scalar_mul(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -376,6 +385,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters); diff --git a/src/scalar.h b/src/scalar.h index 36eb0db8..227913cb 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -65,6 +65,9 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, * the low bits that were shifted off */ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + /** Compute the inverse of a scalar (modulo the group order). */ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 6b0b44ed..585a4b63 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -224,6 +224,28 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { VERIFY_CHECK(c1 >= th); \ } +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint64_t tl, th, th2, tl2; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ + c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ + th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2); /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ @@ -733,10 +755,148 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c #endif } +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast +#undef muladd2 #undef extract #undef extract_fast @@ -758,6 +918,12 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { return ret; } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { r1->d[0] = k->d[0]; r1->d[1] = k->d[1]; diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index acd5ef7d..6086f1ec 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -306,6 +306,28 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { VERIFY_CHECK(c1 >= th); \ } +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint32_t tl, th, th2, tl2; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ + c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ + th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2); /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ @@ -569,10 +591,71 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, con l[15] = c0; } +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast +#undef muladd2 #undef extract #undef extract_fast @@ -598,6 +681,12 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { return ret; } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { r1->d[0] = k->d[0]; r1->d[1] = k->d[1]; diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 47001fcc..aa75f8b0 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -105,6 +105,10 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { return ret; } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { *r1 = *a; *r2 = 0; diff --git a/src/tests.c b/src/tests.c index ca1ded47..89f47432 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1898,6 +1898,14 @@ void scalar_test(void) { CHECK(secp256k1_scalar_eq(&r1, &r2)); } + { + /* Test square. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_sqr(&r1, &s1); + secp256k1_scalar_mul(&r2, &s1, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + { /* Test multiplicative identity. */ secp256k1_scalar r1, v1; @@ -2653,6 +2661,12 @@ void run_scalar_tests(void) { CHECK(!secp256k1_scalar_check_overflow(&zz)); CHECK(secp256k1_scalar_eq(&one, &zz)); } + secp256k1_scalar_mul(&z, &x, &x); + CHECK(!secp256k1_scalar_check_overflow(&z)); + secp256k1_scalar_sqr(&zz, &x); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&zz, &z)); + CHECK(secp256k1_scalar_eq(&r2, &zz)); } } } From 6162d577fec175c620f759675eb09ffa10368de1 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 12 Jul 2022 18:56:44 +0000 Subject: [PATCH 219/381] generator: cleanups in Pedersen/generator code Silence a compiler warning about an unitialized use of a scalar in case the user tries to provide a 0-length list of commitments. Also ensures that commitments have normalized field elements when they are loaded into ges. --- src/modules/generator/main_impl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index c9f6ec8b..e60ecdc2 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -391,6 +391,7 @@ int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, c } secp256k1_scalar_set_int(&sum, 0); + secp256k1_scalar_set_int(&tmp, 0); /* Here, n_total > 0. Thus the loop runs at least once. Thus we may use a do-while loop, which checks the loop From 048f9f8642297578a4e7975fa1e9837a58fc1c66 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 27 Aug 2022 15:02:44 +0000 Subject: [PATCH 220/381] bulletproofs: add new empty module --- .gitignore | 5 ++- Makefile.am | 4 +++ ci/cirrus.sh | 1 + configure.ac | 15 ++++++++ include/secp256k1_bulletproofs.h | 18 ++++++++++ src/bench_bulletproofs.c | 38 ++++++++++++++++++++ src/modules/bulletproofs/Makefile.am.include | 10 ++++++ src/modules/bulletproofs/main_impl.h | 12 +++++++ src/modules/bulletproofs/tests_impl.h | 14 ++++++++ src/secp256k1.c | 4 +++ src/tests.c | 8 +++++ 11 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 include/secp256k1_bulletproofs.h create mode 100644 src/bench_bulletproofs.c create mode 100644 src/modules/bulletproofs/Makefile.am.include create mode 100644 src/modules/bulletproofs/main_impl.h create mode 100644 src/modules/bulletproofs/tests_impl.h diff --git a/.gitignore b/.gitignore index 1ec887de..3c0494d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ bench +bench_bulletproofs bench_ecmult bench_generator bench_rangeproof bench_internal +bench_whitelist tests +example_musig exhaustive_tests precompute_ecmult_gen precompute_ecmult @@ -66,4 +69,4 @@ src/stamp-h1 libsecp256k1.pc contrib/gh-pr-create.sh -musig_example \ No newline at end of file +musig_example diff --git a/Makefile.am b/Makefile.am index 0b50f7a8..722dfac3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,10 @@ clean-precomp: EXTRA_DIST = autogen.sh SECURITY.md +if ENABLE_MODULE_BULLETPROOFS +include src/modules/bulletproofs/Makefile.am.include +endif + if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 431d35e4..74e8ab5b 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -19,6 +19,7 @@ valgrind --version || true --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-ecdsa-s2c="$ECDSA_S2C" \ + --enable-module-bulletproofs="$BULLETPROOFS" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-schnorrsig="$SCHNORRSIG" \ diff --git a/configure.ac b/configure.ac index 3ab35ed8..0b0a9d78 100644 --- a/configure.ac +++ b/configure.ac @@ -140,6 +140,11 @@ AC_ARG_ENABLE(examples, AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) +AC_ARG_ENABLE(module_bulletproofs, + AS_HELP_STRING([--enable-module-bulletproofs],[enable Bulletproofs module (experimental)]), + [], + [SECP_SET_DEFAULT([enable_module_bulletproofs], [no], [yes])]) + AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_ecdh], [no], [yes])]) @@ -417,6 +422,11 @@ if test x"$enable_module_rangeproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) fi +if test x"$enable_module_bulletproofs" = x"yes"; then + enable_module_generator=yes + AC_DEFINE(ENABLE_MODULE_BULLETPROOFS, 1, [Define this symbol to enable the Bulletproofs module]) +fi + if test x"$enable_module_generator" = x"yes"; then AC_DEFINE(ENABLE_MODULE_GENERATOR, 1, [Define this symbol to enable the NUMS generator module]) fi @@ -460,6 +470,9 @@ else # module (which automatically enables the module dependencies) we want to # print an error for the dependent module, not the module dependency. Hence, # we first test dependent modules. + if test x"$enable_module_bulletproofs" = x"yes"; then + AC_MSG_ERROR([Bulletproofs module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_whitelist" = x"yes"; then AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) fi @@ -502,6 +515,7 @@ AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"]) AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_BULLETPROOFS], [test x"$enable_module_bulletproofs" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) @@ -541,6 +555,7 @@ echo " module whitelist = $enable_module_whitelist" echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" +echo " module bulletproofs = $enable_module_bulletproofs" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h new file mode 100644 index 00000000..889d790b --- /dev/null +++ b/include/secp256k1_bulletproofs.h @@ -0,0 +1,18 @@ +#ifndef _SECP256K1_BULLETPROOFS_ +# define _SECP256K1_BULLETPROOFS_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/* TODO */ + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/src/bench_bulletproofs.c b/src/bench_bulletproofs.c new file mode 100644 index 00000000..f113791c --- /dev/null +++ b/src/bench_bulletproofs.c @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1_bulletproofs.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; +} bench_bulletproofs_data; + +static void bench_bulletproofs_setup(void* arg) { + (void) arg; +} + +static void bench_bulletproofs(void* arg, int iters) { + bench_bulletproofs_data *data = (bench_bulletproofs_data*)arg; + + (void) data; + (void) iters; +} + +int main(void) { + bench_bulletproofs_data data; + int iters = get_iters(32); + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + run_benchmark("bulletproofs_verify_bit", bench_bulletproofs, bench_bulletproofs_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include new file mode 100644 index 00000000..6cd9fc3c --- /dev/null +++ b/src/modules/bulletproofs/Makefile.am.include @@ -0,0 +1,10 @@ +include_HEADERS += include/secp256k1_bulletproofs.h +noinst_HEADERS += src/modules/bulletproofs/tests_impl.h +noinst_HEADERS += src/modules/bulletproofs/main_impl.h + +if USE_BENCHMARK +noinst_PROGRAMS += bench_bulletproofs +bench_bulletproofs_SOURCES = src/bench_bulletproofs.c +bench_bulletproofs_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_bulletproofs_LDFLAGS = -static +endif diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h new file mode 100644 index 00000000..9c61eaf7 --- /dev/null +++ b/src/modules/bulletproofs/main_impl.h @@ -0,0 +1,12 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ +#define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ + +/* TODO */ + +#endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h new file mode 100644 index 00000000..0c88fb78 --- /dev/null +++ b/src/modules/bulletproofs/tests_impl.h @@ -0,0 +1,14 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ +#define _SECP256K1_MODULE_BULLETPROOFS_TEST_ + +void run_bulletproofs_tests(void) { + /* TODO */ +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index bf6a1c61..857e9a76 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -800,6 +800,10 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, return 1; } +#ifdef ENABLE_MODULE_BULLETPROOFS +# include "modules/bulletproofs/main_impl.h" +#endif + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index 89f47432..fc84b3cb 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7132,6 +7132,10 @@ void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } +#ifdef ENABLE_MODULE_BULLETPROOFS +# include "modules/bulletproofs/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/tests_impl.h" #endif @@ -7452,6 +7456,10 @@ int main(int argc, char **argv) { /* EC key arithmetic test */ run_eckey_negate_test(); +#ifdef ENABLE_MODULE_BULLETPROOFS + run_bulletproofs_tests(); +#endif + #ifdef ENABLE_MODULE_ECDH /* ecdh tests */ run_ecdh_tests(); From 48563c8c791d2d5ed50dabde9de8c0839f43c8f3 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 12 Nov 2020 03:22:44 +0000 Subject: [PATCH 221/381] bulletproofs: add API functionality to generate a large set of generators --- include/secp256k1_bulletproofs.h | 57 ++++++++++++- src/modules/bulletproofs/main_impl.h | 109 ++++++++++++++++++++++++- src/modules/bulletproofs/tests_impl.h | 110 +++++++++++++++++++++++++- 3 files changed, 273 insertions(+), 3 deletions(-) diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h index 889d790b..1ddd9699 100644 --- a/include/secp256k1_bulletproofs.h +++ b/include/secp256k1_bulletproofs.h @@ -9,7 +9,62 @@ extern "C" { #include -/* TODO */ +/** Opaque structure representing a large number of NUMS generators */ +typedef struct secp256k1_bulletproofs_generators secp256k1_bulletproofs_generators; + +/** Allocates and initializes a list of NUMS generators. + * Returns a list of generators, or calls the error callback if the allocation fails. + * Args: ctx: pointer to a context object + * n: number of NUMS generators to produce. + * + * TODO: In a followup range-proof PR, this is would still require 16 + 8 = 24 NUMS + * points. We will later use G = H0(required for compatibility with pedersen_commitment DS) + * in a separate commit to make review easier. + */ +SECP256K1_API secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create( + const secp256k1_context* ctx, + size_t n +) SECP256K1_ARG_NONNULL(1); + +/** Allocates a list of generators from a static array + * Returns a list of generators or NULL in case of failure. + * Args: ctx: pointer to a context object + * In: data: data that came from `secp256k1_bulletproofs_generators_serialize` + * data_len: the length of the `data` buffer + */ +SECP256K1_API secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse( + const secp256k1_context* ctx, + const unsigned char* data, + size_t data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Serializes a list of generators to an array + * Returns 1 on success, 0 if the provided array was not large enough + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be serialized + * Out: data: pointer to buffer into which the generators will be serialized + * In/Out: data_len: the length of the `data` buffer. Should be at least + * k = 33 * num_gens. Will be set to k on successful return + * + * TODO: For ease of review, this setting G = H0 is not included in this commit. We will + * add it in the follow-up rangeproof PR. + */ +SECP256K1_API int secp256k1_bulletproofs_generators_serialize( + const secp256k1_context* ctx, + const secp256k1_bulletproofs_generators* gen, + unsigned char* data, + size_t *data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Destroys a list of NUMS generators, freeing allocated memory + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be destroyed + * (can be NULL, in which case this function is a no-op) + */ +SECP256K1_API void secp256k1_bulletproofs_generators_destroy( + const secp256k1_context* ctx, + secp256k1_bulletproofs_generators* gen +) SECP256K1_ARG_NONNULL(1); # ifdef __cplusplus } diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h index 9c61eaf7..ef0ac78c 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bulletproofs/main_impl.h @@ -7,6 +7,113 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ #define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ -/* TODO */ +#include "include/secp256k1_bulletproofs.h" +#include "include/secp256k1_generator.h" +#include "modules/generator/main_impl.h" /* for generator_{load, save} */ +#include "hash.h" +#include "util.h" + +struct secp256k1_bulletproofs_generators { + size_t n; + /* n total generators; includes both G_i and H_i */ + secp256k1_ge* gens; +}; + +secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(const secp256k1_context *ctx, size_t n) { + secp256k1_bulletproofs_generators *ret; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned char seed[64]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + ret->n = n; + + secp256k1_fe_get_b32(&seed[0], &secp256k1_ge_const_g.x); + secp256k1_fe_get_b32(&seed[32], &secp256k1_ge_const_g.y); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed, 64); + for (i = 0; i < n; i++) { + secp256k1_generator gen; + unsigned char tmp[32] = { 0 }; + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, tmp)); + secp256k1_generator_load(&ret->gens[i], &gen); + } + + return ret; +} + +secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { + size_t n = data_len / 33; + secp256k1_bulletproofs_generators* ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(data != NULL); + + if (data_len % 33 != 0) { + return NULL; + } + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->n = n; + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + + while (n--) { + secp256k1_generator gen; + if (!secp256k1_generator_parse(ctx, &gen, &data[33 * n])) { + free(ret->gens); + free(ret); + return NULL; + } + secp256k1_generator_load(&ret->gens[n], &gen); + } + return ret; +} + +int secp256k1_bulletproofs_generators_serialize(const secp256k1_context* ctx, const secp256k1_bulletproofs_generators* gens, unsigned char* data, size_t *data_len) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(data != NULL); + ARG_CHECK(data_len != NULL); + ARG_CHECK(*data_len >= 33 * gens->n); + + memset(data, 0, *data_len); + for (i = 0; i < gens->n; i++) { + secp256k1_generator gen; + secp256k1_generator_save(&gen, &gens->gens[i]); + secp256k1_generator_serialize(ctx, &data[33 * i], &gen); + } + + *data_len = 33 * gens->n; + return 1; +} + +void secp256k1_bulletproofs_generators_destroy(const secp256k1_context* ctx, secp256k1_bulletproofs_generators *gens) { + VERIFY_CHECK(ctx != NULL); + (void) ctx; + if (gens != NULL) { + free(gens->gens); + free(gens); + } +} #endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index 0c88fb78..f75b7294 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -7,8 +7,116 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ #define _SECP256K1_MODULE_BULLETPROOFS_TEST_ +static void test_bulletproofs_generators_api(void) { + /* The BP generator API requires no precomp */ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + secp256k1_bulletproofs_generators *gens; + secp256k1_bulletproofs_generators *gens_orig; + unsigned char gens_ser[330]; + size_t len = sizeof(gens_ser); + + int32_t ecount = 0; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + + /* Create */ + gens = secp256k1_bulletproofs_generators_create(none, 10); + CHECK(gens != NULL && ecount == 0); + gens_orig = gens; /* Preserve for round-trip test */ + + /* Serialize */ + ecount = 0; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, NULL, gens_ser, &len)); + CHECK(ecount == 1); + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, NULL, &len)); + CHECK(ecount == 2); + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, NULL)); + CHECK(ecount == 3); + len = 0; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(ecount == 4); + len = sizeof(gens_ser) - 1; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(ecount == 5); + len = sizeof(gens_ser); + { + /* Output buffer can be greater than minimum needed */ + unsigned char gens_ser_tmp[331]; + size_t len_tmp = sizeof(gens_ser_tmp); + CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser_tmp, &len_tmp)); + CHECK(len_tmp == sizeof(gens_ser_tmp) - 1); + CHECK(ecount == 5); + } + + /* Parse */ + CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + ecount = 0; + gens = secp256k1_bulletproofs_generators_parse(none, NULL, sizeof(gens_ser)); + CHECK(gens == NULL && ecount == 1); + /* Not a multiple of 33 */ + gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser) - 1); + CHECK(gens == NULL && ecount == 1); + gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)); + CHECK(gens != NULL && ecount == 1); + /* Not valid generators */ + memset(gens_ser, 1, sizeof(gens_ser)); + CHECK(secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)) == NULL); + CHECK(ecount == 1); + + /* Check that round-trip succeeded */ + CHECK(gens->n == gens_orig->n); + for (len = 0; len < gens->n; len++) { + ge_equals_ge(&gens->gens[len], &gens_orig->gens[len]); + } + + /* Destroy (we allow destroying a NULL context, it's just a noop. like free().) */ + ecount = 0; + secp256k1_bulletproofs_generators_destroy(none, NULL); + secp256k1_bulletproofs_generators_destroy(none, gens); + secp256k1_bulletproofs_generators_destroy(none, gens_orig); + CHECK(ecount == 0); + + secp256k1_context_destroy(none); +} + +static void test_bulletproofs_generators_fixed(void) { + secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, 3); + unsigned char gens_ser[330]; + const unsigned char fixed_first_3[99] = { + 0x0b, + 0xb3, 0x4d, 0x5f, 0xa6, 0xb8, 0xf3, 0xd1, 0x38, + 0x49, 0xce, 0x51, 0x91, 0xb7, 0xf6, 0x76, 0x18, + 0xfe, 0x5b, 0xd1, 0x2a, 0x88, 0xb2, 0x0e, 0xac, + 0x33, 0x89, 0x45, 0x66, 0x7f, 0xb3, 0x30, 0x56, + 0x0a, + 0x62, 0x86, 0x15, 0x16, 0x92, 0x42, 0x10, 0x9e, + 0x9e, 0x64, 0xd4, 0xcb, 0x28, 0x81, 0x60, 0x9c, + 0x24, 0xb9, 0x89, 0x51, 0x2a, 0xd9, 0x01, 0xae, + 0xff, 0x75, 0x64, 0x9c, 0x37, 0x5d, 0xbd, 0x79, + 0x0a, + 0xed, 0xe0, 0x6e, 0x07, 0x5e, 0x79, 0xd0, 0xf7, + 0x7b, 0x03, 0x3e, 0xb9, 0xa9, 0x21, 0xa4, 0x5b, + 0x99, 0xf3, 0x9b, 0xee, 0xfe, 0xa0, 0x37, 0xa2, + 0x1f, 0xe9, 0xd7, 0x4f, 0x95, 0x8b, 0x10, 0xe2, + }; + size_t len; + + len = 99; + CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + len = sizeof(gens_ser); + CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + secp256k1_bulletproofs_generators_destroy(ctx, gens); +} + void run_bulletproofs_tests(void) { - /* TODO */ + test_bulletproofs_generators_api(); + test_bulletproofs_generators_fixed(); } #endif From 17417d44f307a44e42468200458c3eb2c407b6b8 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 21 Nov 2022 19:31:24 -0800 Subject: [PATCH 222/381] Add utilities from uncompressed Bulletproofs PR Add a transcript module for doing a generic Fiat Shamir --- src/modules/bulletproofs/Makefile.am.include | 4 +- .../bulletproofs_pp_transcript_impl.h | 40 ++++++++++++++++++ src/modules/bulletproofs/bulletproofs_util.h | 42 +++++++++++++++++++ src/modules/bulletproofs/main_impl.h | 13 +++--- src/modules/bulletproofs/tests_impl.h | 17 ++++++++ 5 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h create mode 100644 src/modules/bulletproofs/bulletproofs_util.h diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include index 6cd9fc3c..6a0773b3 100644 --- a/src/modules/bulletproofs/Makefile.am.include +++ b/src/modules/bulletproofs/Makefile.am.include @@ -1,6 +1,8 @@ include_HEADERS += include/secp256k1_bulletproofs.h -noinst_HEADERS += src/modules/bulletproofs/tests_impl.h +noinst_HEADERS += src/modules/bulletproofs/bulletproofs_util.h noinst_HEADERS += src/modules/bulletproofs/main_impl.h +noinst_HEADERS += src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h +noinst_HEADERS += src/modules/bulletproofs/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_bulletproofs diff --git a/src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h b/src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h new file mode 100644 index 00000000..e8444e91 --- /dev/null +++ b/src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h @@ -0,0 +1,40 @@ +/********************************************************************** + * Copyright (c) 2022 Sanket Kanjalkar * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#ifndef _SECP256K1_MODULE_BULLETPROOFS_PP_TRANSCRIPT_IMPL_ +#define _SECP256K1_MODULE_BULLETPROOFS_PP_TRANSCRIPT_IMPL_ + +#include "group.h" +#include "scalar.h" +#include "bulletproofs_util.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("Bulletproofs_pp/v0/commitment")||SHA256("Bulletproofs_pp/v0/commitment"). + */ +static void secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x52fc8185ul; + sha->s[1] = 0x0e7debf0ul; + sha->s[2] = 0xb0967270ul; + sha->s[3] = 0x6f5abfe1ul; + sha->s[4] = 0x822bdec0ul; + sha->s[5] = 0x36db8beful; + sha->s[6] = 0x03d9e1f1ul; + sha->s[7] = 0x8a5cef6ful; + + sha->bytes = 64; +} + +/* Obtain a challenge scalar from the current transcript.*/ +static void secp256k1_bulletproofs_challenge_scalar(secp256k1_scalar* ch, const secp256k1_sha256 *transcript, uint64_t idx) { + unsigned char buf[32]; + secp256k1_sha256 sha = *transcript; + secp256k1_bulletproofs_le64(buf, idx); + secp256k1_sha256_write(&sha, buf, 8); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(ch, buf, NULL); +} + +#endif diff --git a/src/modules/bulletproofs/bulletproofs_util.h b/src/modules/bulletproofs/bulletproofs_util.h new file mode 100644 index 00000000..20d1c748 --- /dev/null +++ b/src/modules/bulletproofs/bulletproofs_util.h @@ -0,0 +1,42 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_UTIL_ +#define _SECP256K1_MODULE_BULLETPROOFS_UTIL_ + +#include "field.h" +#include "group.h" +#include "hash.h" + +/* Outputs a pair of points, amortizing the parity byte between them + * Assumes both points' coordinates have been normalized. + */ +static void secp256k1_bulletproofs_serialize_points(unsigned char *output, const secp256k1_ge *lpt, const secp256k1_ge *rpt) { + output[0] = (secp256k1_fe_is_odd(&lpt->y) << 1) + secp256k1_fe_is_odd(&rpt->y); + secp256k1_fe_get_b32(&output[1], &lpt->x); + secp256k1_fe_get_b32(&output[33], &rpt->x); +} + +/* Outputs a serialized point in compressed form. Returns 0 at point at infinity. +*/ +static int secp256k1_bulletproofs_serialize_pt(unsigned char *output, secp256k1_ge *lpt) { + size_t size; + return secp256k1_eckey_pubkey_serialize(lpt, output, &size, 1 /*compressed*/); +} + +/* little-endian encodes a uint64 */ +static void secp256k1_bulletproofs_le64(unsigned char *output, const uint64_t n) { + output[0] = n; + output[1] = n >> 8; + output[2] = n >> 16; + output[3] = n >> 24; + output[4] = n >> 32; + output[5] = n >> 40; + output[6] = n >> 48; + output[7] = n >> 56; +} + +#endif diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h index ef0ac78c..047094c7 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bulletproofs/main_impl.h @@ -7,18 +7,19 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ #define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ -#include "include/secp256k1_bulletproofs.h" -#include "include/secp256k1_generator.h" -#include "modules/generator/main_impl.h" /* for generator_{load, save} */ -#include "hash.h" -#include "util.h" - +/* this type must be completed before any of the modules/bulletproofs includes */ struct secp256k1_bulletproofs_generators { size_t n; /* n total generators; includes both G_i and H_i */ secp256k1_ge* gens; }; +#include "include/secp256k1_bulletproofs.h" +#include "include/secp256k1_generator.h" +#include "modules/generator/main_impl.h" /* for generator_{load, save} */ +#include "hash.h" +#include "util.h" + secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(const secp256k1_context *ctx, size_t n) { secp256k1_bulletproofs_generators *ret; secp256k1_rfc6979_hmac_sha256 rng; diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index f75b7294..930b35af 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -7,6 +7,8 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ #define _SECP256K1_MODULE_BULLETPROOFS_TEST_ +#include "bulletproofs_pp_transcript_impl.h" + static void test_bulletproofs_generators_api(void) { /* The BP generator API requires no precomp */ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -114,9 +116,24 @@ static void test_bulletproofs_generators_fixed(void) { secp256k1_bulletproofs_generators_destroy(ctx, gens); } +static void test_bulletproofs_pp_tagged_hash(void) { + unsigned char tag_data[29] = "Bulletproofs_pp/v0/commitment"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_cached; + unsigned char output[32]; + unsigned char output_cached[32]; + + secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data)); + secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(&sha_cached); + secp256k1_sha256_finalize(&sha, output); + secp256k1_sha256_finalize(&sha_cached, output_cached); + CHECK(secp256k1_memcmp_var(output, output_cached, 32) == 0); +} + void run_bulletproofs_tests(void) { test_bulletproofs_generators_api(); test_bulletproofs_generators_fixed(); + test_bulletproofs_pp_tagged_hash(); } #endif From 420353d7da7793513621da3a5ad7479feaf76713 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 30 Jan 2023 23:46:03 -0800 Subject: [PATCH 223/381] Add utilities for log2 --- src/modules/bulletproofs/bulletproofs_util.h | 13 +++++++++++++ src/modules/bulletproofs/tests_impl.h | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/modules/bulletproofs/bulletproofs_util.h b/src/modules/bulletproofs/bulletproofs_util.h index 20d1c748..2bdae23f 100644 --- a/src/modules/bulletproofs/bulletproofs_util.h +++ b/src/modules/bulletproofs/bulletproofs_util.h @@ -39,4 +39,17 @@ static void secp256k1_bulletproofs_le64(unsigned char *output, const uint64_t n) output[7] = n >> 56; } +/* Check if n is power of two*/ +static int secp256k1_is_power_of_two(size_t n) { + return n > 0 && (n & (n - 1)) == 0; +} + +/* Compute the log2 of n. If n is not a power of two, it returns the largest + * `k` such that 2^k <= n. Assumes n < 2^64. In Bulletproofs, this is bounded + * by len of input vectors which can be safely assumed to be less than 2^64. +*/ +static size_t secp256k1_bulletproofs_pp_log2(size_t n) { + return 64 - 1 - secp256k1_clz64_var((uint64_t)n); +} + #endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index 930b35af..bec7c84f 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -7,6 +7,10 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ #define _SECP256K1_MODULE_BULLETPROOFS_TEST_ +#include + +#include "include/secp256k1_bulletproofs.h" +#include "bulletproofs_util.h" #include "bulletproofs_pp_transcript_impl.h" static void test_bulletproofs_generators_api(void) { @@ -130,7 +134,23 @@ static void test_bulletproofs_pp_tagged_hash(void) { CHECK(secp256k1_memcmp_var(output, output_cached, 32) == 0); } +void test_log_exp(void) { + CHECK(secp256k1_is_power_of_two(0) == 0); + CHECK(secp256k1_is_power_of_two(1) == 1); + CHECK(secp256k1_is_power_of_two(2) == 1); + CHECK(secp256k1_is_power_of_two(64) == 1); + CHECK(secp256k1_is_power_of_two(63) == 0); + CHECK(secp256k1_is_power_of_two(256) == 1); + + CHECK(secp256k1_bulletproofs_pp_log2(1) == 0); + CHECK(secp256k1_bulletproofs_pp_log2(2) == 1); + CHECK(secp256k1_bulletproofs_pp_log2(255) == 7); + CHECK(secp256k1_bulletproofs_pp_log2(256) == 8); + CHECK(secp256k1_bulletproofs_pp_log2(257) == 8); +} + void run_bulletproofs_tests(void) { + test_log_exp(); test_bulletproofs_generators_api(); test_bulletproofs_generators_fixed(); test_bulletproofs_pp_tagged_hash(); From 412f8f66a08ef0e60644c7b5b22ee2a3d19ae3e8 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Tue, 25 Oct 2022 23:29:26 -0700 Subject: [PATCH 224/381] Add utility functions required in norm argument --- src/modules/bulletproofs/Makefile.am.include | 1 + .../bulletproofs_pp_norm_product_impl.h | 81 +++++++++++++++++++ src/modules/bulletproofs/main.h | 13 +++ src/modules/bulletproofs/main_impl.h | 9 +-- src/modules/bulletproofs/tests_impl.h | 39 +++++++++ 5 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h create mode 100644 src/modules/bulletproofs/main.h diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include index 6a0773b3..cfd0916d 100644 --- a/src/modules/bulletproofs/Makefile.am.include +++ b/src/modules/bulletproofs/Makefile.am.include @@ -2,6 +2,7 @@ include_HEADERS += include/secp256k1_bulletproofs.h noinst_HEADERS += src/modules/bulletproofs/bulletproofs_util.h noinst_HEADERS += src/modules/bulletproofs/main_impl.h noinst_HEADERS += src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h +noinst_HEADERS += src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h noinst_HEADERS += src/modules/bulletproofs/tests_impl.h if USE_BENCHMARK diff --git a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h new file mode 100644 index 00000000..1e17ed34 --- /dev/null +++ b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h @@ -0,0 +1,81 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_PP_NORM_PRODUCT_ +#define _SECP256K1_MODULE_BULLETPROOFS_PP_NORM_PRODUCT_ + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#include "hash.h" + +#include "modules/bulletproofs/main.h" +#include "modules/bulletproofs/bulletproofs_util.h" + +/* Computes the inner product of two vectors of scalars + * with elements starting from offset a and offset b + * skipping elements according to specified step. + * Returns: Sum_{i=0..len-1}(a[offset_a + i*step] * b[offset_b + i*step]) */ +static int secp256k1_scalar_inner_product( + secp256k1_scalar* res, + const secp256k1_scalar* a_vec, + const size_t a_offset, + const secp256k1_scalar* b_vec, + const size_t b_offset, + const size_t step, + const size_t len +) { + size_t i; + secp256k1_scalar_set_int(res, 0); + for (i = 0; i < len; i++) { + secp256k1_scalar term; + secp256k1_scalar_mul(&term, &a_vec[a_offset + step*i], &b_vec[b_offset + step*i]); + secp256k1_scalar_add(res, res, &term); + } + return 1; +} + +/* Computes the q-weighted inner product of two vectors of scalars + * for elements starting from offset a and offset b respectively with the + * given step. + * Returns: Sum_{i=0..len-1}(a[offset_a + step*i] * b[offset_b2 + step*i]*q^(i+1)) */ +static int secp256k1_weighted_scalar_inner_product( + secp256k1_scalar* res, + const secp256k1_scalar* a_vec, + const size_t a_offset, + const secp256k1_scalar* b_vec, + const size_t b_offset, + const size_t step, + const size_t len, + const secp256k1_scalar* q +) { + secp256k1_scalar q_pow; + size_t i; + secp256k1_scalar_set_int(res, 0); + q_pow = *q; + for (i = 0; i < len; i++) { + secp256k1_scalar term; + secp256k1_scalar_mul(&term, &a_vec[a_offset + step*i], &b_vec[b_offset + step*i]); + secp256k1_scalar_mul(&term, &term, &q_pow); + secp256k1_scalar_mul(&q_pow, &q_pow, q); + secp256k1_scalar_add(res, res, &term); + } + return 1; +} + +/* Compute the powers of r as r, r^2, r^4 ... r^(2^(n-1)) */ +static void secp256k1_bulletproofs_powers_of_r(secp256k1_scalar *powers, const secp256k1_scalar *r, size_t n) { + size_t i; + if (n == 0) { + return; + } + powers[0] = *r; + for (i = 1; i < n; i++) { + secp256k1_scalar_sqr(&powers[i], &powers[i - 1]); + } +} +#endif diff --git a/src/modules/bulletproofs/main.h b/src/modules/bulletproofs/main.h new file mode 100644 index 00000000..4174102a --- /dev/null +++ b/src/modules/bulletproofs/main.h @@ -0,0 +1,13 @@ +#ifndef SECP256K1_MODULE_BULLETPROOFS_MAIN_H +#define SECP256K1_MODULE_BULLETPROOFS_MAIN_H + +/* this type must be completed before any of the modules/bulletproofs includes */ +struct secp256k1_bulletproofs_generators { + size_t n; + /* n total generators; includes both G_i and H_i */ + /* For BP++, the generators are G_i from [0..(n - 8)] and the last 8 values + are generators are for H_i */ + secp256k1_ge* gens; +}; + +#endif diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h index 047094c7..f87b9876 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bulletproofs/main_impl.h @@ -7,18 +7,13 @@ #ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ #define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ -/* this type must be completed before any of the modules/bulletproofs includes */ -struct secp256k1_bulletproofs_generators { - size_t n; - /* n total generators; includes both G_i and H_i */ - secp256k1_ge* gens; -}; - #include "include/secp256k1_bulletproofs.h" #include "include/secp256k1_generator.h" #include "modules/generator/main_impl.h" /* for generator_{load, save} */ #include "hash.h" #include "util.h" +#include "modules/bulletproofs/main.h" +#include "modules/bulletproofs/bulletproofs_pp_norm_product_impl.h" secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(const secp256k1_context *ctx, size_t n) { secp256k1_bulletproofs_generators *ret; diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index bec7c84f..e3daead4 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -10,6 +10,7 @@ #include #include "include/secp256k1_bulletproofs.h" +#include "bulletproofs_pp_norm_product_impl.h" #include "bulletproofs_util.h" #include "bulletproofs_pp_transcript_impl.h" @@ -149,8 +150,46 @@ void test_log_exp(void) { CHECK(secp256k1_bulletproofs_pp_log2(257) == 8); } +void test_norm_util_helpers(void) { + secp256k1_scalar a_vec[4], b_vec[4], r_pows[4], res, res2, q, r; + int i; + /* a = {1, 2, 3, 4} b = {5, 6, 7, 8}, q = 4, r = 2 */ + for (i = 0; i < 4; i++) { + secp256k1_scalar_set_int(&a_vec[i], i + 1); + secp256k1_scalar_set_int(&b_vec[i], i + 5); + } + secp256k1_scalar_set_int(&q, 4); + secp256k1_scalar_set_int(&r, 2); + secp256k1_scalar_inner_product(&res, a_vec, 0, b_vec, 0, 1, 4); + secp256k1_scalar_set_int(&res2, 70); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_scalar_inner_product(&res, a_vec, 0, b_vec, 1, 2, 2); + secp256k1_scalar_set_int(&res2, 30); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_scalar_inner_product(&res, a_vec, 1, b_vec, 0, 2, 2); + secp256k1_scalar_set_int(&res2, 38); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_scalar_inner_product(&res, a_vec, 1, b_vec, 1, 2, 2); + secp256k1_scalar_set_int(&res2, 44); + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_weighted_scalar_inner_product(&res, a_vec, 0, a_vec, 0, 1, 4, &q); + secp256k1_scalar_set_int(&res2, 4740); /*i*i*4^(i+1) */ + CHECK(secp256k1_scalar_eq(&res2, &res) == 1); + + secp256k1_bulletproofs_powers_of_r(r_pows, &r, 4); + secp256k1_scalar_set_int(&res, 2); CHECK(secp256k1_scalar_eq(&res, &r_pows[0])); + secp256k1_scalar_set_int(&res, 4); CHECK(secp256k1_scalar_eq(&res, &r_pows[1])); + secp256k1_scalar_set_int(&res, 16); CHECK(secp256k1_scalar_eq(&res, &r_pows[2])); + secp256k1_scalar_set_int(&res, 256); CHECK(secp256k1_scalar_eq(&res, &r_pows[3])); +} + void run_bulletproofs_tests(void) { test_log_exp(); + test_norm_util_helpers(); test_bulletproofs_generators_api(); test_bulletproofs_generators_fixed(); test_bulletproofs_pp_tagged_hash(); From 8638f0e0cecad113e11b826a41bed1fe7a8d3b85 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Wed, 26 Oct 2022 00:08:35 -0700 Subject: [PATCH 225/381] Add internal BP++ commit API --- .../bulletproofs_pp_norm_product_impl.h | 66 +++++++++++++++++++ src/modules/bulletproofs/bulletproofs_util.h | 1 + 2 files changed, 67 insertions(+) diff --git a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h index 1e17ed34..f366fea2 100644 --- a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h +++ b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h @@ -78,4 +78,70 @@ static void secp256k1_bulletproofs_powers_of_r(secp256k1_scalar *powers, const s secp256k1_scalar_sqr(&powers[i], &powers[i - 1]); } } + +typedef struct ecmult_bp_commit_cb_data { + const secp256k1_scalar *n; + const secp256k1_ge *g; + const secp256k1_scalar *l; + size_t g_len; +} ecmult_bp_commit_cb_data; + +static int ecmult_bp_commit_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_bp_commit_cb_data *data = (ecmult_bp_commit_cb_data*) cbdata; + *pt = data->g[idx]; + if (idx < data->g_len) { + *sc = data->n[idx]; + } else { + *sc = data->l[idx - data->g_len]; + } + return 1; +} + +/* Create a commitment `commit` = vG + n_vec*G_vec + l_vec*H_vec where + v = |n_vec*n_vec|_q + . |w|_q denotes q-weighted norm of w and + denotes inner product of l and r. +*/ +static int secp256k1_bulletproofs_commit( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + secp256k1_ge* commit, + const secp256k1_bulletproofs_generators* g_vec, + const secp256k1_scalar* n_vec, + size_t n_vec_len, + const secp256k1_scalar* l_vec, + size_t l_vec_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_scalar* q +) { + secp256k1_scalar v, l_c; + /* First n_vec_len generators are Gs, rest are Hs*/ + VERIFY_CHECK(g_vec->n == (n_vec_len + l_vec_len)); + VERIFY_CHECK(l_vec_len == c_vec_len); + + /* It is possible to extend to support n_vec and c_vec to not be power of + two. For the initial iterations of the code, we stick to powers of two for simplicity.*/ + VERIFY_CHECK(secp256k1_is_power_of_two(n_vec_len)); + VERIFY_CHECK(secp256k1_is_power_of_two(c_vec_len)); + + /* Compute v = n_vec*n_vec*q + l_vec*c_vec */ + secp256k1_weighted_scalar_inner_product(&v, n_vec, 0 /*a offset */, n_vec, 0 /*b offset*/, 1 /*step*/, n_vec_len, q); + secp256k1_scalar_inner_product(&l_c, l_vec, 0 /*a offset */, c_vec, 0 /*b offset*/, 1 /*step*/, l_vec_len); + secp256k1_scalar_add(&v, &v, &l_c); + + { + ecmult_bp_commit_cb_data data; + secp256k1_gej commitj; + data.g = g_vec->gens; + data.n = n_vec; + data.l = l_vec; + data.g_len = n_vec_len; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &commitj, &v, ecmult_bp_commit_cb, (void*) &data, n_vec_len + l_vec_len)) { + return 0; + } + secp256k1_ge_set_gej_var(commit, &commitj); + } + return 1; +} #endif diff --git a/src/modules/bulletproofs/bulletproofs_util.h b/src/modules/bulletproofs/bulletproofs_util.h index 2bdae23f..07f6fc62 100644 --- a/src/modules/bulletproofs/bulletproofs_util.h +++ b/src/modules/bulletproofs/bulletproofs_util.h @@ -10,6 +10,7 @@ #include "field.h" #include "group.h" #include "hash.h" +#include "eckey.h" /* Outputs a pair of points, amortizing the parity byte between them * Assumes both points' coordinates have been normalized. From d9145455bb741c9f363c2a085abd0109e63c961f Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Wed, 26 Oct 2022 00:48:27 -0700 Subject: [PATCH 226/381] Add bulletproofs++ norm argument prove API --- .../bulletproofs_pp_norm_product_impl.h | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h index f366fea2..1f7b16f1 100644 --- a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h +++ b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h @@ -15,6 +15,7 @@ #include "modules/bulletproofs/main.h" #include "modules/bulletproofs/bulletproofs_util.h" +#include "modules/bulletproofs/bulletproofs_pp_transcript_impl.h" /* Computes the inner product of two vectors of scalars * with elements starting from offset a and offset b @@ -144,4 +145,218 @@ static int secp256k1_bulletproofs_commit( } return 1; } + +typedef struct ecmult_x_cb_data { + const secp256k1_scalar *n; + const secp256k1_ge *g; + const secp256k1_scalar *l; + const secp256k1_scalar *r; + const secp256k1_scalar *r_inv; + size_t G_GENS_LEN; /* Figure out initialization syntax so that this can also be const */ + size_t n_len; +} ecmult_x_cb_data; + +static int ecmult_x_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_x_cb_data *data = (ecmult_x_cb_data*) cbdata; + if (idx < data->n_len) { + if (idx % 2 == 0) { + secp256k1_scalar_mul(sc, &data->n[idx + 1], data->r); + *pt = data->g[idx]; + } else { + secp256k1_scalar_mul(sc, &data->n[idx - 1], data->r_inv); + *pt = data->g[idx]; + } + } else { + idx -= data->n_len; + if (idx % 2 == 0) { + *sc = data->l[idx + 1]; + *pt = data->g[data->G_GENS_LEN + idx]; + } else { + *sc = data->l[idx - 1]; + *pt = data->g[data->G_GENS_LEN + idx]; + } + } + return 1; +} + +typedef struct ecmult_r_cb_data { + const secp256k1_scalar *n1; + const secp256k1_ge *g1; + const secp256k1_scalar *l1; + size_t G_GENS_LEN; + size_t n_len; +} ecmult_r_cb_data; + +static int ecmult_r_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_r_cb_data *data = (ecmult_r_cb_data*) cbdata; + if (idx < data->n_len) { + *sc = data->n1[2*idx + 1]; + *pt = data->g1[2*idx + 1]; + } else { + idx -= data->n_len; + *sc = data->l1[2*idx + 1]; + *pt = data->g1[data->G_GENS_LEN + 2*idx + 1]; + } + return 1; +} + +/* Recursively compute the norm argument proof satisfying the relation + * _q + = v for some commitment + * C = v*G + + . _q is the weighted inner + * product of x with itself, where the weights are the first n powers of q. + * _q = q*x_1^2 + q^2*x_2^2 + q^3*x_3^2 + ... + q^n*x_n^2. + * The API computes q as square of the r challenge (`r^2`). + * + * The norm argument is not zero knowledge and does not operate on any secret data. + * Thus the following code uses variable time operations while computing the proof. + * This function also modifies the values of n_vec, l_vec, c_vec and g_vec. The caller + * is expected to copy these values if they need to be preserved. + * + * Assumptions: This function is intended to be used in conjunction with the + * some parent protocol. To use this norm protocol in a standalone manner, the user + * should add the commitment, generators and initial public data to the transcript hash. +*/ +static int secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + unsigned char* proof, + size_t *proof_len, + secp256k1_sha256* transcript, /* Transcript hash of the parent protocol */ + const secp256k1_scalar* r, + secp256k1_ge* g_vec, + size_t g_vec_len, + secp256k1_scalar* n_vec, + size_t n_vec_len, + secp256k1_scalar* l_vec, + size_t l_vec_len, + secp256k1_scalar* c_vec, + size_t c_vec_len +) { + secp256k1_scalar q_f, r_f = *r; + size_t proof_idx = 0; + ecmult_x_cb_data x_cb_data; + ecmult_r_cb_data r_cb_data; + size_t g_len = n_vec_len, h_len = l_vec_len; + const size_t G_GENS_LEN = g_len; + size_t log_g_len = secp256k1_bulletproofs_pp_log2(g_len), log_h_len = secp256k1_bulletproofs_pp_log2(h_len); + size_t num_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; + + /* Check proof sizes.*/ + VERIFY_CHECK(*proof_len >= 65 * num_rounds + 64); + VERIFY_CHECK(g_vec_len == (n_vec_len + l_vec_len) && l_vec_len == c_vec_len); + VERIFY_CHECK(secp256k1_is_power_of_two(n_vec_len) && secp256k1_is_power_of_two(c_vec_len)); + + x_cb_data.n = n_vec; + x_cb_data.g = g_vec; + x_cb_data.l = l_vec; + x_cb_data.G_GENS_LEN = G_GENS_LEN; + + r_cb_data.n1 = n_vec; + r_cb_data.g1 = g_vec; + r_cb_data.l1 = l_vec; + r_cb_data.G_GENS_LEN = G_GENS_LEN; + secp256k1_scalar_sqr(&q_f, &r_f); + + + while (g_len > 1 || h_len > 1) { + size_t i, num_points; + secp256k1_scalar q_sq, r_inv, c0_l1, c1_l0, x_v, c1_l1, r_v; + secp256k1_gej rj, xj; + secp256k1_ge r_ge, x_ge; + secp256k1_scalar e; + + secp256k1_scalar_inverse_var(&r_inv, &r_f); + secp256k1_scalar_sqr(&q_sq, &q_f); + + /* Compute the X commitment X = WIP(r_inv*n0,n1)_q2 * g + r + */ + secp256k1_scalar_inner_product(&c0_l1, c_vec, 0, l_vec, 1, 2, h_len/2); + secp256k1_scalar_inner_product(&c1_l0, c_vec, 1, l_vec, 0, 2, h_len/2); + secp256k1_weighted_scalar_inner_product(&x_v, n_vec, 0, n_vec, 1, 2, g_len/2, &q_sq); + secp256k1_scalar_mul(&x_v, &x_v, &r_inv); + secp256k1_scalar_add(&x_v, &x_v, &x_v); + secp256k1_scalar_add(&x_v, &x_v, &c0_l1); + secp256k1_scalar_add(&x_v, &x_v, &c1_l0); + + x_cb_data.r = &r_f; + x_cb_data.r_inv = &r_inv; + x_cb_data.n_len = g_len >= 2 ? g_len : 0; + num_points = x_cb_data.n_len + (h_len >= 2 ? h_len : 0); + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &xj, &x_v, ecmult_x_cb, (void*)&x_cb_data, num_points)) { + return 0; + } + + secp256k1_weighted_scalar_inner_product(&r_v, n_vec, 1, n_vec, 1, 2, g_len/2, &q_sq); + secp256k1_scalar_inner_product(&c1_l1, c_vec, 1, l_vec, 1, 2, h_len/2); + secp256k1_scalar_add(&r_v, &r_v, &c1_l1); + + r_cb_data.n_len = g_len/2; + num_points = r_cb_data.n_len + h_len/2; + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &rj, &r_v, ecmult_r_cb, (void*)&r_cb_data, num_points)) { + return 0; + } + + /* We only fail here because we cannot serialize points at infinity. */ + if (secp256k1_gej_is_infinity(&xj) || secp256k1_gej_is_infinity(&rj)) { + return 0; + } + + secp256k1_ge_set_gej_var(&x_ge, &xj); + secp256k1_fe_normalize_var(&x_ge.x); + secp256k1_fe_normalize_var(&x_ge.y); + secp256k1_ge_set_gej_var(&r_ge, &rj); + secp256k1_fe_normalize_var(&r_ge.x); + secp256k1_fe_normalize_var(&r_ge.y); + secp256k1_bulletproofs_serialize_points(&proof[proof_idx], &x_ge, &r_ge); + proof_idx += 65; + + /* Obtain challenge e for the the next round */ + secp256k1_sha256_write(transcript, &proof[proof_idx - 65], 65); + secp256k1_bulletproofs_challenge_scalar(&e, transcript, 0); + + if (g_len > 1) { + for (i = 0; i < g_len; i = i + 2) { + secp256k1_scalar nl, nr; + secp256k1_gej gl, gr; + secp256k1_scalar_mul(&nl, &n_vec[i], &r_inv); + secp256k1_scalar_mul(&nr, &n_vec[i + 1], &e); + secp256k1_scalar_add(&n_vec[i/2], &nl, &nr); + + secp256k1_gej_set_ge(&gl, &g_vec[i]); + secp256k1_ecmult(&gl, &gl, &r_f, NULL); + secp256k1_gej_set_ge(&gr, &g_vec[i + 1]); + secp256k1_ecmult(&gr, &gr, &e, NULL); + secp256k1_gej_add_var(&gl, &gl, &gr, NULL); + secp256k1_ge_set_gej_var(&g_vec[i/2], &gl); + } + } + + if (h_len > 1) { + for (i = 0; i < h_len; i = i + 2) { + secp256k1_scalar temp1; + secp256k1_gej grj; + secp256k1_scalar_mul(&temp1, &c_vec[i + 1], &e); + secp256k1_scalar_add(&c_vec[i/2], &c_vec[i], &temp1); + + secp256k1_scalar_mul(&temp1, &l_vec[i + 1], &e); + secp256k1_scalar_add(&l_vec[i/2], &l_vec[i], &temp1); + + secp256k1_gej_set_ge(&grj, &g_vec[G_GENS_LEN + i + 1]); + secp256k1_ecmult(&grj, &grj, &e, NULL); + secp256k1_gej_add_ge_var(&grj, &grj, &g_vec[G_GENS_LEN + i], NULL); + secp256k1_ge_set_gej_var(&g_vec[G_GENS_LEN + i/2], &grj); + } + } + g_len = g_len / 2; + h_len = h_len / 2; + r_f = q_f; + q_f = q_sq; + } + + secp256k1_scalar_get_b32(&proof[proof_idx], &n_vec[0]); + secp256k1_scalar_get_b32(&proof[proof_idx + 32], &l_vec[0]); + proof_idx += 64; + *proof_len = proof_idx; + return 1; +} #endif From 060887e9d749062242b4de3935b27fdcb0802c87 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 20 Dec 2022 13:18:02 +0000 Subject: [PATCH 227/381] musig: update to BIP v0.5.1 "Rename ordinary tweaking to plain" --- examples/musig.c | 6 +++--- include/secp256k1_musig.h | 4 ++-- src/modules/musig/musig.md | 4 ++-- src/modules/musig/tests_impl.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 3a657410..faaacc1e 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -55,13 +55,13 @@ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_s * and return the tweaked aggregate pk. */ int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *cache) { secp256k1_pubkey output_pk; - unsigned char ordinary_tweak[32] = "this could be a BIP32 tweak...."; + unsigned char plain_tweak[32] = "this could be a BIP32 tweak...."; unsigned char xonly_tweak[32] = "this could be a taproot tweak.."; - /* Ordinary tweaking which, for example, allows deriving multiple child + /* Plain tweaking which, for example, allows deriving multiple child * public keys from a single aggregate key using BIP32 */ - if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, ordinary_tweak)) { + if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, plain_tweak)) { return 0; } /* Note that we did not provided an output_pk argument, because the diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index adcc060f..2e086b9c 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -226,7 +226,7 @@ SECP256K1_API int secp256k1_musig_pubkey_agg( /** Obtain the aggregate public key from a keyagg_cache. * * This is only useful if you need the non-xonly public key, in particular for - * ordinary (non-xonly) tweaking or batch-verifying multiple key aggregations + * plain (non-xonly) tweaking or batch-verifying multiple key aggregations * (not implemented). * * Returns: 0 if the arguments are invalid, 1 otherwise @@ -241,7 +241,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( secp256k1_musig_keyagg_cache *keyagg_cache ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Apply ordinary "EC" tweaking to a public key in a given keyagg_cache by +/** Apply plain "EC" tweaking to a public key in a given keyagg_cache by * adding the generator multiplied with `tweak32` to it. This is useful for * deriving child keys from an aggregate public key via BIP32. * diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index 5c9b8d78..9a70b312 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -23,7 +23,7 @@ Therefore, users of the musig module must take great care to make sure of the fo # Key Aggregation and (Taproot) Tweaking Given a set of public keys, the aggregate public key is computed with `secp256k1_musig_pubkey_agg`. -A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly_pubkey_tweak_add` and an ordinary tweak can be added with `secp256k1_ec_pubkey_tweak_add`. +A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly_pubkey_tweak_add` and a plain tweak can be added with `secp256k1_ec_pubkey_tweak_add`. # Signing @@ -32,7 +32,7 @@ Essentially, the protocol proceeds in the following steps: 1. Generate a keypair with `secp256k1_keypair_create` and obtain the xonly public key with `secp256k1_keypair_xonly_pub`. 2. Call `secp256k1_musig_pubkey_agg` with the xonly pubkeys of all participants. -3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and an ordinary tweak with `secp256k1_musig_pubkey_ec_tweak_add`. +3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and a plain tweak with `secp256k1_musig_pubkey_ec_tweak_add`. 4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers. 5. Someone (not necessarily the signer) aggregates the public nonce with `secp256k1_musig_nonce_agg` and sends it to the signers. 6. Process the aggregate nonce with `secp256k1_musig_nonce_process`. diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index b18a6610..3daf3ac6 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -857,7 +857,7 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigne } /* Create aggregate public key P[0], tweak multiple times (using xonly and - * ordinary tweaking) and test signing. */ + * plain tweaking) and test signing. */ void musig_tweak_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; secp256k1_xonly_pubkey pk[2]; From 73d5b6654d472eb0cebbffd5a934caf174d29307 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 20 Dec 2022 13:39:44 +0000 Subject: [PATCH 228/381] musig: update to BIP v0.7.0 (NonceGen) - 0.7.0: Change ''NonceGen'' such that output when message is not present is different from when message is present but has length 0. - 0.6.0: Change order of arguments and serialization of the message in the ''NonceGen'' hash function --- src/modules/musig/session_impl.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 559680c8..1e294c98 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -311,14 +311,13 @@ static int secp256k1_xonly_ge_serialize(unsigned char *output32, secp256k1_ge *g /* Write optional inputs into the hash */ static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data32) { - /* The spec requires length prefix to be 4 bytes for `extra_in`, 1 byte - * otherwise */ - VERIFY_CHECK(prefix_size == 4 || prefix_size == 1); - if (prefix_size == 4) { - /* Four byte big-endian value, pad first three bytes with 0 */ - unsigned char zero[3] = {0}; - secp256k1_sha256_write(sha, zero, 3); - } + unsigned char zero[7] = { 0 }; + /* The spec requires length prefixes to be between 1 and 8 bytes + * (inclusive) */ + VERIFY_CHECK(prefix_size <= 8); + /* Since the length of all input data is <= 32, we can always pad the length + * prefix with prefix_size - 1 zero bytes. */ + secp256k1_sha256_write(sha, zero, prefix_size - 1); if (data32 != NULL) { unsigned char len = 32; secp256k1_sha256_write(sha, &len, 1); @@ -333,6 +332,7 @@ static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c secp256k1_sha256 sha; unsigned char rand[32]; unsigned char i; + unsigned char msg_present; if (key32 != NULL) { secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/aux", sizeof("MuSig/aux") - 1); @@ -349,7 +349,11 @@ static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/nonce", sizeof("MuSig/nonce") - 1); secp256k1_sha256_write(&sha, rand, sizeof(rand)); secp256k1_nonce_function_musig_helper(&sha, 1, agg_pk32); - secp256k1_nonce_function_musig_helper(&sha, 1, msg32); + msg_present = msg32 != NULL; + secp256k1_sha256_write(&sha, &msg_present, 1); + if (msg_present) { + secp256k1_nonce_function_musig_helper(&sha, 8, msg32); + } secp256k1_nonce_function_musig_helper(&sha, 4, extra_input32); for (i = 0; i < 2; i++) { From 98242fcdd9519d0d5a349b0344aeea0ab4e796e9 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 13 Jan 2023 19:41:57 +0000 Subject: [PATCH 229/381] extrakeys: add secp256k1_pubkey_cmp --- include/secp256k1_extrakeys.h | 15 +++++++++++ src/modules/extrakeys/main_impl.h | 27 ++++++++++++++++++++ src/modules/extrakeys/tests_impl.h | 40 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index 685d6316..6e0733aa 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -256,6 +256,21 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Compare two public keys using lexicographic order + * + * Returns: <0 if the first public key is less than the second + * >0 if the first public key is greater than the second + * 0 if the two public keys are equal + * Args: ctx: a secp256k1 context object. + * In: pubkey1: first public key to compare + * pubkey2: second public key to compare + */ +SECP256K1_API int secp256k1_pubkey_cmp( + const secp256k1_context* ctx, + const secp256k1_pubkey* pk1, + const secp256k1_pubkey* pk2 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + #ifdef __cplusplus } #endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 8ed76a98..f362b9a0 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -304,4 +304,31 @@ int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_ke return ret; } +int secp256k1_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* pk0, const secp256k1_pubkey* pk1) { + unsigned char out[2][33]; + const secp256k1_pubkey* pk[2]; + int i; + + VERIFY_CHECK(ctx != NULL); + pk[0] = pk0; pk[1] = pk1; + for (i = 0; i < 2; i++) { + size_t outputlen = sizeof(out[i]); + /* If the public key is NULL or invalid, pubkey_serialize will + * call the illegal_callback and return 0. In that case we will + * serialize the key as all zeros which is less than any valid public + * key. This results in consistent comparisons even if NULL or invalid + * pubkeys are involved and prevents edge cases such as sorting + * algorithms that use this function and do not terminate as a + * result. */ + if (!secp256k1_ec_pubkey_serialize(ctx, out[i], &outputlen, pk[i], SECP256K1_EC_COMPRESSED)) { + /* Note that pubkey_serialize should already set the output to + * zero in that case, but it's not guaranteed by the API, we can't + * test it and writing a VERIFY_CHECK is more complex than + * explicitly memsetting (again). */ + memset(out[i], 0, sizeof(out[i])); + } + } + return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1])); +} + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 4121df0c..5f891dea 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -619,6 +619,45 @@ void test_hsort(void) { } #undef NUM +void test_pubkey_comparison(void) { + unsigned char pk1_ser[33] = { + 0x02, + 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11, + 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23 + }; + const unsigned char pk2_ser[33] = { + 0x03, + 0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d, + 0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c + }; + secp256k1_pubkey pk1; + secp256k1_pubkey pk2; + int ecount = 0; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + + CHECK(secp256k1_ec_pubkey_parse(none, &pk1, pk1_ser, sizeof(pk1_ser)) == 1); + CHECK(secp256k1_ec_pubkey_parse(none, &pk2, pk2_ser, sizeof(pk2_ser)) == 1); + + CHECK(secp256k1_pubkey_cmp(none, NULL, &pk2) < 0); + CHECK(ecount == 1); + CHECK(secp256k1_pubkey_cmp(none, &pk1, NULL) > 0); + CHECK(ecount == 2); + CHECK(secp256k1_pubkey_cmp(none, &pk1, &pk2) < 0); + CHECK(secp256k1_pubkey_cmp(none, &pk2, &pk1) > 0); + CHECK(secp256k1_pubkey_cmp(none, &pk1, &pk1) == 0); + CHECK(secp256k1_pubkey_cmp(none, &pk2, &pk2) == 0); + CHECK(ecount == 2); + memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */ + CHECK(secp256k1_pubkey_cmp(none, &pk1, &pk2) < 0); + CHECK(ecount == 3); + CHECK(secp256k1_pubkey_cmp(none, &pk1, &pk1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_pubkey_cmp(none, &pk2, &pk1) > 0); + CHECK(ecount == 6); + + secp256k1_context_destroy(none); +} + void test_xonly_sort_helper(secp256k1_xonly_pubkey *pk, size_t *pk_order, size_t n_pk) { size_t i; const secp256k1_xonly_pubkey *pk_test[5]; @@ -740,6 +779,7 @@ void run_extrakeys_tests(void) { test_hsort(); test_xonly_sort_api(); test_xonly_sort(); + test_pubkey_comparison(); } #endif From ae89051547435cab5042a13d85562def9cabdd61 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 13 Jan 2023 19:42:34 +0000 Subject: [PATCH 230/381] extrakeys: replace xonly_sort with pubkey_sort --- include/secp256k1_extrakeys.h | 28 +++++------ src/modules/extrakeys/main_impl.h | 44 +++++++++--------- src/modules/extrakeys/tests_impl.h | 74 +++++++++++++++--------------- 3 files changed, 72 insertions(+), 74 deletions(-) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index 6e0733aa..deb8dc8b 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -155,20 +155,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_ const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); -/** Sorts xonly public keys according to secp256k1_xonly_pubkey_cmp - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * - * Args: ctx: pointer to a context object - * In: pubkeys: array of pointers to pubkeys to sort - * n_pubkeys: number of elements in the pubkeys array - */ -SECP256K1_API int secp256k1_xonly_sort( - const secp256k1_context* ctx, - const secp256k1_xonly_pubkey **pubkeys, - size_t n_pubkeys -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - /** Compute the keypair for a secret key. * * Returns: 1: secret was valid, keypair is ready to use @@ -271,6 +257,20 @@ SECP256K1_API int secp256k1_pubkey_cmp( const secp256k1_pubkey* pk2 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Sorts public keys using lexicographic order + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * + * Args: ctx: pointer to a context object + * In: pubkeys: array of pointers to pubkeys to sort + * n_pubkeys: number of elements in the pubkeys array + */ +SECP256K1_API int secp256k1_pubkey_sort( + const secp256k1_context* ctx, + const secp256k1_pubkey **pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + #ifdef __cplusplus } #endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index f362b9a0..cadabab0 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -153,28 +153,6 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity; } -/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api - * which expects a non-const cmp_data pointer. */ -typedef struct { - const secp256k1_context *ctx; -} secp256k1_xonly_sort_cmp_data; - -static int secp256k1_xonly_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) { - return secp256k1_xonly_pubkey_cmp(((secp256k1_xonly_sort_cmp_data*)cmp_data)->ctx, - *(secp256k1_xonly_pubkey **)pk1, - *(secp256k1_xonly_pubkey **)pk2); -} - -int secp256k1_xonly_sort(const secp256k1_context* ctx, const secp256k1_xonly_pubkey **pubkeys, size_t n_pubkeys) { - secp256k1_xonly_sort_cmp_data cmp_data; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkeys != NULL); - - cmp_data.ctx = ctx; - secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_xonly_sort_cmp, &cmp_data); - return 1; -} - static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) { secp256k1_scalar_get_b32(&keypair->data[0], sk); secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk); @@ -331,4 +309,26 @@ int secp256k1_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* p return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1])); } +/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api + * which expects a non-const cmp_data pointer. */ +typedef struct { + const secp256k1_context *ctx; +} secp256k1_pubkey_sort_cmp_data; + +static int secp256k1_pubkey_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) { + return secp256k1_pubkey_cmp(((secp256k1_pubkey_sort_cmp_data*)cmp_data)->ctx, + *(secp256k1_pubkey **)pk1, + *(secp256k1_pubkey **)pk2); +} + +int secp256k1_pubkey_sort(const secp256k1_context* ctx, const secp256k1_pubkey **pubkeys, size_t n_pubkeys) { + secp256k1_pubkey_sort_cmp_data cmp_data; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkeys != NULL); + + cmp_data.ctx = ctx; + secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_pubkey_sort_cmp, &cmp_data); + return 1; +} + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 5f891dea..a6aa99b3 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -658,14 +658,14 @@ void test_pubkey_comparison(void) { secp256k1_context_destroy(none); } -void test_xonly_sort_helper(secp256k1_xonly_pubkey *pk, size_t *pk_order, size_t n_pk) { +void test_sort_helper(secp256k1_pubkey *pk, size_t *pk_order, size_t n_pk) { size_t i; - const secp256k1_xonly_pubkey *pk_test[5]; + const secp256k1_pubkey *pk_test[5]; for (i = 0; i < n_pk; i++) { pk_test[i] = &pk[pk_order[i]]; } - secp256k1_xonly_sort(ctx, pk_test, n_pk); + secp256k1_pubkey_sort(ctx, pk_test, n_pk); for (i = 0; i < n_pk; i++) { CHECK(secp256k1_memcmp_var(pk_test[i], &pk[i], sizeof(*pk_test[i])) == 0); } @@ -682,84 +682,82 @@ void permute(size_t *arr, size_t n) { } } -void rand_xonly_pk(secp256k1_xonly_pubkey *pk) { +void rand_pk(secp256k1_pubkey *pk) { unsigned char seckey[32]; secp256k1_keypair keypair; secp256k1_testrand256(seckey); CHECK(secp256k1_keypair_create(ctx, &keypair, seckey) == 1); - CHECK(secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_pub(ctx, pk, &keypair) == 1); } -void test_xonly_sort_api(void) { +void test_sort_api(void) { int ecount = 0; - secp256k1_xonly_pubkey pks[2]; - const secp256k1_xonly_pubkey *pks_ptr[2]; + secp256k1_pubkey pks[2]; + const secp256k1_pubkey *pks_ptr[2]; secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); pks_ptr[0] = &pks[0]; pks_ptr[1] = &pks[1]; - rand_xonly_pk(&pks[0]); - rand_xonly_pk(&pks[1]); + rand_pk(&pks[0]); + rand_pk(&pks[1]); - CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1); - CHECK(secp256k1_xonly_sort(none, NULL, 2) == 0); + CHECK(secp256k1_pubkey_sort(none, pks_ptr, 2) == 1); + CHECK(secp256k1_pubkey_sort(none, NULL, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_xonly_sort(none, pks_ptr, 0) == 1); + CHECK(secp256k1_pubkey_sort(none, pks_ptr, 0) == 1); /* Test illegal public keys */ memset(&pks[0], 0, sizeof(pks[0])); - CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1); + CHECK(secp256k1_pubkey_sort(none, pks_ptr, 2) == 1); CHECK(ecount == 2); memset(&pks[1], 0, sizeof(pks[1])); - CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1); + CHECK(secp256k1_pubkey_sort(none, pks_ptr, 2) == 1); CHECK(ecount > 2); secp256k1_context_destroy(none); } -void test_xonly_sort(void) { - secp256k1_xonly_pubkey pk[5]; - unsigned char pk_ser[5][32]; +void test_sort(void) { + secp256k1_pubkey pk[5]; + unsigned char pk_ser[5][33] = { + { 0x02, 0x08 }, + { 0x02, 0x0b }, + { 0x02, 0x0c }, + { 0x03, 0x05 }, + { 0x03, 0x0a }, + }; int i; size_t pk_order[5] = { 0, 1, 2, 3, 4 }; for (i = 0; i < 5; i++) { - memset(pk_ser[i], 0, sizeof(pk_ser[i])); - } - pk_ser[0][0] = 5; - pk_ser[1][0] = 8; - pk_ser[2][0] = 0x0a; - pk_ser[3][0] = 0x0b; - pk_ser[4][0] = 0x0c; - for (i = 0; i < 5; i++) { - CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i])); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk[i], pk_ser[i], sizeof(pk_ser[i]))); } permute(pk_order, 1); - test_xonly_sort_helper(pk, pk_order, 1); + test_sort_helper(pk, pk_order, 1); permute(pk_order, 2); - test_xonly_sort_helper(pk, pk_order, 2); + test_sort_helper(pk, pk_order, 2); permute(pk_order, 3); - test_xonly_sort_helper(pk, pk_order, 3); + test_sort_helper(pk, pk_order, 3); for (i = 0; i < count; i++) { permute(pk_order, 4); - test_xonly_sort_helper(pk, pk_order, 4); + test_sort_helper(pk, pk_order, 4); } for (i = 0; i < count; i++) { permute(pk_order, 5); - test_xonly_sort_helper(pk, pk_order, 5); + test_sort_helper(pk, pk_order, 5); } /* Check that sorting also works for random pubkeys */ for (i = 0; i < count; i++) { int j; - const secp256k1_xonly_pubkey *pk_ptr[5]; + const secp256k1_pubkey *pk_ptr[5]; for (j = 0; j < 5; j++) { - rand_xonly_pk(&pk[j]); + rand_pk(&pk[j]); pk_ptr[j] = &pk[j]; } - secp256k1_xonly_sort(ctx, pk_ptr, 5); + secp256k1_pubkey_sort(ctx, pk_ptr, 5); for (j = 1; j < 5; j++) { - CHECK(secp256k1_xonly_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], ctx) <= 0); + CHECK(secp256k1_pubkey_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], ctx) <= 0); } } } @@ -777,9 +775,9 @@ void run_extrakeys_tests(void) { test_keypair_add(); test_hsort(); - test_xonly_sort_api(); - test_xonly_sort(); test_pubkey_comparison(); + test_sort_api(); + test_sort(); } #endif From 304f1bc96d6bdb5c1b5b1b9a321eac8f9a27fde4 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 6 Jan 2023 16:41:01 +0000 Subject: [PATCH 231/381] extrakeys: add pubkey_sort test vectors from BIP MuSig2 --- src/modules/extrakeys/tests_impl.h | 46 ++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index a6aa99b3..c4e695b4 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -762,6 +762,51 @@ void test_sort(void) { } } +/* Test vectors from BIP-MuSig2 */ +void test_sort_vectors(void) { + enum { N_PUBKEYS = 6 }; + unsigned char pk_ser[N_PUBKEYS][33] = { + { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, + 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, + 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 }, + { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, + 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, + 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, + { 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, + 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, + 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }, + { 0x02, 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, + 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, + 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 }, + { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, + 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, + 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xFF }, + { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, + 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, + 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 } + }; + secp256k1_pubkey pubkeys[N_PUBKEYS]; + secp256k1_pubkey *sorted[N_PUBKEYS]; + const secp256k1_pubkey *pks_ptr[N_PUBKEYS]; + int i; + + sorted[0] = &pubkeys[3]; + sorted[1] = &pubkeys[0]; + sorted[2] = &pubkeys[0]; + sorted[3] = &pubkeys[4]; + sorted[4] = &pubkeys[1]; + sorted[5] = &pubkeys[2]; + + for (i = 0; i < N_PUBKEYS; i++) { + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkeys[i], pk_ser[i], sizeof(pk_ser[i]))); + pks_ptr[i] = &pubkeys[i]; + } + CHECK(secp256k1_pubkey_sort(ctx, pks_ptr, N_PUBKEYS) == 1); + for (i = 0; i < N_PUBKEYS; i++) { + CHECK(secp256k1_memcmp_var(pks_ptr[i], sorted[i], sizeof(secp256k1_pubkey)) == 0); + } +} + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); @@ -778,6 +823,7 @@ void run_extrakeys_tests(void) { test_pubkey_comparison(); test_sort_api(); test_sort(); + test_sort_vectors(); } #endif From d717a4980bc3e2e36bd32a02466226ef49a5d625 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 20 Dec 2022 16:59:12 +0000 Subject: [PATCH 232/381] musig: update to BIP v0.8 "Switch from X-only to plain pk inputs." --- examples/musig.c | 6 +-- include/secp256k1_musig.h | 12 ++--- src/modules/musig/keyagg.h | 18 +++++-- src/modules/musig/keyagg_impl.h | 88 +++++++++++++++++++++----------- src/modules/musig/musig.md | 4 +- src/modules/musig/session_impl.h | 50 ++++-------------- src/modules/musig/tests_impl.h | 32 ++++++------ src/valgrind_ctime_test.c | 6 +-- 8 files changed, 113 insertions(+), 103 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index faaacc1e..9e681653 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -26,7 +26,7 @@ struct signer_secrets { }; struct signer { - secp256k1_xonly_pubkey pubkey; + secp256k1_pubkey pubkey; secp256k1_musig_pubnonce pubnonce; secp256k1_musig_partial_sig partial_sig; }; @@ -45,7 +45,7 @@ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_s break; } } - if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) { + if (!secp256k1_keypair_pub(ctx, &signer->pubkey, &signer_secrets->keypair)) { return 0; } return 1; @@ -164,7 +164,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st int i; struct signer_secrets signer_secrets[N_SIGNERS]; struct signer signers[N_SIGNERS]; - const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; + const secp256k1_pubkey *pubkeys_ptr[N_SIGNERS]; secp256k1_xonly_pubkey agg_pk; secp256k1_musig_keyagg_cache cache; unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 2e086b9c..f6343717 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -40,11 +40,11 @@ extern "C" { /** Opaque data structure that caches information about public key aggregation. * - * Guaranteed to be 165 bytes in size. It can be safely copied/moved. No + * Guaranteed to be 197 bytes in size. It can be safely copied/moved. No * serialization and parsing functions (yet). */ typedef struct { - unsigned char data[165]; + unsigned char data[197]; } secp256k1_musig_keyagg_cache; /** Opaque data structure that holds a signer's _secret_ nonce. @@ -190,8 +190,8 @@ SECP256K1_API int secp256k1_musig_partial_sig_parse( * * Different orders of `pubkeys` result in different `agg_pk`s. * - * The pubkeys can be sorted before combining with `secp256k1_xonly_sort` which - * ensures the same `agg_pk` result for the same multiset of pubkeys. + * Before aggregating, the pubkeys can be sorted with `secp256k1_pubkey_sort` + * which ensures the same `agg_pk` result for the same multiset of pubkeys. * This is useful to do before `pubkey_agg`, such that the order of pubkeys * does not affect the aggregate public key. * @@ -219,7 +219,7 @@ SECP256K1_API int secp256k1_musig_pubkey_agg( secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_xonly_pubkey * const* pubkeys, + const secp256k1_pubkey * const* pubkeys, size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); @@ -494,7 +494,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, - const secp256k1_xonly_pubkey *pubkey, + const secp256k1_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); diff --git a/src/modules/musig/keyagg.h b/src/modules/musig/keyagg.h index a56dc9ef..9ccea847 100644 --- a/src/modules/musig/keyagg.h +++ b/src/modules/musig/keyagg.h @@ -16,7 +16,9 @@ typedef struct { secp256k1_ge pk; - secp256k1_fe second_pk_x; + /* If there is no "second" public key, second_pk is set to the point at + * infinity */ + secp256k1_ge second_pk; unsigned char pk_hash[32]; /* tweak is identical to value tacc[v] in the specification. */ secp256k1_scalar tweak; @@ -25,13 +27,23 @@ typedef struct { int parity_acc; } secp256k1_keyagg_cache_internal; -/* Requires that the saved point is not infinity */ +/* Save and load points to and from byte arrays, similar to + * secp256k1_pubkey_{save,load}. */ static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge); +/* In contrast to pubkey_load, point_load does not attempt to check that data + * has been initialized, since it is assumed that this check already happened + * (e.g. by comparing magic bytes) */ static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data); +/* point_save_ext and point_load_ext are identical to point_save and point_load + * except that they allow saving and loading the point at infinity */ +static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge); + +static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data); + static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache); -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_fe *x); +static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk); #endif diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 33cdec2a..85bbacfa 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -47,13 +47,30 @@ static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data) { } } +static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) { + if (secp256k1_ge_is_infinity(ge)) { + memset(data, 0, 64); + } else { + secp256k1_point_save(data, ge); + } +} + +static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data) { + unsigned char zeros[64] = { 0 }; + if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) { + secp256k1_ge_set_infinity(ge); + } else { + secp256k1_point_load(ge, data); + } +} + static const unsigned char secp256k1_musig_keyagg_cache_magic[4] = { 0xf4, 0xad, 0xbb, 0xdf }; /* A keyagg cache consists of * - 4 byte magic set during initialization to allow detecting an uninitialized * object. * - 64 byte aggregate (and potentially tweaked) public key - * - 32 byte X-coordinate of "second" public key (0 if not present) + * - 64 byte "second" public key (set to the point at infinity if not present) * - 32 byte hash of all public keys * - 1 byte the parity of the internal key (if tweaked, otherwise 0) * - 32 byte tweak @@ -65,8 +82,8 @@ static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, sec ptr += 4; secp256k1_point_save(ptr, &cache_i->pk); ptr += 64; - secp256k1_fe_get_b32(ptr, &cache_i->second_pk_x); - ptr += 32; + secp256k1_point_save_ext(ptr, &cache_i->second_pk); + ptr += 64; memcpy(ptr, cache_i->pk_hash, 32); ptr += 32; *ptr = cache_i->parity_acc; @@ -80,8 +97,8 @@ static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_k ptr += 4; secp256k1_point_load(&cache_i->pk, ptr); ptr += 64; - secp256k1_fe_set_b32(&cache_i->second_pk_x, ptr); - ptr += 32; + secp256k1_point_load_ext(&cache_i->second_pk, ptr); + ptr += 64; memcpy(cache_i->pk_hash, ptr, 32); ptr += 32; cache_i->parity_acc = *ptr & 1; @@ -107,17 +124,19 @@ static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) { } /* Computes pk_hash = tagged_hash(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_pk_hash(const secp256k1_context *ctx, unsigned char *pk_hash, const secp256k1_xonly_pubkey * const* pk, size_t np) { +static int secp256k1_musig_compute_pk_hash(const secp256k1_context *ctx, unsigned char *pk_hash, const secp256k1_pubkey * const* pk, size_t np) { secp256k1_sha256 sha; size_t i; secp256k1_musig_keyagglist_sha256(&sha); for (i = 0; i < np; i++) { - unsigned char ser[32]; - if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) { + unsigned char ser[33]; + size_t ser_len = sizeof(ser); + if (!secp256k1_ec_pubkey_serialize(ctx, ser, &ser_len, pk[i], SECP256K1_EC_COMPRESSED)) { return 0; } - secp256k1_sha256_write(&sha, ser, 32); + VERIFY_CHECK(ser_len == sizeof(ser)); + secp256k1_sha256_write(&sha, ser, sizeof(ser)); } secp256k1_sha256_finalize(&sha, pk_hash); return 1; @@ -140,52 +159,59 @@ static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { } /* Compute KeyAgg coefficient which is constant 1 for the second pubkey and - * tagged_hash(pk_hash, x) where pk_hash is the hash of public keys otherwise. - * second_pk_x can be 0 in case there is no second_pk. Assumes both field - * elements x and second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, const secp256k1_fe *x, const secp256k1_fe *second_pk_x) { + * otherwise tagged_hash(pk_hash, x) where pk_hash is the hash of public keys. + * second_pk is the point at infinity in case there is no second_pk. Assumes + * that pk is not the point at infinity and that the coordinates of pk and + * second_pk are normalized. */ +static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, secp256k1_ge *pk, const secp256k1_ge *second_pk) { secp256k1_sha256 sha; - unsigned char buf[32]; - if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) { + if (!secp256k1_ge_is_infinity(second_pk) + && secp256k1_fe_equal(&pk->x, &second_pk->x) + && secp256k1_fe_is_odd(&pk->y) == secp256k1_fe_is_odd(&second_pk->y)) { secp256k1_scalar_set_int(r, 1); } else { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + int ret; secp256k1_musig_keyaggcoef_sha256(&sha); secp256k1_sha256_write(&sha, pk_hash, 32); - secp256k1_fe_get_b32(buf, x); - secp256k1_sha256_write(&sha, buf, 32); + ret = secp256k1_eckey_pubkey_serialize(pk, buf, &buflen, 1); + /* Serialization does not fail since the pk is not the point at infinity + * (according to this function's precondition). */ + VERIFY_CHECK(ret && buflen == sizeof(buf)); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); secp256k1_sha256_finalize(&sha, buf); secp256k1_scalar_set_b32(r, buf, NULL); } - } /* Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_fe *x) { - secp256k1_musig_keyaggcoef_internal(r, cache_i->pk_hash, x, &cache_i->second_pk_x); +static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk) { + secp256k1_musig_keyaggcoef_internal(r, cache_i->pk_hash, pk, &cache_i->second_pk); } typedef struct { const secp256k1_context *ctx; /* pk_hash is the hash of the public keys */ unsigned char pk_hash[32]; - const secp256k1_xonly_pubkey * const* pks; - secp256k1_fe second_pk_x; + const secp256k1_pubkey * const* pks; + secp256k1_ge second_pk; } secp256k1_musig_pubkey_agg_ecmult_data; /* Callback for batch EC multiplication to compute keyaggcoef_0*P0 + keyaggcoef_1*P1 + ... */ static int secp256k1_musig_pubkey_agg_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_musig_pubkey_agg_ecmult_data *ctx = (secp256k1_musig_pubkey_agg_ecmult_data *) data; int ret; - ret = secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); + ret = secp256k1_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); /* pubkey_load can't fail because the same pks have already been loaded in * `musig_compute_pk_hash` (and we test this). */ VERIFY_CHECK(ret); - secp256k1_musig_keyaggcoef_internal(sc, ctx->pk_hash, &pt->x, &ctx->second_pk_x); + secp256k1_musig_keyaggcoef_internal(sc, ctx->pk_hash, pt, &ctx->second_pk); return 1; } -int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys) { +int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey * const* pubkeys, size_t n_pubkeys) { secp256k1_musig_pubkey_agg_ecmult_data ecmult_data; secp256k1_gej pkj; secp256k1_ge pkp; @@ -201,15 +227,15 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s ecmult_data.ctx = ctx; ecmult_data.pks = pubkeys; - /* No point on the curve has an X coordinate equal to 0 */ - secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0); + + secp256k1_ge_set_infinity(&ecmult_data.second_pk); for (i = 1; i < n_pubkeys; i++) { if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) { - secp256k1_ge pt; - if (!secp256k1_xonly_pubkey_load(ctx, &pt, pubkeys[i])) { + secp256k1_ge pk; + if (!secp256k1_pubkey_load(ctx, &pk, pubkeys[i])) { return 0; } - ecmult_data.second_pk_x = pt.x; + ecmult_data.second_pk = pk; break; } } @@ -232,7 +258,7 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s if (keyagg_cache != NULL) { secp256k1_keyagg_cache_internal cache_i = { 0 }; cache_i.pk = pkp; - cache_i.second_pk_x = ecmult_data.second_pk_x; + cache_i.second_pk = ecmult_data.second_pk; memcpy(cache_i.pk_hash, ecmult_data.pk_hash, sizeof(cache_i.pk_hash)); secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); } diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md index 9a70b312..44b0de4f 100644 --- a/src/modules/musig/musig.md +++ b/src/modules/musig/musig.md @@ -30,8 +30,8 @@ A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly This is covered by `examples/musig.c`. Essentially, the protocol proceeds in the following steps: -1. Generate a keypair with `secp256k1_keypair_create` and obtain the xonly public key with `secp256k1_keypair_xonly_pub`. -2. Call `secp256k1_musig_pubkey_agg` with the xonly pubkeys of all participants. +1. Generate a keypair with `secp256k1_keypair_create` and obtain the public key with `secp256k1_keypair_pub`. +2. Call `secp256k1_musig_pubkey_agg` with the pubkeys of all participants. 3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and a plain tweak with `secp256k1_musig_pubkey_ec_tweak_add`. 4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers. 5. Someone (not necessarily the signer) aggregates the public nonce with `secp256k1_musig_nonce_agg` and sends it to the signers. diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 1e294c98..5792ae35 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -20,25 +20,6 @@ #include "../../scalar.h" #include "../../util.h" -/* point_save_ext and point_load_ext are identical to point_save and point_load - * except that they allow saving and loading the point at infinity */ -static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) { - if (secp256k1_ge_is_infinity(ge)) { - memset(data, 0, 64); - } else { - secp256k1_point_save(data, ge); - } -} - -static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data) { - unsigned char zeros[64] = { 0 }; - if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) { - secp256k1_ge_set_infinity(ge); - } else { - secp256k1_point_load(ge, data); - } -} - static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 }; static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, secp256k1_scalar *k) { @@ -608,25 +589,18 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p } secp256k1_fe_normalize_var(&pk.y); - /* The specification requires that the secret key is multiplied by - * g[v]*g*gp. All factors are -1 or 1. The value g[v] is -1 iff - * secp256k1_fe_is_odd(&cache_i.pk.y)), g is is -1 iff parity_acc is 1 and - * gp is -1 if secp256k1_fe_is_odd(&pk.y). Therefore, multiplying by - * g[v]*g*gp is equivalent to negating if - * secp256k1_fe_is_odd(&cache_i.pk.y)) - * XOR cache_i.parity_acc - * XOR secp256k1_fe_is_odd(&pk.y). - */ + /* Negate sk if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc. + * This corresponds to the line "Let d = g⋅gacc⋅d' mod n" in the + * specification. */ if ((secp256k1_fe_is_odd(&cache_i.pk.y) - != cache_i.parity_acc) - != secp256k1_fe_is_odd(&pk.y)) { + != cache_i.parity_acc)) { secp256k1_scalar_negate(&sk, &sk); } /* Multiply KeyAgg coefficient */ secp256k1_fe_normalize_var(&pk.x); /* TODO Cache mu */ - secp256k1_musig_keyaggcoef(&mu, &cache_i, &pk.x); + secp256k1_musig_keyaggcoef(&mu, &cache_i, &pk); secp256k1_scalar_mul(&sk, &sk, &mu); if (!secp256k1_musig_session_load(ctx, &session_i, session)) { @@ -649,7 +623,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p return 1; } -int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_xonly_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { secp256k1_keyagg_cache_internal cache_i; secp256k1_musig_session_internal session_i; secp256k1_scalar mu, e, s; @@ -679,7 +653,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_ecmult(&rj, &rj, &session_i.noncecoef, NULL); secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pt[0], NULL); - if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { + if (!secp256k1_pubkey_load(ctx, &pkp, pubkey)) { return 0; } if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { @@ -688,14 +662,12 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* Multiplying the challenge by the KeyAgg coefficient is equivalent * to multiplying the signer's public key by the coefficient, except * much easier to do. */ - secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); + secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp); secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - /* The specification requires that the public key is multiplied by g[v]*g. - * All factors are -1 or 1. The value g[v] is -1 iff - * secp256k1_fe_is_odd(&cache_i.pk.y)) and g is is -1 iff parity_acc is 1. - * Therefore, multiplying by g[v]*g is equivalent to negating if - * fe_is_odd(&cache_i.pk.y) XOR parity_acc. */ + /* Negate e if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc. + * This corresponds to the line "Let g' = g⋅gacc mod n" and the multiplication "g'⋅e" + * in the specification. */ if (secp256k1_fe_is_odd(&cache_i.pk.y) != cache_i.parity_acc) { secp256k1_scalar_negate(&e, &e); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 3daf3ac6..bfa0813c 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -23,11 +23,11 @@ #include "../../hash.h" #include "../../util.h" -static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_xonly_pubkey *pk, const unsigned char *sk) { +static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_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); + ret &= secp256k1_keypair_pub(ctx, pk, &keypair_tmp); if (keypair != NULL) { *keypair = keypair_tmp; } @@ -47,8 +47,8 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { secp256k1_musig_keyagg_cache keyagg_cache; unsigned char session_id[2][32]; secp256k1_musig_secnonce secnonce[2]; - secp256k1_xonly_pubkey pk[2]; - const secp256k1_xonly_pubkey *pk_ptr[2]; + secp256k1_pubkey pk[2]; + const secp256k1_pubkey *pk_ptr[2]; secp256k1_musig_partial_sig partial_sig[2]; const secp256k1_musig_partial_sig *partial_sig_ptr[2]; unsigned char final_sig[64]; @@ -145,11 +145,11 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_keyagg_cache invalid_keyagg_cache; secp256k1_musig_session session; secp256k1_musig_session invalid_session; - secp256k1_xonly_pubkey pk[2]; - const secp256k1_xonly_pubkey *pk_ptr[2]; - secp256k1_xonly_pubkey invalid_pk; - const secp256k1_xonly_pubkey *invalid_pk_ptr2[2]; - const secp256k1_xonly_pubkey *invalid_pk_ptr3[3]; + secp256k1_pubkey pk[2]; + const secp256k1_pubkey *pk_ptr[2]; + secp256k1_pubkey invalid_pk; + const secp256k1_pubkey *invalid_pk_ptr2[2]; + const secp256k1_pubkey *invalid_pk_ptr3[3]; unsigned char tweak[32]; int nonce_parity; unsigned char sec_adaptor[32]; @@ -684,10 +684,10 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { unsigned char sk_b[2][32]; secp256k1_keypair keypair_a[2]; secp256k1_keypair keypair_b[2]; - secp256k1_xonly_pubkey pk_a[2]; - const secp256k1_xonly_pubkey *pk_a_ptr[2]; - secp256k1_xonly_pubkey pk_b[2]; - const secp256k1_xonly_pubkey *pk_b_ptr[2]; + secp256k1_pubkey pk_a[2]; + const secp256k1_pubkey *pk_a_ptr[2]; + secp256k1_pubkey pk_b[2]; + const secp256k1_pubkey *pk_b_ptr[2]; secp256k1_musig_keyagg_cache keyagg_cache_a; secp256k1_musig_keyagg_cache keyagg_cache_b; secp256k1_xonly_pubkey agg_pk_a; @@ -816,7 +816,7 @@ void sha256_tag_test(void) { /* Attempts to create a signature for the aggregate public key using given secret * keys and keyagg_cache. */ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_keyagg_cache *keyagg_cache) { - secp256k1_xonly_pubkey pk[2]; + secp256k1_pubkey pk[2]; unsigned char session_id[2][32]; unsigned char msg[32]; secp256k1_musig_secnonce secnonce[2]; @@ -860,8 +860,8 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigne * plain tweaking) and test signing. */ void musig_tweak_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; - secp256k1_xonly_pubkey pk[2]; - const secp256k1_xonly_pubkey *pk_ptr[2]; + secp256k1_pubkey pk[2]; + const secp256k1_pubkey *pk_ptr[2]; secp256k1_musig_keyagg_cache keyagg_cache; enum { N_TWEAKS = 8 }; secp256k1_pubkey P[N_TWEAKS + 1]; diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 4fe93ef8..7c7baac8 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -249,8 +249,8 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { #ifdef ENABLE_MODULE_MUSIG { - secp256k1_xonly_pubkey pk; - const secp256k1_xonly_pubkey *pk_ptr[1]; + secp256k1_pubkey pk; + const secp256k1_pubkey *pk_ptr[1]; secp256k1_xonly_pubkey agg_pk; unsigned char session_id[32]; secp256k1_musig_secnonce secnonce; @@ -279,7 +279,7 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { partial_sig_ptr[0] = &partial_sig; CHECK(secp256k1_keypair_create(ctx, &keypair, key)); - CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair)); CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pk_ptr, 1)); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor)); VALGRIND_MAKE_MEM_UNDEFINED(key, 32); From 36621d13bedf44eeedd2a1773e30e849972e5bff Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 20 Dec 2022 19:11:35 +0000 Subject: [PATCH 233/381] musig: update to BIP v1.0.0-rc.2 "Add ''pk'' arg to ''NonceGen''" --- examples/musig.c | 2 +- include/secp256k1_musig.h | 4 +- src/modules/musig/session_impl.h | 46 ++++++++------- src/modules/musig/tests_impl.h | 96 ++++++++++++++++++-------------- src/valgrind_ctime_test.c | 2 +- 5 files changed, 85 insertions(+), 65 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 9e681653..a34e7e78 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -112,7 +112,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st } /* Initialize session and create secret nonce for signing and public * nonce to send to the other signers. */ - if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) { + if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, &signer[i].pubkey, msg32, NULL, NULL)) { return 0; } pubnonces[i] = &signer[i].pubnonce; diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index f6343717..33fecbdf 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -357,6 +357,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_twea * unless you really know what you are doing. * seckey: the 32-byte secret key that will later be used for signing, if * already known (can be NULL) + * pubkey: public key of the signer creating the nonce * msg32: the 32-byte message that will later be signed, if already known * (can be NULL) * keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate @@ -371,10 +372,11 @@ SECP256K1_API int secp256k1_musig_nonce_gen( secp256k1_musig_pubnonce *pubnonce, const unsigned char *session_id32, const unsigned char *seckey, + const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6); /** Aggregates the nonces of all signers into a single nonce * diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 5792ae35..96b8eff1 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -291,36 +291,35 @@ static int secp256k1_xonly_ge_serialize(unsigned char *output32, secp256k1_ge *g } /* Write optional inputs into the hash */ -static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data32) { +static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data, unsigned char len) { unsigned char zero[7] = { 0 }; /* The spec requires length prefixes to be between 1 and 8 bytes * (inclusive) */ VERIFY_CHECK(prefix_size <= 8); - /* Since the length of all input data is <= 32, we can always pad the length - * prefix with prefix_size - 1 zero bytes. */ + /* Since the length of all input data fits in a byte, we can always pad the + * length prefix with prefix_size - 1 zero bytes. */ secp256k1_sha256_write(sha, zero, prefix_size - 1); - if (data32 != NULL) { - unsigned char len = 32; + if (data != NULL) { secp256k1_sha256_write(sha, &len, 1); - secp256k1_sha256_write(sha, data32, 32); + secp256k1_sha256_write(sha, data, len); } else { - unsigned char len = 0; + len = 0; secp256k1_sha256_write(sha, &len, 1); } } -static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *msg32, const unsigned char *key32, const unsigned char *agg_pk32, const unsigned char *extra_input32) { +static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *msg32, const unsigned char *seckey32, const unsigned char *pk33, const unsigned char *agg_pk32, const unsigned char *extra_input32) { secp256k1_sha256 sha; unsigned char rand[32]; unsigned char i; unsigned char msg_present; - if (key32 != NULL) { + if (seckey32 != NULL) { secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/aux", sizeof("MuSig/aux") - 1); secp256k1_sha256_write(&sha, session_id, 32); secp256k1_sha256_finalize(&sha, rand); for (i = 0; i < 32; i++) { - rand[i] ^= key32[i]; + rand[i] ^= seckey32[i]; } } else { memcpy(rand, session_id, sizeof(rand)); @@ -329,13 +328,14 @@ static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c /* Subtract one from `sizeof` to avoid hashing the implicit null byte */ secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/nonce", sizeof("MuSig/nonce") - 1); secp256k1_sha256_write(&sha, rand, sizeof(rand)); - secp256k1_nonce_function_musig_helper(&sha, 1, agg_pk32); + secp256k1_nonce_function_musig_helper(&sha, 1, pk33, 33); + secp256k1_nonce_function_musig_helper(&sha, 1, agg_pk32, 32); msg_present = msg32 != NULL; secp256k1_sha256_write(&sha, &msg_present, 1); if (msg_present) { - secp256k1_nonce_function_musig_helper(&sha, 8, msg32); + secp256k1_nonce_function_musig_helper(&sha, 8, msg32, 32); } - secp256k1_nonce_function_musig_helper(&sha, 4, extra_input32); + secp256k1_nonce_function_musig_helper(&sha, 4, extra_input32, 32); for (i = 0; i < 2; i++) { unsigned char buf[32]; @@ -346,13 +346,15 @@ static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c } } -int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *session_id32, const unsigned char *seckey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) { +int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *session_id32, const unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) { secp256k1_keyagg_cache_internal cache_i; secp256k1_scalar k[2]; secp256k1_ge nonce_pt[2]; int i; - unsigned char pk_ser[32]; - unsigned char *pk_ser_ptr = NULL; + unsigned char pk_ser[33]; + size_t pk_ser_len = sizeof(pk_ser); + unsigned char aggpk_ser[32]; + unsigned char *aggpk_ser_ptr = NULL; int ret = 1; VERIFY_CHECK(ctx != NULL); @@ -361,6 +363,7 @@ int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secn ARG_CHECK(pubnonce != NULL); memset(pubnonce, 0, sizeof(*pubnonce)); ARG_CHECK(session_id32 != NULL); + ARG_CHECK(pubkey != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); if (seckey == NULL) { /* Check in constant time that the session_id is not 0 as a @@ -385,12 +388,17 @@ int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secn if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { return 0; } - ret_tmp = secp256k1_xonly_ge_serialize(pk_ser, &cache_i.pk); + ret_tmp = secp256k1_xonly_ge_serialize(aggpk_ser, &cache_i.pk); /* Serialization can not fail because the loaded point can not be infinity. */ VERIFY_CHECK(ret_tmp); - pk_ser_ptr = pk_ser; + aggpk_ser_ptr = aggpk_ser; } - secp256k1_nonce_function_musig(k, session_id32, msg32, seckey, pk_ser_ptr, extra_input32); + if (!secp256k1_ec_pubkey_serialize(ctx, pk_ser, &pk_ser_len, pubkey, SECP256K1_EC_COMPRESSED)) { + return 0; + } + VERIFY_CHECK(pk_ser_len == sizeof(pk_ser)); + + secp256k1_nonce_function_musig(k, session_id32, msg32, seckey, pk_ser, aggpk_ser_ptr, extra_input32); VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1])); VERIFY_CHECK(!secp256k1_scalar_eq(&k[0], &k[1])); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index bfa0813c..5459f63a 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -64,7 +64,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) { partial_sig_ptr[i] = &partial_sig[i]; CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i])); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[i], &pubnonce[i], session_id[i], sk[i], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[i], &pubnonce[i], session_id[i], sk[i], &pk[i], NULL, NULL, NULL) == 1); } CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); @@ -294,44 +294,48 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Session creation **/ ecount = 0; - CHECK(secp256k1_musig_nonce_gen(none, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 1); - CHECK(secp256k1_musig_nonce_gen(vrfy, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 1); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(none, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(vrfy, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); CHECK(ecount == 0); - CHECK(secp256k1_musig_nonce_gen(sttc, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sttc, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_nonce_gen(sign, NULL, &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, NULL, &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], NULL, session_id[0], sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], NULL, session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], NULL, sk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], NULL, sk[0], &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 4); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); /* no seckey and session_id is 0 */ - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, NULL, msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, NULL, &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 4); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); /* session_id 0 is fine when a seckey is provided */ - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, sk[0], msg, &keyagg_cache, max64) == 1); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, &pk[0], msg, &keyagg_cache, max64) == 1); CHECK(ecount == 4); /* invalid seckey */ - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], max64, msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], max64, &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, &keyagg_cache, max64) == 1); - CHECK(ecount == 4); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, NULL, max64) == 1); - CHECK(ecount == 4); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &invalid_keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, msg, &keyagg_cache, max64) == 0); CHECK(ecount == 5); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &invalid_pk, msg, &keyagg_cache, max64) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], NULL, &keyagg_cache, max64) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, NULL, max64) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &invalid_keyagg_cache, max64) == 0); + CHECK(ecount == 7); CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], msg, &keyagg_cache, NULL) == 1); - CHECK(ecount == 5); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, NULL) == 1); + CHECK(ecount == 7); - /* Every in-argument except session_id can be NULL */ - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[1], &pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1); + /* Every in-argument except session_id and pubkey can be NULL */ + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, &pk[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[1], &pubnonce[1], session_id[1], sk[1], &pk[1], NULL, NULL, NULL) == 1); /** Serialize and parse public nonces **/ ecount = 0; @@ -608,25 +612,27 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { void musig_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { secp256k1_scalar k1[2], k2[2]; - secp256k1_nonce_function_musig(k1, args[0], args[1], args[2], args[3], args[4]); + secp256k1_nonce_function_musig(k1, args[0], args[1], args[2], args[3], args[4], args[5]); secp256k1_testrand_flip(args[n_flip], n_bytes); - secp256k1_nonce_function_musig(k2, args[0], args[1], args[2], args[3], args[4]); + secp256k1_nonce_function_musig(k2, args[0], args[1], args[2], args[3], args[4], args[5]); CHECK(secp256k1_scalar_eq(&k1[0], &k2[0]) == 0); CHECK(secp256k1_scalar_eq(&k1[1], &k2[1]) == 0); } void musig_nonce_test(void) { - unsigned char *args[5]; + unsigned char *args[6]; unsigned char session_id[32]; unsigned char sk[32]; + unsigned char pk[33]; unsigned char msg[32]; unsigned char agg_pk[32]; unsigned char extra_input[32]; int i, j; - secp256k1_scalar k[5][2]; + secp256k1_scalar k[6][2]; secp256k1_testrand_bytes_test(session_id, sizeof(session_id)); secp256k1_testrand_bytes_test(sk, sizeof(sk)); + secp256k1_testrand_bytes_test(pk, sizeof(pk)); secp256k1_testrand_bytes_test(msg, sizeof(msg)); secp256k1_testrand_bytes_test(agg_pk, sizeof(agg_pk)); secp256k1_testrand_bytes_test(extra_input, sizeof(extra_input)); @@ -635,29 +641,33 @@ void musig_nonce_test(void) { args[0] = session_id; args[1] = msg; args[2] = sk; - args[3] = agg_pk; - args[4] = extra_input; + args[3] = pk; + args[4] = agg_pk; + args[5] = extra_input; for (i = 0; i < count; i++) { musig_nonce_bitflip(args, 0, sizeof(session_id)); musig_nonce_bitflip(args, 1, sizeof(msg)); musig_nonce_bitflip(args, 2, sizeof(sk)); - musig_nonce_bitflip(args, 3, sizeof(agg_pk)); - musig_nonce_bitflip(args, 4, sizeof(extra_input)); + musig_nonce_bitflip(args, 3, sizeof(pk)); + musig_nonce_bitflip(args, 4, sizeof(agg_pk)); + musig_nonce_bitflip(args, 5, sizeof(extra_input)); } /* Check that if any argument is NULL, a different nonce is produced than if * any other argument is NULL. */ memcpy(msg, session_id, sizeof(msg)); memcpy(sk, session_id, sizeof(sk)); + memcpy(pk, session_id, sizeof(session_id)); memcpy(agg_pk, session_id, sizeof(agg_pk)); memcpy(extra_input, session_id, sizeof(extra_input)); - secp256k1_nonce_function_musig(k[0], args[0], args[1], args[2], args[3], args[4]); - secp256k1_nonce_function_musig(k[1], args[0], NULL, args[2], args[3], args[4]); - secp256k1_nonce_function_musig(k[2], args[0], args[1], NULL, args[3], args[4]); - secp256k1_nonce_function_musig(k[3], args[0], args[1], args[2], NULL, args[4]); - secp256k1_nonce_function_musig(k[4], args[0], args[1], args[2], args[3], NULL); - for (i = 0; i < 5; i++) { + secp256k1_nonce_function_musig(k[0], args[0], args[1], args[2], args[3], args[4], args[5]); + secp256k1_nonce_function_musig(k[1], args[0], NULL, args[2], args[3], args[4], args[5]); + secp256k1_nonce_function_musig(k[2], args[0], args[1], NULL, args[3], args[4], args[5]); + secp256k1_nonce_function_musig(k[3], args[0], args[1], args[2], NULL, args[4], args[5]); + secp256k1_nonce_function_musig(k[4], args[0], args[1], args[2], args[3], NULL, args[5]); + secp256k1_nonce_function_musig(k[5], args[0], args[1], args[2], args[3], args[4], NULL); + for (i = 0; i < 6; i++) { CHECK(!secp256k1_scalar_eq(&k[i][0], &k[i][1])); - for (j = i+1; j < 5; j++) { + for (j = i+1; j < 6; j++) { CHECK(!secp256k1_scalar_eq(&k[i][0], &k[j][0])); CHECK(!secp256k1_scalar_eq(&k[i][1], &k[j][1])); } @@ -729,10 +739,10 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk_a, &keyagg_cache_a, pk_a_ptr, 2) == 1); CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], &pk_a[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_b[1], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], &pk_b[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], &pk_b[1], NULL, NULL, NULL) == 1); /* Step 2: Exchange nonces */ CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce_a, pubnonce_ptr_a, 2) == 1); @@ -840,8 +850,8 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigne CHECK(create_keypair_and_pk(&keypair[1], &pk[1], sk1) == 1); secp256k1_testrand256(msg); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[0], &pubnonce[0], session_id[0], sk0, NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[1], &pubnonce[1], session_id[1], sk1, NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[0], &pubnonce[0], session_id[0], sk0, &pk[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce[1], &pubnonce[1], session_id[1], sk1, &pk[1], NULL, NULL, NULL) == 1); CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 2) == 1); CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, keyagg_cache, NULL) == 1); diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 7c7baac8..92ebac22 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -286,7 +286,7 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { VALGRIND_MAKE_MEM_UNDEFINED(session_id, sizeof(session_id)); VALGRIND_MAKE_MEM_UNDEFINED(extra_input, sizeof(extra_input)); VALGRIND_MAKE_MEM_UNDEFINED(sec_adaptor, sizeof(sec_adaptor)); - ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_id, key, msg, &cache, extra_input); + ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_id, key, &pk, msg, &cache, extra_input); VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret == 1); CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1)); From 068e6a036a953e48bc90f9a96b318e350f474a3a Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 11 Jan 2023 09:45:22 +0000 Subject: [PATCH 234/381] musig: add test vectors from BIP MuSig --- contrib/musig2-vectors.py | 654 +++++++++++++++++++++++++++++++++ include/secp256k1_musig.h | 16 +- src/modules/musig/tests_impl.h | 380 +++++++++++++++++++ src/modules/musig/vectors.h | 345 +++++++++++++++++ 4 files changed, 1384 insertions(+), 11 deletions(-) create mode 100755 contrib/musig2-vectors.py create mode 100644 src/modules/musig/vectors.h diff --git a/contrib/musig2-vectors.py b/contrib/musig2-vectors.py new file mode 100755 index 00000000..60e8e7c9 --- /dev/null +++ b/contrib/musig2-vectors.py @@ -0,0 +1,654 @@ +#!/usr/bin/env python + +import sys +import json +import textwrap + +max_pubkeys = 0 + +if len(sys.argv) < 2: + print( + "This script converts BIP MuSig2 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 hexstr_to_intarray(str): + return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)]) + + +def create_init(name): + return """ +static const struct musig_%s_vector musig_%s_vector = { +""" % ( + name, + name, + ) + + +def init_array(key): + return textwrap.indent("{ %s },\n" % hexstr_to_intarray(data[key]), 4 * " ") + + +def init_arrays(key): + s = textwrap.indent("{\n", 4 * " ") + s += textwrap.indent( + ",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in data[key]]), 8 * " " + ) + s += textwrap.indent("\n},\n", 4 * " ") + return s + + +def init_indices(array): + return " %d, { %s }" % ( + len(array), + ", ".join(map(str, array) if len(array) > 0 else "0"), + ) + + +def init_is_xonly(case): + if len(case["tweak_indices"]) > 0: + return ", ".join(map(lambda x: "1" if x else "0", case["is_xonly"])) + return "0" + + +def init_optional_expected(case): + return hexstr_to_intarray(case["expected"]) if "expected" in case else 0 + + +def init_cases(cases, f): + s = textwrap.indent("{\n", 4 * " ") + for (i, case) in enumerate(cases): + s += textwrap.indent("%s\n" % f(case), 8 * " ") + s += textwrap.indent("},\n", 4 * " ") + return s + + +def finish_init(): + return "};\n" + + +s = ( + """/** + * Automatically generated by %s. + * + * The test vectors for the KeySort function are included in this file. They can + * be found in src/modules/extrakeys/tests_impl.h. */ +""" + % sys.argv[0] +) + + +s += """ +enum MUSIG_ERROR { + MUSIG_PUBKEY, + MUSIG_TWEAK, + MUSIG_PUBNONCE, + MUSIG_AGGNONCE, + MUSIG_SECNONCE, + MUSIG_SIG, + MUSIG_SIG_VERIFY, + MUSIG_OTHER +}; +""" + +# key agg vectors +with open(sys.argv[1] + "/key_agg_vectors.json", "r") as f: + data = json.load(f) + + max_key_indices = max( + len(test_case["key_indices"]) for test_case in data["valid_test_cases"] + ) + max_tweak_indices = max( + len(test_case["tweak_indices"]) for test_case in data["error_test_cases"] + ) + num_pubkeys = len(data["pubkeys"]) + max_pubkeys = max(num_pubkeys, max_pubkeys) + num_tweaks = len(data["tweaks"]) + num_valid_cases = len(data["valid_test_cases"]) + num_error_cases = len(data["error_test_cases"]) + + # Add structures for valid and error cases + s += ( + """ +struct musig_key_agg_valid_test_case { + size_t key_indices_len; + size_t key_indices[%d]; + unsigned char expected[32]; +}; +""" + % max_key_indices + ) + s += """ +struct musig_key_agg_error_test_case { + size_t key_indices_len; + size_t key_indices[%d]; + size_t tweak_indices_len; + size_t tweak_indices[%d]; + int is_xonly[%d]; + enum MUSIG_ERROR error; +}; +""" % ( + max_key_indices, + max_tweak_indices, + max_tweak_indices, + ) + + # Add structure for entire vector + s += """ +struct musig_key_agg_vector { + unsigned char pubkeys[%d][33]; + unsigned char tweaks[%d][32]; + struct musig_key_agg_valid_test_case valid_case[%d]; + struct musig_key_agg_error_test_case error_case[%d]; +}; +""" % ( + num_pubkeys, + num_tweaks, + num_valid_cases, + num_error_cases, + ) + + s += create_init("key_agg") + # Add pubkeys and tweaks to the vector + s += init_arrays("pubkeys") + s += init_arrays("tweaks") + + # Add valid cases to the vector + s += init_cases( + data["valid_test_cases"], + lambda case: "{ %s, { %s }}," + % (init_indices(case["key_indices"]), hexstr_to_intarray(case["expected"])), + ) + + def comment_to_error(case): + comment = case["comment"] + if "public key" in comment.lower(): + return "MUSIG_PUBKEY" + elif "tweak" in comment.lower(): + return "MUSIG_TWEAK" + else: + sys.exit("Unknown error") + + # Add error cases to the vector + s += init_cases( + data["error_test_cases"], + lambda case: "{ %s, %s, { %s }, %s }," + % ( + init_indices(case["key_indices"]), + init_indices(case["tweak_indices"]), + init_is_xonly(case), + comment_to_error(case), + ), + ) + + s += finish_init() + +# nonce gen vectors +with open(sys.argv[1] + "/nonce_gen_vectors.json", "r") as f: + data = json.load(f) + + # The MuSig2 implementation only allows messages of length 32 + data["test_cases"] = list( + filter(lambda c: c["msg"] is None or len(c["msg"]) == 64, data["test_cases"]) + ) + + num_tests = len(data["test_cases"]) + + s += """ +struct musig_nonce_gen_test_case { + unsigned char rand_[32]; + int has_sk; + unsigned char sk[32]; + unsigned char pk[33]; + int has_aggpk; + unsigned char aggpk[32]; + int has_msg; + unsigned char msg[32]; + int has_extra_in; + unsigned char extra_in[32]; + unsigned char expected[97]; +}; +""" + + s += ( + """ +struct musig_nonce_gen_vector { + struct musig_nonce_gen_test_case test_case[%d]; +}; +""" + % num_tests + ) + + s += create_init("nonce_gen") + + def init_array_maybe(array): + return "%d , { %s }" % ( + 0 if array is None else 1, + hexstr_to_intarray(array) if array is not None else 0, + ) + + s += init_cases( + data["test_cases"], + lambda case: "{ { %s }, %s, { %s }, %s, %s, %s, { %s } }," + % ( + hexstr_to_intarray(case["rand_"]), + init_array_maybe(case["sk"]), + hexstr_to_intarray(case["pk"]), + init_array_maybe(case["aggpk"]), + init_array_maybe(case["msg"]), + init_array_maybe(case["extra_in"]), + hexstr_to_intarray(case["expected"]), + ), + ) + + s += finish_init() + +# nonce agg vectors +with open(sys.argv[1] + "/nonce_agg_vectors.json", "r") as f: + data = json.load(f) + + num_pnonces = len(data["pnonces"]) + num_valid_cases = len(data["valid_test_cases"]) + num_error_cases = len(data["error_test_cases"]) + + pnonce_indices_len = 2 + for case in data["valid_test_cases"] + data["error_test_cases"]: + assert len(case["pnonce_indices"]) == pnonce_indices_len + + # Add structures for valid and error cases + s += """ +struct musig_nonce_agg_test_case { + size_t pnonce_indices[2]; + /* if valid case */ + unsigned char expected[66]; + /* if error case */ + int invalid_nonce_idx; +}; +""" + # Add structure for entire vector + s += """ +struct musig_nonce_agg_vector { + unsigned char pnonces[%d][66]; + struct musig_nonce_agg_test_case valid_case[%d]; + struct musig_nonce_agg_test_case error_case[%d]; +}; +""" % ( + num_pnonces, + num_valid_cases, + num_error_cases, + ) + + s += create_init("nonce_agg") + s += init_arrays("pnonces") + + for cases in (data["valid_test_cases"], data["error_test_cases"]): + s += init_cases( + cases, + lambda case: "{ { %s }, { %s }, %d }," + % ( + ", ".join(map(str, case["pnonce_indices"])), + init_optional_expected(case), + case["error"]["signer"] if "error" in case else 0, + ), + ) + s += finish_init() + +# sign/verify vectors +with open(sys.argv[1] + "/sign_verify_vectors.json", "r") as f: + data = json.load(f) + + # The MuSig2 implementation only allows messages of length 32 + assert list(filter(lambda x: len(x) == 64, data["msgs"]))[0] == data["msgs"][0] + data["msgs"] = [data["msgs"][0]] + + def filter_msg32(k): + return list(filter(lambda x: x["msg_index"] == 0, data[k])) + + data["valid_test_cases"] = filter_msg32("valid_test_cases") + data["sign_error_test_cases"] = filter_msg32("sign_error_test_cases") + data["verify_error_test_cases"] = filter_msg32("verify_error_test_cases") + data["verify_fail_test_cases"] = filter_msg32("verify_fail_test_cases") + + num_pubkeys = len(data["pubkeys"]) + max_pubkeys = max(num_pubkeys, max_pubkeys) + num_secnonces = len(data["secnonces"]) + num_pubnonces = len(data["pnonces"]) + num_aggnonces = len(data["aggnonces"]) + num_msgs = len(data["msgs"]) + num_valid_cases = len(data["valid_test_cases"]) + num_sign_error_cases = len(data["sign_error_test_cases"]) + num_verify_fail_cases = len(data["verify_fail_test_cases"]) + num_verify_error_cases = len(data["verify_error_test_cases"]) + + all_cases = ( + data["valid_test_cases"] + + data["sign_error_test_cases"] + + data["verify_error_test_cases"] + + data["verify_fail_test_cases"] + ) + max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases) + max_nonce_indices = max( + len(test_case["nonce_indices"]) if "nonce_indices" in test_case else 0 + for test_case in all_cases + ) + # Add structures for valid and error cases + s += ( + """ +/* Omit pubnonces in the test vectors because our partial signature verification + * implementation is able to accept the aggnonce directly. */ +struct musig_valid_case { + size_t key_indices_len; + size_t key_indices[%d]; + size_t aggnonce_index; + size_t msg_index; + size_t signer_index; + unsigned char expected[32]; +}; +""" + % max_key_indices + ) + + s += ( + """ +struct musig_sign_error_case { + size_t key_indices_len; + size_t key_indices[%d]; + size_t aggnonce_index; + size_t msg_index; + size_t secnonce_index; + enum MUSIG_ERROR error; +}; +""" + % max_key_indices + ) + + s += """ +struct musig_verify_fail_error_case { + unsigned char sig[32]; + size_t key_indices_len; + size_t key_indices[%d]; + size_t nonce_indices_len; + size_t nonce_indices[%d]; + size_t msg_index; + size_t signer_index; + enum MUSIG_ERROR error; +}; +""" % ( + max_key_indices, + max_nonce_indices, + ) + + # Add structure for entire vector + s += """ +struct musig_sign_verify_vector { + unsigned char sk[32]; + unsigned char pubkeys[%d][33]; + unsigned char secnonces[%d][194]; + unsigned char pubnonces[%d][194]; + unsigned char aggnonces[%d][66]; + unsigned char msgs[%d][32]; + struct musig_valid_case valid_case[%d]; + struct musig_sign_error_case sign_error_case[%d]; + struct musig_verify_fail_error_case verify_fail_case[%d]; + struct musig_verify_fail_error_case verify_error_case[%d]; +}; +""" % ( + num_pubkeys, + num_secnonces, + num_pubnonces, + num_aggnonces, + num_msgs, + num_valid_cases, + num_sign_error_cases, + num_verify_fail_cases, + num_verify_error_cases, + ) + + s += create_init("sign_verify") + s += init_array("sk") + s += init_arrays("pubkeys") + s += init_arrays("secnonces") + s += init_arrays("pnonces") + s += init_arrays("aggnonces") + s += init_arrays("msgs") + + s += init_cases( + data["valid_test_cases"], + lambda case: "{ %s, %d, %d, %d, { %s }}," + % ( + init_indices(case["key_indices"]), + case["aggnonce_index"], + case["msg_index"], + case["signer_index"], + init_optional_expected(case), + ), + ) + + def sign_error(case): + comment = case["comment"] + if "pubkey" in comment or "public key" in comment: + return "MUSIG_PUBKEY" + elif "Aggregate nonce" in comment: + return "MUSIG_AGGNONCE" + elif "Secnonce" in comment: + return "MUSIG_SECNONCE" + else: + sys.exit("Unknown sign error") + + s += init_cases( + data["sign_error_test_cases"], + lambda case: "{ %s, %d, %d, %d, %s }," + % ( + init_indices(case["key_indices"]), + case["aggnonce_index"], + case["msg_index"], + case["secnonce_index"], + sign_error(case), + ), + ) + + def verify_error(case): + comment = case["comment"] + if "exceeds" in comment: + return "MUSIG_SIG" + elif "Wrong signer" in comment or "Wrong signature" in comment: + return "MUSIG_SIG_VERIFY" + elif "pubnonce" in comment: + return "MUSIG_PUBNONCE" + elif "pubkey" in comment: + return "MUSIG_PUBKEY" + else: + sys.exit("Unknown verify error") + + for cases in ("verify_fail_test_cases", "verify_error_test_cases"): + s += init_cases( + data[cases], + lambda case: "{ { %s }, %s, %s, %d, %d, %s }," + % ( + hexstr_to_intarray(case["sig"]), + init_indices(case["key_indices"]), + init_indices(case["nonce_indices"]), + case["msg_index"], + case["signer_index"], + verify_error(case), + ), + ) + + s += finish_init() + +# tweak vectors +with open(sys.argv[1] + "/tweak_vectors.json", "r") as f: + data = json.load(f) + + num_pubkeys = len(data["pubkeys"]) + max_pubkeys = max(num_pubkeys, max_pubkeys) + num_pubnonces = len(data["pnonces"]) + num_tweaks = len(data["tweaks"]) + num_valid_cases = len(data["valid_test_cases"]) + num_error_cases = len(data["error_test_cases"]) + + all_cases = data["valid_test_cases"] + data["error_test_cases"] + max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases) + max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases) + max_nonce_indices = max(len(test_case["nonce_indices"]) for test_case in all_cases) + # Add structures for valid and error cases + s += """ +struct musig_tweak_case { + size_t key_indices_len; + size_t key_indices[%d]; + size_t nonce_indices_len; + size_t nonce_indices[%d]; + size_t tweak_indices_len; + size_t tweak_indices[%d]; + int is_xonly[%d]; + size_t signer_index; + unsigned char expected[32]; +}; +""" % ( + max_key_indices, + max_nonce_indices, + max_tweak_indices, + max_tweak_indices, + ) + + # Add structure for entire vector + s += """ +struct musig_tweak_vector { + unsigned char sk[32]; + unsigned char secnonce[97]; + unsigned char aggnonce[66]; + unsigned char msg[32]; + unsigned char pubkeys[%d][33]; + unsigned char pubnonces[%d][194]; + unsigned char tweaks[%d][32]; + struct musig_tweak_case valid_case[%d]; + struct musig_tweak_case error_case[%d]; +}; +""" % ( + num_pubkeys, + num_pubnonces, + num_tweaks, + num_valid_cases, + num_error_cases, + ) + s += create_init("tweak") + s += init_array("sk") + s += init_array("secnonce") + s += init_array("aggnonce") + s += init_array("msg") + s += init_arrays("pubkeys") + s += init_arrays("pnonces") + s += init_arrays("tweaks") + + s += init_cases( + data["valid_test_cases"], + lambda case: "{ %s, %s, %s, { %s }, %d, { %s }}," + % ( + init_indices(case["key_indices"]), + init_indices(case["nonce_indices"]), + init_indices(case["tweak_indices"]), + init_is_xonly(case), + case["signer_index"], + init_optional_expected(case), + ), + ) + + s += init_cases( + data["error_test_cases"], + lambda case: "{ %s, %s, %s, { %s }, %d, { %s }}," + % ( + init_indices(case["key_indices"]), + init_indices(case["nonce_indices"]), + init_indices(case["tweak_indices"]), + init_is_xonly(case), + case["signer_index"], + init_optional_expected(case), + ), + ) + + s += finish_init() + +# sigagg vectors +with open(sys.argv[1] + "/sig_agg_vectors.json", "r") as f: + data = json.load(f) + + num_pubkeys = len(data["pubkeys"]) + max_pubkeys = max(num_pubkeys, max_pubkeys) + num_tweaks = len(data["tweaks"]) + num_psigs = len(data["psigs"]) + num_valid_cases = len(data["valid_test_cases"]) + num_error_cases = len(data["error_test_cases"]) + + all_cases = data["valid_test_cases"] + data["error_test_cases"] + max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases) + max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases) + max_psig_indices = max(len(test_case["psig_indices"]) for test_case in all_cases) + + # Add structures for valid and error cases + s += """ +/* Omit pubnonces in the test vectors because they're only needed for + * implementations that do not directly accept an aggnonce. */ +struct musig_sig_agg_case { + size_t key_indices_len; + size_t key_indices[%d]; + size_t tweak_indices_len; + size_t tweak_indices[%d]; + int is_xonly[%d]; + unsigned char aggnonce[66]; + size_t psig_indices_len; + size_t psig_indices[%d]; + /* if valid case */ + unsigned char expected[64]; + /* if error case */ + int invalid_sig_idx; +}; +""" % ( + max_key_indices, + max_tweak_indices, + max_tweak_indices, + max_psig_indices, + ) + + # Add structure for entire vector + s += """ +struct musig_sig_agg_vector { + unsigned char pubkeys[%d][33]; + unsigned char tweaks[%d][32]; + unsigned char psigs[%d][32]; + unsigned char msg[32]; + struct musig_sig_agg_case valid_case[%d]; + struct musig_sig_agg_case error_case[%d]; +}; +""" % ( + num_pubkeys, + num_tweaks, + num_psigs, + num_valid_cases, + num_error_cases, + ) + + s += create_init("sig_agg") + s += init_arrays("pubkeys") + s += init_arrays("tweaks") + s += init_arrays("psigs") + s += init_array("msg") + + for cases in (data["valid_test_cases"], data["error_test_cases"]): + s += init_cases( + cases, + lambda case: "{ %s, %s, { %s }, { %s }, %s, { %s }, %d }," + % ( + init_indices(case["key_indices"]), + init_indices(case["tweak_indices"]), + init_is_xonly(case), + hexstr_to_intarray(case["aggnonce"]), + init_indices(case["psig_indices"]), + init_optional_expected(case), + case["error"]["signer"] if "error" in case else 0, + ), + ) + s += finish_init() +s += "enum { MUSIG_VECTORS_MAX_PUBKEYS = %d };" % max_pubkeys +print(s) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 33fecbdf..c71b92ce 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -9,11 +9,9 @@ extern "C" { #include -/** This module implements a Schnorr-based multi-signature scheme called MuSig2 - * (https://eprint.iacr.org/2020/1261, see Appendix B for the exact variant). - * Signatures are compatible with BIP-340 ("Schnorr"). - * There's an example C source file in the module's directory - * (examples/musig.c) that demonstrates how it can be used. +/** This module implements BIP MuSig2 v1.0.0-rc.3, a multi-signature scheme + * compatible with BIP-340 ("Schnorr"). You can find an example demonstrating + * the musig module in examples/musig.c. * * The module also supports BIP-341 ("Taproot") public key tweaking and adaptor * signatures as described in @@ -22,12 +20,8 @@ extern "C" { * It is recommended to read the documentation in this include file carefully. * Further notes on API usage can be found in src/modules/musig/musig.md * - * You may know that the MuSig2 scheme uses two "nonces" instead of one. This - * is not wrong, but only a technical detail we don't want to bother the user - * with. Therefore, the API only uses the singular term "nonce". - * - * Since the first version of MuSig is essentially replaced by MuSig2, when - * writing MuSig or musig here we mean MuSig2. + * Since the first version of MuSig is essentially replaced by MuSig2, we use + * MuSig, musig and MuSig2 synonymously unless noted otherwise. */ /** Opaque data structures diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 5459f63a..ff6637d0 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -23,6 +23,8 @@ #include "../../hash.h" #include "../../util.h" +#include "vectors.h" + static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_pubkey *pk, const unsigned char *sk) { int ret; secp256k1_keypair keypair_tmp; @@ -921,6 +923,378 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { } } +int musig_vectors_keyagg_and_tweak(enum MUSIG_ERROR *error, + secp256k1_musig_keyagg_cache *keyagg_cache, + unsigned char *agg_pk_ser, + const unsigned char pubkeys33[][33], + const unsigned char tweaks32[][32], + size_t key_indices_len, + const size_t *key_indices, + size_t tweak_indices_len, + const size_t *tweak_indices, + const int *is_xonly) { + secp256k1_pubkey pubkeys[MUSIG_VECTORS_MAX_PUBKEYS]; + const secp256k1_pubkey *pk_ptr[MUSIG_VECTORS_MAX_PUBKEYS]; + int i; + secp256k1_pubkey agg_pk; + secp256k1_xonly_pubkey agg_pk_xonly; + + for (i = 0; i < (int)key_indices_len; i++) { + if (!secp256k1_ec_pubkey_parse(ctx, &pubkeys[i], pubkeys33[key_indices[i]], 33)) { + *error = MUSIG_PUBKEY; + return 0; + } + pk_ptr[i] = &pubkeys[i]; + } + if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, keyagg_cache, pk_ptr, key_indices_len)) { + *error = MUSIG_OTHER; + return 0; + } + + for (i = 0; i < (int)tweak_indices_len; i++) { + if (is_xonly[i]) { + if (!secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweaks32[tweak_indices[i]])) { + *error = MUSIG_TWEAK; + return 0; + } + } else { + if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, keyagg_cache, tweaks32[tweak_indices[i]])) { + *error = MUSIG_TWEAK; + return 0; + } + } + } + if (!secp256k1_musig_pubkey_get(ctx, &agg_pk, keyagg_cache)) { + *error = MUSIG_OTHER; + return 0; + } + + if (!secp256k1_xonly_pubkey_from_pubkey(ctx, &agg_pk_xonly, NULL, &agg_pk)) { + *error = MUSIG_OTHER; + return 0; + } + + if (agg_pk_ser != NULL) { + if (!secp256k1_xonly_pubkey_serialize(ctx, agg_pk_ser, &agg_pk_xonly)) { + *error = MUSIG_OTHER; + return 0; + } + } + + return 1; +} + +void musig_test_vectors_keyagg(void) { + size_t i; + const struct musig_key_agg_vector *vector = &musig_key_agg_vector; + + for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { + const struct musig_key_agg_valid_test_case *c = &vector->valid_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + unsigned char agg_pk[32]; + + CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, 0, NULL, NULL)); + CHECK(secp256k1_memcmp_var(agg_pk, c->expected, sizeof(agg_pk)) == 0); + } + + for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { + const struct musig_key_agg_error_test_case *c = &vector->error_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + + CHECK(!musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); + CHECK(c->error == error); + } +} + +void musig_test_vectors_noncegen(void) { + size_t i; + const struct musig_nonce_gen_vector *vector = &musig_nonce_gen_vector; + + for (i = 0; i < sizeof(vector->test_case)/sizeof(vector->test_case[0]); i++) { + const struct musig_nonce_gen_test_case *c = &vector->test_case[i]; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_musig_keyagg_cache *keyagg_cache_ptr = NULL; + secp256k1_musig_secnonce secnonce; + secp256k1_musig_pubnonce pubnonce; + const unsigned char *sk = NULL; + const unsigned char *msg = NULL; + const unsigned char *extra_in = NULL; + secp256k1_pubkey pk; + + if (c->has_sk) { + sk = c->sk; + } + if (c->has_aggpk) { + /* Create keyagg_cache from aggpk */ + secp256k1_keyagg_cache_internal cache_i; + secp256k1_xonly_pubkey aggpk; + memset(&cache_i, 0, sizeof(cache_i)); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &aggpk, c->aggpk)); + CHECK(secp256k1_xonly_pubkey_load(ctx, &cache_i.pk, &aggpk)); + secp256k1_keyagg_cache_save(&keyagg_cache, &cache_i); + keyagg_cache_ptr = &keyagg_cache; + } + if (c->has_msg) { + msg = c->msg; + } + if (c->has_extra_in) { + extra_in = c->extra_in; + } + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, c->pk, sizeof(c->pk))); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, c->rand_, sk, &pk, msg, keyagg_cache_ptr, extra_in) == 1); + CHECK(secp256k1_memcmp_var(&secnonce.data[4], c->expected, sizeof(secnonce)-4) == 0); + } +} + + +void musig_test_vectors_nonceagg(void) { + size_t i; + int j; + const struct musig_nonce_agg_vector *vector = &musig_nonce_agg_vector; + + for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { + const struct musig_nonce_agg_test_case *c = &vector->valid_case[i]; + secp256k1_musig_pubnonce pubnonce[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr[2]; + secp256k1_musig_aggnonce aggnonce; + unsigned char aggnonce66[66]; + + for (j = 0; j < 2; j++) { + CHECK(secp256k1_musig_pubnonce_parse(ctx, &pubnonce[j], vector->pnonces[c->pnonce_indices[j]]) == 1); + pubnonce_ptr[j] = &pubnonce[j]; + } + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 2)); + CHECK(secp256k1_musig_aggnonce_serialize(ctx, aggnonce66, &aggnonce)); + CHECK(secp256k1_memcmp_var(aggnonce66, c->expected, 33) == 0); + } + for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { + const struct musig_nonce_agg_test_case *c = &vector->error_case[i]; + secp256k1_musig_pubnonce pubnonce[2]; + for (j = 0; j < 2; j++) { + int expected = c->invalid_nonce_idx != j; + CHECK(expected == secp256k1_musig_pubnonce_parse(ctx, &pubnonce[j], vector->pnonces[c->pnonce_indices[j]])); + } + } +} + +void musig_test_vectors_signverify(void) { + size_t i; + const struct musig_sign_verify_vector *vector = &musig_sign_verify_vector; + + for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { + const struct musig_valid_case *c = &vector->valid_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_pubkey pubkey; + secp256k1_musig_pubnonce pubnonce; + secp256k1_musig_aggnonce aggnonce; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig; + secp256k1_musig_secnonce secnonce; + secp256k1_keypair keypair; + unsigned char partial_sig32[32]; + + CHECK(secp256k1_keypair_create(ctx, &keypair, vector->sk)); + CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); + + CHECK(secp256k1_musig_aggnonce_parse(ctx, &aggnonce, vector->aggnonces[c->aggnonce_index])); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); + + memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); + memcpy(&secnonce.data[4], vector->secnonces[0], sizeof(secnonce.data) - 4); + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); + CHECK(secp256k1_musig_partial_sig_serialize(ctx, partial_sig32, &partial_sig)); + CHECK(secp256k1_memcmp_var(partial_sig32, c->expected, sizeof(partial_sig32)) == 0); + + CHECK(secp256k1_musig_pubnonce_parse(ctx, &pubnonce, vector->pubnonces[0])); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig, &pubnonce, &pubkey, &keyagg_cache, &session)); + } + for (i = 0; i < sizeof(vector->sign_error_case)/sizeof(vector->sign_error_case[0]); i++) { + const struct musig_sign_error_case *c = &vector->sign_error_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_musig_aggnonce aggnonce; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig; + secp256k1_musig_secnonce secnonce; + secp256k1_keypair keypair; + int expected; + + if (i == 0) { + /* Skip this vector since the implementation does not error out when + * the signing key does not belong to any pubkey. */ + continue; + } + expected = c->error != MUSIG_PUBKEY; + CHECK(expected == musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); + CHECK(expected || c->error == error); + if (!expected) { + continue; + } + + expected = c->error != MUSIG_AGGNONCE; + CHECK(expected == secp256k1_musig_aggnonce_parse(ctx, &aggnonce, vector->aggnonces[c->aggnonce_index])); + if (!expected) { + continue; + } + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); + + memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); + memcpy(&secnonce.data[4], vector->secnonces[c->secnonce_index], sizeof(secnonce.data) - 4); + { + /* In the last test vector we sign with an invalid secnonce, which + * triggers an illegal_callback. Hence, we need to use a custom + * context that does not abort in this case. */ + secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + int32_t ecount = 0; + secp256k1_context_set_error_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); + expected = c->error != MUSIG_SECNONCE; + CHECK(expected == secp256k1_musig_partial_sign(ctx_tmp, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); + CHECK((!expected) == ecount); + secp256k1_context_destroy(ctx_tmp); + } + } + for (i = 0; i < sizeof(vector->verify_fail_case)/sizeof(vector->verify_fail_case[0]); i++) { + const struct musig_verify_fail_error_case *c = &vector->verify_fail_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_musig_aggnonce aggnonce; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig; + enum { NUM_PUBNONCES = 3 }; + secp256k1_musig_pubnonce pubnonce[NUM_PUBNONCES]; + const secp256k1_musig_pubnonce *pubnonce_ptr[NUM_PUBNONCES]; + secp256k1_pubkey pubkey; + int expected; + size_t j; + + CHECK(NUM_PUBNONCES <= c->nonce_indices_len); + for (j = 0; j < c->nonce_indices_len; j++) { + CHECK(secp256k1_musig_pubnonce_parse(ctx, &pubnonce[j], vector->pubnonces[c->nonce_indices[j]])); + pubnonce_ptr[j] = &pubnonce[j]; + } + + CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); + CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, c->nonce_indices_len) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, vector->pubkeys[c->signer_index], sizeof(vector->pubkeys[0]))); + + expected = c->error != MUSIG_SIG; + CHECK(expected == secp256k1_musig_partial_sig_parse(ctx, &partial_sig, c->sig)); + if (!expected) { + continue; + } + expected = c->error != MUSIG_SIG_VERIFY; + CHECK(expected == secp256k1_musig_partial_sig_verify(ctx, &partial_sig, pubnonce, &pubkey, &keyagg_cache, &session)); + } + for (i = 0; i < sizeof(vector->verify_error_case)/sizeof(vector->verify_error_case[0]); i++) { + const struct musig_verify_fail_error_case *c = &vector->verify_error_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_musig_pubnonce pubnonce; + int expected; + + expected = c->error != MUSIG_PUBKEY; + CHECK(expected == musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); + CHECK(expected || c->error == error); + if (!expected) { + continue; + } + expected = c->error != MUSIG_PUBNONCE; + CHECK(expected == secp256k1_musig_pubnonce_parse(ctx, &pubnonce, vector->pubnonces[c->nonce_indices[c->signer_index]])); + } +} + +void musig_test_vectors_tweak(void) { + size_t i; + const struct musig_tweak_vector *vector = &musig_tweak_vector; + secp256k1_pubkey pubkey; + secp256k1_musig_aggnonce aggnonce; + secp256k1_musig_secnonce secnonce; + + CHECK(secp256k1_musig_aggnonce_parse(ctx, &aggnonce, vector->aggnonce)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); + + for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { + const struct musig_tweak_case *c = &vector->valid_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_musig_pubnonce pubnonce; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig; + secp256k1_keypair keypair; + unsigned char partial_sig32[32]; + + memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); + memcpy(&secnonce.data[4], vector->secnonce, sizeof(secnonce.data) - 4); + + CHECK(secp256k1_keypair_create(ctx, &keypair, vector->sk)); + CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); + + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); + + CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); + CHECK(secp256k1_musig_partial_sig_serialize(ctx, partial_sig32, &partial_sig)); + CHECK(secp256k1_memcmp_var(partial_sig32, c->expected, sizeof(partial_sig32)) == 0); + + CHECK(secp256k1_musig_pubnonce_parse(ctx, &pubnonce, vector->pubnonces[c->nonce_indices[c->signer_index]])); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig, &pubnonce, &pubkey, &keyagg_cache, &session)); + } + for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { + const struct musig_tweak_case *c = &vector->error_case[i]; + enum MUSIG_ERROR error; + secp256k1_musig_keyagg_cache keyagg_cache; + CHECK(!musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); + CHECK(error == MUSIG_TWEAK); + } +} + +void musig_test_vectors_sigagg(void) { + size_t i, j; + const struct musig_sig_agg_vector *vector = &musig_sig_agg_vector; + + for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { + const struct musig_sig_agg_case *c = &vector->valid_case[i]; + enum MUSIG_ERROR error; + unsigned char final_sig[64]; + secp256k1_musig_keyagg_cache keyagg_cache; + unsigned char agg_pk32[32]; + secp256k1_xonly_pubkey agg_pk; + secp256k1_musig_aggnonce aggnonce; + secp256k1_musig_session session; + secp256k1_musig_partial_sig partial_sig[(sizeof(vector->psigs)/sizeof(vector->psigs[0]))]; + const secp256k1_musig_partial_sig *partial_sig_ptr[(sizeof(vector->psigs)/sizeof(vector->psigs[0]))]; + + CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk32, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); + CHECK(secp256k1_musig_aggnonce_parse(ctx, &aggnonce, c->aggnonce)); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); + for (j = 0; j < c->psig_indices_len; j++) { + CHECK(secp256k1_musig_partial_sig_parse(ctx, &partial_sig[j], vector->psigs[c->psig_indices[j]])); + partial_sig_ptr[j] = &partial_sig[j]; + } + + CHECK(secp256k1_musig_partial_sig_agg(ctx, final_sig, &session, partial_sig_ptr, c->psig_indices_len) == 1); + CHECK(secp256k1_memcmp_var(final_sig, c->expected, sizeof(final_sig)) == 0); + + CHECK(secp256k1_xonly_pubkey_parse(ctx, &agg_pk, agg_pk32)); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, vector->msg, sizeof(vector->msg), &agg_pk) == 1); + } + for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { + const struct musig_sig_agg_case *c = &vector->error_case[i]; + secp256k1_musig_partial_sig partial_sig[(sizeof(vector->psigs)/sizeof(vector->psigs[0]))]; + for (j = 0; j < c->psig_indices_len; j++) { + int expected = c->invalid_sig_idx != (int)j; + CHECK(expected == secp256k1_musig_partial_sig_parse(ctx, &partial_sig[j], vector->psigs[c->psig_indices[j]])); + } + } +} + void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); @@ -937,6 +1311,12 @@ void run_musig_tests(void) { musig_tweak_test(scratch); } sha256_tag_test(); + musig_test_vectors_keyagg(); + musig_test_vectors_noncegen(); + musig_test_vectors_nonceagg(); + musig_test_vectors_signverify(); + musig_test_vectors_tweak(); + musig_test_vectors_sigagg(); secp256k1_scratch_space_destroy(ctx, scratch); } diff --git a/src/modules/musig/vectors.h b/src/modules/musig/vectors.h new file mode 100644 index 00000000..744c4050 --- /dev/null +++ b/src/modules/musig/vectors.h @@ -0,0 +1,345 @@ +/** + * Automatically generated by contrib/musig2-vectors.py. + * + * The test vectors for the KeySort function are included in this file. They can + * be found in src/modules/extrakeys/tests_impl.h. */ + +enum MUSIG_ERROR { + MUSIG_PUBKEY, + MUSIG_TWEAK, + MUSIG_PUBNONCE, + MUSIG_AGGNONCE, + MUSIG_SECNONCE, + MUSIG_SIG, + MUSIG_SIG_VERIFY, + MUSIG_OTHER +}; + +struct musig_key_agg_valid_test_case { + size_t key_indices_len; + size_t key_indices[4]; + unsigned char expected[32]; +}; + +struct musig_key_agg_error_test_case { + size_t key_indices_len; + size_t key_indices[4]; + size_t tweak_indices_len; + size_t tweak_indices[1]; + int is_xonly[1]; + enum MUSIG_ERROR error; +}; + +struct musig_key_agg_vector { + unsigned char pubkeys[7][33]; + unsigned char tweaks[2][32]; + struct musig_key_agg_valid_test_case valid_case[4]; + struct musig_key_agg_error_test_case error_case[5]; +}; + +static const struct musig_key_agg_vector musig_key_agg_vector = { + { + { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, + { 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }, + { 0x02, 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 }, + { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 }, + { 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 }, + { 0x04, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, + { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 } + }, + { + { 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 }, + { 0x25, 0x2E, 0x4B, 0xD6, 0x74, 0x10, 0xA7, 0x6C, 0xDF, 0x93, 0x3D, 0x30, 0xEA, 0xA1, 0x60, 0x82, 0x14, 0x03, 0x7F, 0x1B, 0x10, 0x5A, 0x01, 0x3E, 0xCC, 0xD3, 0xC5, 0xC1, 0x84, 0xA6, 0x11, 0x0B } + }, + { + { 3, { 0, 1, 2 }, { 0x90, 0x53, 0x9E, 0xED, 0xE5, 0x65, 0xF5, 0xD0, 0x54, 0xF3, 0x2C, 0xC0, 0xC2, 0x20, 0x12, 0x68, 0x89, 0xED, 0x1E, 0x5D, 0x19, 0x3B, 0xAF, 0x15, 0xAE, 0xF3, 0x44, 0xFE, 0x59, 0xD4, 0x61, 0x0C }}, + { 3, { 2, 1, 0 }, { 0x62, 0x04, 0xDE, 0x8B, 0x08, 0x34, 0x26, 0xDC, 0x6E, 0xAF, 0x95, 0x02, 0xD2, 0x70, 0x24, 0xD5, 0x3F, 0xC8, 0x26, 0xBF, 0x7D, 0x20, 0x12, 0x14, 0x8A, 0x05, 0x75, 0x43, 0x5D, 0xF5, 0x4B, 0x2B }}, + { 3, { 0, 0, 0 }, { 0xB4, 0x36, 0xE3, 0xBA, 0xD6, 0x2B, 0x8C, 0xD4, 0x09, 0x96, 0x9A, 0x22, 0x47, 0x31, 0xC1, 0x93, 0xD0, 0x51, 0x16, 0x2D, 0x8C, 0x5A, 0xE8, 0xB1, 0x09, 0x30, 0x61, 0x27, 0xDA, 0x3A, 0xA9, 0x35 }}, + { 4, { 0, 0, 1, 1 }, { 0x69, 0xBC, 0x22, 0xBF, 0xA5, 0xD1, 0x06, 0x30, 0x6E, 0x48, 0xA2, 0x06, 0x79, 0xDE, 0x1D, 0x73, 0x89, 0x38, 0x61, 0x24, 0xD0, 0x75, 0x71, 0xD0, 0xD8, 0x72, 0x68, 0x60, 0x28, 0xC2, 0x6A, 0x3E }}, + }, + { + { 2, { 0, 3 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY }, + { 2, { 0, 4 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY }, + { 2, { 5, 0 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY }, + { 2, { 0, 1 }, 1, { 0 }, { 1 }, MUSIG_TWEAK }, + { 1, { 6 }, 1, { 1 }, { 0 }, MUSIG_TWEAK }, + }, +}; + +struct musig_nonce_gen_test_case { + unsigned char rand_[32]; + int has_sk; + unsigned char sk[32]; + unsigned char pk[33]; + int has_aggpk; + unsigned char aggpk[32]; + int has_msg; + unsigned char msg[32]; + int has_extra_in; + unsigned char extra_in[32]; + unsigned char expected[97]; +}; + +struct musig_nonce_gen_vector { + struct musig_nonce_gen_test_case test_case[2]; +}; + +static const struct musig_nonce_gen_vector musig_nonce_gen_vector = { + { + { { 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 }, 1 , { 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 }, { 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 }, 1 , { 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 }, 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 }, 1 , { 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 }, { 0xB1, 0x14, 0xE5, 0x02, 0xBE, 0xAA, 0x4E, 0x30, 0x1D, 0xD0, 0x8A, 0x50, 0x26, 0x41, 0x72, 0xC8, 0x4E, 0x41, 0x65, 0x0E, 0x6C, 0xB7, 0x26, 0xB4, 0x10, 0xC0, 0x69, 0x4D, 0x59, 0xEF, 0xFB, 0x64, 0x95, 0xB5, 0xCA, 0xF2, 0x8D, 0x04, 0x5B, 0x97, 0x3D, 0x63, 0xE3, 0xC9, 0x9A, 0x44, 0xB8, 0x07, 0xBD, 0xE3, 0x75, 0xFD, 0x6C, 0xB3, 0x9E, 0x46, 0xDC, 0x4A, 0x51, 0x17, 0x08, 0xD0, 0xE9, 0xD2, 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 } }, + { { 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 }, 0 , { 0 }, { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, 0 , { 0 }, 0 , { 0 }, 0 , { 0 }, { 0x89, 0xBD, 0xD7, 0x87, 0xD0, 0x28, 0x4E, 0x5E, 0x4D, 0x5F, 0xC5, 0x72, 0xE4, 0x9E, 0x31, 0x6B, 0xAB, 0x7E, 0x21, 0xE3, 0xB1, 0x83, 0x0D, 0xE3, 0x7D, 0xFE, 0x80, 0x15, 0x6F, 0xA4, 0x1A, 0x6D, 0x0B, 0x17, 0xAE, 0x8D, 0x02, 0x4C, 0x53, 0x67, 0x96, 0x99, 0xA6, 0xFD, 0x79, 0x44, 0xD9, 0xC4, 0xA3, 0x66, 0xB5, 0x14, 0xBA, 0xF4, 0x30, 0x88, 0xE0, 0x70, 0x8B, 0x10, 0x23, 0xDD, 0x28, 0x97, 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 } }, + }, +}; + +struct musig_nonce_agg_test_case { + size_t pnonce_indices[2]; + /* if valid case */ + unsigned char expected[66]; + /* if error case */ + int invalid_nonce_idx; +}; + +struct musig_nonce_agg_vector { + unsigned char pnonces[7][66]; + struct musig_nonce_agg_test_case valid_case[2]; + struct musig_nonce_agg_test_case error_case[3]; +}; + +static const struct musig_nonce_agg_vector musig_nonce_agg_vector = { + { + { 0x02, 0x01, 0x51, 0xC8, 0x0F, 0x43, 0x56, 0x48, 0xDF, 0x67, 0xA2, 0x2B, 0x74, 0x9C, 0xD7, 0x98, 0xCE, 0x54, 0xE0, 0x32, 0x1D, 0x03, 0x4B, 0x92, 0xB7, 0x09, 0xB5, 0x67, 0xD6, 0x0A, 0x42, 0xE6, 0x66, 0x03, 0xBA, 0x47, 0xFB, 0xC1, 0x83, 0x44, 0x37, 0xB3, 0x21, 0x2E, 0x89, 0xA8, 0x4D, 0x84, 0x25, 0xE7, 0xBF, 0x12, 0xE0, 0x24, 0x5D, 0x98, 0x26, 0x22, 0x68, 0xEB, 0xDC, 0xB3, 0x85, 0xD5, 0x06, 0x41 }, + { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x33 }, + { 0x02, 0x01, 0x51, 0xC8, 0x0F, 0x43, 0x56, 0x48, 0xDF, 0x67, 0xA2, 0x2B, 0x74, 0x9C, 0xD7, 0x98, 0xCE, 0x54, 0xE0, 0x32, 0x1D, 0x03, 0x4B, 0x92, 0xB7, 0x09, 0xB5, 0x67, 0xD6, 0x0A, 0x42, 0xE6, 0x66, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, + { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x03, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, + { 0x04, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x33 }, + { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x31 }, + { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 } + }, + { + { { 0, 1 }, { 0x03, 0x5F, 0xE1, 0x87, 0x3B, 0x4F, 0x29, 0x67, 0xF5, 0x2F, 0xEA, 0x4A, 0x06, 0xAD, 0x5A, 0x8E, 0xCC, 0xBE, 0x9D, 0x0F, 0xD7, 0x30, 0x68, 0x01, 0x2C, 0x89, 0x4E, 0x2E, 0x87, 0xCC, 0xB5, 0x80, 0x4B, 0x02, 0x47, 0x25, 0x37, 0x73, 0x45, 0xBD, 0xE0, 0xE9, 0xC3, 0x3A, 0xF3, 0xC4, 0x3C, 0x0A, 0x29, 0xA9, 0x24, 0x9F, 0x2F, 0x29, 0x56, 0xFA, 0x8C, 0xFE, 0xB5, 0x5C, 0x85, 0x73, 0xD0, 0x26, 0x2D, 0xC8 }, 0 }, + { { 2, 3 }, { 0x03, 0x5F, 0xE1, 0x87, 0x3B, 0x4F, 0x29, 0x67, 0xF5, 0x2F, 0xEA, 0x4A, 0x06, 0xAD, 0x5A, 0x8E, 0xCC, 0xBE, 0x9D, 0x0F, 0xD7, 0x30, 0x68, 0x01, 0x2C, 0x89, 0x4E, 0x2E, 0x87, 0xCC, 0xB5, 0x80, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 }, + }, + { + { { 0, 4 }, { 0 }, 1 }, + { { 5, 1 }, { 0 }, 0 }, + { { 6, 1 }, { 0 }, 0 }, + }, +}; + +/* Omit pubnonces in the test vectors because our partial signature verification + * implementation is able to accept the aggnonce directly. */ +struct musig_valid_case { + size_t key_indices_len; + size_t key_indices[3]; + size_t aggnonce_index; + size_t msg_index; + size_t signer_index; + unsigned char expected[32]; +}; + +struct musig_sign_error_case { + size_t key_indices_len; + size_t key_indices[3]; + size_t aggnonce_index; + size_t msg_index; + size_t secnonce_index; + enum MUSIG_ERROR error; +}; + +struct musig_verify_fail_error_case { + unsigned char sig[32]; + size_t key_indices_len; + size_t key_indices[3]; + size_t nonce_indices_len; + size_t nonce_indices[3]; + size_t msg_index; + size_t signer_index; + enum MUSIG_ERROR error; +}; + +struct musig_sign_verify_vector { + unsigned char sk[32]; + unsigned char pubkeys[4][33]; + unsigned char secnonces[2][194]; + unsigned char pubnonces[5][194]; + unsigned char aggnonces[5][66]; + unsigned char msgs[1][32]; + struct musig_valid_case valid_case[4]; + struct musig_sign_error_case sign_error_case[6]; + struct musig_verify_fail_error_case verify_fail_case[3]; + struct musig_verify_fail_error_case verify_error_case[2]; +}; + +static const struct musig_sign_verify_vector musig_sign_verify_vector = { + { 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71 }, + { + { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, + { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, + { 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x61 }, + { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 } + }, + { + { 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 } + }, + { + { 0x03, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }, + { 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, + { 0x03, 0x2D, 0xE2, 0x66, 0x26, 0x28, 0xC9, 0x0B, 0x03, 0xF5, 0xE7, 0x20, 0x28, 0x4E, 0xB5, 0x2F, 0xF7, 0xD7, 0x1F, 0x42, 0x84, 0xF6, 0x27, 0xB6, 0x8A, 0x85, 0x3D, 0x78, 0xC7, 0x8E, 0x1F, 0xFE, 0x93, 0x03, 0xE4, 0xC5, 0x52, 0x4E, 0x83, 0xFF, 0xE1, 0x49, 0x3B, 0x90, 0x77, 0xCF, 0x1C, 0xA6, 0xBE, 0xB2, 0x09, 0x0C, 0x93, 0xD9, 0x30, 0x32, 0x10, 0x71, 0xAD, 0x40, 0xB2, 0xF4, 0x4E, 0x59, 0x90, 0x46 }, + { 0x02, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x03, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }, + { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 } + }, + { + { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x04, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 }, + { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 }, + { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 } + }, + { + { 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF } + }, + { + { 3, { 0, 1, 2 }, 0, 0, 0, { 0x01, 0x2A, 0xBB, 0xCB, 0x52, 0xB3, 0x01, 0x6A, 0xC0, 0x3A, 0xD8, 0x23, 0x95, 0xA1, 0xA4, 0x15, 0xC4, 0x8B, 0x93, 0xDE, 0xF7, 0x87, 0x18, 0xE6, 0x2A, 0x7A, 0x90, 0x05, 0x2F, 0xE2, 0x24, 0xFB }}, + { 3, { 1, 0, 2 }, 0, 0, 1, { 0x9F, 0xF2, 0xF7, 0xAA, 0xA8, 0x56, 0x15, 0x0C, 0xC8, 0x81, 0x92, 0x54, 0x21, 0x8D, 0x3A, 0xDE, 0xEB, 0x05, 0x35, 0x26, 0x90, 0x51, 0x89, 0x77, 0x24, 0xF9, 0xDB, 0x37, 0x89, 0x51, 0x3A, 0x52 }}, + { 3, { 1, 2, 0 }, 0, 0, 2, { 0xFA, 0x23, 0xC3, 0x59, 0xF6, 0xFA, 0xC4, 0xE7, 0x79, 0x6B, 0xB9, 0x3B, 0xC9, 0xF0, 0x53, 0x2A, 0x95, 0x46, 0x8C, 0x53, 0x9B, 0xA2, 0x0F, 0xF8, 0x6D, 0x7C, 0x76, 0xED, 0x92, 0x22, 0x79, 0x00 }}, + { 2, { 0, 1 }, 1, 0, 0, { 0xAE, 0x38, 0x60, 0x64, 0xB2, 0x61, 0x05, 0x40, 0x47, 0x98, 0xF7, 0x5D, 0xE2, 0xEB, 0x9A, 0xF5, 0xED, 0xA5, 0x38, 0x7B, 0x06, 0x4B, 0x83, 0xD0, 0x49, 0xCB, 0x7C, 0x5E, 0x08, 0x87, 0x95, 0x31 }}, + }, + { + { 2, { 1, 2 }, 0, 0, 0, MUSIG_PUBKEY }, + { 3, { 1, 0, 3 }, 0, 0, 0, MUSIG_PUBKEY }, + { 3, { 1, 2, 0 }, 2, 0, 0, MUSIG_AGGNONCE }, + { 3, { 1, 2, 0 }, 3, 0, 0, MUSIG_AGGNONCE }, + { 3, { 1, 2, 0 }, 4, 0, 0, MUSIG_AGGNONCE }, + { 3, { 0, 1, 2 }, 0, 0, 1, MUSIG_SECNONCE }, + }, + { + { { 0x97, 0xAC, 0x83, 0x3A, 0xDC, 0xB1, 0xAF, 0xA4, 0x2E, 0xBF, 0x9E, 0x07, 0x25, 0x61, 0x6F, 0x3C, 0x9A, 0x0D, 0x5B, 0x61, 0x4F, 0x6F, 0xE2, 0x83, 0xCE, 0xAA, 0xA3, 0x7A, 0x8F, 0xFA, 0xF4, 0x06 }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_SIG_VERIFY }, + { { 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 1, MUSIG_SIG_VERIFY }, + { { 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 }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_SIG }, + }, + { + { { 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B }, 3, { 0, 1, 2 }, 3, { 4, 1, 2 }, 0, 0, MUSIG_PUBNONCE }, + { { 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B }, 3, { 3, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_PUBKEY }, + }, +}; + +struct musig_tweak_case { + size_t key_indices_len; + size_t key_indices[3]; + size_t nonce_indices_len; + size_t nonce_indices[3]; + size_t tweak_indices_len; + size_t tweak_indices[4]; + int is_xonly[4]; + size_t signer_index; + unsigned char expected[32]; +}; + +struct musig_tweak_vector { + unsigned char sk[32]; + unsigned char secnonce[97]; + unsigned char aggnonce[66]; + unsigned char msg[32]; + unsigned char pubkeys[3][33]; + unsigned char pubnonces[3][194]; + unsigned char tweaks[5][32]; + struct musig_tweak_case valid_case[5]; + struct musig_tweak_case error_case[1]; +}; + +static const struct musig_tweak_vector musig_tweak_vector = { + { 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71 }, + { 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, + { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 }, + { 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF }, + { + { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, + { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, + { 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 } + }, + { + { 0x03, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }, + { 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, + { 0x03, 0x2D, 0xE2, 0x66, 0x26, 0x28, 0xC9, 0x0B, 0x03, 0xF5, 0xE7, 0x20, 0x28, 0x4E, 0xB5, 0x2F, 0xF7, 0xD7, 0x1F, 0x42, 0x84, 0xF6, 0x27, 0xB6, 0x8A, 0x85, 0x3D, 0x78, 0xC7, 0x8E, 0x1F, 0xFE, 0x93, 0x03, 0xE4, 0xC5, 0x52, 0x4E, 0x83, 0xFF, 0xE1, 0x49, 0x3B, 0x90, 0x77, 0xCF, 0x1C, 0xA6, 0xBE, 0xB2, 0x09, 0x0C, 0x93, 0xD9, 0x30, 0x32, 0x10, 0x71, 0xAD, 0x40, 0xB2, 0xF4, 0x4E, 0x59, 0x90, 0x46 } + }, + { + { 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB }, + { 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D, 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, 0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55 }, + { 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, 0x45, 0x7E, 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, 0x83, 0x16, 0x26, 0xF2, 0x90, 0xFA, 0x87, 0xE0 }, + { 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, 0xBF, 0x99, 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, 0x74, 0xFE, 0xD0, 0xA3, 0xCF, 0x5A, 0xEF, 0x9D }, + { 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 } + }, + { + { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 0 }, { 1 }, 2, { 0xE2, 0x8A, 0x5C, 0x66, 0xE6, 0x1E, 0x17, 0x8C, 0x2B, 0xA1, 0x9D, 0xB7, 0x7B, 0x6C, 0xF9, 0xF7, 0xE2, 0xF0, 0xF5, 0x6C, 0x17, 0x91, 0x8C, 0xD1, 0x31, 0x35, 0xE6, 0x0C, 0xC8, 0x48, 0xFE, 0x91 }}, + { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 0 }, { 0 }, 2, { 0x38, 0xB0, 0x76, 0x77, 0x98, 0x25, 0x2F, 0x21, 0xBF, 0x57, 0x02, 0xC4, 0x80, 0x28, 0xB0, 0x95, 0x42, 0x83, 0x20, 0xF7, 0x3A, 0x4B, 0x14, 0xDB, 0x1E, 0x25, 0xDE, 0x58, 0x54, 0x3D, 0x2D, 0x2D }}, + { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 2, { 0, 1 }, { 0, 1 }, 2, { 0x40, 0x8A, 0x0A, 0x21, 0xC4, 0xA0, 0xF5, 0xDA, 0xCA, 0xF9, 0x64, 0x6A, 0xD6, 0xEB, 0x6F, 0xEC, 0xD7, 0xF7, 0xA1, 0x1F, 0x03, 0xED, 0x1F, 0x48, 0xDF, 0xFF, 0x21, 0x85, 0xBC, 0x2C, 0x24, 0x08 }}, + { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 4, { 0, 1, 2, 3 }, { 0, 0, 1, 1 }, 2, { 0x45, 0xAB, 0xD2, 0x06, 0xE6, 0x1E, 0x3D, 0xF2, 0xEC, 0x9E, 0x26, 0x4A, 0x6F, 0xEC, 0x82, 0x92, 0x14, 0x1A, 0x63, 0x3C, 0x28, 0x58, 0x63, 0x88, 0x23, 0x55, 0x41, 0xF9, 0xAD, 0xE7, 0x54, 0x35 }}, + { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 4, { 0, 1, 2, 3 }, { 1, 0, 1, 0 }, 2, { 0xB2, 0x55, 0xFD, 0xCA, 0xC2, 0x7B, 0x40, 0xC7, 0xCE, 0x78, 0x48, 0xE2, 0xD3, 0xB7, 0xBF, 0x5E, 0xA0, 0xED, 0x75, 0x6D, 0xA8, 0x15, 0x65, 0xAC, 0x80, 0x4C, 0xCC, 0xA3, 0xE1, 0xD5, 0xD2, 0x39 }}, + }, + { + { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 4 }, { 0 }, 2, { 0 }}, + }, +}; + +/* Omit pubnonces in the test vectors because they're only needed for + * implementations that do not directly accept an aggnonce. */ +struct musig_sig_agg_case { + size_t key_indices_len; + size_t key_indices[2]; + size_t tweak_indices_len; + size_t tweak_indices[3]; + int is_xonly[3]; + unsigned char aggnonce[66]; + size_t psig_indices_len; + size_t psig_indices[2]; + /* if valid case */ + unsigned char expected[64]; + /* if error case */ + int invalid_sig_idx; +}; + +struct musig_sig_agg_vector { + unsigned char pubkeys[4][33]; + unsigned char tweaks[3][32]; + unsigned char psigs[9][32]; + unsigned char msg[32]; + struct musig_sig_agg_case valid_case[4]; + struct musig_sig_agg_case error_case[1]; +}; + +static const struct musig_sig_agg_vector musig_sig_agg_vector = { + { + { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, + { 0x02, 0xD2, 0xDC, 0x6F, 0x5D, 0xF7, 0xC5, 0x6A, 0xCF, 0x38, 0xC7, 0xFA, 0x0A, 0xE7, 0xA7, 0x59, 0xAE, 0x30, 0xE1, 0x9B, 0x37, 0x35, 0x9D, 0xFD, 0xE0, 0x15, 0x87, 0x23, 0x24, 0xC7, 0xEF, 0x6E, 0x05 }, + { 0x03, 0xC7, 0xFB, 0x10, 0x1D, 0x97, 0xFF, 0x93, 0x0A, 0xCD, 0x0C, 0x67, 0x60, 0x85, 0x2E, 0xF6, 0x4E, 0x69, 0x08, 0x3D, 0xE0, 0xB0, 0x6A, 0xC6, 0x33, 0x57, 0x24, 0x75, 0x4B, 0xB4, 0xB0, 0x52, 0x2C }, + { 0x02, 0x35, 0x24, 0x33, 0xB2, 0x1E, 0x7E, 0x05, 0xD3, 0xB4, 0x52, 0xB8, 0x1C, 0xAE, 0x56, 0x6E, 0x06, 0xD2, 0xE0, 0x03, 0xEC, 0xE1, 0x6D, 0x10, 0x74, 0xAA, 0xBA, 0x42, 0x89, 0xE0, 0xE3, 0xD5, 0x81 } + }, + { + { 0xB5, 0x11, 0xDA, 0x49, 0x21, 0x82, 0xA9, 0x1B, 0x0F, 0xFB, 0x9A, 0x98, 0x02, 0x0D, 0x55, 0xF2, 0x60, 0xAE, 0x86, 0xD7, 0xEC, 0xBD, 0x03, 0x99, 0xC7, 0x38, 0x3D, 0x59, 0xA5, 0xF2, 0xAF, 0x7C }, + { 0xA8, 0x15, 0xFE, 0x04, 0x9E, 0xE3, 0xC5, 0xAA, 0xB6, 0x63, 0x10, 0x47, 0x7F, 0xBC, 0x8B, 0xCC, 0xCA, 0xC2, 0xF3, 0x39, 0x5F, 0x59, 0xF9, 0x21, 0xC3, 0x64, 0xAC, 0xD7, 0x8A, 0x2F, 0x48, 0xDC }, + { 0x75, 0x44, 0x8A, 0x87, 0x27, 0x4B, 0x05, 0x64, 0x68, 0xB9, 0x77, 0xBE, 0x06, 0xEB, 0x1E, 0x9F, 0x65, 0x75, 0x77, 0xB7, 0x32, 0x0B, 0x0A, 0x33, 0x76, 0xEA, 0x51, 0xFD, 0x42, 0x0D, 0x18, 0xA8 } + }, + { + { 0xB1, 0x5D, 0x2C, 0xD3, 0xC3, 0xD2, 0x2B, 0x04, 0xDA, 0xE4, 0x38, 0xCE, 0x65, 0x3F, 0x6B, 0x4E, 0xCF, 0x04, 0x2F, 0x42, 0xCF, 0xDE, 0xD7, 0xC4, 0x1B, 0x64, 0xAA, 0xF9, 0xB4, 0xAF, 0x53, 0xFB }, + { 0x61, 0x93, 0xD6, 0xAC, 0x61, 0xB3, 0x54, 0xE9, 0x10, 0x5B, 0xBD, 0xC8, 0x93, 0x7A, 0x34, 0x54, 0xA6, 0xD7, 0x05, 0xB6, 0xD5, 0x73, 0x22, 0xA5, 0xA4, 0x72, 0xA0, 0x2C, 0xE9, 0x9F, 0xCB, 0x64 }, + { 0x9A, 0x87, 0xD3, 0xB7, 0x9E, 0xC6, 0x72, 0x28, 0xCB, 0x97, 0x87, 0x8B, 0x76, 0x04, 0x9B, 0x15, 0xDB, 0xD0, 0x5B, 0x81, 0x58, 0xD1, 0x7B, 0x5B, 0x91, 0x14, 0xD3, 0xC2, 0x26, 0x88, 0x75, 0x05 }, + { 0x66, 0xF8, 0x2E, 0xA9, 0x09, 0x23, 0x68, 0x9B, 0x85, 0x5D, 0x36, 0xC6, 0xB7, 0xE0, 0x32, 0xFB, 0x99, 0x70, 0x30, 0x14, 0x81, 0xB9, 0x9E, 0x01, 0xCD, 0xB4, 0xD6, 0xAC, 0x7C, 0x34, 0x7A, 0x15 }, + { 0x4F, 0x5A, 0xEE, 0x41, 0x51, 0x08, 0x48, 0xA6, 0x44, 0x7D, 0xCD, 0x1B, 0xBC, 0x78, 0x45, 0x7E, 0xF6, 0x90, 0x24, 0x94, 0x4C, 0x87, 0xF4, 0x02, 0x50, 0xD3, 0xEF, 0x2C, 0x25, 0xD3, 0x3E, 0xFE }, + { 0xDD, 0xEF, 0x42, 0x7B, 0xBB, 0x84, 0x7C, 0xC0, 0x27, 0xBE, 0xFF, 0x4E, 0xDB, 0x01, 0x03, 0x81, 0x48, 0x91, 0x78, 0x32, 0x25, 0x3E, 0xBC, 0x35, 0x5F, 0xC3, 0x3F, 0x4A, 0x8E, 0x2F, 0xCC, 0xE4 }, + { 0x97, 0xB8, 0x90, 0xA2, 0x6C, 0x98, 0x1D, 0xA8, 0x10, 0x2D, 0x3B, 0xC2, 0x94, 0x15, 0x9D, 0x17, 0x1D, 0x72, 0x81, 0x0F, 0xDF, 0x7C, 0x6A, 0x69, 0x1D, 0xEF, 0x02, 0xF0, 0xF7, 0xAF, 0x3F, 0xDC }, + { 0x53, 0xFA, 0x9E, 0x08, 0xBA, 0x52, 0x43, 0xCB, 0xCB, 0x0D, 0x79, 0x7C, 0x5E, 0xE8, 0x3B, 0xC6, 0x72, 0x8E, 0x53, 0x9E, 0xB7, 0x6C, 0x2D, 0x0B, 0xF0, 0xF9, 0x71, 0xEE, 0x4E, 0x90, 0x99, 0x71 }, + { 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 } + }, + { 0x59, 0x9C, 0x67, 0xEA, 0x41, 0x0D, 0x00, 0x5B, 0x9D, 0xA9, 0x08, 0x17, 0xCF, 0x03, 0xED, 0x3B, 0x1C, 0x86, 0x8E, 0x4D, 0xA4, 0xED, 0xF0, 0x0A, 0x58, 0x80, 0xB0, 0x08, 0x2C, 0x23, 0x78, 0x69 }, + { + { 2, { 0, 1 }, 0, { 0 }, { 0 }, { 0x03, 0x41, 0x43, 0x27, 0x22, 0xC5, 0xCD, 0x02, 0x68, 0xD8, 0x29, 0xC7, 0x02, 0xCF, 0x0D, 0x1C, 0xBC, 0xE5, 0x70, 0x33, 0xEE, 0xD2, 0x01, 0xFD, 0x33, 0x51, 0x91, 0x38, 0x52, 0x27, 0xC3, 0x21, 0x0C, 0x03, 0xD3, 0x77, 0xF2, 0xD2, 0x58, 0xB6, 0x4A, 0xAD, 0xC0, 0xE1, 0x6F, 0x26, 0x46, 0x23, 0x23, 0xD7, 0x01, 0xD2, 0x86, 0x04, 0x6A, 0x2E, 0xA9, 0x33, 0x65, 0x65, 0x6A, 0xFD, 0x98, 0x75, 0x98, 0x2B }, 2, { 0, 1 }, { 0x04, 0x1D, 0xA2, 0x22, 0x23, 0xCE, 0x65, 0xC9, 0x2C, 0x9A, 0x0D, 0x6C, 0x2C, 0xAC, 0x82, 0x8A, 0xAF, 0x1E, 0xEE, 0x56, 0x30, 0x4F, 0xEC, 0x37, 0x1D, 0xDF, 0x91, 0xEB, 0xB2, 0xB9, 0xEF, 0x09, 0x12, 0xF1, 0x03, 0x80, 0x25, 0x85, 0x7F, 0xED, 0xEB, 0x3F, 0xF6, 0x96, 0xF8, 0xB9, 0x9F, 0xA4, 0xBB, 0x2C, 0x58, 0x12, 0xF6, 0x09, 0x5A, 0x2E, 0x00, 0x04, 0xEC, 0x99, 0xCE, 0x18, 0xDE, 0x1E }, 0 }, + { 2, { 0, 2 }, 0, { 0 }, { 0 }, { 0x02, 0x24, 0xAF, 0xD3, 0x6C, 0x90, 0x20, 0x84, 0x05, 0x8B, 0x51, 0xB5, 0xD3, 0x66, 0x76, 0xBB, 0xA4, 0xDC, 0x97, 0xC7, 0x75, 0x87, 0x37, 0x68, 0xE5, 0x88, 0x22, 0xF8, 0x7F, 0xE4, 0x37, 0xD7, 0x92, 0x02, 0x8C, 0xB1, 0x59, 0x29, 0x09, 0x9E, 0xEE, 0x2F, 0x5D, 0xAE, 0x40, 0x4C, 0xD3, 0x93, 0x57, 0x59, 0x1B, 0xA3, 0x2E, 0x9A, 0xF4, 0xE1, 0x62, 0xB8, 0xD3, 0xE7, 0xCB, 0x5E, 0xFE, 0x31, 0xCB, 0x20 }, 2, { 2, 3 }, { 0x10, 0x69, 0xB6, 0x7E, 0xC3, 0xD2, 0xF3, 0xC7, 0xC0, 0x82, 0x91, 0xAC, 0xCB, 0x17, 0xA9, 0xC9, 0xB8, 0xF2, 0x81, 0x9A, 0x52, 0xEB, 0x5D, 0xF8, 0x72, 0x6E, 0x17, 0xE7, 0xD6, 0xB5, 0x2E, 0x9F, 0x01, 0x80, 0x02, 0x60, 0xA7, 0xE9, 0xDA, 0xC4, 0x50, 0xF4, 0xBE, 0x52, 0x2D, 0xE4, 0xCE, 0x12, 0xBA, 0x91, 0xAE, 0xAF, 0x2B, 0x42, 0x79, 0x21, 0x9E, 0xF7, 0x4B, 0xE1, 0xD2, 0x86, 0xAD, 0xD9 }, 0 }, + { 2, { 0, 2 }, 1, { 0 }, { 0 }, { 0x02, 0x08, 0xC5, 0xC4, 0x38, 0xC7, 0x10, 0xF4, 0xF9, 0x6A, 0x61, 0xE9, 0xFF, 0x3C, 0x37, 0x75, 0x88, 0x14, 0xB8, 0xC3, 0xAE, 0x12, 0xBF, 0xEA, 0x0E, 0xD2, 0xC8, 0x7F, 0xF6, 0x95, 0x4F, 0xF1, 0x86, 0x02, 0x0B, 0x18, 0x16, 0xEA, 0x10, 0x4B, 0x4F, 0xCA, 0x2D, 0x30, 0x4D, 0x73, 0x3E, 0x0E, 0x19, 0xCE, 0xAD, 0x51, 0x30, 0x3F, 0xF6, 0x42, 0x0B, 0xFD, 0x22, 0x23, 0x35, 0xCA, 0xA4, 0x02, 0x91, 0x6D }, 2, { 4, 5 }, { 0x5C, 0x55, 0x8E, 0x1D, 0xCA, 0xDE, 0x86, 0xDA, 0x0B, 0x2F, 0x02, 0x62, 0x6A, 0x51, 0x2E, 0x30, 0xA2, 0x2C, 0xF5, 0x25, 0x5C, 0xAE, 0xA7, 0xEE, 0x32, 0xC3, 0x8E, 0x9A, 0x71, 0xA0, 0xE9, 0x14, 0x8B, 0xA6, 0xC0, 0xE6, 0xEC, 0x76, 0x83, 0xB6, 0x42, 0x20, 0xF0, 0x29, 0x86, 0x96, 0xF1, 0xB8, 0x78, 0xCD, 0x47, 0xB1, 0x07, 0xB8, 0x1F, 0x71, 0x88, 0x81, 0x2D, 0x59, 0x39, 0x71, 0xE0, 0xCC }, 0 }, + { 2, { 0, 3 }, 3, { 0, 1, 2 }, { 1, 0, 1 }, { 0x02, 0xB5, 0xAD, 0x07, 0xAF, 0xCD, 0x99, 0xB6, 0xD9, 0x2C, 0xB4, 0x33, 0xFB, 0xD2, 0xA2, 0x8F, 0xDE, 0xB9, 0x8E, 0xAE, 0x2E, 0xB0, 0x9B, 0x60, 0x14, 0xEF, 0x0F, 0x81, 0x97, 0xCD, 0x58, 0x40, 0x33, 0x02, 0xE8, 0x61, 0x69, 0x10, 0xF9, 0x29, 0x3C, 0xF6, 0x92, 0xC4, 0x9F, 0x35, 0x1D, 0xB8, 0x6B, 0x25, 0xE3, 0x52, 0x90, 0x1F, 0x0E, 0x23, 0x7B, 0xAF, 0xDA, 0x11, 0xF1, 0xC1, 0xCE, 0xF2, 0x9F, 0xFD }, 2, { 6, 7 }, { 0x83, 0x9B, 0x08, 0x82, 0x0B, 0x68, 0x1D, 0xBA, 0x8D, 0xAF, 0x4C, 0xC7, 0xB1, 0x04, 0xE8, 0xF2, 0x63, 0x8F, 0x93, 0x88, 0xF8, 0xD7, 0xA5, 0x55, 0xDC, 0x17, 0xB6, 0xE6, 0x97, 0x1D, 0x74, 0x26, 0xCE, 0x07, 0xBF, 0x6A, 0xB0, 0x1F, 0x1D, 0xB5, 0x0E, 0x4E, 0x33, 0x71, 0x92, 0x95, 0xF4, 0x09, 0x45, 0x72, 0xB7, 0x98, 0x68, 0xE4, 0x40, 0xFB, 0x3D, 0xEF, 0xD3, 0xFA, 0xC1, 0xDB, 0x58, 0x9E }, 0 }, + }, + { + { 2, { 0, 3 }, 3, { 0, 1, 2 }, { 1, 0, 1 }, { 0x02, 0xB5, 0xAD, 0x07, 0xAF, 0xCD, 0x99, 0xB6, 0xD9, 0x2C, 0xB4, 0x33, 0xFB, 0xD2, 0xA2, 0x8F, 0xDE, 0xB9, 0x8E, 0xAE, 0x2E, 0xB0, 0x9B, 0x60, 0x14, 0xEF, 0x0F, 0x81, 0x97, 0xCD, 0x58, 0x40, 0x33, 0x02, 0xE8, 0x61, 0x69, 0x10, 0xF9, 0x29, 0x3C, 0xF6, 0x92, 0xC4, 0x9F, 0x35, 0x1D, 0xB8, 0x6B, 0x25, 0xE3, 0x52, 0x90, 0x1F, 0x0E, 0x23, 0x7B, 0xAF, 0xDA, 0x11, 0xF1, 0xC1, 0xCE, 0xF2, 0x9F, 0xFD }, 2, { 7, 8 }, { 0 }, 1 }, + }, +}; +enum { MUSIG_VECTORS_MAX_PUBKEYS = 7 }; From b43dd83b43eac0ca8ad9ee1f557e9126c9e08d9e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Jan 2023 16:41:43 +0000 Subject: [PATCH 235/381] musig: add missing static keyword to function --- src/modules/musig/session_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 96b8eff1..91420731 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -554,7 +554,7 @@ int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_ return 1; } -void secp256k1_musig_partial_sign_clear(secp256k1_scalar *sk, secp256k1_scalar *k) { +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]); From 46c7391154a7325133f97f9ec816ccf98ba76ede Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Wed, 26 Oct 2022 00:18:14 -0700 Subject: [PATCH 236/381] Add norm argument verify API --- .../bulletproofs_pp_norm_product_impl.h | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h index 1f7b16f1..c380bba1 100644 --- a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h +++ b/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h @@ -359,4 +359,190 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( *proof_len = proof_idx; return 1; } + +typedef struct ec_mult_verify_cb_data1 { + const unsigned char *proof; + const secp256k1_ge *commit; + const secp256k1_scalar *challenges; +} ec_mult_verify_cb_data1; + +static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ec_mult_verify_cb_data1 *data = (ec_mult_verify_cb_data1*) cbdata; + if (idx == 0) { + *pt = *data->commit; + secp256k1_scalar_set_int(sc, 1); + return 1; + } + idx -= 1; + if (idx % 2 == 0) { + unsigned char pk_buf[33]; + idx /= 2; + *sc = data->challenges[idx]; + pk_buf[0] = 2 | (data->proof[65*idx] >> 1); + memcpy(&pk_buf[1], &data->proof[65*idx + 1], 32); + if (!secp256k1_eckey_pubkey_parse(pt, pk_buf, sizeof(pk_buf))) { + return 0; + } + } else { + unsigned char pk_buf[33]; + secp256k1_scalar neg_one; + idx /= 2; + secp256k1_scalar_set_int(&neg_one, 1); + secp256k1_scalar_negate(&neg_one, &neg_one); + *sc = data->challenges[idx]; + secp256k1_scalar_sqr(sc, sc); + secp256k1_scalar_add(sc, sc, &neg_one); + pk_buf[0] = 2 | data->proof[65*idx]; + memcpy(&pk_buf[1], &data->proof[65*idx + 33], 32); + if (!secp256k1_eckey_pubkey_parse(pt, pk_buf, sizeof(pk_buf))) { + return 0; + } + } + return 1; +} + +typedef struct ec_mult_verify_cb_data2 { + const secp256k1_scalar *s_g; + const secp256k1_scalar *s_h; + const secp256k1_ge *g_vec; + size_t g_vec_len; +} ec_mult_verify_cb_data2; + +static int ec_mult_verify_cb2(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ec_mult_verify_cb_data2 *data = (ec_mult_verify_cb_data2*) cbdata; + if (idx < data->g_vec_len) { + *sc = data->s_g[idx]; + } else { + *sc = data->s_h[idx - data->g_vec_len]; + } + *pt = data->g_vec[idx]; + return 1; +} + +/* Verify the proof. This function modifies the generators, c_vec and the challenge r. The + caller should make sure to back them up if they need to be reused. +*/ +static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const unsigned char* proof, + size_t proof_len, + secp256k1_sha256* transcript, + const secp256k1_scalar* r, + const secp256k1_bulletproofs_generators* g_vec, + size_t g_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + secp256k1_scalar r_f, q_f, v, n, l, r_inv, h_c; + secp256k1_scalar *es, *s_g, *s_h, *r_inv_pows; + secp256k1_gej res1, res2; + size_t i = 0, scratch_checkpoint; + int overflow; + size_t log_g_len = secp256k1_bulletproofs_pp_log2(g_len), log_h_len = secp256k1_bulletproofs_pp_log2(c_vec_len); + size_t n_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; + size_t h_len = c_vec_len; + + if (g_vec->n != (h_len + g_len) || (proof_len != 65 * n_rounds + 64)) { + return 0; + } + + if (!secp256k1_is_power_of_two(g_len) || !secp256k1_is_power_of_two(h_len)) { + return 0; + } + + secp256k1_scalar_set_b32(&n, &proof[n_rounds*65], &overflow); /* n */ + if (overflow) return 0; + secp256k1_scalar_set_b32(&l, &proof[n_rounds*65 + 32], &overflow); /* l */ + if (overflow) return 0; + if (secp256k1_scalar_is_zero(r)) return 0; + + /* Collect the challenges in a new vector */ + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + es = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_rounds * sizeof(secp256k1_scalar)); + s_g = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); + s_h = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + r_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, log_g_len * sizeof(secp256k1_scalar)); + if (es == NULL || s_g == NULL || s_h == NULL || r_inv_pows == NULL) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + /* Compute powers of r_inv. Later used in g_factor computations*/ + secp256k1_scalar_inverse_var(&r_inv, r); + secp256k1_bulletproofs_powers_of_r(r_inv_pows, &r_inv, log_g_len); + + /* Compute r_f = r^(2^log_g_len) */ + r_f = *r; + for (i = 0; i < log_g_len; i++) { + secp256k1_scalar_sqr(&r_f, &r_f); + } + + for (i = 0; i < n_rounds; i++) { + secp256k1_scalar e; + secp256k1_sha256_write(transcript, &proof[i * 65], 65); + secp256k1_bulletproofs_challenge_scalar(&e, transcript, 0); + es[i] = e; + } + /* s_g[0] = n * \prod_{j=0}^{log_g_len - 1} r^(2^j) + * = n * r^(2^log_g_len - 1) + * = n * r_f * r_inv */ + secp256k1_scalar_mul(&s_g[0], &n, &r_f); + secp256k1_scalar_mul(&s_g[0], &s_g[0], &r_inv); + for (i = 1; i < g_len; i++) { + size_t log_i = secp256k1_bulletproofs_pp_log2(i); + size_t nearest_pow_of_two = (size_t)1 << log_i; + /* This combines the two multiplications of challenges and r_invs in a + * single loop. + * s_g[i] = s_g[i - nearest_pow_of_two] + * * e[log_i] * r_inv^(2^log_i) */ + secp256k1_scalar_mul(&s_g[i], &s_g[i - nearest_pow_of_two], &es[log_i]); + secp256k1_scalar_mul(&s_g[i], &s_g[i], &r_inv_pows[log_i]); + } + s_h[0] = l; + secp256k1_scalar_set_int(&h_c, 0); + for (i = 1; i < h_len; i++) { + size_t log_i = secp256k1_bulletproofs_pp_log2(i); + size_t nearest_pow_of_two = (size_t)1 << log_i; + secp256k1_scalar_mul(&s_h[i], &s_h[i - nearest_pow_of_two], &es[log_i]); + } + secp256k1_scalar_inner_product(&h_c, c_vec, 0 /* a_offset */ , s_h, 0 /* b_offset */, 1 /* step */, h_len); + /* Compute v = n*n*q_f + l*h_c where q_f = r_f^2 */ + secp256k1_scalar_sqr(&q_f, &r_f); + secp256k1_scalar_mul(&v, &n, &n); + secp256k1_scalar_mul(&v, &v, &q_f); + secp256k1_scalar_add(&v, &v, &h_c); + + { + ec_mult_verify_cb_data1 data; + data.proof = proof; + data.commit = commit; + data.challenges = es; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res1, NULL, ec_mult_verify_cb1, &data, 2*n_rounds + 1)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + } + { + ec_mult_verify_cb_data2 data; + data.g_vec = g_vec->gens; + data.g_vec_len = g_len; + data.s_g = s_g; + data.s_h = s_h; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res2, &v, ec_mult_verify_cb2, &data, g_len + h_len)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + } + + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + + /* res1 and res2 should be equal. Could not find a simpler way to compare them */ + secp256k1_gej_neg(&res1, &res1); + secp256k1_gej_add_var(&res1, &res1, &res2, NULL); + return secp256k1_gej_is_infinity(&res1); +} #endif From 25745164835669d71e86863d1de747f26480ec08 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Wed, 26 Oct 2022 00:20:08 -0700 Subject: [PATCH 237/381] Add testcases for bulletproofs++ norm arugment --- src/modules/bulletproofs/tests_impl.h | 187 ++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index e3daead4..ff7519f2 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -187,12 +187,199 @@ void test_norm_util_helpers(void) { secp256k1_scalar_set_int(&res, 256); CHECK(secp256k1_scalar_eq(&res, &r_pows[3])); } +static void secp256k1_norm_arg_commit_initial_data( + secp256k1_sha256* transcript, + const secp256k1_scalar* r, + const secp256k1_bulletproofs_generators* gens_vec, + size_t g_len, /* Same as n_vec_len, g_len + c_vec_len = gens->n */ + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + /* Commit to the initial public values */ + unsigned char ser_commit[33], ser_scalar[32], ser_le64[8]; + size_t i; + secp256k1_ge comm = *commit; + secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(transcript); + secp256k1_fe_normalize(&comm.x); + secp256k1_fe_normalize(&comm.y); + CHECK(secp256k1_bulletproofs_serialize_pt(&ser_commit[0], &comm)); + secp256k1_sha256_write(transcript, ser_commit, 33); + secp256k1_scalar_get_b32(ser_scalar, r); + secp256k1_sha256_write(transcript, ser_scalar, 32); + secp256k1_bulletproofs_le64(ser_le64, g_len); + secp256k1_sha256_write(transcript, ser_le64, 8); + secp256k1_bulletproofs_le64(ser_le64, gens_vec->n); + secp256k1_sha256_write(transcript, ser_le64, 8); + for (i = 0; i < gens_vec->n; i++) { + secp256k1_fe_normalize(&gens_vec->gens[i].x); + secp256k1_fe_normalize(&gens_vec->gens[i].y); + CHECK(secp256k1_bulletproofs_serialize_pt(&ser_commit[0], &gens_vec->gens[i])); + secp256k1_sha256_write(transcript, ser_commit, 33); + } + secp256k1_bulletproofs_le64(ser_le64, c_vec_len); + secp256k1_sha256_write(transcript, ser_le64, 8); + for (i = 0; i < c_vec_len; i++) { + secp256k1_scalar_get_b32(ser_scalar, &c_vec[i]); + secp256k1_sha256_write(transcript, ser_scalar, 32); + } +} + +/* A complete norm argument. In contrast to secp256k1_bulletproofs_pp_rangeproof_norm_product_prove, this is meant + to be used as a standalone norm argument. + This is a simple wrapper around secp256k1_bulletproofs_pp_rangeproof_norm_product_prove + that also commits to the initial public values used in the protocol. In this case, these public + values are commitment. +*/ +static int secp256k1_norm_arg_prove( + secp256k1_scratch_space* scratch, + unsigned char* proof, + size_t *proof_len, + const secp256k1_scalar* r, + const secp256k1_bulletproofs_generators* gens_vec, + const secp256k1_scalar* n_vec, + size_t n_vec_len, + const secp256k1_scalar* l_vec, + size_t l_vec_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + secp256k1_scalar *ns, *ls, *cs; + secp256k1_ge *gs, comm = *commit; + size_t scratch_checkpoint; + size_t g_len = n_vec_len, h_len = l_vec_len; + int res; + secp256k1_sha256 transcript; + + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + ns = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); + ls = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + cs = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + gs = (secp256k1_ge*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, (g_len + h_len) * sizeof(secp256k1_ge)); + if (ns == NULL || ls == NULL || cs == NULL || gs == NULL) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + memcpy(ns, n_vec, g_len * sizeof(secp256k1_scalar)); + memcpy(ls, l_vec, h_len * sizeof(secp256k1_scalar)); + memcpy(cs, c_vec, h_len * sizeof(secp256k1_scalar)); + memcpy(gs, gens_vec->gens, (g_len + h_len) * sizeof(secp256k1_ge)); + + /* Commit to the initial public values */ + secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); + + res = secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( + ctx, + scratch, + proof, + proof_len, + &transcript, /* Transcript hash of the parent protocol */ + r, + gs, + gens_vec->n, + ns, + n_vec_len, + ls, + l_vec_len, + cs, + c_vec_len + ); + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return res; +} + +/* Verify the proof */ +static int secp256k1_norm_arg_verify( + secp256k1_scratch_space* scratch, + const unsigned char* proof, + size_t proof_len, + const secp256k1_scalar* r, + const secp256k1_bulletproofs_generators* gens_vec, + size_t g_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len, + const secp256k1_ge* commit +) { + secp256k1_ge comm = *commit; + int res; + secp256k1_sha256 transcript; + + /* Commit to the initial public values */ + secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); + + res = secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( + ctx, + scratch, + proof, + proof_len, + &transcript, + r, + gens_vec, + g_len, + c_vec, + c_vec_len, + commit + ); + return res; +} + +void norm_arg_test(unsigned int n, unsigned int m) { + secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; + secp256k1_scalar r, q; + secp256k1_ge commit; + size_t i, plen; + int res; + secp256k1_bulletproofs_generators *gs = secp256k1_bulletproofs_generators_create(ctx, n + m); + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*1000); /* shouldn't need much */ + unsigned char proof[1000]; + plen = 1000; + random_scalar_order(&r); + secp256k1_scalar_sqr(&q, &r); + + for (i = 0; i < n; i++) { + random_scalar_order(&n_vec[i]); + } + + for (i = 0; i < m; i++) { + random_scalar_order(&l_vec[i]); + random_scalar_order(&c_vec[i]); + } + + res = secp256k1_bulletproofs_commit(ctx, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &q); + CHECK(res == 1); + res = secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n, l_vec, m, c_vec, m, &commit); + CHECK(res == 1); + + res = secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n, c_vec, m, &commit); + CHECK(res == 1); + + /* Changing any of last two scalars should break the proof */ + proof[plen - 1] ^= 1; + res = secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n, c_vec, m, &commit); + CHECK(res == 0); + proof[plen - 1 - 32] ^= 1; + res = secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n, c_vec, m, &commit); + CHECK(res == 0); + + secp256k1_scratch_space_destroy(ctx, scratch); + secp256k1_bulletproofs_generators_destroy(ctx, gs); +} + void run_bulletproofs_tests(void) { test_log_exp(); test_norm_util_helpers(); test_bulletproofs_generators_api(); test_bulletproofs_generators_fixed(); test_bulletproofs_pp_tagged_hash(); + + norm_arg_test(1, 1); + norm_arg_test(1, 64); + norm_arg_test(64, 1); + norm_arg_test(32, 32); + norm_arg_test(32, 64); + norm_arg_test(64, 32); + norm_arg_test(64, 64); } #endif From 34c4847a6a72e340dac2c078bbea4d65441e5971 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 20 Jan 2023 13:41:01 +0000 Subject: [PATCH 238/381] ci: add bulletproofs --- .cirrus.yml | 10 +++++++--- ci/cirrus.sh | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index d9d98d43..e4ef0e34 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -23,6 +23,7 @@ env: WHITELIST: no MUSIG: no ECDSAADAPTOR: no + BULLETPROOFS: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -72,12 +73,12 @@ task: << : *LINUX_CONTAINER matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BULLETPROOFS: yes} - env: {WIDEMUL: int128} - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} + - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BULLETPROOFS: yes} - env: {WIDEMUL: int128, ASM: x86_64} - - env: { RECOVERY: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} + - env: { RECOVERY: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BULLETPROOFS: yes} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} - env: {CFLAGS: -O0, CTIMETEST: no} @@ -108,6 +109,7 @@ task: GENERATOR: yes MUSIG: yes ECDSAADAPTOR: yes + BULLETPROOFS: yes matrix: - env: CC: i686-linux-gnu-gcc @@ -165,6 +167,7 @@ task: GENERATOR: yes MUSIG: yes ECDSAADAPTOR: yes + BULLETPROOFS: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -259,6 +262,7 @@ task: GENERATOR: yes MUSIG: yes ECDSAADAPTOR: yes + BULLETPROOFS: yes CTIMETEST: no matrix: - name: "Valgrind (memcheck)" diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 74e8ab5b..8f2b105d 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -52,6 +52,10 @@ then $EXEC ./bench_ecmult $EXEC ./bench_internal $EXEC ./bench + if [ "$BULLETPROOFS" = "yes" ] + then + $EXEC ./bench_bulletproofs + fi } >> bench.log 2>&1 fi From 13ad32e814ece805a5bd2ef7c4b46fa37cedf136 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 26 Jan 2023 22:23:17 +0000 Subject: [PATCH 239/381] norm arg: add tests for zero length and zero vectors --- src/modules/bulletproofs/tests_impl.h | 99 +++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 12 deletions(-) diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index ff7519f2..9943fef5 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -203,6 +203,7 @@ static void secp256k1_norm_arg_commit_initial_data( secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(transcript); secp256k1_fe_normalize(&comm.x); secp256k1_fe_normalize(&comm.y); + CHECK(secp256k1_ge_is_infinity(&comm) == 0); CHECK(secp256k1_bulletproofs_serialize_pt(&ser_commit[0], &comm)); secp256k1_sha256_write(transcript, ser_commit, 33); secp256k1_scalar_get_b32(ser_scalar, r); @@ -225,6 +226,28 @@ static void secp256k1_norm_arg_commit_initial_data( } } +static void copy_vectors_into_scratch(secp256k1_scratch_space* scratch, + secp256k1_scalar **ns, + secp256k1_scalar **ls, + secp256k1_scalar **cs, + secp256k1_ge **gs, + const secp256k1_scalar *n_vec, + const secp256k1_scalar *l_vec, + const secp256k1_scalar *c_vec, + const secp256k1_ge *gens_vec, + size_t g_len, + size_t h_len) { + *ns = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); + *ls = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + *cs = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + *gs = (secp256k1_ge*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, (g_len + h_len) * sizeof(secp256k1_ge)); + CHECK(ns != NULL && ls != NULL && cs != NULL && gs != NULL); + memcpy(*ns, n_vec, g_len * sizeof(secp256k1_scalar)); + memcpy(*ls, l_vec, h_len * sizeof(secp256k1_scalar)); + memcpy(*cs, c_vec, h_len * sizeof(secp256k1_scalar)); + memcpy(*gs, gens_vec, (g_len + h_len) * sizeof(secp256k1_ge)); +} + /* A complete norm argument. In contrast to secp256k1_bulletproofs_pp_rangeproof_norm_product_prove, this is meant to be used as a standalone norm argument. This is a simple wrapper around secp256k1_bulletproofs_pp_rangeproof_norm_product_prove @@ -253,18 +276,8 @@ static int secp256k1_norm_arg_prove( secp256k1_sha256 transcript; scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - ns = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); - ls = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); - cs = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); - gs = (secp256k1_ge*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, (g_len + h_len) * sizeof(secp256k1_ge)); - if (ns == NULL || ls == NULL || cs == NULL || gs == NULL) { - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); - return 0; - } - memcpy(ns, n_vec, g_len * sizeof(secp256k1_scalar)); - memcpy(ls, l_vec, h_len * sizeof(secp256k1_scalar)); - memcpy(cs, c_vec, h_len * sizeof(secp256k1_scalar)); - memcpy(gs, gens_vec->gens, (g_len + h_len) * sizeof(secp256k1_ge)); + + copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens_vec->gens, g_len, h_len); /* Commit to the initial public values */ secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); @@ -324,6 +337,67 @@ static int secp256k1_norm_arg_verify( return res; } +void norm_arg_zero(void) { + secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; + secp256k1_scalar r, q; + secp256k1_ge commit; + size_t i; + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*10); /* shouldn't need much */ + unsigned char proof[1000]; + secp256k1_sha256 transcript; + + random_scalar_order(&r); + secp256k1_scalar_sqr(&q, &r); + + /* l is zero vector and n is zero vectors of length 1 each. */ + { + size_t plen = sizeof(proof); + unsigned int n_vec_len = 1; + unsigned int c_vec_len = 1; + secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, n_vec_len + c_vec_len); + + secp256k1_scalar_set_int(&n_vec[0], 0); + secp256k1_scalar_set_int(&l_vec[0], 0); + random_scalar_order(&c_vec[0]); + + secp256k1_sha256_initialize(&transcript); /* No challenges used in n = 1, l = 1, but we set transcript as a good practice*/ + CHECK(secp256k1_bulletproofs_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); + { + secp256k1_scalar *ns, *ls, *cs; + secp256k1_ge *gs; + size_t scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens->gens, n_vec_len, c_vec_len); + CHECK(secp256k1_bulletproofs_pp_rangeproof_norm_product_prove(ctx, scratch, proof, &plen, &transcript, &r, gs, gens->n, ns, n_vec_len, ls, c_vec_len, cs, c_vec_len)); + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + } + secp256k1_sha256_initialize(&transcript); + CHECK(secp256k1_bulletproofs_pp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gens, c_vec_len, c_vec, c_vec_len, &commit)); + + secp256k1_bulletproofs_generators_destroy(ctx, gens); + } + + /* l is the zero vector and longer than n. This results in one of the + * internal commitments X or R to be the point at infinity. */ + { + unsigned int n_vec_len = 1; + unsigned int c_vec_len = 2; + secp256k1_bulletproofs_generators *gs = secp256k1_bulletproofs_generators_create(ctx, n_vec_len + c_vec_len); + size_t plen = sizeof(proof); + for (i = 0; i < n_vec_len; i++) { + random_scalar_order(&n_vec[i]); + } + for (i = 0; i < c_vec_len; i++) { + secp256k1_scalar_set_int(&l_vec[i], 0); + random_scalar_order(&c_vec[i]); + } + CHECK(secp256k1_bulletproofs_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); + CHECK(!secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + secp256k1_bulletproofs_generators_destroy(ctx, gs); + } + + secp256k1_scratch_space_destroy(ctx, scratch); +} + void norm_arg_test(unsigned int n, unsigned int m) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; secp256k1_scalar r, q; @@ -373,6 +447,7 @@ void run_bulletproofs_tests(void) { test_bulletproofs_generators_fixed(); test_bulletproofs_pp_tagged_hash(); + norm_arg_zero(); norm_arg_test(1, 1); norm_arg_test(1, 64); norm_arg_test(64, 1); From 73edc75528a9a4d4cf69b77d38f108023a132994 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 31 Jan 2023 14:05:58 +0000 Subject: [PATCH 240/381] norm arg: add verification vectors norm arg: add verify test vector with vector size > 1 --- .../bulletproofs/test_vectors/verify.h | 65 +++++++++++++++ src/modules/bulletproofs/tests_impl.h | 82 +++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/modules/bulletproofs/test_vectors/verify.h diff --git a/src/modules/bulletproofs/test_vectors/verify.h b/src/modules/bulletproofs/test_vectors/verify.h new file mode 100644 index 00000000..5397f67a --- /dev/null +++ b/src/modules/bulletproofs/test_vectors/verify.h @@ -0,0 +1,65 @@ +static const unsigned char verify_vector_gens[264] = { 0x03, 0xAF, 0x2C, 0x40, 0xAD, 0x03, 0xCD, 0xC5, 0x76, 0x8C, 0x07, 0x1E, 0x58, 0xD6, 0x8C, 0x73, 0x45, 0xBA, 0xEB, 0xB5, 0x3F, 0x40, 0xFA, 0x8B, 0xBF, 0x73, 0x6E, 0x7B, 0x4A, 0x54, 0x06, 0xED, 0x32, 0x03, 0xCC, 0x11, 0x19, 0x22, 0x2C, 0xA1, 0x0A, 0x45, 0x23, 0xAF, 0x9B, 0x40, 0x0D, 0xA4, 0x5E, 0x06, 0x24, 0xF4, 0x5F, 0x07, 0x89, 0x88, 0xCD, 0x71, 0xAE, 0x77, 0xC1, 0xF5, 0x87, 0x4E, 0xFC, 0xA5, 0x03, 0xDE, 0x61, 0xB1, 0x8F, 0x2C, 0xAC, 0x18, 0xF5, 0xE4, 0x06, 0x8F, 0x65, 0x55, 0xA1, 0x30, 0x5E, 0xF5, 0xF4, 0x84, 0xED, 0x6B, 0xDD, 0xC2, 0xCC, 0xE8, 0x51, 0x38, 0xB8, 0xA5, 0x4C, 0x43, 0xBD, 0x02, 0xA5, 0xF9, 0x8C, 0x1F, 0x82, 0x2D, 0xC6, 0xF3, 0x0F, 0x53, 0xDB, 0x74, 0x77, 0xC7, 0x91, 0x04, 0xB0, 0xB1, 0xA6, 0x17, 0xB2, 0x91, 0xF4, 0x8B, 0x93, 0x3E, 0xBB, 0x73, 0x15, 0x3E, 0x5A, 0xD1, 0x02, 0x44, 0xF5, 0xC6, 0x4E, 0x77, 0x60, 0x81, 0x83, 0xFF, 0xC2, 0x8E, 0x06, 0xFE, 0x67, 0x0C, 0x9A, 0x4B, 0xF2, 0x34, 0xB9, 0xEA, 0xE9, 0x37, 0xDA, 0x30, 0xE2, 0x32, 0x27, 0xF3, 0x88, 0x5F, 0x2A, 0x02, 0x1D, 0x49, 0x5D, 0x04, 0xED, 0x61, 0x95, 0x37, 0xDD, 0x95, 0xB1, 0x4F, 0x64, 0x0E, 0x1E, 0xFB, 0x47, 0x9F, 0xA7, 0xD7, 0xE0, 0x7A, 0xB1, 0x02, 0x81, 0x95, 0xD1, 0xA5, 0x7E, 0xB2, 0x74, 0x8F, 0x03, 0x26, 0xA5, 0xEC, 0xE9, 0x71, 0x46, 0x37, 0xAC, 0x3D, 0x74, 0x84, 0x26, 0xCB, 0x7C, 0xE8, 0xFE, 0x4E, 0xB0, 0x6D, 0x70, 0x3D, 0x00, 0x10, 0x1A, 0x3A, 0x5B, 0xB8, 0xAA, 0x29, 0x59, 0x93, 0x15, 0x03, 0xE1, 0xA5, 0x39, 0x44, 0x75, 0x16, 0x28, 0x5F, 0xBA, 0x69, 0xA2, 0x4A, 0x2A, 0xC3, 0x5B, 0x63, 0x1F, 0x40, 0x10, 0x36, 0xF9, 0x4C, 0xD2, 0x76, 0x0F, 0xCF, 0x7F, 0x50, 0x30, 0x6E, 0x2B, 0x1D }; +static const unsigned char verify_vector_0_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_0_n_vec_len = 1; +static const unsigned char verify_vector_0_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_0_c_vec[1]; +static const unsigned char verify_vector_0_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_0_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_0_result = 1; +static const unsigned char verify_vector_1_commit33[33] = { 0x02, 0x6C, 0x09, 0xD7, 0x06, 0x2D, 0x1C, 0x07, 0x0A, 0x64, 0x34, 0x82, 0xF6, 0x46, 0x03, 0xEB, 0x24, 0x3E, 0x54, 0x0F, 0xDA, 0xAF, 0x3A, 0x69, 0x5F, 0x86, 0xB6, 0xD2, 0xC2, 0x06, 0xE9, 0x49, 0xC7 }; +static const size_t verify_vector_1_n_vec_len = 1; +static const unsigned char verify_vector_1_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_1_c_vec[1]; +static const unsigned char verify_vector_1_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_1_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_1_result = 0; +static const unsigned char verify_vector_2_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_2_n_vec_len = 1; +static const unsigned char verify_vector_2_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_2_c_vec[1]; +static const unsigned char verify_vector_2_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_2_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; +static const int verify_vector_2_result = 0; +static const unsigned char verify_vector_3_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_3_n_vec_len = 1; +static const unsigned char verify_vector_3_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_3_c_vec[1]; +static const unsigned char verify_vector_3_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_3_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; +static const int verify_vector_3_result = 0; +static const unsigned char verify_vector_4_commit33[33] = { 0x03, 0xD7, 0x53, 0x31, 0x5B, 0xAA, 0x04, 0xD5, 0x7C, 0x4A, 0x34, 0x94, 0x98, 0xBC, 0xA9, 0x1E, 0xD6, 0xA3, 0xBF, 0x81, 0xFC, 0x38, 0x30, 0x7C, 0x3B, 0x7C, 0xFC, 0xC6, 0xFF, 0x1A, 0x13, 0x36, 0x72 }; +static const size_t verify_vector_4_n_vec_len = 1; +static const unsigned char verify_vector_4_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_4_c_vec[1]; +static const unsigned char verify_vector_4_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_4_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41 }; +static const int verify_vector_4_result = 0; +static const unsigned char verify_vector_5_commit33[33] = { 0x03, 0x83, 0x6A, 0xD4, 0x2D, 0xD2, 0x02, 0x49, 0xC8, 0x6E, 0x53, 0x22, 0x53, 0x24, 0xDA, 0x52, 0x08, 0xC0, 0x62, 0x4C, 0xCB, 0xB3, 0x13, 0xD7, 0x14, 0x59, 0x68, 0x47, 0x56, 0x00, 0xC0, 0x8D, 0xBA }; +static const size_t verify_vector_5_n_vec_len = 2; +static const unsigned char verify_vector_5_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_5_c_vec[1]; +static const unsigned char verify_vector_5_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x36 }; +static const unsigned char verify_vector_5_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x4C, 0xB9, 0xD4, 0x34, 0xA2, 0xD6, 0xD5, 0x4C, 0x0F, 0x2E, 0x2C, 0xE3, 0x82, 0x17, 0x48, 0x63, 0xE0, 0xAE, 0x6B, 0xD7, 0x64, 0x9D, 0x43, 0x2B, 0x6E, 0x6E, 0x1C, 0x62, 0x55, 0x4B, 0xC5, 0x73, 0x3D, 0x74, 0x7B, 0x78, 0x43, 0xF4, 0x8B, 0x7C, 0x84, 0x10, 0x00, 0x8B, 0x12, 0xAF, 0xA4, 0xF1, 0xF4, 0x01, 0x96, 0x21, 0x8B, 0xE9, 0x05, 0x01, 0xF8, 0x23, 0x7A, 0x8F, 0x66, 0xC9, 0xDE, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_5_result = 0; +static const unsigned char verify_vector_6_commit33[33] = { 0x03, 0xCF, 0x7F, 0x08, 0xF5, 0x8A, 0x06, 0x74, 0x5C, 0xDB, 0xCE, 0xC6, 0x51, 0xF3, 0xE5, 0xE4, 0xDC, 0xAD, 0xF4, 0x40, 0x3C, 0xFA, 0xE6, 0x78, 0xBE, 0x49, 0x2D, 0x90, 0xC8, 0xD0, 0x16, 0x3D, 0x78 }; +static const size_t verify_vector_6_n_vec_len = 2; +static const unsigned char verify_vector_6_c_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x30 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D } }; +static secp256k1_scalar verify_vector_6_c_vec[4]; +static const unsigned char verify_vector_6_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char verify_vector_6_proof[] = { 0x00, 0xD2, 0xEC, 0xE2, 0x53, 0x97, 0x28, 0x68, 0x22, 0x59, 0x34, 0xEF, 0xE4, 0x7B, 0x87, 0x4D, 0xE9, 0x57, 0xD5, 0xB7, 0xC7, 0x72, 0xF4, 0xC9, 0xEA, 0x66, 0x14, 0x59, 0xE1, 0xA9, 0xD5, 0xB2, 0x10, 0xDF, 0xE2, 0xFF, 0xF5, 0xA4, 0x38, 0x6B, 0xFE, 0x36, 0x89, 0xE4, 0x9D, 0x90, 0x9F, 0x71, 0x19, 0xE6, 0xA3, 0x1E, 0xAA, 0xAA, 0x4E, 0xFE, 0xC2, 0xD3, 0x37, 0xBB, 0xDE, 0xDB, 0x46, 0x43, 0xC2, 0x01, 0x42, 0x5F, 0xFC, 0xC6, 0x25, 0xA0, 0xB4, 0xF0, 0x76, 0x99, 0xF4, 0x7C, 0xE9, 0x83, 0x82, 0xED, 0x7C, 0x95, 0xBA, 0xD0, 0xE6, 0x5B, 0x88, 0xFD, 0x38, 0xEA, 0x23, 0x54, 0xD4, 0xBD, 0xD4, 0x37, 0xB8, 0x2B, 0x49, 0xAF, 0x81, 0xFD, 0xBE, 0x88, 0xB2, 0xE5, 0x3F, 0xF4, 0x30, 0x52, 0x00, 0x63, 0x9D, 0xAE, 0x82, 0x44, 0xE9, 0x62, 0x87, 0x2A, 0x23, 0x89, 0x10, 0xE4, 0x9A, 0x64, 0x9F, 0x71, 0xD9, 0x32, 0x57, 0x3B, 0xCB, 0xAC, 0x30, 0xAE, 0x71, 0x61, 0xE9, 0x50, 0x1F, 0xCB, 0x49, 0x9C, 0x52, 0xBA, 0x0C, 0xC4, 0x00, 0x58, 0x73, 0x63, 0xD3, 0x42, 0xDE, 0x42, 0x5E, 0xC5, 0x97, 0xE5, 0xDA, 0x88, 0x76, 0x49, 0x6C, 0x8B, 0x92, 0x99, 0xEE, 0xD0, 0xA9, 0xEB, 0x6E, 0xCA, 0xE1, 0x93, 0x81, 0x56, 0x2E, 0xCA, 0xF3, 0x8E, 0xF0, 0x04, 0xD2, 0x96, 0xD8, 0xDB, 0xEE, 0xEE, 0x1C, 0x44 }; +static const int verify_vector_6_result = 1; +static const unsigned char verify_vector_7_commit33[33] = { 0x02, 0x7A, 0xAA, 0xB2, 0x7E, 0xA5, 0x5B, 0x77, 0x08, 0xE5, 0x43, 0xB6, 0x22, 0x7F, 0xC9, 0xAC, 0x53, 0x10, 0x32, 0x61, 0x7B, 0x7D, 0xAC, 0xB1, 0xB6, 0xF6, 0xAC, 0xDE, 0x63, 0x79, 0x82, 0x9C, 0x24 }; +static const size_t verify_vector_7_n_vec_len = 4; +static const unsigned char verify_vector_7_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_7_c_vec[1]; +static const unsigned char verify_vector_7_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_7_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_7_result = 1; +static const unsigned char verify_vector_8_commit33[33] = { 0x02, 0x2D, 0x4F, 0xF9, 0xB7, 0x15, 0x22, 0xBC, 0xB0, 0x8B, 0xF8, 0xBA, 0x31, 0x0A, 0x80, 0x76, 0x7A, 0xE9, 0xA9, 0x83, 0x00, 0xBC, 0x5A, 0x01, 0xCC, 0xE9, 0x00, 0x83, 0x56, 0xEA, 0x77, 0xEB, 0x75 }; +static const size_t verify_vector_8_n_vec_len = 4; +static const unsigned char verify_vector_8_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_8_c_vec[1]; +static const unsigned char verify_vector_8_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_8_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int verify_vector_8_result = 0; + diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index 9943fef5..012afefa 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -13,6 +13,7 @@ #include "bulletproofs_pp_norm_product_impl.h" #include "bulletproofs_util.h" #include "bulletproofs_pp_transcript_impl.h" +#include "test_vectors/verify.h" static void test_bulletproofs_generators_api(void) { /* The BP generator API requires no precomp */ @@ -440,6 +441,86 @@ void norm_arg_test(unsigned int n, unsigned int m) { secp256k1_bulletproofs_generators_destroy(ctx, gs); } +/* Parses generators from points compressed as pubkeys */ +secp256k1_bulletproofs_generators* bulletproofs_generators_parse_regular(const unsigned char* data, size_t data_len) { + size_t n = data_len / 33; + secp256k1_bulletproofs_generators* ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(data != NULL); + + if (data_len % 33 != 0) { + return NULL; + } + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->n = n; + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + + while (n--) { + if (!secp256k1_eckey_pubkey_parse(&ret->gens[n], &data[33 * n], 33)) { + free(ret->gens); + free(ret); + return NULL; + } + } + return ret; +} + +int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, size_t n_vec_len, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, const unsigned char *commit33) { + secp256k1_sha256 transcript; + secp256k1_bulletproofs_generators *gs = bulletproofs_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); + secp256k1_scalar r; + secp256k1_ge commit; + int overflow; + int i; + int ret; + + CHECK(gs != NULL); + secp256k1_sha256_initialize(&transcript); + + secp256k1_scalar_set_b32(&r, r32, &overflow); + CHECK(!overflow); + + for (i = 0; i < (int)c_vec_len; i++) { + secp256k1_scalar_set_b32(&c_vec[i], c_vec32[i], &overflow); + CHECK(!overflow); + } + CHECK(secp256k1_eckey_pubkey_parse(&commit, commit33, 33)); + ret = secp256k1_bulletproofs_pp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gs, n_vec_len, c_vec, c_vec_len, &commit); + + secp256k1_bulletproofs_generators_destroy(ctx, gs); + return ret; +} + +#define IDX_TO_TEST(i) (norm_arg_verify_vectors_helper(scratch, verify_vector_gens, verify_vector_##i##_proof, sizeof(verify_vector_##i##_proof), verify_vector_##i##_r32, verify_vector_##i##_n_vec_len, verify_vector_##i##_c_vec32, verify_vector_##i##_c_vec, sizeof(verify_vector_##i##_c_vec)/sizeof(secp256k1_scalar), verify_vector_##i##_commit33) == verify_vector_##i##_result) + +void norm_arg_verify_vectors(void) { + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*1000); /* shouldn't need much */ + size_t alloc = scratch->alloc_size; + + CHECK(IDX_TO_TEST(0)); + CHECK(IDX_TO_TEST(1)); + CHECK(IDX_TO_TEST(2)); + CHECK(IDX_TO_TEST(3)); + CHECK(IDX_TO_TEST(4)); + CHECK(IDX_TO_TEST(5)); + CHECK(IDX_TO_TEST(6)); + CHECK(IDX_TO_TEST(7)); + CHECK(IDX_TO_TEST(8)); + + CHECK(alloc == scratch->alloc_size); + secp256k1_scratch_space_destroy(ctx, scratch); +} +#undef IDX_TO_TEST + void run_bulletproofs_tests(void) { test_log_exp(); test_norm_util_helpers(); @@ -455,6 +536,7 @@ void run_bulletproofs_tests(void) { norm_arg_test(32, 64); norm_arg_test(64, 32); norm_arg_test(64, 64); + norm_arg_verify_vectors(); } #endif From c9831868723b06cca72141651f9e27f37c6ca3eb Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 31 Jan 2023 16:32:32 +0000 Subject: [PATCH 241/381] transcript: add tests --- src/modules/bulletproofs/tests_impl.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index 012afefa..dd3e4130 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -128,12 +128,36 @@ static void test_bulletproofs_pp_tagged_hash(void) { secp256k1_sha256 sha_cached; unsigned char output[32]; unsigned char output_cached[32]; + secp256k1_scalar s; secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data)); secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(&sha_cached); secp256k1_sha256_finalize(&sha, output); secp256k1_sha256_finalize(&sha_cached, output_cached); CHECK(secp256k1_memcmp_var(output, output_cached, 32) == 0); + + { + unsigned char expected[32] = { 0x21, 0x2F, 0xB6, 0x4F, 0x9D, 0x8C, 0x3B, 0xC5, + 0xF6, 0x91, 0x15, 0xEE, 0x74, 0xF5, 0x12, 0x67, + 0x8A, 0x41, 0xC6, 0x85, 0x1A, 0x79, 0x14, 0xFC, + 0x48, 0x15, 0xC7, 0x2D, 0xF8, 0x63, 0x8F, 0x1B }; + secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(&sha); + secp256k1_bulletproofs_challenge_scalar(&s, &sha, 0); + secp256k1_scalar_get_b32(output, &s); + CHECK(memcmp(output, expected, sizeof(output)) == 0); + } + + { + unsigned char tmp[3] = {0, 1, 2}; + unsigned char expected[32] = { 0x8D, 0xAA, 0xB7, 0x7E, 0x3C, 0x6A, 0x9E, 0xEC, + 0x72, 0x7E, 0x3E, 0xB7, 0x10, 0x03, 0xF0, 0xE9, + 0x69, 0x4D, 0xAA, 0x96, 0xCE, 0x98, 0xBB, 0x39, + 0x1C, 0x2F, 0x7C, 0x2E, 0x1C, 0x17, 0x78, 0x6D }; + secp256k1_sha256_write(&sha, tmp, sizeof(tmp)); + secp256k1_bulletproofs_challenge_scalar(&s, &sha, 0); + secp256k1_scalar_get_b32(output, &s); + CHECK(memcmp(output, expected, sizeof(output)) == 0); + } } void test_log_exp(void) { From e5a01d12c63b30d3627cd0114a042a9853b0d233 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 6 Feb 2023 13:53:02 -0800 Subject: [PATCH 242/381] Rename buletproof_pp* to bppp* --- .cirrus.yml | 14 +- .gitignore | 2 +- Makefile.am | 4 +- ci/cirrus.sh | 6 +- configure.ac | 18 +-- ...p256k1_bulletproofs.h => secp256k1_bppp.h} | 20 +-- src/{bench_bulletproofs.c => bench_bppp.c} | 14 +- src/modules/bppp/Makefile.am.include | 13 ++ .../bppp_norm_product_impl.h} | 38 ++--- .../bppp_transcript_impl.h} | 12 +- .../bulletproofs_util.h => bppp/bppp_util.h} | 12 +- src/modules/{bulletproofs => bppp}/main.h | 8 +- .../{bulletproofs => bppp}/main_impl.h | 26 +-- .../test_vectors/verify.h | 0 .../{bulletproofs => bppp}/tests_impl.h | 148 +++++++++--------- src/modules/bulletproofs/Makefile.am.include | 13 -- src/secp256k1.c | 4 +- src/tests.c | 8 +- 18 files changed, 180 insertions(+), 180 deletions(-) rename include/{secp256k1_bulletproofs.h => secp256k1_bppp.h} (77%) rename src/{bench_bulletproofs.c => bench_bppp.c} (64%) create mode 100644 src/modules/bppp/Makefile.am.include rename src/modules/{bulletproofs/bulletproofs_pp_norm_product_impl.h => bppp/bppp_norm_product_impl.h} (93%) rename src/modules/{bulletproofs/bulletproofs_pp_transcript_impl.h => bppp/bppp_transcript_impl.h} (73%) rename src/modules/{bulletproofs/bulletproofs_util.h => bppp/bppp_util.h} (78%) rename src/modules/{bulletproofs => bppp}/main.h (50%) rename src/modules/{bulletproofs => bppp}/main_impl.h (70%) rename src/modules/{bulletproofs => bppp}/test_vectors/verify.h (100%) rename src/modules/{bulletproofs => bppp}/tests_impl.h (75%) delete mode 100644 src/modules/bulletproofs/Makefile.am.include diff --git a/.cirrus.yml b/.cirrus.yml index e4ef0e34..48c9be8c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -23,7 +23,7 @@ env: WHITELIST: no MUSIG: no ECDSAADAPTOR: no - BULLETPROOFS: no + BPPP: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -73,12 +73,12 @@ task: << : *LINUX_CONTAINER matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BULLETPROOFS: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BPPP: yes} - env: {WIDEMUL: int128} - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BULLETPROOFS: yes} + - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BPPP: yes} - env: {WIDEMUL: int128, ASM: x86_64} - - env: { RECOVERY: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BULLETPROOFS: yes} + - env: { RECOVERY: yes, SCHNORRSIG: yes, EXPERIMENTAL: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes, BPPP: yes} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} - env: {CFLAGS: -O0, CTIMETEST: no} @@ -109,7 +109,7 @@ task: GENERATOR: yes MUSIG: yes ECDSAADAPTOR: yes - BULLETPROOFS: yes + BPPP: yes matrix: - env: CC: i686-linux-gnu-gcc @@ -167,7 +167,7 @@ task: GENERATOR: yes MUSIG: yes ECDSAADAPTOR: yes - BULLETPROOFS: yes + BPPP: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -262,7 +262,7 @@ task: GENERATOR: yes MUSIG: yes ECDSAADAPTOR: yes - BULLETPROOFS: yes + BPPP: yes CTIMETEST: no matrix: - name: "Valgrind (memcheck)" diff --git a/.gitignore b/.gitignore index 3c0494d5..9be37772 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ bench -bench_bulletproofs +bench_bppp bench_ecmult bench_generator bench_rangeproof diff --git a/Makefile.am b/Makefile.am index 722dfac3..482d870d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,8 +226,8 @@ clean-precomp: EXTRA_DIST = autogen.sh SECURITY.md -if ENABLE_MODULE_BULLETPROOFS -include src/modules/bulletproofs/Makefile.am.include +if ENABLE_MODULE_BPPP +include src/modules/bppp/Makefile.am.include endif if ENABLE_MODULE_ECDH diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 8f2b105d..14c8dbe9 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -19,7 +19,7 @@ valgrind --version || true --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-ecdsa-s2c="$ECDSA_S2C" \ - --enable-module-bulletproofs="$BULLETPROOFS" \ + --enable-module-bppp="$BPPP" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-schnorrsig="$SCHNORRSIG" \ @@ -52,9 +52,9 @@ then $EXEC ./bench_ecmult $EXEC ./bench_internal $EXEC ./bench - if [ "$BULLETPROOFS" = "yes" ] + if [ "$BPPP" = "yes" ] then - $EXEC ./bench_bulletproofs + $EXEC ./bench_bppp fi } >> bench.log 2>&1 fi diff --git a/configure.ac b/configure.ac index 0b0a9d78..c168694c 100644 --- a/configure.ac +++ b/configure.ac @@ -140,10 +140,10 @@ AC_ARG_ENABLE(examples, AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) -AC_ARG_ENABLE(module_bulletproofs, - AS_HELP_STRING([--enable-module-bulletproofs],[enable Bulletproofs module (experimental)]), +AC_ARG_ENABLE(module_bppp, + AS_HELP_STRING([--enable-module-bppp],[enable Bulletproofs++ module (experimental)]), [], - [SECP_SET_DEFAULT([enable_module_bulletproofs], [no], [yes])]) + [SECP_SET_DEFAULT([enable_module_bppp], [no], [yes])]) AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=no]]), [], @@ -422,9 +422,9 @@ if test x"$enable_module_rangeproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) fi -if test x"$enable_module_bulletproofs" = x"yes"; then +if test x"$enable_module_bppp" = x"yes"; then enable_module_generator=yes - AC_DEFINE(ENABLE_MODULE_BULLETPROOFS, 1, [Define this symbol to enable the Bulletproofs module]) + AC_DEFINE(ENABLE_MODULE_BPPP, 1, [Define this symbol to enable the Bulletproofs++ module]) fi if test x"$enable_module_generator" = x"yes"; then @@ -470,8 +470,8 @@ else # module (which automatically enables the module dependencies) we want to # print an error for the dependent module, not the module dependency. Hence, # we first test dependent modules. - if test x"$enable_module_bulletproofs" = x"yes"; then - AC_MSG_ERROR([Bulletproofs module is experimental. Use --enable-experimental to allow.]) + if test x"$enable_module_bppp" = x"yes"; then + AC_MSG_ERROR([Bulletproofs++ module is experimental. Use --enable-experimental to allow.]) fi if test x"$enable_module_whitelist" = x"yes"; then AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) @@ -515,7 +515,7 @@ AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"]) AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"]) -AM_CONDITIONAL([ENABLE_MODULE_BULLETPROOFS], [test x"$enable_module_bulletproofs" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_BPPP], [test x"$enable_module_bppp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) @@ -555,7 +555,7 @@ echo " module whitelist = $enable_module_whitelist" echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" -echo " module bulletproofs = $enable_module_bulletproofs" +echo " module bppp = $enable_module_bppp" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bppp.h similarity index 77% rename from include/secp256k1_bulletproofs.h rename to include/secp256k1_bppp.h index 1ddd9699..c880ee48 100644 --- a/include/secp256k1_bulletproofs.h +++ b/include/secp256k1_bppp.h @@ -1,5 +1,5 @@ -#ifndef _SECP256K1_BULLETPROOFS_ -# define _SECP256K1_BULLETPROOFS_ +#ifndef _SECP256K1_BPPP_ +# define _SECP256K1_BPPP_ # include "secp256k1.h" @@ -10,7 +10,7 @@ extern "C" { #include /** Opaque structure representing a large number of NUMS generators */ -typedef struct secp256k1_bulletproofs_generators secp256k1_bulletproofs_generators; +typedef struct secp256k1_bppp_generators secp256k1_bppp_generators; /** Allocates and initializes a list of NUMS generators. * Returns a list of generators, or calls the error callback if the allocation fails. @@ -21,7 +21,7 @@ typedef struct secp256k1_bulletproofs_generators secp256k1_bulletproofs_generato * points. We will later use G = H0(required for compatibility with pedersen_commitment DS) * in a separate commit to make review easier. */ -SECP256K1_API secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create( +SECP256K1_API secp256k1_bppp_generators *secp256k1_bppp_generators_create( const secp256k1_context* ctx, size_t n ) SECP256K1_ARG_NONNULL(1); @@ -29,10 +29,10 @@ SECP256K1_API secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generato /** Allocates a list of generators from a static array * Returns a list of generators or NULL in case of failure. * Args: ctx: pointer to a context object - * In: data: data that came from `secp256k1_bulletproofs_generators_serialize` + * In: data: data that came from `secp256k1_bppp_generators_serialize` * data_len: the length of the `data` buffer */ -SECP256K1_API secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse( +SECP256K1_API secp256k1_bppp_generators* secp256k1_bppp_generators_parse( const secp256k1_context* ctx, const unsigned char* data, size_t data_len @@ -49,9 +49,9 @@ SECP256K1_API secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generato * TODO: For ease of review, this setting G = H0 is not included in this commit. We will * add it in the follow-up rangeproof PR. */ -SECP256K1_API int secp256k1_bulletproofs_generators_serialize( +SECP256K1_API int secp256k1_bppp_generators_serialize( const secp256k1_context* ctx, - const secp256k1_bulletproofs_generators* gen, + const secp256k1_bppp_generators* gen, unsigned char* data, size_t *data_len ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); @@ -61,9 +61,9 @@ SECP256K1_API int secp256k1_bulletproofs_generators_serialize( * gen: pointer to the generator set to be destroyed * (can be NULL, in which case this function is a no-op) */ -SECP256K1_API void secp256k1_bulletproofs_generators_destroy( +SECP256K1_API void secp256k1_bppp_generators_destroy( const secp256k1_context* ctx, - secp256k1_bulletproofs_generators* gen + secp256k1_bppp_generators* gen ) SECP256K1_ARG_NONNULL(1); # ifdef __cplusplus diff --git a/src/bench_bulletproofs.c b/src/bench_bppp.c similarity index 64% rename from src/bench_bulletproofs.c rename to src/bench_bppp.c index f113791c..c2846182 100644 --- a/src/bench_bulletproofs.c +++ b/src/bench_bppp.c @@ -6,32 +6,32 @@ #include -#include "include/secp256k1_bulletproofs.h" +#include "include/secp256k1_bppp.h" #include "util.h" #include "bench.h" typedef struct { secp256k1_context* ctx; -} bench_bulletproofs_data; +} bench_bppp_data; -static void bench_bulletproofs_setup(void* arg) { +static void bench_bppp_setup(void* arg) { (void) arg; } -static void bench_bulletproofs(void* arg, int iters) { - bench_bulletproofs_data *data = (bench_bulletproofs_data*)arg; +static void bench_bppp(void* arg, int iters) { + bench_bppp_data *data = (bench_bppp_data*)arg; (void) data; (void) iters; } int main(void) { - bench_bulletproofs_data data; + bench_bppp_data data; int iters = get_iters(32); data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - run_benchmark("bulletproofs_verify_bit", bench_bulletproofs, bench_bulletproofs_setup, NULL, &data, 10, iters); + run_benchmark("bppp_verify_bit", bench_bppp, bench_bppp_setup, NULL, &data, 10, iters); secp256k1_context_destroy(data.ctx); return 0; diff --git a/src/modules/bppp/Makefile.am.include b/src/modules/bppp/Makefile.am.include new file mode 100644 index 00000000..13e8ea03 --- /dev/null +++ b/src/modules/bppp/Makefile.am.include @@ -0,0 +1,13 @@ +include_HEADERS += include/secp256k1_bppp.h +noinst_HEADERS += src/modules/bppp/bppp_util.h +noinst_HEADERS += src/modules/bppp/main_impl.h +noinst_HEADERS += src/modules/bppp/bppp_transcript_impl.h +noinst_HEADERS += src/modules/bppp/bppp_norm_product_impl.h +noinst_HEADERS += src/modules/bppp/tests_impl.h + +if USE_BENCHMARK +noinst_PROGRAMS += bench_bppp +bench_bppp_SOURCES = src/bench_bppp.c +bench_bppp_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_bppp_LDFLAGS = -static +endif diff --git a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h similarity index 93% rename from src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h rename to src/modules/bppp/bppp_norm_product_impl.h index c380bba1..9d61b0d0 100644 --- a/src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BULLETPROOFS_PP_NORM_PRODUCT_ -#define _SECP256K1_MODULE_BULLETPROOFS_PP_NORM_PRODUCT_ +#ifndef _SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_ +#define _SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_ #include "group.h" #include "scalar.h" @@ -13,9 +13,9 @@ #include "ecmult_gen.h" #include "hash.h" -#include "modules/bulletproofs/main.h" -#include "modules/bulletproofs/bulletproofs_util.h" -#include "modules/bulletproofs/bulletproofs_pp_transcript_impl.h" +#include "modules/bppp/main.h" +#include "modules/bppp/bppp_util.h" +#include "modules/bppp/bppp_transcript_impl.h" /* Computes the inner product of two vectors of scalars * with elements starting from offset a and offset b @@ -69,7 +69,7 @@ static int secp256k1_weighted_scalar_inner_product( } /* Compute the powers of r as r, r^2, r^4 ... r^(2^(n-1)) */ -static void secp256k1_bulletproofs_powers_of_r(secp256k1_scalar *powers, const secp256k1_scalar *r, size_t n) { +static void secp256k1_bppp_powers_of_r(secp256k1_scalar *powers, const secp256k1_scalar *r, size_t n) { size_t i; if (n == 0) { return; @@ -102,11 +102,11 @@ static int ecmult_bp_commit_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t id v = |n_vec*n_vec|_q + . |w|_q denotes q-weighted norm of w and denotes inner product of l and r. */ -static int secp256k1_bulletproofs_commit( +static int secp256k1_bppp_commit( const secp256k1_context* ctx, secp256k1_scratch_space* scratch, secp256k1_ge* commit, - const secp256k1_bulletproofs_generators* g_vec, + const secp256k1_bppp_generators* g_vec, const secp256k1_scalar* n_vec, size_t n_vec_len, const secp256k1_scalar* l_vec, @@ -216,7 +216,7 @@ static int ecmult_r_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void * some parent protocol. To use this norm protocol in a standalone manner, the user * should add the commitment, generators and initial public data to the transcript hash. */ -static int secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( +static int secp256k1_bppp_rangeproof_norm_product_prove( const secp256k1_context* ctx, secp256k1_scratch_space* scratch, unsigned char* proof, @@ -238,7 +238,7 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( ecmult_r_cb_data r_cb_data; size_t g_len = n_vec_len, h_len = l_vec_len; const size_t G_GENS_LEN = g_len; - size_t log_g_len = secp256k1_bulletproofs_pp_log2(g_len), log_h_len = secp256k1_bulletproofs_pp_log2(h_len); + size_t log_g_len = secp256k1_bppp_log2(g_len), log_h_len = secp256k1_bppp_log2(h_len); size_t num_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; /* Check proof sizes.*/ @@ -307,12 +307,12 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( secp256k1_ge_set_gej_var(&r_ge, &rj); secp256k1_fe_normalize_var(&r_ge.x); secp256k1_fe_normalize_var(&r_ge.y); - secp256k1_bulletproofs_serialize_points(&proof[proof_idx], &x_ge, &r_ge); + secp256k1_bppp_serialize_points(&proof[proof_idx], &x_ge, &r_ge); proof_idx += 65; /* Obtain challenge e for the the next round */ secp256k1_sha256_write(transcript, &proof[proof_idx - 65], 65); - secp256k1_bulletproofs_challenge_scalar(&e, transcript, 0); + secp256k1_bppp_challenge_scalar(&e, transcript, 0); if (g_len > 1) { for (i = 0; i < g_len; i = i + 2) { @@ -422,14 +422,14 @@ static int ec_mult_verify_cb2(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx /* Verify the proof. This function modifies the generators, c_vec and the challenge r. The caller should make sure to back them up if they need to be reused. */ -static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( +static int secp256k1_bppp_rangeproof_norm_product_verify( const secp256k1_context* ctx, secp256k1_scratch_space* scratch, const unsigned char* proof, size_t proof_len, secp256k1_sha256* transcript, const secp256k1_scalar* r, - const secp256k1_bulletproofs_generators* g_vec, + const secp256k1_bppp_generators* g_vec, size_t g_len, const secp256k1_scalar* c_vec, size_t c_vec_len, @@ -440,7 +440,7 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( secp256k1_gej res1, res2; size_t i = 0, scratch_checkpoint; int overflow; - size_t log_g_len = secp256k1_bulletproofs_pp_log2(g_len), log_h_len = secp256k1_bulletproofs_pp_log2(c_vec_len); + size_t log_g_len = secp256k1_bppp_log2(g_len), log_h_len = secp256k1_bppp_log2(c_vec_len); size_t n_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; size_t h_len = c_vec_len; @@ -471,7 +471,7 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( /* Compute powers of r_inv. Later used in g_factor computations*/ secp256k1_scalar_inverse_var(&r_inv, r); - secp256k1_bulletproofs_powers_of_r(r_inv_pows, &r_inv, log_g_len); + secp256k1_bppp_powers_of_r(r_inv_pows, &r_inv, log_g_len); /* Compute r_f = r^(2^log_g_len) */ r_f = *r; @@ -482,7 +482,7 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( for (i = 0; i < n_rounds; i++) { secp256k1_scalar e; secp256k1_sha256_write(transcript, &proof[i * 65], 65); - secp256k1_bulletproofs_challenge_scalar(&e, transcript, 0); + secp256k1_bppp_challenge_scalar(&e, transcript, 0); es[i] = e; } /* s_g[0] = n * \prod_{j=0}^{log_g_len - 1} r^(2^j) @@ -491,7 +491,7 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( secp256k1_scalar_mul(&s_g[0], &n, &r_f); secp256k1_scalar_mul(&s_g[0], &s_g[0], &r_inv); for (i = 1; i < g_len; i++) { - size_t log_i = secp256k1_bulletproofs_pp_log2(i); + size_t log_i = secp256k1_bppp_log2(i); size_t nearest_pow_of_two = (size_t)1 << log_i; /* This combines the two multiplications of challenges and r_invs in a * single loop. @@ -503,7 +503,7 @@ static int secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( s_h[0] = l; secp256k1_scalar_set_int(&h_c, 0); for (i = 1; i < h_len; i++) { - size_t log_i = secp256k1_bulletproofs_pp_log2(i); + size_t log_i = secp256k1_bppp_log2(i); size_t nearest_pow_of_two = (size_t)1 << log_i; secp256k1_scalar_mul(&s_h[i], &s_h[i - nearest_pow_of_two], &es[log_i]); } diff --git a/src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h similarity index 73% rename from src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h rename to src/modules/bppp/bppp_transcript_impl.h index e8444e91..a734ea2d 100644 --- a/src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -3,17 +3,17 @@ * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BULLETPROOFS_PP_TRANSCRIPT_IMPL_ -#define _SECP256K1_MODULE_BULLETPROOFS_PP_TRANSCRIPT_IMPL_ +#ifndef _SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_ +#define _SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_ #include "group.h" #include "scalar.h" -#include "bulletproofs_util.h" +#include "bppp_util.h" /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("Bulletproofs_pp/v0/commitment")||SHA256("Bulletproofs_pp/v0/commitment"). */ -static void secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(secp256k1_sha256 *sha) { +static void secp256k1_bppp_sha256_tagged_commitment_init(secp256k1_sha256 *sha) { secp256k1_sha256_initialize(sha); sha->s[0] = 0x52fc8185ul; sha->s[1] = 0x0e7debf0ul; @@ -28,10 +28,10 @@ static void secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(secp256k1_sh } /* Obtain a challenge scalar from the current transcript.*/ -static void secp256k1_bulletproofs_challenge_scalar(secp256k1_scalar* ch, const secp256k1_sha256 *transcript, uint64_t idx) { +static void secp256k1_bppp_challenge_scalar(secp256k1_scalar* ch, const secp256k1_sha256 *transcript, uint64_t idx) { unsigned char buf[32]; secp256k1_sha256 sha = *transcript; - secp256k1_bulletproofs_le64(buf, idx); + secp256k1_bppp_le64(buf, idx); secp256k1_sha256_write(&sha, buf, 8); secp256k1_sha256_finalize(&sha, buf); secp256k1_scalar_set_b32(ch, buf, NULL); diff --git a/src/modules/bulletproofs/bulletproofs_util.h b/src/modules/bppp/bppp_util.h similarity index 78% rename from src/modules/bulletproofs/bulletproofs_util.h rename to src/modules/bppp/bppp_util.h index 07f6fc62..10c58bb7 100644 --- a/src/modules/bulletproofs/bulletproofs_util.h +++ b/src/modules/bppp/bppp_util.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BULLETPROOFS_UTIL_ -#define _SECP256K1_MODULE_BULLETPROOFS_UTIL_ +#ifndef _SECP256K1_MODULE_BPPP_UTIL_ +#define _SECP256K1_MODULE_BPPP_UTIL_ #include "field.h" #include "group.h" @@ -15,7 +15,7 @@ /* Outputs a pair of points, amortizing the parity byte between them * Assumes both points' coordinates have been normalized. */ -static void secp256k1_bulletproofs_serialize_points(unsigned char *output, const secp256k1_ge *lpt, const secp256k1_ge *rpt) { +static void secp256k1_bppp_serialize_points(unsigned char *output, const secp256k1_ge *lpt, const secp256k1_ge *rpt) { output[0] = (secp256k1_fe_is_odd(&lpt->y) << 1) + secp256k1_fe_is_odd(&rpt->y); secp256k1_fe_get_b32(&output[1], &lpt->x); secp256k1_fe_get_b32(&output[33], &rpt->x); @@ -23,13 +23,13 @@ static void secp256k1_bulletproofs_serialize_points(unsigned char *output, const /* Outputs a serialized point in compressed form. Returns 0 at point at infinity. */ -static int secp256k1_bulletproofs_serialize_pt(unsigned char *output, secp256k1_ge *lpt) { +static int secp256k1_bppp_serialize_pt(unsigned char *output, secp256k1_ge *lpt) { size_t size; return secp256k1_eckey_pubkey_serialize(lpt, output, &size, 1 /*compressed*/); } /* little-endian encodes a uint64 */ -static void secp256k1_bulletproofs_le64(unsigned char *output, const uint64_t n) { +static void secp256k1_bppp_le64(unsigned char *output, const uint64_t n) { output[0] = n; output[1] = n >> 8; output[2] = n >> 16; @@ -49,7 +49,7 @@ static int secp256k1_is_power_of_two(size_t n) { * `k` such that 2^k <= n. Assumes n < 2^64. In Bulletproofs, this is bounded * by len of input vectors which can be safely assumed to be less than 2^64. */ -static size_t secp256k1_bulletproofs_pp_log2(size_t n) { +static size_t secp256k1_bppp_log2(size_t n) { return 64 - 1 - secp256k1_clz64_var((uint64_t)n); } diff --git a/src/modules/bulletproofs/main.h b/src/modules/bppp/main.h similarity index 50% rename from src/modules/bulletproofs/main.h rename to src/modules/bppp/main.h index 4174102a..47405f45 100644 --- a/src/modules/bulletproofs/main.h +++ b/src/modules/bppp/main.h @@ -1,8 +1,8 @@ -#ifndef SECP256K1_MODULE_BULLETPROOFS_MAIN_H -#define SECP256K1_MODULE_BULLETPROOFS_MAIN_H +#ifndef SECP256K1_MODULE_BPPP_MAIN_H +#define SECP256K1_MODULE_BPPP_MAIN_H -/* this type must be completed before any of the modules/bulletproofs includes */ -struct secp256k1_bulletproofs_generators { +/* this type must be completed before any of the modules/bppp includes */ +struct secp256k1_bppp_generators { size_t n; /* n total generators; includes both G_i and H_i */ /* For BP++, the generators are G_i from [0..(n - 8)] and the last 8 values diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bppp/main_impl.h similarity index 70% rename from src/modules/bulletproofs/main_impl.h rename to src/modules/bppp/main_impl.h index f87b9876..dfebde10 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bppp/main_impl.h @@ -4,26 +4,26 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ -#define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ +#ifndef _SECP256K1_MODULE_BPPP_MAIN_ +#define _SECP256K1_MODULE_BPPP_MAIN_ -#include "include/secp256k1_bulletproofs.h" +#include "include/secp256k1_bppp.h" #include "include/secp256k1_generator.h" #include "modules/generator/main_impl.h" /* for generator_{load, save} */ #include "hash.h" #include "util.h" -#include "modules/bulletproofs/main.h" -#include "modules/bulletproofs/bulletproofs_pp_norm_product_impl.h" +#include "modules/bppp/main.h" +#include "modules/bppp/bppp_norm_product_impl.h" -secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(const secp256k1_context *ctx, size_t n) { - secp256k1_bulletproofs_generators *ret; +secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_context *ctx, size_t n) { + secp256k1_bppp_generators *ret; secp256k1_rfc6979_hmac_sha256 rng; unsigned char seed[64]; size_t i; VERIFY_CHECK(ctx != NULL); - ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + ret = (secp256k1_bppp_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; } @@ -49,9 +49,9 @@ secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(cons return ret; } -secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { +secp256k1_bppp_generators* secp256k1_bppp_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { size_t n = data_len / 33; - secp256k1_bulletproofs_generators* ret; + secp256k1_bppp_generators* ret; VERIFY_CHECK(ctx != NULL); ARG_CHECK(data != NULL); @@ -60,7 +60,7 @@ secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse(const return NULL; } - ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + ret = (secp256k1_bppp_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; } @@ -83,7 +83,7 @@ secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse(const return ret; } -int secp256k1_bulletproofs_generators_serialize(const secp256k1_context* ctx, const secp256k1_bulletproofs_generators* gens, unsigned char* data, size_t *data_len) { +int secp256k1_bppp_generators_serialize(const secp256k1_context* ctx, const secp256k1_bppp_generators* gens, unsigned char* data, size_t *data_len) { size_t i; VERIFY_CHECK(ctx != NULL); @@ -103,7 +103,7 @@ int secp256k1_bulletproofs_generators_serialize(const secp256k1_context* ctx, co return 1; } -void secp256k1_bulletproofs_generators_destroy(const secp256k1_context* ctx, secp256k1_bulletproofs_generators *gens) { +void secp256k1_bppp_generators_destroy(const secp256k1_context* ctx, secp256k1_bppp_generators *gens) { VERIFY_CHECK(ctx != NULL); (void) ctx; if (gens != NULL) { diff --git a/src/modules/bulletproofs/test_vectors/verify.h b/src/modules/bppp/test_vectors/verify.h similarity index 100% rename from src/modules/bulletproofs/test_vectors/verify.h rename to src/modules/bppp/test_vectors/verify.h diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bppp/tests_impl.h similarity index 75% rename from src/modules/bulletproofs/tests_impl.h rename to src/modules/bppp/tests_impl.h index dd3e4130..ab101fac 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -4,23 +4,23 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ -#define _SECP256K1_MODULE_BULLETPROOFS_TEST_ +#ifndef _SECP256K1_MODULE_BPPP_TEST_ +#define _SECP256K1_MODULE_BPPP_TEST_ #include -#include "include/secp256k1_bulletproofs.h" -#include "bulletproofs_pp_norm_product_impl.h" -#include "bulletproofs_util.h" -#include "bulletproofs_pp_transcript_impl.h" +#include "include/secp256k1_bppp.h" +#include "bppp_norm_product_impl.h" +#include "bppp_util.h" +#include "bppp_transcript_impl.h" #include "test_vectors/verify.h" -static void test_bulletproofs_generators_api(void) { +static void test_bppp_generators_api(void) { /* The BP generator API requires no precomp */ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - secp256k1_bulletproofs_generators *gens; - secp256k1_bulletproofs_generators *gens_orig; + secp256k1_bppp_generators *gens; + secp256k1_bppp_generators *gens_orig; unsigned char gens_ser[330]; size_t len = sizeof(gens_ser); @@ -30,47 +30,47 @@ static void test_bulletproofs_generators_api(void) { secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); /* Create */ - gens = secp256k1_bulletproofs_generators_create(none, 10); + gens = secp256k1_bppp_generators_create(none, 10); CHECK(gens != NULL && ecount == 0); gens_orig = gens; /* Preserve for round-trip test */ /* Serialize */ ecount = 0; - CHECK(!secp256k1_bulletproofs_generators_serialize(none, NULL, gens_ser, &len)); + CHECK(!secp256k1_bppp_generators_serialize(none, NULL, gens_ser, &len)); CHECK(ecount == 1); - CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, NULL, &len)); + CHECK(!secp256k1_bppp_generators_serialize(none, gens, NULL, &len)); CHECK(ecount == 2); - CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, NULL)); + CHECK(!secp256k1_bppp_generators_serialize(none, gens, gens_ser, NULL)); CHECK(ecount == 3); len = 0; - CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(!secp256k1_bppp_generators_serialize(none, gens, gens_ser, &len)); CHECK(ecount == 4); len = sizeof(gens_ser) - 1; - CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(!secp256k1_bppp_generators_serialize(none, gens, gens_ser, &len)); CHECK(ecount == 5); len = sizeof(gens_ser); { /* Output buffer can be greater than minimum needed */ unsigned char gens_ser_tmp[331]; size_t len_tmp = sizeof(gens_ser_tmp); - CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser_tmp, &len_tmp)); + CHECK(secp256k1_bppp_generators_serialize(none, gens, gens_ser_tmp, &len_tmp)); CHECK(len_tmp == sizeof(gens_ser_tmp) - 1); CHECK(ecount == 5); } /* Parse */ - CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(secp256k1_bppp_generators_serialize(none, gens, gens_ser, &len)); ecount = 0; - gens = secp256k1_bulletproofs_generators_parse(none, NULL, sizeof(gens_ser)); + gens = secp256k1_bppp_generators_parse(none, NULL, sizeof(gens_ser)); CHECK(gens == NULL && ecount == 1); /* Not a multiple of 33 */ - gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser) - 1); + gens = secp256k1_bppp_generators_parse(none, gens_ser, sizeof(gens_ser) - 1); CHECK(gens == NULL && ecount == 1); - gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)); + gens = secp256k1_bppp_generators_parse(none, gens_ser, sizeof(gens_ser)); CHECK(gens != NULL && ecount == 1); /* Not valid generators */ memset(gens_ser, 1, sizeof(gens_ser)); - CHECK(secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)) == NULL); + CHECK(secp256k1_bppp_generators_parse(none, gens_ser, sizeof(gens_ser)) == NULL); CHECK(ecount == 1); /* Check that round-trip succeeded */ @@ -81,16 +81,16 @@ static void test_bulletproofs_generators_api(void) { /* Destroy (we allow destroying a NULL context, it's just a noop. like free().) */ ecount = 0; - secp256k1_bulletproofs_generators_destroy(none, NULL); - secp256k1_bulletproofs_generators_destroy(none, gens); - secp256k1_bulletproofs_generators_destroy(none, gens_orig); + secp256k1_bppp_generators_destroy(none, NULL); + secp256k1_bppp_generators_destroy(none, gens); + secp256k1_bppp_generators_destroy(none, gens_orig); CHECK(ecount == 0); secp256k1_context_destroy(none); } -static void test_bulletproofs_generators_fixed(void) { - secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, 3); +static void test_bppp_generators_fixed(void) { + secp256k1_bppp_generators *gens = secp256k1_bppp_generators_create(ctx, 3); unsigned char gens_ser[330]; const unsigned char fixed_first_3[99] = { 0x0b, @@ -112,17 +112,17 @@ static void test_bulletproofs_generators_fixed(void) { size_t len; len = 99; - CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(secp256k1_bppp_generators_serialize(ctx, gens, gens_ser, &len)); CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); len = sizeof(gens_ser); - CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(secp256k1_bppp_generators_serialize(ctx, gens, gens_ser, &len)); CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); - secp256k1_bulletproofs_generators_destroy(ctx, gens); + secp256k1_bppp_generators_destroy(ctx, gens); } -static void test_bulletproofs_pp_tagged_hash(void) { +static void test_bppp_tagged_hash(void) { unsigned char tag_data[29] = "Bulletproofs_pp/v0/commitment"; secp256k1_sha256 sha; secp256k1_sha256 sha_cached; @@ -131,7 +131,7 @@ static void test_bulletproofs_pp_tagged_hash(void) { secp256k1_scalar s; secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data)); - secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(&sha_cached); + secp256k1_bppp_sha256_tagged_commitment_init(&sha_cached); secp256k1_sha256_finalize(&sha, output); secp256k1_sha256_finalize(&sha_cached, output_cached); CHECK(secp256k1_memcmp_var(output, output_cached, 32) == 0); @@ -141,8 +141,8 @@ static void test_bulletproofs_pp_tagged_hash(void) { 0xF6, 0x91, 0x15, 0xEE, 0x74, 0xF5, 0x12, 0x67, 0x8A, 0x41, 0xC6, 0x85, 0x1A, 0x79, 0x14, 0xFC, 0x48, 0x15, 0xC7, 0x2D, 0xF8, 0x63, 0x8F, 0x1B }; - secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(&sha); - secp256k1_bulletproofs_challenge_scalar(&s, &sha, 0); + secp256k1_bppp_sha256_tagged_commitment_init(&sha); + secp256k1_bppp_challenge_scalar(&s, &sha, 0); secp256k1_scalar_get_b32(output, &s); CHECK(memcmp(output, expected, sizeof(output)) == 0); } @@ -154,7 +154,7 @@ static void test_bulletproofs_pp_tagged_hash(void) { 0x69, 0x4D, 0xAA, 0x96, 0xCE, 0x98, 0xBB, 0x39, 0x1C, 0x2F, 0x7C, 0x2E, 0x1C, 0x17, 0x78, 0x6D }; secp256k1_sha256_write(&sha, tmp, sizeof(tmp)); - secp256k1_bulletproofs_challenge_scalar(&s, &sha, 0); + secp256k1_bppp_challenge_scalar(&s, &sha, 0); secp256k1_scalar_get_b32(output, &s); CHECK(memcmp(output, expected, sizeof(output)) == 0); } @@ -168,11 +168,11 @@ void test_log_exp(void) { CHECK(secp256k1_is_power_of_two(63) == 0); CHECK(secp256k1_is_power_of_two(256) == 1); - CHECK(secp256k1_bulletproofs_pp_log2(1) == 0); - CHECK(secp256k1_bulletproofs_pp_log2(2) == 1); - CHECK(secp256k1_bulletproofs_pp_log2(255) == 7); - CHECK(secp256k1_bulletproofs_pp_log2(256) == 8); - CHECK(secp256k1_bulletproofs_pp_log2(257) == 8); + CHECK(secp256k1_bppp_log2(1) == 0); + CHECK(secp256k1_bppp_log2(2) == 1); + CHECK(secp256k1_bppp_log2(255) == 7); + CHECK(secp256k1_bppp_log2(256) == 8); + CHECK(secp256k1_bppp_log2(257) == 8); } void test_norm_util_helpers(void) { @@ -205,7 +205,7 @@ void test_norm_util_helpers(void) { secp256k1_scalar_set_int(&res2, 4740); /*i*i*4^(i+1) */ CHECK(secp256k1_scalar_eq(&res2, &res) == 1); - secp256k1_bulletproofs_powers_of_r(r_pows, &r, 4); + secp256k1_bppp_powers_of_r(r_pows, &r, 4); secp256k1_scalar_set_int(&res, 2); CHECK(secp256k1_scalar_eq(&res, &r_pows[0])); secp256k1_scalar_set_int(&res, 4); CHECK(secp256k1_scalar_eq(&res, &r_pows[1])); secp256k1_scalar_set_int(&res, 16); CHECK(secp256k1_scalar_eq(&res, &r_pows[2])); @@ -215,7 +215,7 @@ void test_norm_util_helpers(void) { static void secp256k1_norm_arg_commit_initial_data( secp256k1_sha256* transcript, const secp256k1_scalar* r, - const secp256k1_bulletproofs_generators* gens_vec, + const secp256k1_bppp_generators* gens_vec, size_t g_len, /* Same as n_vec_len, g_len + c_vec_len = gens->n */ const secp256k1_scalar* c_vec, size_t c_vec_len, @@ -225,25 +225,25 @@ static void secp256k1_norm_arg_commit_initial_data( unsigned char ser_commit[33], ser_scalar[32], ser_le64[8]; size_t i; secp256k1_ge comm = *commit; - secp256k1_bulletproofs_pp_sha256_tagged_commitment_init(transcript); + secp256k1_bppp_sha256_tagged_commitment_init(transcript); secp256k1_fe_normalize(&comm.x); secp256k1_fe_normalize(&comm.y); CHECK(secp256k1_ge_is_infinity(&comm) == 0); - CHECK(secp256k1_bulletproofs_serialize_pt(&ser_commit[0], &comm)); + CHECK(secp256k1_bppp_serialize_pt(&ser_commit[0], &comm)); secp256k1_sha256_write(transcript, ser_commit, 33); secp256k1_scalar_get_b32(ser_scalar, r); secp256k1_sha256_write(transcript, ser_scalar, 32); - secp256k1_bulletproofs_le64(ser_le64, g_len); + secp256k1_bppp_le64(ser_le64, g_len); secp256k1_sha256_write(transcript, ser_le64, 8); - secp256k1_bulletproofs_le64(ser_le64, gens_vec->n); + secp256k1_bppp_le64(ser_le64, gens_vec->n); secp256k1_sha256_write(transcript, ser_le64, 8); for (i = 0; i < gens_vec->n; i++) { secp256k1_fe_normalize(&gens_vec->gens[i].x); secp256k1_fe_normalize(&gens_vec->gens[i].y); - CHECK(secp256k1_bulletproofs_serialize_pt(&ser_commit[0], &gens_vec->gens[i])); + CHECK(secp256k1_bppp_serialize_pt(&ser_commit[0], &gens_vec->gens[i])); secp256k1_sha256_write(transcript, ser_commit, 33); } - secp256k1_bulletproofs_le64(ser_le64, c_vec_len); + secp256k1_bppp_le64(ser_le64, c_vec_len); secp256k1_sha256_write(transcript, ser_le64, 8); for (i = 0; i < c_vec_len; i++) { secp256k1_scalar_get_b32(ser_scalar, &c_vec[i]); @@ -273,9 +273,9 @@ static void copy_vectors_into_scratch(secp256k1_scratch_space* scratch, memcpy(*gs, gens_vec, (g_len + h_len) * sizeof(secp256k1_ge)); } -/* A complete norm argument. In contrast to secp256k1_bulletproofs_pp_rangeproof_norm_product_prove, this is meant +/* A complete norm argument. In contrast to secp256k1_bppp_rangeproof_norm_product_prove, this is meant to be used as a standalone norm argument. - This is a simple wrapper around secp256k1_bulletproofs_pp_rangeproof_norm_product_prove + This is a simple wrapper around secp256k1_bppp_rangeproof_norm_product_prove that also commits to the initial public values used in the protocol. In this case, these public values are commitment. */ @@ -284,7 +284,7 @@ static int secp256k1_norm_arg_prove( unsigned char* proof, size_t *proof_len, const secp256k1_scalar* r, - const secp256k1_bulletproofs_generators* gens_vec, + const secp256k1_bppp_generators* gens_vec, const secp256k1_scalar* n_vec, size_t n_vec_len, const secp256k1_scalar* l_vec, @@ -307,7 +307,7 @@ static int secp256k1_norm_arg_prove( /* Commit to the initial public values */ secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); - res = secp256k1_bulletproofs_pp_rangeproof_norm_product_prove( + res = secp256k1_bppp_rangeproof_norm_product_prove( ctx, scratch, proof, @@ -333,7 +333,7 @@ static int secp256k1_norm_arg_verify( const unsigned char* proof, size_t proof_len, const secp256k1_scalar* r, - const secp256k1_bulletproofs_generators* gens_vec, + const secp256k1_bppp_generators* gens_vec, size_t g_len, const secp256k1_scalar* c_vec, size_t c_vec_len, @@ -346,7 +346,7 @@ static int secp256k1_norm_arg_verify( /* Commit to the initial public values */ secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); - res = secp256k1_bulletproofs_pp_rangeproof_norm_product_verify( + res = secp256k1_bppp_rangeproof_norm_product_verify( ctx, scratch, proof, @@ -379,26 +379,26 @@ void norm_arg_zero(void) { size_t plen = sizeof(proof); unsigned int n_vec_len = 1; unsigned int c_vec_len = 1; - secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, n_vec_len + c_vec_len); + secp256k1_bppp_generators *gens = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); secp256k1_scalar_set_int(&n_vec[0], 0); secp256k1_scalar_set_int(&l_vec[0], 0); random_scalar_order(&c_vec[0]); secp256k1_sha256_initialize(&transcript); /* No challenges used in n = 1, l = 1, but we set transcript as a good practice*/ - CHECK(secp256k1_bulletproofs_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); { secp256k1_scalar *ns, *ls, *cs; secp256k1_ge *gs; size_t scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens->gens, n_vec_len, c_vec_len); - CHECK(secp256k1_bulletproofs_pp_rangeproof_norm_product_prove(ctx, scratch, proof, &plen, &transcript, &r, gs, gens->n, ns, n_vec_len, ls, c_vec_len, cs, c_vec_len)); + CHECK(secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, proof, &plen, &transcript, &r, gs, gens->n, ns, n_vec_len, ls, c_vec_len, cs, c_vec_len)); secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); } secp256k1_sha256_initialize(&transcript); - CHECK(secp256k1_bulletproofs_pp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gens, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gens, c_vec_len, c_vec, c_vec_len, &commit)); - secp256k1_bulletproofs_generators_destroy(ctx, gens); + secp256k1_bppp_generators_destroy(ctx, gens); } /* l is the zero vector and longer than n. This results in one of the @@ -406,7 +406,7 @@ void norm_arg_zero(void) { { unsigned int n_vec_len = 1; unsigned int c_vec_len = 2; - secp256k1_bulletproofs_generators *gs = secp256k1_bulletproofs_generators_create(ctx, n_vec_len + c_vec_len); + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); size_t plen = sizeof(proof); for (i = 0; i < n_vec_len; i++) { random_scalar_order(&n_vec[i]); @@ -415,9 +415,9 @@ void norm_arg_zero(void) { secp256k1_scalar_set_int(&l_vec[i], 0); random_scalar_order(&c_vec[i]); } - CHECK(secp256k1_bulletproofs_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); CHECK(!secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); - secp256k1_bulletproofs_generators_destroy(ctx, gs); + secp256k1_bppp_generators_destroy(ctx, gs); } secp256k1_scratch_space_destroy(ctx, scratch); @@ -429,7 +429,7 @@ void norm_arg_test(unsigned int n, unsigned int m) { secp256k1_ge commit; size_t i, plen; int res; - secp256k1_bulletproofs_generators *gs = secp256k1_bulletproofs_generators_create(ctx, n + m); + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(ctx, n + m); secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*1000); /* shouldn't need much */ unsigned char proof[1000]; plen = 1000; @@ -445,7 +445,7 @@ void norm_arg_test(unsigned int n, unsigned int m) { random_scalar_order(&c_vec[i]); } - res = secp256k1_bulletproofs_commit(ctx, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &q); + res = secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &q); CHECK(res == 1); res = secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n, l_vec, m, c_vec, m, &commit); CHECK(res == 1); @@ -462,13 +462,13 @@ void norm_arg_test(unsigned int n, unsigned int m) { CHECK(res == 0); secp256k1_scratch_space_destroy(ctx, scratch); - secp256k1_bulletproofs_generators_destroy(ctx, gs); + secp256k1_bppp_generators_destroy(ctx, gs); } /* Parses generators from points compressed as pubkeys */ -secp256k1_bulletproofs_generators* bulletproofs_generators_parse_regular(const unsigned char* data, size_t data_len) { +secp256k1_bppp_generators* bppp_generators_parse_regular(const unsigned char* data, size_t data_len) { size_t n = data_len / 33; - secp256k1_bulletproofs_generators* ret; + secp256k1_bppp_generators* ret; VERIFY_CHECK(ctx != NULL); ARG_CHECK(data != NULL); @@ -477,7 +477,7 @@ secp256k1_bulletproofs_generators* bulletproofs_generators_parse_regular(const u return NULL; } - ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + ret = (secp256k1_bppp_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; } @@ -500,7 +500,7 @@ secp256k1_bulletproofs_generators* bulletproofs_generators_parse_regular(const u int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, size_t n_vec_len, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, const unsigned char *commit33) { secp256k1_sha256 transcript; - secp256k1_bulletproofs_generators *gs = bulletproofs_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); + secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); secp256k1_scalar r; secp256k1_ge commit; int overflow; @@ -518,9 +518,9 @@ int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned ch CHECK(!overflow); } CHECK(secp256k1_eckey_pubkey_parse(&commit, commit33, 33)); - ret = secp256k1_bulletproofs_pp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gs, n_vec_len, c_vec, c_vec_len, &commit); + ret = secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gs, n_vec_len, c_vec, c_vec_len, &commit); - secp256k1_bulletproofs_generators_destroy(ctx, gs); + secp256k1_bppp_generators_destroy(ctx, gs); return ret; } @@ -545,12 +545,12 @@ void norm_arg_verify_vectors(void) { } #undef IDX_TO_TEST -void run_bulletproofs_tests(void) { +void run_bppp_tests(void) { test_log_exp(); test_norm_util_helpers(); - test_bulletproofs_generators_api(); - test_bulletproofs_generators_fixed(); - test_bulletproofs_pp_tagged_hash(); + test_bppp_generators_api(); + test_bppp_generators_fixed(); + test_bppp_tagged_hash(); norm_arg_zero(); norm_arg_test(1, 1); diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include deleted file mode 100644 index cfd0916d..00000000 --- a/src/modules/bulletproofs/Makefile.am.include +++ /dev/null @@ -1,13 +0,0 @@ -include_HEADERS += include/secp256k1_bulletproofs.h -noinst_HEADERS += src/modules/bulletproofs/bulletproofs_util.h -noinst_HEADERS += src/modules/bulletproofs/main_impl.h -noinst_HEADERS += src/modules/bulletproofs/bulletproofs_pp_transcript_impl.h -noinst_HEADERS += src/modules/bulletproofs/bulletproofs_pp_norm_product_impl.h -noinst_HEADERS += src/modules/bulletproofs/tests_impl.h - -if USE_BENCHMARK -noinst_PROGRAMS += bench_bulletproofs -bench_bulletproofs_SOURCES = src/bench_bulletproofs.c -bench_bulletproofs_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_bulletproofs_LDFLAGS = -static -endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 857e9a76..c147eae5 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -800,8 +800,8 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, return 1; } -#ifdef ENABLE_MODULE_BULLETPROOFS -# include "modules/bulletproofs/main_impl.h" +#ifdef ENABLE_MODULE_BPPP +# include "modules/bppp/main_impl.h" #endif #ifdef ENABLE_MODULE_ECDH diff --git a/src/tests.c b/src/tests.c index fc84b3cb..9e7efe3a 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7132,8 +7132,8 @@ void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } -#ifdef ENABLE_MODULE_BULLETPROOFS -# include "modules/bulletproofs/tests_impl.h" +#ifdef ENABLE_MODULE_BPPP +# include "modules/bppp/tests_impl.h" #endif #ifdef ENABLE_MODULE_ECDH @@ -7456,8 +7456,8 @@ int main(int argc, char **argv) { /* EC key arithmetic test */ run_eckey_negate_test(); -#ifdef ENABLE_MODULE_BULLETPROOFS - run_bulletproofs_tests(); +#ifdef ENABLE_MODULE_BPPP + run_bppp_tests(); #endif #ifdef ENABLE_MODULE_ECDH From d7fb25c8ca5bda0e969ce94ccabedfd7b8432769 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 9 Feb 2023 21:31:43 +0000 Subject: [PATCH 243/381] Make sure that bppp_log2 isn't called with value 0 Author: Jonas Nick Date: Thu Feb 9 21:31:43 2023 +0000 --- src/modules/bppp/bppp_norm_product_impl.h | 19 +++++++++++++++---- src/modules/bppp/bppp_util.h | 7 ++++--- src/modules/bppp/tests_impl.h | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index 9d61b0d0..ecb758c3 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -238,9 +238,13 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( ecmult_r_cb_data r_cb_data; size_t g_len = n_vec_len, h_len = l_vec_len; const size_t G_GENS_LEN = g_len; - size_t log_g_len = secp256k1_bppp_log2(g_len), log_h_len = secp256k1_bppp_log2(h_len); - size_t num_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; + size_t log_g_len, log_h_len; + size_t num_rounds; + VERIFY_CHECK(g_len > 0 && h_len > 0); + log_g_len = secp256k1_bppp_log2(g_len); + log_h_len = secp256k1_bppp_log2(h_len); + num_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; /* Check proof sizes.*/ VERIFY_CHECK(*proof_len >= 65 * num_rounds + 64); VERIFY_CHECK(g_vec_len == (n_vec_len + l_vec_len) && l_vec_len == c_vec_len); @@ -440,10 +444,17 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( secp256k1_gej res1, res2; size_t i = 0, scratch_checkpoint; int overflow; - size_t log_g_len = secp256k1_bppp_log2(g_len), log_h_len = secp256k1_bppp_log2(c_vec_len); - size_t n_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; + size_t log_g_len, log_h_len; + size_t n_rounds; size_t h_len = c_vec_len; + if (g_len == 0 || c_vec_len == 0) { + return 0; + } + log_g_len = secp256k1_bppp_log2(g_len); + log_h_len = secp256k1_bppp_log2(c_vec_len); + n_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; + if (g_vec->n != (h_len + g_len) || (proof_len != 65 * n_rounds + 64)) { return 0; } diff --git a/src/modules/bppp/bppp_util.h b/src/modules/bppp/bppp_util.h index 10c58bb7..fd04ae69 100644 --- a/src/modules/bppp/bppp_util.h +++ b/src/modules/bppp/bppp_util.h @@ -45,9 +45,10 @@ static int secp256k1_is_power_of_two(size_t n) { return n > 0 && (n & (n - 1)) == 0; } -/* Compute the log2 of n. If n is not a power of two, it returns the largest - * `k` such that 2^k <= n. Assumes n < 2^64. In Bulletproofs, this is bounded - * by len of input vectors which can be safely assumed to be less than 2^64. +/* Compute the log2 of n. n must NOT be 0. If n is not a power of two, it + * returns the largest `k` such that 2^k <= n. Assumes 0 < n < 2^64. In + * Bulletproofs, this is bounded by len of input vectors which can be safely + * assumed to be less than 2^64. */ static size_t secp256k1_bppp_log2(size_t n) { return 64 - 1 - secp256k1_clz64_var((uint64_t)n); diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index ab101fac..e9231656 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -420,6 +420,24 @@ void norm_arg_zero(void) { secp256k1_bppp_generators_destroy(ctx, gs); } + /* Verify vectors of length 0 */ + { + unsigned int n_vec_len = 1; + unsigned int c_vec_len = 1; + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); + size_t plen = sizeof(proof); + random_scalar_order(&n_vec[0]); + random_scalar_order(&c_vec[0]); + random_scalar_order(&l_vec[0]); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); + CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n_vec_len, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, 0, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n_vec_len, c_vec, 0, &commit)); + + secp256k1_bppp_generators_destroy(ctx, gs); + } + secp256k1_scratch_space_destroy(ctx, scratch); } From bd57a017aa90ac1fdde2c0f1a9df321d6a38c132 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 4 Feb 2023 17:38:42 +0000 Subject: [PATCH 244/381] musig: include pubkey in secnonce and compare when signing --- include/secp256k1_musig.h | 17 +++++-- src/modules/musig/session_impl.h | 31 ++++++++----- src/modules/musig/tests_impl.h | 76 ++++++++++++++++++-------------- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index c71b92ce..38b8c0b8 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -43,7 +43,7 @@ typedef struct { /** Opaque data structure that holds a signer's _secret_ nonce. * - * Guaranteed to be 68 bytes in size. + * 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 @@ -57,7 +57,7 @@ typedef struct { * leak the secret signing key. */ typedef struct { - unsigned char data[68]; + unsigned char data[132]; } secp256k1_musig_secnonce; /** Opaque data structure that holds a signer's public nonce. @@ -351,7 +351,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_twea * unless you really know what you are doing. * seckey: the 32-byte secret key that will later be used for signing, if * already known (can be NULL) - * pubkey: public key of the signer creating the nonce + * pubkey: public key of the signer creating the nonce. The secnonce + * output of this function cannot be used to sign for any + * other public key. * msg32: the 32-byte message that will later be signed, if already known * (can be NULL) * keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate @@ -432,13 +434,20 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process( * 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 means that when signing for a + * keypair consisting of a seckey and pubkey, the secnonce must have been + * created by calling musig_nonce_gen with that pubkey. Otherwise, the + * illegal_callback is called. + * * 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 * Out: partial_sig: pointer to struct to store the partial signature * In/Out: secnonce: pointer to the secnonce struct created in * musig_nonce_gen that has been never used in a - * partial_sign call before + * partial_sign call before and has been created for the + * keypair * In: keypair: pointer to keypair to sign the message with * keyagg_cache: pointer to the keyagg_cache that was output when the * aggregate public key for this session diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 91420731..9a30cadb 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -22,17 +22,19 @@ static const unsigned char secp256k1_musig_secnonce_magic[4] = { 0x22, 0x0e, 0xdc, 0xf1 }; -static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, secp256k1_scalar *k) { +static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, const secp256k1_scalar *k, 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_point_save(&secnonce->data[68], pk); } -static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_musig_secnonce *secnonce) { +static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_ge *pk, secp256k1_musig_secnonce *secnonce) { int is_zero; ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0); secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL); secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL); + secp256k1_point_load(pk, &secnonce->data[68]); /* We make very sure that the nonce isn't invalidated by checking the values * in addition to the magic. */ is_zero = secp256k1_scalar_is_zero(&k[0]) & secp256k1_scalar_is_zero(&k[1]); @@ -44,10 +46,12 @@ static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1 /* If flag is true, invalidate the secnonce; otherwise leave it. Constant-time. */ 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, above code makes the magic - * classified. However, we need the magic to be declassified to be able to - * compare it during secnonce_load. */ + /* 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); } static const unsigned char secp256k1_musig_pubnonce_magic[4] = { 0xf5, 0x7a, 0x3d, 0xa0 }; @@ -355,6 +359,8 @@ int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secn size_t pk_ser_len = sizeof(pk_ser); unsigned char aggpk_ser[32]; unsigned char *aggpk_ser_ptr = NULL; + secp256k1_ge pk; + int pk_serialize_success; int ret = 1; VERIFY_CHECK(ctx != NULL); @@ -393,16 +399,19 @@ int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secn VERIFY_CHECK(ret_tmp); aggpk_ser_ptr = aggpk_ser; } - if (!secp256k1_ec_pubkey_serialize(ctx, pk_ser, &pk_ser_len, pubkey, SECP256K1_EC_COMPRESSED)) { + if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) { return 0; } + pk_serialize_success = secp256k1_eckey_pubkey_serialize(&pk, pk_ser, &pk_ser_len, SECP256K1_EC_COMPRESSED); + /* A pubkey cannot be the point at infinity */ + VERIFY_CHECK(pk_serialize_success); VERIFY_CHECK(pk_ser_len == sizeof(pk_ser)); secp256k1_nonce_function_musig(k, session_id32, msg32, seckey, pk_ser, aggpk_ser_ptr, extra_input32); VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1])); VERIFY_CHECK(!secp256k1_scalar_eq(&k[0], &k[1])); - secp256k1_musig_secnonce_save(secnonce, k); + secp256k1_musig_secnonce_save(secnonce, k, &pk); secp256k1_musig_secnonce_invalidate(ctx, secnonce, !ret); for (i = 0; i < 2; i++) { @@ -562,7 +571,7 @@ static void secp256k1_musig_partial_sign_clear(secp256k1_scalar *sk, secp256k1_s 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; + secp256k1_ge pk, keypair_pk; secp256k1_scalar k[2]; secp256k1_scalar mu, s; secp256k1_keyagg_cache_internal cache_i; @@ -573,7 +582,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p ARG_CHECK(secnonce != NULL); /* Fails if the magic doesn't match */ - ret = secp256k1_musig_secnonce_load(ctx, k, secnonce); + ret = secp256k1_musig_secnonce_load(ctx, k, &pk, secnonce); /* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls * of this function to fail */ memset(secnonce, 0, sizeof(*secnonce)); @@ -587,10 +596,12 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p ARG_CHECK(keyagg_cache != NULL); ARG_CHECK(session != NULL); - if (!secp256k1_keypair_load(ctx, &sk, &pk, keypair)) { + if (!secp256k1_keypair_load(ctx, &sk, &keypair_pk, keypair)) { secp256k1_musig_partial_sign_clear(&sk, k); return 0; } + ARG_CHECK(secp256k1_fe_equal_var(&pk.x, &keypair_pk.x) + && secp256k1_fe_equal_var(&pk.y, &keypair_pk.y)); if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { secp256k1_musig_partial_sign_clear(&sk, k); return 0; diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ff6637d0..b63820c0 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -126,7 +126,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_keypair keypair[2]; secp256k1_keypair invalid_keypair; unsigned char max64[64]; - unsigned char zeros68[68] = { 0 }; + unsigned char zeros132[132] = { 0 }; unsigned char session_id[2][32]; secp256k1_musig_secnonce secnonce[2]; secp256k1_musig_secnonce secnonce_tmp; @@ -228,19 +228,19 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, NULL, pk_ptr, 2) == 1); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, NULL, 2) == 0); CHECK(ecount == 1); - CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, invalid_pk_ptr2, 2) == 0); CHECK(ecount == 2); - CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, invalid_pk_ptr3, 3) == 0); CHECK(ecount == 3); - CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, pk_ptr, 0) == 0); CHECK(ecount == 4); - CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, NULL, 0) == 0); CHECK(ecount == 5); - CHECK(memcmp_and_randomize(agg_pk.data, zeros68, sizeof(agg_pk.data)) == 0); + CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); CHECK(secp256k1_musig_pubkey_agg(none, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); CHECK(secp256k1_musig_pubkey_agg(sign, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); @@ -253,7 +253,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 1); CHECK(secp256k1_musig_pubkey_get(none, &full_agg_pk, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros68, sizeof(full_agg_pk)) == 0); + CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros132, sizeof(full_agg_pk)) == 0); /** Tweaking **/ { @@ -277,20 +277,20 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { tmp_keyagg_cache = keyagg_cache; CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, NULL, tweak) == 0); CHECK(ecount == 1); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); tmp_keyagg_cache = keyagg_cache; CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, NULL) == 0); CHECK(ecount == 2); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); tmp_keyagg_cache = keyagg_cache; CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); CHECK(ecount == 2); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); tmp_keyagg_cache = keyagg_cache; /* Uninitialized keyagg_cache */ CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &invalid_keyagg_cache, tweak) == 0); CHECK(ecount == 3); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); } } @@ -308,18 +308,18 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 3); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], NULL, sk[0], &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 4); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); /* no seckey and session_id is 0 */ - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, NULL, &pk[0], msg, &keyagg_cache, max64) == 0); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros132, NULL, &pk[0], msg, &keyagg_cache, max64) == 0); CHECK(ecount == 4); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); /* session_id 0 is fine when a seckey is provided */ - CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros68, sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); + CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], zeros132, sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], NULL, &pk[0], msg, &keyagg_cache, max64) == 1); CHECK(ecount == 4); /* invalid seckey */ CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], max64, &pk[0], msg, &keyagg_cache, max64) == 0); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); CHECK(ecount == 4); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, msg, &keyagg_cache, max64) == 0); CHECK(ecount == 5); @@ -331,7 +331,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 6); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &invalid_keyagg_cache, max64) == 0); CHECK(ecount == 7); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros68, sizeof(secnonce[0].data)) == 0); + CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); CHECK(secp256k1_musig_nonce_gen(sign, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, NULL) == 1); CHECK(ecount == 7); @@ -345,10 +345,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 1); CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, NULL) == 0); CHECK(ecount == 2); - CHECK(memcmp_and_randomize(pubnonce_ser, zeros68, sizeof(pubnonce_ser)) == 0); + CHECK(memcmp_and_randomize(pubnonce_ser, zeros132, sizeof(pubnonce_ser)) == 0); CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, &invalid_pubnonce) == 0); CHECK(ecount == 3); - CHECK(memcmp_and_randomize(pubnonce_ser, zeros68, sizeof(pubnonce_ser)) == 0); + CHECK(memcmp_and_randomize(pubnonce_ser, zeros132, sizeof(pubnonce_ser)) == 0); CHECK(secp256k1_musig_pubnonce_serialize(none, pubnonce_ser, &pubnonce[0]) == 1); ecount = 0; @@ -357,7 +357,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 1); CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], zeros68) == 0); + CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], zeros132) == 0); CHECK(ecount == 2); CHECK(secp256k1_musig_pubnonce_parse(none, &pubnonce[0], pubnonce_ser) == 1); @@ -399,10 +399,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 1); CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, NULL) == 0); CHECK(ecount == 2); - CHECK(memcmp_and_randomize(aggnonce_ser, zeros68, sizeof(aggnonce_ser)) == 0); + CHECK(memcmp_and_randomize(aggnonce_ser, zeros132, sizeof(aggnonce_ser)) == 0); CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, (secp256k1_musig_aggnonce*) &invalid_pubnonce) == 0); CHECK(ecount == 3); - CHECK(memcmp_and_randomize(aggnonce_ser, zeros68, sizeof(aggnonce_ser)) == 0); + CHECK(memcmp_and_randomize(aggnonce_ser, zeros132, sizeof(aggnonce_ser)) == 0); CHECK(secp256k1_musig_aggnonce_serialize(none, aggnonce_ser, &aggnonce) == 1); ecount = 0; @@ -411,7 +411,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(ecount == 1); CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, zeros68) == 1); + CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, zeros132) == 1); CHECK(secp256k1_musig_aggnonce_parse(none, &aggnonce, aggnonce_ser) == 1); { @@ -449,7 +449,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1); /* The secnonce is set to 0 and subsequent signing attempts fail */ - CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros68, sizeof(secnonce_tmp)) == 0); + CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros132, sizeof(secnonce_tmp)) == 0); CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 0); CHECK(ecount == 1); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); @@ -742,7 +742,7 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1); CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], &pk_a[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_b[1], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_a[1], NULL, NULL, NULL) == 1); CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], &pk_b[0], NULL, NULL, NULL) == 1); CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], &pk_b[1], NULL, NULL, NULL) == 1); @@ -1045,7 +1045,8 @@ void musig_test_vectors_noncegen(void) { CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, c->pk, sizeof(c->pk))); CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, c->rand_, sk, &pk, msg, keyagg_cache_ptr, extra_in) == 1); - CHECK(secp256k1_memcmp_var(&secnonce.data[4], c->expected, sizeof(secnonce)-4) == 0); + CHECK(secp256k1_memcmp_var(&secnonce.data[4], c->expected, 2*32) == 0); + CHECK(secp256k1_memcmp_var(&secnonce.data[4+2*32], &pk, sizeof(pk)) == 0); } } @@ -1080,6 +1081,16 @@ void musig_test_vectors_nonceagg(void) { } } +void musig_test_set_secnonce(secp256k1_musig_secnonce *secnonce, const unsigned char *secnonce64, const secp256k1_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_pubkey_load(ctx, &pk, pubkey)); + secp256k1_musig_secnonce_save(secnonce, k, &pk); +} + void musig_test_vectors_signverify(void) { size_t i; const struct musig_sign_verify_vector *vector = &musig_sign_verify_vector; @@ -1103,20 +1114,20 @@ void musig_test_vectors_signverify(void) { CHECK(secp256k1_musig_aggnonce_parse(ctx, &aggnonce, vector->aggnonces[c->aggnonce_index])); CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); - memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); - memcpy(&secnonce.data[4], vector->secnonces[0], sizeof(secnonce.data) - 4); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); + musig_test_set_secnonce(&secnonce, vector->secnonces[0], &pubkey); CHECK(secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); CHECK(secp256k1_musig_partial_sig_serialize(ctx, partial_sig32, &partial_sig)); CHECK(secp256k1_memcmp_var(partial_sig32, c->expected, sizeof(partial_sig32)) == 0); CHECK(secp256k1_musig_pubnonce_parse(ctx, &pubnonce, vector->pubnonces[0])); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); CHECK(secp256k1_musig_partial_sig_verify(ctx, &partial_sig, &pubnonce, &pubkey, &keyagg_cache, &session)); } for (i = 0; i < sizeof(vector->sign_error_case)/sizeof(vector->sign_error_case[0]); i++) { const struct musig_sign_error_case *c = &vector->sign_error_case[i]; enum MUSIG_ERROR error; secp256k1_musig_keyagg_cache keyagg_cache; + secp256k1_pubkey pubkey; secp256k1_musig_aggnonce aggnonce; secp256k1_musig_session session; secp256k1_musig_partial_sig partial_sig; @@ -1143,8 +1154,8 @@ void musig_test_vectors_signverify(void) { } CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); - memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); - memcpy(&secnonce.data[4], vector->secnonces[c->secnonce_index], sizeof(secnonce.data) - 4); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); + musig_test_set_secnonce(&secnonce, vector->secnonces[c->secnonce_index], &pubkey); { /* In the last test vector we sign with an invalid secnonce, which * triggers an illegal_callback. Hence, we need to use a custom @@ -1231,8 +1242,7 @@ void musig_test_vectors_tweak(void) { secp256k1_keypair keypair; unsigned char partial_sig32[32]; - memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); - memcpy(&secnonce.data[4], vector->secnonce, sizeof(secnonce.data) - 4); + musig_test_set_secnonce(&secnonce, vector->secnonce, &pubkey); CHECK(secp256k1_keypair_create(ctx, &keypair, vector->sk)); CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); From a1ec2bb67b05dbbec12bb6e2902cf96247a4341f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 4 Feb 2023 18:25:36 +0000 Subject: [PATCH 245/381] musig: add test for signing with wrong secnonce for a keypair --- src/modules/musig/tests_impl.h | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index b63820c0..9b7e7646 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -466,18 +466,27 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &invalid_keypair, &keyagg_cache, &session) == 0); CHECK(ecount == 6); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + { + unsigned char sk_tmp[32]; + secp256k1_keypair keypair_tmp; + secp256k1_testrand256(sk_tmp); + CHECK(secp256k1_keypair_create(ctx, &keypair_tmp, sk_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair_tmp, &keyagg_cache, &session) == 0); + CHECK(ecount == 7); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + } CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], NULL, &session) == 0); - CHECK(ecount == 7); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &invalid_keyagg_cache, &session) == 0); CHECK(ecount == 8); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, NULL) == 0); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &invalid_keyagg_cache, &session) == 0); CHECK(ecount == 9); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &invalid_session) == 0); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, NULL) == 0); CHECK(ecount == 10); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); + CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &invalid_session) == 0); + CHECK(ecount == 11); + memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); CHECK(secp256k1_musig_partial_sign(none, &partial_sig[0], &secnonce[0], &keypair[0], &keyagg_cache, &session) == 1); CHECK(secp256k1_musig_partial_sign(none, &partial_sig[1], &secnonce[1], &keypair[1], &keyagg_cache, &session) == 1); From d23c23e24d64d9837d0ab728a88d8501b3a6130b Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 3 Mar 2023 17:33:09 +0100 Subject: [PATCH 246/381] musig: Update to BIP v1.0.0-rc.4 (Check pubnonce in NonceGen vectors) --- contrib/musig2-vectors.py | 8 +++++--- src/modules/musig/tests_impl.h | 7 ++++++- src/modules/musig/vectors.h | 7 ++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/contrib/musig2-vectors.py b/contrib/musig2-vectors.py index 60e8e7c9..8df3870f 100755 --- a/contrib/musig2-vectors.py +++ b/contrib/musig2-vectors.py @@ -208,7 +208,8 @@ struct musig_nonce_gen_test_case { unsigned char msg[32]; int has_extra_in; unsigned char extra_in[32]; - unsigned char expected[97]; + unsigned char expected_secnonce[97]; + unsigned char expected_pubnonce[66]; }; """ @@ -231,7 +232,7 @@ struct musig_nonce_gen_vector { s += init_cases( data["test_cases"], - lambda case: "{ { %s }, %s, { %s }, %s, %s, %s, { %s } }," + lambda case: "{ { %s }, %s, { %s }, %s, %s, %s, { %s }, { %s } }," % ( hexstr_to_intarray(case["rand_"]), init_array_maybe(case["sk"]), @@ -239,7 +240,8 @@ struct musig_nonce_gen_vector { init_array_maybe(case["aggpk"]), init_array_maybe(case["msg"]), init_array_maybe(case["extra_in"]), - hexstr_to_intarray(case["expected"]), + hexstr_to_intarray(case["expected_secnonce"]), + hexstr_to_intarray(case["expected_pubnonce"]), ), ) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 9b7e7646..b4de8167 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -1031,6 +1031,7 @@ void musig_test_vectors_noncegen(void) { const unsigned char *msg = NULL; const unsigned char *extra_in = NULL; secp256k1_pubkey pk; + unsigned char pubnonce66[66]; if (c->has_sk) { sk = c->sk; @@ -1054,8 +1055,12 @@ void musig_test_vectors_noncegen(void) { CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, c->pk, sizeof(c->pk))); CHECK(secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, c->rand_, sk, &pk, msg, keyagg_cache_ptr, extra_in) == 1); - CHECK(secp256k1_memcmp_var(&secnonce.data[4], c->expected, 2*32) == 0); + CHECK(secp256k1_memcmp_var(&secnonce.data[4], c->expected_secnonce, 2*32) == 0); CHECK(secp256k1_memcmp_var(&secnonce.data[4+2*32], &pk, sizeof(pk)) == 0); + + CHECK(secp256k1_musig_pubnonce_serialize(ctx, pubnonce66, &pubnonce) == 1); + CHECK(sizeof(c->expected_pubnonce) == sizeof(pubnonce66)); + CHECK(secp256k1_memcmp_var(pubnonce66, c->expected_pubnonce, sizeof(pubnonce66)) == 0); } } diff --git a/src/modules/musig/vectors.h b/src/modules/musig/vectors.h index 744c4050..b959e0a2 100644 --- a/src/modules/musig/vectors.h +++ b/src/modules/musig/vectors.h @@ -77,7 +77,8 @@ struct musig_nonce_gen_test_case { unsigned char msg[32]; int has_extra_in; unsigned char extra_in[32]; - unsigned char expected[97]; + unsigned char expected_secnonce[97]; + unsigned char expected_pubnonce[66]; }; struct musig_nonce_gen_vector { @@ -86,8 +87,8 @@ struct musig_nonce_gen_vector { static const struct musig_nonce_gen_vector musig_nonce_gen_vector = { { - { { 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 }, 1 , { 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 }, { 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 }, 1 , { 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 }, 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 }, 1 , { 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 }, { 0xB1, 0x14, 0xE5, 0x02, 0xBE, 0xAA, 0x4E, 0x30, 0x1D, 0xD0, 0x8A, 0x50, 0x26, 0x41, 0x72, 0xC8, 0x4E, 0x41, 0x65, 0x0E, 0x6C, 0xB7, 0x26, 0xB4, 0x10, 0xC0, 0x69, 0x4D, 0x59, 0xEF, 0xFB, 0x64, 0x95, 0xB5, 0xCA, 0xF2, 0x8D, 0x04, 0x5B, 0x97, 0x3D, 0x63, 0xE3, 0xC9, 0x9A, 0x44, 0xB8, 0x07, 0xBD, 0xE3, 0x75, 0xFD, 0x6C, 0xB3, 0x9E, 0x46, 0xDC, 0x4A, 0x51, 0x17, 0x08, 0xD0, 0xE9, 0xD2, 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 } }, - { { 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 }, 0 , { 0 }, { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, 0 , { 0 }, 0 , { 0 }, 0 , { 0 }, { 0x89, 0xBD, 0xD7, 0x87, 0xD0, 0x28, 0x4E, 0x5E, 0x4D, 0x5F, 0xC5, 0x72, 0xE4, 0x9E, 0x31, 0x6B, 0xAB, 0x7E, 0x21, 0xE3, 0xB1, 0x83, 0x0D, 0xE3, 0x7D, 0xFE, 0x80, 0x15, 0x6F, 0xA4, 0x1A, 0x6D, 0x0B, 0x17, 0xAE, 0x8D, 0x02, 0x4C, 0x53, 0x67, 0x96, 0x99, 0xA6, 0xFD, 0x79, 0x44, 0xD9, 0xC4, 0xA3, 0x66, 0xB5, 0x14, 0xBA, 0xF4, 0x30, 0x88, 0xE0, 0x70, 0x8B, 0x10, 0x23, 0xDD, 0x28, 0x97, 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 } }, + { { 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 }, 1 , { 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 }, { 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 }, 1 , { 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 }, 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 }, 1 , { 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 }, { 0xB1, 0x14, 0xE5, 0x02, 0xBE, 0xAA, 0x4E, 0x30, 0x1D, 0xD0, 0x8A, 0x50, 0x26, 0x41, 0x72, 0xC8, 0x4E, 0x41, 0x65, 0x0E, 0x6C, 0xB7, 0x26, 0xB4, 0x10, 0xC0, 0x69, 0x4D, 0x59, 0xEF, 0xFB, 0x64, 0x95, 0xB5, 0xCA, 0xF2, 0x8D, 0x04, 0x5B, 0x97, 0x3D, 0x63, 0xE3, 0xC9, 0x9A, 0x44, 0xB8, 0x07, 0xBD, 0xE3, 0x75, 0xFD, 0x6C, 0xB3, 0x9E, 0x46, 0xDC, 0x4A, 0x51, 0x17, 0x08, 0xD0, 0xE9, 0xD2, 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, 0xF7, 0xBE, 0x70, 0x89, 0xE8, 0x37, 0x6E, 0xB3, 0x55, 0x27, 0x23, 0x68, 0x76, 0x6B, 0x17, 0xE8, 0x8E, 0x7D, 0xB7, 0x20, 0x47, 0xD0, 0x5E, 0x56, 0xAA, 0x88, 0x1E, 0xA5, 0x2B, 0x3B, 0x35, 0xDF, 0x02, 0xC2, 0x9C, 0x80, 0x46, 0xFD, 0xD0, 0xDE, 0xD4, 0xC7, 0xE5, 0x58, 0x69, 0x13, 0x72, 0x00, 0xFB, 0xDB, 0xFE, 0x2E, 0xB6, 0x54, 0x26, 0x7B, 0x6D, 0x70, 0x13, 0x60, 0x2C, 0xAE, 0xD3, 0x11, 0x5A } }, + { { 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 }, 0 , { 0 }, { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, 0 , { 0 }, 0 , { 0 }, 0 , { 0 }, { 0x89, 0xBD, 0xD7, 0x87, 0xD0, 0x28, 0x4E, 0x5E, 0x4D, 0x5F, 0xC5, 0x72, 0xE4, 0x9E, 0x31, 0x6B, 0xAB, 0x7E, 0x21, 0xE3, 0xB1, 0x83, 0x0D, 0xE3, 0x7D, 0xFE, 0x80, 0x15, 0x6F, 0xA4, 0x1A, 0x6D, 0x0B, 0x17, 0xAE, 0x8D, 0x02, 0x4C, 0x53, 0x67, 0x96, 0x99, 0xA6, 0xFD, 0x79, 0x44, 0xD9, 0xC4, 0xA3, 0x66, 0xB5, 0x14, 0xBA, 0xF4, 0x30, 0x88, 0xE0, 0x70, 0x8B, 0x10, 0x23, 0xDD, 0x28, 0x97, 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, { 0x02, 0xC9, 0x6E, 0x7C, 0xB1, 0xE8, 0xAA, 0x5D, 0xAC, 0x64, 0xD8, 0x72, 0x94, 0x79, 0x14, 0x19, 0x8F, 0x60, 0x7D, 0x90, 0xEC, 0xDE, 0x52, 0x00, 0xDE, 0x52, 0x97, 0x8A, 0xD5, 0xDE, 0xD6, 0x3C, 0x00, 0x02, 0x99, 0xEC, 0x51, 0x17, 0xC2, 0xD2, 0x9E, 0xDE, 0xE8, 0xA2, 0x09, 0x25, 0x87, 0xC3, 0x90, 0x9B, 0xE6, 0x94, 0xD5, 0xCF, 0xF0, 0x66, 0x7D, 0x6C, 0x02, 0xEA, 0x40, 0x59, 0xF7, 0xCD, 0x97, 0x86 } }, }, }; From 96f48538503ff40bf0017652c4b4f3a42cd3fa94 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Sat, 1 Apr 2023 15:35:50 +0900 Subject: [PATCH 247/381] ct: Use volatile "trick" in all fe/scalar cmov implementations Apparently clang 15 is able to compile our cmov code into a branch, at least for fe_cmov and fe_storage_cmov. This commit makes the condition volatile in all cmov implementations (except ge but that one only calls into the fe impls). This is just a quick fix. We should still look into other methods, e.g., asm and #457. We should also consider not caring about constant-time in scalar_low_impl.h We should also consider testing on very new compilers in nightly CI, see https://github.com/bitcoin-core/secp256k1/pull/864#issuecomment-769211867 --- src/field_10x26_impl.h | 6 ++++-- src/field_5x52_impl.h | 6 ++++-- src/scalar_4x64_impl.h | 3 ++- src/scalar_8x32_impl.h | 3 ++- src/scalar_low_impl.h | 3 ++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h index 21742bf6..0eeb8a8d 100644 --- a/src/field_10x26_impl.h +++ b/src/field_10x26_impl.h @@ -1132,8 +1132,9 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint32_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint32_t)0); + mask0 = vflag + ~((uint32_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); @@ -1231,8 +1232,9 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint32_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint32_t)0); + mask0 = vflag + ~((uint32_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index 6bd202f5..dc0467db 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -476,8 +476,9 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint64_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint64_t)0); + mask0 = vflag + ~((uint64_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); @@ -559,8 +560,9 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint64_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r->n, sizeof(r->n)); - mask0 = flag + ~((uint64_t)0); + mask0 = vflag + ~((uint64_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 585a4b63..60aca8c1 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -958,8 +958,9 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { uint64_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r->d, sizeof(r->d)); - mask0 = flag + ~((uint64_t)0); + mask0 = vflag + ~((uint64_t)0); mask1 = ~mask0; r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1); diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 6086f1ec..ad025cff 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -733,8 +733,9 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { uint32_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r->d, sizeof(r->d)); - mask0 = flag + ~((uint32_t)0); + mask0 = vflag + ~((uint32_t)0); mask1 = ~mask0; r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1); diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index aa75f8b0..4005cc8c 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -120,8 +120,9 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { uint32_t mask0, mask1; + volatile int vflag = flag; VG_CHECK_VERIFY(r, sizeof(*r)); - mask0 = flag + ~((uint32_t)0); + mask0 = vflag + ~((uint32_t)0); mask1 = ~mask0; *r = (*r & mask0) | (*a & mask1); } From 13c438cdeed358a20b1f0324ee36a6cadfaf0016 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 11 Apr 2023 12:21:14 +0200 Subject: [PATCH 248/381] sync-upstream: Use --autostash to handle uncommitted changes This makes it possible to use sync-upstream with uncommitted changes. (This is in particular helpful when working on the script itself.) Without this commit, git pull will fail due to the uncommitted changes. --- contrib/sync-upstream.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index f4ccc449..c64acbbd 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -97,7 +97,7 @@ echo "$BODY" echo "-----------------------------------" # Create branch from PR commit and create PR git checkout master -git pull +git pull --autostash git checkout -b temp-merge-"$PRNUM" # Escape single quote From dbf2e4d3e1dda55a1a33dd4d86918a3c2281d8dc Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 19 Apr 2023 12:20:32 +0000 Subject: [PATCH 249/381] bppp: align terminology with paper (mu, rho) q-> mu, r -> rho --- src/modules/bppp/bppp_norm_product_impl.h | 122 +++++++++++----------- src/modules/bppp/tests_impl.h | 84 +++++++-------- 2 files changed, 103 insertions(+), 103 deletions(-) diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index ecb758c3..4ca4eca6 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -43,7 +43,7 @@ static int secp256k1_scalar_inner_product( /* Computes the q-weighted inner product of two vectors of scalars * for elements starting from offset a and offset b respectively with the * given step. - * Returns: Sum_{i=0..len-1}(a[offset_a + step*i] * b[offset_b2 + step*i]*q^(i+1)) */ + * Returns: Sum_{i=0..len-1}(a[offset_a + step*i] * b[offset_b2 + step*i]*mu^(i+1)) */ static int secp256k1_weighted_scalar_inner_product( secp256k1_scalar* res, const secp256k1_scalar* a_vec, @@ -52,29 +52,29 @@ static int secp256k1_weighted_scalar_inner_product( const size_t b_offset, const size_t step, const size_t len, - const secp256k1_scalar* q + const secp256k1_scalar* mu ) { - secp256k1_scalar q_pow; + secp256k1_scalar mu_pow; size_t i; secp256k1_scalar_set_int(res, 0); - q_pow = *q; + mu_pow = *mu; for (i = 0; i < len; i++) { secp256k1_scalar term; secp256k1_scalar_mul(&term, &a_vec[a_offset + step*i], &b_vec[b_offset + step*i]); - secp256k1_scalar_mul(&term, &term, &q_pow); - secp256k1_scalar_mul(&q_pow, &q_pow, q); + secp256k1_scalar_mul(&term, &term, &mu_pow); + secp256k1_scalar_mul(&mu_pow, &mu_pow, mu); secp256k1_scalar_add(res, res, &term); } return 1; } -/* Compute the powers of r as r, r^2, r^4 ... r^(2^(n-1)) */ -static void secp256k1_bppp_powers_of_r(secp256k1_scalar *powers, const secp256k1_scalar *r, size_t n) { +/* Compute the powers of rho as rho, rho^2, rho^4 ... rho^(2^(n-1)) */ +static void secp256k1_bppp_powers_of_rho(secp256k1_scalar *powers, const secp256k1_scalar *rho, size_t n) { size_t i; if (n == 0) { return; } - powers[0] = *r; + powers[0] = *rho; for (i = 1; i < n; i++) { secp256k1_scalar_sqr(&powers[i], &powers[i - 1]); } @@ -99,7 +99,7 @@ static int ecmult_bp_commit_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t id } /* Create a commitment `commit` = vG + n_vec*G_vec + l_vec*H_vec where - v = |n_vec*n_vec|_q + . |w|_q denotes q-weighted norm of w and + v = |n_vec*n_vec|_mu + . |w|_mu denotes mu-weighted norm of w and denotes inner product of l and r. */ static int secp256k1_bppp_commit( @@ -113,7 +113,7 @@ static int secp256k1_bppp_commit( size_t l_vec_len, const secp256k1_scalar* c_vec, size_t c_vec_len, - const secp256k1_scalar* q + const secp256k1_scalar* mu ) { secp256k1_scalar v, l_c; /* First n_vec_len generators are Gs, rest are Hs*/ @@ -125,8 +125,8 @@ static int secp256k1_bppp_commit( VERIFY_CHECK(secp256k1_is_power_of_two(n_vec_len)); VERIFY_CHECK(secp256k1_is_power_of_two(c_vec_len)); - /* Compute v = n_vec*n_vec*q + l_vec*c_vec */ - secp256k1_weighted_scalar_inner_product(&v, n_vec, 0 /*a offset */, n_vec, 0 /*b offset*/, 1 /*step*/, n_vec_len, q); + /* Compute v = n_vec*n_vec*mu + l_vec*c_vec */ + secp256k1_weighted_scalar_inner_product(&v, n_vec, 0 /*a offset */, n_vec, 0 /*b offset*/, 1 /*step*/, n_vec_len, mu); secp256k1_scalar_inner_product(&l_c, l_vec, 0 /*a offset */, c_vec, 0 /*b offset*/, 1 /*step*/, l_vec_len); secp256k1_scalar_add(&v, &v, &l_c); @@ -150,8 +150,8 @@ typedef struct ecmult_x_cb_data { const secp256k1_scalar *n; const secp256k1_ge *g; const secp256k1_scalar *l; - const secp256k1_scalar *r; - const secp256k1_scalar *r_inv; + const secp256k1_scalar *rho; + const secp256k1_scalar *rho_inv; size_t G_GENS_LEN; /* Figure out initialization syntax so that this can also be const */ size_t n_len; } ecmult_x_cb_data; @@ -160,10 +160,10 @@ static int ecmult_x_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void ecmult_x_cb_data *data = (ecmult_x_cb_data*) cbdata; if (idx < data->n_len) { if (idx % 2 == 0) { - secp256k1_scalar_mul(sc, &data->n[idx + 1], data->r); + secp256k1_scalar_mul(sc, &data->n[idx + 1], data->rho); *pt = data->g[idx]; } else { - secp256k1_scalar_mul(sc, &data->n[idx - 1], data->r_inv); + secp256k1_scalar_mul(sc, &data->n[idx - 1], data->rho_inv); *pt = data->g[idx]; } } else { @@ -201,11 +201,11 @@ static int ecmult_r_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void } /* Recursively compute the norm argument proof satisfying the relation - * _q + = v for some commitment - * C = v*G + + . _q is the weighted inner - * product of x with itself, where the weights are the first n powers of q. - * _q = q*x_1^2 + q^2*x_2^2 + q^3*x_3^2 + ... + q^n*x_n^2. - * The API computes q as square of the r challenge (`r^2`). + * _mu + = v for some commitment + * C = v*G + + . _mu is the weighted inner + * product of x with itself, where the weights are the first n powers of mu. + * _mu = mu*x_1^2 + mu^2*x_2^2 + mu^3*x_3^2 + ... + mu^n*x_n^2. + * The API computes mu as square of the r challenge (`r^2`). * * The norm argument is not zero knowledge and does not operate on any secret data. * Thus the following code uses variable time operations while computing the proof. @@ -222,7 +222,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( unsigned char* proof, size_t *proof_len, secp256k1_sha256* transcript, /* Transcript hash of the parent protocol */ - const secp256k1_scalar* r, + const secp256k1_scalar* rho, secp256k1_ge* g_vec, size_t g_vec_len, secp256k1_scalar* n_vec, @@ -232,7 +232,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( secp256k1_scalar* c_vec, size_t c_vec_len ) { - secp256k1_scalar q_f, r_f = *r; + secp256k1_scalar mu_f, rho_f = *rho; size_t proof_idx = 0; ecmult_x_cb_data x_cb_data; ecmult_r_cb_data r_cb_data; @@ -259,30 +259,30 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( r_cb_data.g1 = g_vec; r_cb_data.l1 = l_vec; r_cb_data.G_GENS_LEN = G_GENS_LEN; - secp256k1_scalar_sqr(&q_f, &r_f); + secp256k1_scalar_sqr(&mu_f, &rho_f); while (g_len > 1 || h_len > 1) { size_t i, num_points; - secp256k1_scalar q_sq, r_inv, c0_l1, c1_l0, x_v, c1_l1, r_v; + secp256k1_scalar mu_sq, rho_inv, c0_l1, c1_l0, x_v, c1_l1, r_v; secp256k1_gej rj, xj; secp256k1_ge r_ge, x_ge; secp256k1_scalar e; - secp256k1_scalar_inverse_var(&r_inv, &r_f); - secp256k1_scalar_sqr(&q_sq, &q_f); + secp256k1_scalar_inverse_var(&rho_inv, &rho_f); + secp256k1_scalar_sqr(&mu_sq, &mu_f); - /* Compute the X commitment X = WIP(r_inv*n0,n1)_q2 * g + r + */ + /* Compute the X commitment X = WIP(rho_inv*n0,n1)_mu2 * g + r + */ secp256k1_scalar_inner_product(&c0_l1, c_vec, 0, l_vec, 1, 2, h_len/2); secp256k1_scalar_inner_product(&c1_l0, c_vec, 1, l_vec, 0, 2, h_len/2); - secp256k1_weighted_scalar_inner_product(&x_v, n_vec, 0, n_vec, 1, 2, g_len/2, &q_sq); - secp256k1_scalar_mul(&x_v, &x_v, &r_inv); + secp256k1_weighted_scalar_inner_product(&x_v, n_vec, 0, n_vec, 1, 2, g_len/2, &mu_sq); + secp256k1_scalar_mul(&x_v, &x_v, &rho_inv); secp256k1_scalar_add(&x_v, &x_v, &x_v); secp256k1_scalar_add(&x_v, &x_v, &c0_l1); secp256k1_scalar_add(&x_v, &x_v, &c1_l0); - x_cb_data.r = &r_f; - x_cb_data.r_inv = &r_inv; + x_cb_data.rho = &rho_f; + x_cb_data.rho_inv = &rho_inv; x_cb_data.n_len = g_len >= 2 ? g_len : 0; num_points = x_cb_data.n_len + (h_len >= 2 ? h_len : 0); @@ -290,7 +290,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( return 0; } - secp256k1_weighted_scalar_inner_product(&r_v, n_vec, 1, n_vec, 1, 2, g_len/2, &q_sq); + secp256k1_weighted_scalar_inner_product(&r_v, n_vec, 1, n_vec, 1, 2, g_len/2, &mu_sq); secp256k1_scalar_inner_product(&c1_l1, c_vec, 1, l_vec, 1, 2, h_len/2); secp256k1_scalar_add(&r_v, &r_v, &c1_l1); @@ -322,12 +322,12 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( for (i = 0; i < g_len; i = i + 2) { secp256k1_scalar nl, nr; secp256k1_gej gl, gr; - secp256k1_scalar_mul(&nl, &n_vec[i], &r_inv); + secp256k1_scalar_mul(&nl, &n_vec[i], &rho_inv); secp256k1_scalar_mul(&nr, &n_vec[i + 1], &e); secp256k1_scalar_add(&n_vec[i/2], &nl, &nr); secp256k1_gej_set_ge(&gl, &g_vec[i]); - secp256k1_ecmult(&gl, &gl, &r_f, NULL); + secp256k1_ecmult(&gl, &gl, &rho_f, NULL); secp256k1_gej_set_ge(&gr, &g_vec[i + 1]); secp256k1_ecmult(&gr, &gr, &e, NULL); secp256k1_gej_add_var(&gl, &gl, &gr, NULL); @@ -353,8 +353,8 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( } g_len = g_len / 2; h_len = h_len / 2; - r_f = q_f; - q_f = q_sq; + rho_f = mu_f; + mu_f = mu_sq; } secp256k1_scalar_get_b32(&proof[proof_idx], &n_vec[0]); @@ -432,15 +432,15 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( const unsigned char* proof, size_t proof_len, secp256k1_sha256* transcript, - const secp256k1_scalar* r, + const secp256k1_scalar* rho, const secp256k1_bppp_generators* g_vec, size_t g_len, const secp256k1_scalar* c_vec, size_t c_vec_len, const secp256k1_ge* commit ) { - secp256k1_scalar r_f, q_f, v, n, l, r_inv, h_c; - secp256k1_scalar *es, *s_g, *s_h, *r_inv_pows; + secp256k1_scalar rho_f, mu_f, v, n, l, rho_inv, h_c; + secp256k1_scalar *es, *s_g, *s_h, *rho_inv_pows; secp256k1_gej res1, res2; size_t i = 0, scratch_checkpoint; int overflow; @@ -467,27 +467,27 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( if (overflow) return 0; secp256k1_scalar_set_b32(&l, &proof[n_rounds*65 + 32], &overflow); /* l */ if (overflow) return 0; - if (secp256k1_scalar_is_zero(r)) return 0; + if (secp256k1_scalar_is_zero(rho)) return 0; /* Collect the challenges in a new vector */ scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); es = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_rounds * sizeof(secp256k1_scalar)); s_g = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); s_h = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); - r_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, log_g_len * sizeof(secp256k1_scalar)); - if (es == NULL || s_g == NULL || s_h == NULL || r_inv_pows == NULL) { + rho_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, log_g_len * sizeof(secp256k1_scalar)); + if (es == NULL || s_g == NULL || s_h == NULL || rho_inv_pows == NULL) { secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); return 0; } - /* Compute powers of r_inv. Later used in g_factor computations*/ - secp256k1_scalar_inverse_var(&r_inv, r); - secp256k1_bppp_powers_of_r(r_inv_pows, &r_inv, log_g_len); + /* Compute powers of rho_inv. Later used in g_factor computations*/ + secp256k1_scalar_inverse_var(&rho_inv, rho); + secp256k1_bppp_powers_of_rho(rho_inv_pows, &rho_inv, log_g_len); - /* Compute r_f = r^(2^log_g_len) */ - r_f = *r; + /* Compute rho_f = rho^(2^log_g_len) */ + rho_f = *rho; for (i = 0; i < log_g_len; i++) { - secp256k1_scalar_sqr(&r_f, &r_f); + secp256k1_scalar_sqr(&rho_f, &rho_f); } for (i = 0; i < n_rounds; i++) { @@ -496,20 +496,20 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( secp256k1_bppp_challenge_scalar(&e, transcript, 0); es[i] = e; } - /* s_g[0] = n * \prod_{j=0}^{log_g_len - 1} r^(2^j) - * = n * r^(2^log_g_len - 1) - * = n * r_f * r_inv */ - secp256k1_scalar_mul(&s_g[0], &n, &r_f); - secp256k1_scalar_mul(&s_g[0], &s_g[0], &r_inv); + /* s_g[0] = n * \prod_{j=0}^{log_g_len - 1} rho^(2^j) + * = n * rho^(2^log_g_len - 1) + * = n * rho_f * rho_inv */ + secp256k1_scalar_mul(&s_g[0], &n, &rho_f); + secp256k1_scalar_mul(&s_g[0], &s_g[0], &rho_inv); for (i = 1; i < g_len; i++) { size_t log_i = secp256k1_bppp_log2(i); size_t nearest_pow_of_two = (size_t)1 << log_i; - /* This combines the two multiplications of challenges and r_invs in a + /* This combines the two multiplications of challenges and rho_invs in a * single loop. * s_g[i] = s_g[i - nearest_pow_of_two] - * * e[log_i] * r_inv^(2^log_i) */ + * * e[log_i] * rho_inv^(2^log_i) */ secp256k1_scalar_mul(&s_g[i], &s_g[i - nearest_pow_of_two], &es[log_i]); - secp256k1_scalar_mul(&s_g[i], &s_g[i], &r_inv_pows[log_i]); + secp256k1_scalar_mul(&s_g[i], &s_g[i], &rho_inv_pows[log_i]); } s_h[0] = l; secp256k1_scalar_set_int(&h_c, 0); @@ -519,10 +519,10 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( secp256k1_scalar_mul(&s_h[i], &s_h[i - nearest_pow_of_two], &es[log_i]); } secp256k1_scalar_inner_product(&h_c, c_vec, 0 /* a_offset */ , s_h, 0 /* b_offset */, 1 /* step */, h_len); - /* Compute v = n*n*q_f + l*h_c where q_f = r_f^2 */ - secp256k1_scalar_sqr(&q_f, &r_f); + /* Compute v = n*n*mu_f + l*h_c where mu_f = rho_f^2 */ + secp256k1_scalar_sqr(&mu_f, &rho_f); secp256k1_scalar_mul(&v, &n, &n); - secp256k1_scalar_mul(&v, &v, &q_f); + secp256k1_scalar_mul(&v, &v, &mu_f); secp256k1_scalar_add(&v, &v, &h_c); { diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index e9231656..bdb261a0 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -176,15 +176,15 @@ void test_log_exp(void) { } void test_norm_util_helpers(void) { - secp256k1_scalar a_vec[4], b_vec[4], r_pows[4], res, res2, q, r; + secp256k1_scalar a_vec[4], b_vec[4], rho_pows[4], res, res2, mu, rho; int i; - /* a = {1, 2, 3, 4} b = {5, 6, 7, 8}, q = 4, r = 2 */ + /* a = {1, 2, 3, 4} b = {5, 6, 7, 8}, mu = 4, rho = 2 */ for (i = 0; i < 4; i++) { secp256k1_scalar_set_int(&a_vec[i], i + 1); secp256k1_scalar_set_int(&b_vec[i], i + 5); } - secp256k1_scalar_set_int(&q, 4); - secp256k1_scalar_set_int(&r, 2); + secp256k1_scalar_set_int(&mu, 4); + secp256k1_scalar_set_int(&rho, 2); secp256k1_scalar_inner_product(&res, a_vec, 0, b_vec, 0, 1, 4); secp256k1_scalar_set_int(&res2, 70); CHECK(secp256k1_scalar_eq(&res2, &res) == 1); @@ -201,20 +201,20 @@ void test_norm_util_helpers(void) { secp256k1_scalar_set_int(&res2, 44); CHECK(secp256k1_scalar_eq(&res2, &res) == 1); - secp256k1_weighted_scalar_inner_product(&res, a_vec, 0, a_vec, 0, 1, 4, &q); + secp256k1_weighted_scalar_inner_product(&res, a_vec, 0, a_vec, 0, 1, 4, &mu); secp256k1_scalar_set_int(&res2, 4740); /*i*i*4^(i+1) */ CHECK(secp256k1_scalar_eq(&res2, &res) == 1); - secp256k1_bppp_powers_of_r(r_pows, &r, 4); - secp256k1_scalar_set_int(&res, 2); CHECK(secp256k1_scalar_eq(&res, &r_pows[0])); - secp256k1_scalar_set_int(&res, 4); CHECK(secp256k1_scalar_eq(&res, &r_pows[1])); - secp256k1_scalar_set_int(&res, 16); CHECK(secp256k1_scalar_eq(&res, &r_pows[2])); - secp256k1_scalar_set_int(&res, 256); CHECK(secp256k1_scalar_eq(&res, &r_pows[3])); + secp256k1_bppp_powers_of_rho(rho_pows, &rho, 4); + secp256k1_scalar_set_int(&res, 2); CHECK(secp256k1_scalar_eq(&res, &rho_pows[0])); + secp256k1_scalar_set_int(&res, 4); CHECK(secp256k1_scalar_eq(&res, &rho_pows[1])); + secp256k1_scalar_set_int(&res, 16); CHECK(secp256k1_scalar_eq(&res, &rho_pows[2])); + secp256k1_scalar_set_int(&res, 256); CHECK(secp256k1_scalar_eq(&res, &rho_pows[3])); } static void secp256k1_norm_arg_commit_initial_data( secp256k1_sha256* transcript, - const secp256k1_scalar* r, + const secp256k1_scalar* rho, const secp256k1_bppp_generators* gens_vec, size_t g_len, /* Same as n_vec_len, g_len + c_vec_len = gens->n */ const secp256k1_scalar* c_vec, @@ -231,7 +231,7 @@ static void secp256k1_norm_arg_commit_initial_data( CHECK(secp256k1_ge_is_infinity(&comm) == 0); CHECK(secp256k1_bppp_serialize_pt(&ser_commit[0], &comm)); secp256k1_sha256_write(transcript, ser_commit, 33); - secp256k1_scalar_get_b32(ser_scalar, r); + secp256k1_scalar_get_b32(ser_scalar, rho); secp256k1_sha256_write(transcript, ser_scalar, 32); secp256k1_bppp_le64(ser_le64, g_len); secp256k1_sha256_write(transcript, ser_le64, 8); @@ -283,7 +283,7 @@ static int secp256k1_norm_arg_prove( secp256k1_scratch_space* scratch, unsigned char* proof, size_t *proof_len, - const secp256k1_scalar* r, + const secp256k1_scalar* rho, const secp256k1_bppp_generators* gens_vec, const secp256k1_scalar* n_vec, size_t n_vec_len, @@ -305,7 +305,7 @@ static int secp256k1_norm_arg_prove( copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens_vec->gens, g_len, h_len); /* Commit to the initial public values */ - secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); + secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, g_len, c_vec, c_vec_len, &comm); res = secp256k1_bppp_rangeproof_norm_product_prove( ctx, @@ -313,7 +313,7 @@ static int secp256k1_norm_arg_prove( proof, proof_len, &transcript, /* Transcript hash of the parent protocol */ - r, + rho, gs, gens_vec->n, ns, @@ -332,7 +332,7 @@ static int secp256k1_norm_arg_verify( secp256k1_scratch_space* scratch, const unsigned char* proof, size_t proof_len, - const secp256k1_scalar* r, + const secp256k1_scalar* rho, const secp256k1_bppp_generators* gens_vec, size_t g_len, const secp256k1_scalar* c_vec, @@ -344,7 +344,7 @@ static int secp256k1_norm_arg_verify( secp256k1_sha256 transcript; /* Commit to the initial public values */ - secp256k1_norm_arg_commit_initial_data(&transcript, r, gens_vec, g_len, c_vec, c_vec_len, &comm); + secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, g_len, c_vec, c_vec_len, &comm); res = secp256k1_bppp_rangeproof_norm_product_verify( ctx, @@ -352,7 +352,7 @@ static int secp256k1_norm_arg_verify( proof, proof_len, &transcript, - r, + rho, gens_vec, g_len, c_vec, @@ -364,15 +364,15 @@ static int secp256k1_norm_arg_verify( void norm_arg_zero(void) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; - secp256k1_scalar r, q; + secp256k1_scalar rho, mu; secp256k1_ge commit; size_t i; secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*10); /* shouldn't need much */ unsigned char proof[1000]; secp256k1_sha256 transcript; - random_scalar_order(&r); - secp256k1_scalar_sqr(&q, &r); + random_scalar_order(&rho); + secp256k1_scalar_sqr(&mu, &rho); /* l is zero vector and n is zero vectors of length 1 each. */ { @@ -386,17 +386,17 @@ void norm_arg_zero(void) { random_scalar_order(&c_vec[0]); secp256k1_sha256_initialize(&transcript); /* No challenges used in n = 1, l = 1, but we set transcript as a good practice*/ - CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); { secp256k1_scalar *ns, *ls, *cs; secp256k1_ge *gs; size_t scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens->gens, n_vec_len, c_vec_len); - CHECK(secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, proof, &plen, &transcript, &r, gs, gens->n, ns, n_vec_len, ls, c_vec_len, cs, c_vec_len)); + CHECK(secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, proof, &plen, &transcript, &rho, gs, gens->n, ns, n_vec_len, ls, c_vec_len, cs, c_vec_len)); secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); } secp256k1_sha256_initialize(&transcript); - CHECK(secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gens, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &rho, gens, c_vec_len, c_vec, c_vec_len, &commit)); secp256k1_bppp_generators_destroy(ctx, gens); } @@ -415,8 +415,8 @@ void norm_arg_zero(void) { secp256k1_scalar_set_int(&l_vec[i], 0); random_scalar_order(&c_vec[i]); } - CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); - CHECK(!secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + CHECK(!secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); secp256k1_bppp_generators_destroy(ctx, gs); } @@ -429,11 +429,11 @@ void norm_arg_zero(void) { random_scalar_order(&n_vec[0]); random_scalar_order(&c_vec[0]); random_scalar_order(&l_vec[0]); - CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &q)); - CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); - CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n_vec_len, c_vec, c_vec_len, &commit)); - CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, 0, c_vec, c_vec_len, &commit)); - CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n_vec_len, c_vec, 0, &commit)); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, 0, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, 0, &commit)); secp256k1_bppp_generators_destroy(ctx, gs); } @@ -443,7 +443,7 @@ void norm_arg_zero(void) { void norm_arg_test(unsigned int n, unsigned int m) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; - secp256k1_scalar r, q; + secp256k1_scalar rho, mu; secp256k1_ge commit; size_t i, plen; int res; @@ -451,8 +451,8 @@ void norm_arg_test(unsigned int n, unsigned int m) { secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*1000); /* shouldn't need much */ unsigned char proof[1000]; plen = 1000; - random_scalar_order(&r); - secp256k1_scalar_sqr(&q, &r); + random_scalar_order(&rho); + secp256k1_scalar_sqr(&mu, &rho); for (i = 0; i < n; i++) { random_scalar_order(&n_vec[i]); @@ -463,20 +463,20 @@ void norm_arg_test(unsigned int n, unsigned int m) { random_scalar_order(&c_vec[i]); } - res = secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &q); + res = secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &mu); CHECK(res == 1); - res = secp256k1_norm_arg_prove(scratch, proof, &plen, &r, gs, n_vec, n, l_vec, m, c_vec, m, &commit); + res = secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n, l_vec, m, c_vec, m, &commit); CHECK(res == 1); - res = secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n, c_vec, m, &commit); + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); CHECK(res == 1); /* Changing any of last two scalars should break the proof */ proof[plen - 1] ^= 1; - res = secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n, c_vec, m, &commit); + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); CHECK(res == 0); proof[plen - 1 - 32] ^= 1; - res = secp256k1_norm_arg_verify(scratch, proof, plen, &r, gs, n, c_vec, m, &commit); + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); CHECK(res == 0); secp256k1_scratch_space_destroy(ctx, scratch); @@ -519,7 +519,7 @@ secp256k1_bppp_generators* bppp_generators_parse_regular(const unsigned char* da int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, size_t n_vec_len, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, const unsigned char *commit33) { secp256k1_sha256 transcript; secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); - secp256k1_scalar r; + secp256k1_scalar rho; secp256k1_ge commit; int overflow; int i; @@ -528,7 +528,7 @@ int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned ch CHECK(gs != NULL); secp256k1_sha256_initialize(&transcript); - secp256k1_scalar_set_b32(&r, r32, &overflow); + secp256k1_scalar_set_b32(&rho, r32, &overflow); CHECK(!overflow); for (i = 0; i < (int)c_vec_len; i++) { @@ -536,7 +536,7 @@ int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned ch CHECK(!overflow); } CHECK(secp256k1_eckey_pubkey_parse(&commit, commit33, 33)); - ret = secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &r, gs, n_vec_len, c_vec, c_vec_len, &commit); + ret = secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit); secp256k1_bppp_generators_destroy(ctx, gs); return ret; From 2c63d17c1e3b89950af9b6e51b14f025a2251c95 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 19 Apr 2023 12:36:24 +0000 Subject: [PATCH 250/381] bppp: align terminology with paper (gamma) e -> gamma --- src/modules/bppp/bppp_norm_product_impl.h | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index 4ca4eca6..6b7521a1 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -267,7 +267,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( secp256k1_scalar mu_sq, rho_inv, c0_l1, c1_l0, x_v, c1_l1, r_v; secp256k1_gej rj, xj; secp256k1_ge r_ge, x_ge; - secp256k1_scalar e; + secp256k1_scalar gamma; secp256k1_scalar_inverse_var(&rho_inv, &rho_f); secp256k1_scalar_sqr(&mu_sq, &mu_f); @@ -314,22 +314,22 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( secp256k1_bppp_serialize_points(&proof[proof_idx], &x_ge, &r_ge); proof_idx += 65; - /* Obtain challenge e for the the next round */ + /* Obtain challenge gamma for the the next round */ secp256k1_sha256_write(transcript, &proof[proof_idx - 65], 65); - secp256k1_bppp_challenge_scalar(&e, transcript, 0); + secp256k1_bppp_challenge_scalar(&gamma, transcript, 0); if (g_len > 1) { for (i = 0; i < g_len; i = i + 2) { secp256k1_scalar nl, nr; secp256k1_gej gl, gr; secp256k1_scalar_mul(&nl, &n_vec[i], &rho_inv); - secp256k1_scalar_mul(&nr, &n_vec[i + 1], &e); + secp256k1_scalar_mul(&nr, &n_vec[i + 1], &gamma); secp256k1_scalar_add(&n_vec[i/2], &nl, &nr); secp256k1_gej_set_ge(&gl, &g_vec[i]); secp256k1_ecmult(&gl, &gl, &rho_f, NULL); secp256k1_gej_set_ge(&gr, &g_vec[i + 1]); - secp256k1_ecmult(&gr, &gr, &e, NULL); + secp256k1_ecmult(&gr, &gr, &gamma, NULL); secp256k1_gej_add_var(&gl, &gl, &gr, NULL); secp256k1_ge_set_gej_var(&g_vec[i/2], &gl); } @@ -339,14 +339,14 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( for (i = 0; i < h_len; i = i + 2) { secp256k1_scalar temp1; secp256k1_gej grj; - secp256k1_scalar_mul(&temp1, &c_vec[i + 1], &e); + secp256k1_scalar_mul(&temp1, &c_vec[i + 1], &gamma); secp256k1_scalar_add(&c_vec[i/2], &c_vec[i], &temp1); - secp256k1_scalar_mul(&temp1, &l_vec[i + 1], &e); + secp256k1_scalar_mul(&temp1, &l_vec[i + 1], &gamma); secp256k1_scalar_add(&l_vec[i/2], &l_vec[i], &temp1); secp256k1_gej_set_ge(&grj, &g_vec[G_GENS_LEN + i + 1]); - secp256k1_ecmult(&grj, &grj, &e, NULL); + secp256k1_ecmult(&grj, &grj, &gamma, NULL); secp256k1_gej_add_ge_var(&grj, &grj, &g_vec[G_GENS_LEN + i], NULL); secp256k1_ge_set_gej_var(&g_vec[G_GENS_LEN + i/2], &grj); } @@ -367,7 +367,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( typedef struct ec_mult_verify_cb_data1 { const unsigned char *proof; const secp256k1_ge *commit; - const secp256k1_scalar *challenges; + const secp256k1_scalar *gammas; } ec_mult_verify_cb_data1; static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { @@ -381,7 +381,7 @@ static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx if (idx % 2 == 0) { unsigned char pk_buf[33]; idx /= 2; - *sc = data->challenges[idx]; + *sc = data->gammas[idx]; pk_buf[0] = 2 | (data->proof[65*idx] >> 1); memcpy(&pk_buf[1], &data->proof[65*idx + 1], 32); if (!secp256k1_eckey_pubkey_parse(pt, pk_buf, sizeof(pk_buf))) { @@ -393,7 +393,7 @@ static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx idx /= 2; secp256k1_scalar_set_int(&neg_one, 1); secp256k1_scalar_negate(&neg_one, &neg_one); - *sc = data->challenges[idx]; + *sc = data->gammas[idx]; secp256k1_scalar_sqr(sc, sc); secp256k1_scalar_add(sc, sc, &neg_one); pk_buf[0] = 2 | data->proof[65*idx]; @@ -440,7 +440,7 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( const secp256k1_ge* commit ) { secp256k1_scalar rho_f, mu_f, v, n, l, rho_inv, h_c; - secp256k1_scalar *es, *s_g, *s_h, *rho_inv_pows; + secp256k1_scalar *gammas, *s_g, *s_h, *rho_inv_pows; secp256k1_gej res1, res2; size_t i = 0, scratch_checkpoint; int overflow; @@ -469,13 +469,13 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( if (overflow) return 0; if (secp256k1_scalar_is_zero(rho)) return 0; - /* Collect the challenges in a new vector */ + /* Collect the gammas in a new vector */ scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - es = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_rounds * sizeof(secp256k1_scalar)); + gammas = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_rounds * sizeof(secp256k1_scalar)); s_g = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_len * sizeof(secp256k1_scalar)); s_h = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); rho_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, log_g_len * sizeof(secp256k1_scalar)); - if (es == NULL || s_g == NULL || s_h == NULL || rho_inv_pows == NULL) { + if (gammas == NULL || s_g == NULL || s_h == NULL || rho_inv_pows == NULL) { secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); return 0; } @@ -491,10 +491,10 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( } for (i = 0; i < n_rounds; i++) { - secp256k1_scalar e; + secp256k1_scalar gamma; secp256k1_sha256_write(transcript, &proof[i * 65], 65); - secp256k1_bppp_challenge_scalar(&e, transcript, 0); - es[i] = e; + secp256k1_bppp_challenge_scalar(&gamma, transcript, 0); + gammas[i] = gamma; } /* s_g[0] = n * \prod_{j=0}^{log_g_len - 1} rho^(2^j) * = n * rho^(2^log_g_len - 1) @@ -504,11 +504,11 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( for (i = 1; i < g_len; i++) { size_t log_i = secp256k1_bppp_log2(i); size_t nearest_pow_of_two = (size_t)1 << log_i; - /* This combines the two multiplications of challenges and rho_invs in a + /* This combines the two multiplications of gammas and rho_invs in a * single loop. * s_g[i] = s_g[i - nearest_pow_of_two] * * e[log_i] * rho_inv^(2^log_i) */ - secp256k1_scalar_mul(&s_g[i], &s_g[i - nearest_pow_of_two], &es[log_i]); + secp256k1_scalar_mul(&s_g[i], &s_g[i - nearest_pow_of_two], &gammas[log_i]); secp256k1_scalar_mul(&s_g[i], &s_g[i], &rho_inv_pows[log_i]); } s_h[0] = l; @@ -516,7 +516,7 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( for (i = 1; i < h_len; i++) { size_t log_i = secp256k1_bppp_log2(i); size_t nearest_pow_of_two = (size_t)1 << log_i; - secp256k1_scalar_mul(&s_h[i], &s_h[i - nearest_pow_of_two], &es[log_i]); + secp256k1_scalar_mul(&s_h[i], &s_h[i - nearest_pow_of_two], &gammas[log_i]); } secp256k1_scalar_inner_product(&h_c, c_vec, 0 /* a_offset */ , s_h, 0 /* b_offset */, 1 /* step */, h_len); /* Compute v = n*n*mu_f + l*h_c where mu_f = rho_f^2 */ @@ -529,7 +529,7 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( ec_mult_verify_cb_data1 data; data.proof = proof; data.commit = commit; - data.challenges = es; + data.gammas = gammas; if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res1, NULL, ec_mult_verify_cb1, &data, 2*n_rounds + 1)) { secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); From d8e7f3763bac9e52d07643a01c8352cadded64d2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 19 Apr 2023 11:26:44 +0000 Subject: [PATCH 251/381] musig: move ge_{serialize,parse}_ext to module-independent file --- src/modules/musig/session_impl.h | 26 -------------------------- src/secp256k1.c | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 9a30cadb..d2d400fe 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -200,32 +200,6 @@ int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig return 1; } -/* Outputs 33 zero bytes if the given group element is the point at infinity and - * otherwise outputs the compressed serialization */ -static void secp256k1_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { - if (secp256k1_ge_is_infinity(ge)) { - memset(out33, 0, 33); - } else { - int ret; - size_t size = 33; - ret = secp256k1_eckey_pubkey_serialize(ge, out33, &size, 1); - /* Serialize must succeed because the point is not at infinity */ - VERIFY_CHECK(ret && size == 33); - } -} - -/* Outputs the point at infinity if the given byte array is all zero, otherwise - * attempts to parse compressed point serialization. */ -static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { - unsigned char zeros[33] = { 0 }; - - if (memcmp(in33, zeros, sizeof(zeros)) == 0) { - secp256k1_ge_set_infinity(ge); - return 1; - } - return secp256k1_eckey_pubkey_parse(ge, in33, 33); -} - int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_aggnonce* nonce) { secp256k1_ge ge[2]; int i; diff --git a/src/secp256k1.c b/src/secp256k1.c index c147eae5..c0163b98 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -800,6 +800,32 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, return 1; } +/* Outputs 33 zero bytes if the given group element is the point at infinity and + * otherwise outputs the compressed serialization */ +static void secp256k1_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { + if (secp256k1_ge_is_infinity(ge)) { + memset(out33, 0, 33); + } else { + int ret; + size_t size = 33; + ret = secp256k1_eckey_pubkey_serialize(ge, out33, &size, 1); + /* Serialize must succeed because the point is not at infinity */ + VERIFY_CHECK(ret && size == 33); + } +} + +/* Outputs the point at infinity if the given byte array is all zero, otherwise + * attempts to parse compressed point serialization. */ +static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { + unsigned char zeros[33] = { 0 }; + + if (memcmp(in33, zeros, sizeof(zeros)) == 0) { + secp256k1_ge_set_infinity(ge); + return 1; + } + return secp256k1_eckey_pubkey_parse(ge, in33, 33); +} + #ifdef ENABLE_MODULE_BPPP # include "modules/bppp/main_impl.h" #endif From f22834f20252f9ca3e17f36093940e2aa2735790 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 28 Feb 2023 21:19:50 +0000 Subject: [PATCH 252/381] norm arg: add verify vector for n = [0], l = [0] --- src/modules/bppp/test_vectors/verify.h | 7 +++++++ src/modules/bppp/tests_impl.h | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/bppp/test_vectors/verify.h b/src/modules/bppp/test_vectors/verify.h index 5397f67a..cc07850e 100644 --- a/src/modules/bppp/test_vectors/verify.h +++ b/src/modules/bppp/test_vectors/verify.h @@ -62,4 +62,11 @@ static secp256k1_scalar verify_vector_8_c_vec[1]; static const unsigned char verify_vector_8_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; static const unsigned char verify_vector_8_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; static const int verify_vector_8_result = 0; +static const unsigned char verify_vector_9_commit33[33] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const size_t verify_vector_9_n_vec_len = 1; +static const unsigned char verify_vector_9_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_9_c_vec[1]; +static const unsigned char verify_vector_9_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_9_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const int verify_vector_9_result = 1; diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index bdb261a0..bbbc2b1f 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -535,7 +535,7 @@ int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned ch secp256k1_scalar_set_b32(&c_vec[i], c_vec32[i], &overflow); CHECK(!overflow); } - CHECK(secp256k1_eckey_pubkey_parse(&commit, commit33, 33)); + CHECK(secp256k1_ge_parse_ext(&commit, commit33)); ret = secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit); secp256k1_bppp_generators_destroy(ctx, gs); @@ -557,6 +557,7 @@ void norm_arg_verify_vectors(void) { CHECK(IDX_TO_TEST(6)); CHECK(IDX_TO_TEST(7)); CHECK(IDX_TO_TEST(8)); + CHECK(IDX_TO_TEST(9)); CHECK(alloc == scratch->alloc_size); secp256k1_scratch_space_destroy(ctx, scratch); From c0de361fc53dbfb0b058895f4824eba4d423e191 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 1 Mar 2023 12:51:15 +0000 Subject: [PATCH 253/381] norm arg: allow X and R to be the point at infinity Add test vector --- src/modules/bppp/bppp_norm_product_impl.h | 19 +----- src/modules/bppp/bppp_util.h | 31 +++++++-- src/modules/bppp/test_vectors/verify.h | 7 ++ src/modules/bppp/tests_impl.h | 80 ++++++++++++++++++++++- 4 files changed, 115 insertions(+), 22 deletions(-) diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index 6b7521a1..dd1a0d3b 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -300,17 +300,8 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( return 0; } - /* We only fail here because we cannot serialize points at infinity. */ - if (secp256k1_gej_is_infinity(&xj) || secp256k1_gej_is_infinity(&rj)) { - return 0; - } - secp256k1_ge_set_gej_var(&x_ge, &xj); - secp256k1_fe_normalize_var(&x_ge.x); - secp256k1_fe_normalize_var(&x_ge.y); secp256k1_ge_set_gej_var(&r_ge, &rj); - secp256k1_fe_normalize_var(&r_ge.x); - secp256k1_fe_normalize_var(&r_ge.y); secp256k1_bppp_serialize_points(&proof[proof_idx], &x_ge, &r_ge); proof_idx += 65; @@ -379,16 +370,12 @@ static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx } idx -= 1; if (idx % 2 == 0) { - unsigned char pk_buf[33]; idx /= 2; *sc = data->gammas[idx]; - pk_buf[0] = 2 | (data->proof[65*idx] >> 1); - memcpy(&pk_buf[1], &data->proof[65*idx + 1], 32); - if (!secp256k1_eckey_pubkey_parse(pt, pk_buf, sizeof(pk_buf))) { + if (!secp256k1_bppp_parse_one_of_points(pt, &data->proof[65*idx], 0)) { return 0; } } else { - unsigned char pk_buf[33]; secp256k1_scalar neg_one; idx /= 2; secp256k1_scalar_set_int(&neg_one, 1); @@ -396,9 +383,7 @@ static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx *sc = data->gammas[idx]; secp256k1_scalar_sqr(sc, sc); secp256k1_scalar_add(sc, sc, &neg_one); - pk_buf[0] = 2 | data->proof[65*idx]; - memcpy(&pk_buf[1], &data->proof[65*idx + 33], 32); - if (!secp256k1_eckey_pubkey_parse(pt, pk_buf, sizeof(pk_buf))) { + if (!secp256k1_bppp_parse_one_of_points(pt, &data->proof[65*idx], 1)) { return 0; } } diff --git a/src/modules/bppp/bppp_util.h b/src/modules/bppp/bppp_util.h index fd04ae69..12f3da5a 100644 --- a/src/modules/bppp/bppp_util.h +++ b/src/modules/bppp/bppp_util.h @@ -15,10 +15,33 @@ /* Outputs a pair of points, amortizing the parity byte between them * Assumes both points' coordinates have been normalized. */ -static void secp256k1_bppp_serialize_points(unsigned char *output, const secp256k1_ge *lpt, const secp256k1_ge *rpt) { - output[0] = (secp256k1_fe_is_odd(&lpt->y) << 1) + secp256k1_fe_is_odd(&rpt->y); - secp256k1_fe_get_b32(&output[1], &lpt->x); - secp256k1_fe_get_b32(&output[33], &rpt->x); +static void secp256k1_bppp_serialize_points(unsigned char *output, secp256k1_ge *lpt, secp256k1_ge *rpt) { + unsigned char tmp[33]; + secp256k1_ge_serialize_ext(tmp, lpt); + output[0] = (tmp[0] & 1) << 1; + memcpy(&output[1], &tmp[1], 32); + secp256k1_ge_serialize_ext(tmp, rpt); + output[0] |= (tmp[0] & 1); + memcpy(&output[33], &tmp[1], 32); +} + +static int secp256k1_bppp_parse_one_of_points(secp256k1_ge *pt, const unsigned char *in65, int idx) { + unsigned char tmp[33] = { 0 }; + if (in65[0] > 3) { + return 0; + } + /* Check if the input array encodes the point at infinity */ + if ((secp256k1_memcmp_var(tmp, &in65[1 + 32*idx], 32)) != 0) { + tmp[0] = 2 | ((in65[0] & (2 - idx)) >> (1 - idx)); + memcpy(&tmp[1], &in65[1 + 32*idx], 32); + } else { + /* If we're parsing the point at infinity, enforce that the sign bit is + * 0. */ + if ((in65[0] & (2 - idx)) != 0) { + return 0; + } + } + return secp256k1_ge_parse_ext(pt, tmp); } /* Outputs a serialized point in compressed form. Returns 0 at point at infinity. diff --git a/src/modules/bppp/test_vectors/verify.h b/src/modules/bppp/test_vectors/verify.h index cc07850e..7b4bc634 100644 --- a/src/modules/bppp/test_vectors/verify.h +++ b/src/modules/bppp/test_vectors/verify.h @@ -69,4 +69,11 @@ static secp256k1_scalar verify_vector_9_c_vec[1]; static const unsigned char verify_vector_9_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; static const unsigned char verify_vector_9_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const int verify_vector_9_result = 1; +static const unsigned char verify_vector_10_commit33[33] = { 0x03, 0x62, 0x8A, 0xC2, 0xF1, 0xF2, 0x00, 0xE0, 0x81, 0xBD, 0xA0, 0xA9, 0x6D, 0x25, 0x53, 0xB4, 0x17, 0xC1, 0x02, 0x93, 0x50, 0x3E, 0x91, 0xD4, 0xD1, 0x3A, 0x82, 0x89, 0x02, 0x24, 0x78, 0x49, 0xA5 }; +static const size_t verify_vector_10_n_vec_len = 2; +static const unsigned char verify_vector_10_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_10_c_vec[1]; +static const unsigned char verify_vector_10_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_10_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int verify_vector_10_result = 1; diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index bbbc2b1f..4792519a 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -212,6 +212,80 @@ void test_norm_util_helpers(void) { secp256k1_scalar_set_int(&res, 256); CHECK(secp256k1_scalar_eq(&res, &rho_pows[3])); } + +void test_serialize_two_points_roundtrip(secp256k1_ge *X, secp256k1_ge *R) { + secp256k1_ge X_tmp, R_tmp; + unsigned char buf[65]; + secp256k1_bppp_serialize_points(buf, X, R); + CHECK(secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); + CHECK(secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1)); + ge_equals_ge(X, &X_tmp); + ge_equals_ge(R, &R_tmp); +} + +void test_serialize_two_points(void) { + secp256k1_ge X, R; + int i; + + for (i = 0; i < count; i++) { + random_group_element_test(&X); + random_group_element_test(&R); + test_serialize_two_points_roundtrip(&X, &R); + } + + for (i = 0; i < count; i++) { + random_group_element_test(&X); + secp256k1_ge_set_infinity(&R); + test_serialize_two_points_roundtrip(&X, &R); + } + + for (i = 0; i < count; i++) { + secp256k1_ge_set_infinity(&X); + random_group_element_test(&R); + test_serialize_two_points_roundtrip(&X, &R); + } + + secp256k1_ge_set_infinity(&X); + secp256k1_ge_set_infinity(&R); + test_serialize_two_points_roundtrip(&X, &R); + + /* Test invalid sign byte */ + { + secp256k1_ge X_tmp, R_tmp; + unsigned char buf[65]; + random_group_element_test(&X); + random_group_element_test(&R); + secp256k1_bppp_serialize_points(buf, &X, &R); + buf[0] |= 4 + (unsigned char)secp256k1_testrandi64(4, 255); + CHECK(!secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); + CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 0)); + } + /* Check that sign bit is 0 for point at infinity */ + for (i = 0; i < count; i++) { + secp256k1_ge X_tmp, R_tmp; + unsigned char buf[65]; + int expect; + random_group_element_test(&X); + random_group_element_test(&R); + secp256k1_bppp_serialize_points(buf, &X, &R); + memset(&buf[1], 0, 32); + if ((buf[0] & 2) == 0) { + expect = 1; + } else { + expect = 0; + } + CHECK(secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0) == expect); + CHECK(secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1)); + memset(&buf[33], 0, 32); + if ((buf[0] & 1) == 0) { + expect = 1; + } else { + expect = 0; + } + CHECK(secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1) == expect); + } +} + static void secp256k1_norm_arg_commit_initial_data( secp256k1_sha256* transcript, const secp256k1_scalar* rho, @@ -416,7 +490,9 @@ void norm_arg_zero(void) { random_scalar_order(&c_vec[i]); } CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); - CHECK(!secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + secp256k1_sha256_initialize(&transcript); + CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); secp256k1_bppp_generators_destroy(ctx, gs); } @@ -558,6 +634,7 @@ void norm_arg_verify_vectors(void) { CHECK(IDX_TO_TEST(7)); CHECK(IDX_TO_TEST(8)); CHECK(IDX_TO_TEST(9)); + CHECK(IDX_TO_TEST(10)); CHECK(alloc == scratch->alloc_size); secp256k1_scratch_space_destroy(ctx, scratch); @@ -567,6 +644,7 @@ void norm_arg_verify_vectors(void) { void run_bppp_tests(void) { test_log_exp(); test_norm_util_helpers(); + test_serialize_two_points(); test_bppp_generators_api(); test_bppp_generators_fixed(); test_bppp_tagged_hash(); From f5e4b16f0f96ae871d221900673f426e9c9ce85e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 1 Mar 2023 13:02:36 +0000 Subject: [PATCH 254/381] norm arg: add test vector for sign bit malleability R is point at infinity but sign is != 0 --- src/modules/bppp/test_vectors/verify.h | 7 +++++++ src/modules/bppp/tests_impl.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/modules/bppp/test_vectors/verify.h b/src/modules/bppp/test_vectors/verify.h index 7b4bc634..be9a4f63 100644 --- a/src/modules/bppp/test_vectors/verify.h +++ b/src/modules/bppp/test_vectors/verify.h @@ -76,4 +76,11 @@ static secp256k1_scalar verify_vector_10_c_vec[1]; static const unsigned char verify_vector_10_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; static const unsigned char verify_vector_10_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; static const int verify_vector_10_result = 1; +static const unsigned char verify_vector_11_commit33[33] = { 0x03, 0x62, 0x8A, 0xC2, 0xF1, 0xF2, 0x00, 0xE0, 0x81, 0xBD, 0xA0, 0xA9, 0x6D, 0x25, 0x53, 0xB4, 0x17, 0xC1, 0x02, 0x93, 0x50, 0x3E, 0x91, 0xD4, 0xD1, 0x3A, 0x82, 0x89, 0x02, 0x24, 0x78, 0x49, 0xA5 }; +static const size_t verify_vector_11_n_vec_len = 2; +static const unsigned char verify_vector_11_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_11_c_vec[1]; +static const unsigned char verify_vector_11_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_11_proof[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int verify_vector_11_result = 0; diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 4792519a..61895b56 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -635,6 +635,7 @@ void norm_arg_verify_vectors(void) { CHECK(IDX_TO_TEST(8)); CHECK(IDX_TO_TEST(9)); CHECK(IDX_TO_TEST(10)); + CHECK(IDX_TO_TEST(11)); CHECK(alloc == scratch->alloc_size); secp256k1_scratch_space_destroy(ctx, scratch); From a70c4d4a8a6970f8e299de541cc75f2fc2e39e76 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 1 Mar 2023 14:12:43 +0000 Subject: [PATCH 255/381] norm arg: add test vector for |n| = 0 --- src/modules/bppp/test_vectors/verify.h | 7 +++++++ src/modules/bppp/tests_impl.h | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/bppp/test_vectors/verify.h b/src/modules/bppp/test_vectors/verify.h index be9a4f63..9ab43fd0 100644 --- a/src/modules/bppp/test_vectors/verify.h +++ b/src/modules/bppp/test_vectors/verify.h @@ -83,4 +83,11 @@ static secp256k1_scalar verify_vector_11_c_vec[1]; static const unsigned char verify_vector_11_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; static const unsigned char verify_vector_11_proof[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; static const int verify_vector_11_result = 0; +static const unsigned char verify_vector_12_commit33[33] = { 0x02, 0x7D, 0x5F, 0x4B, 0x11, 0xC0, 0xE4, 0x2E, 0x4C, 0x1B, 0x56, 0xAE, 0xF0, 0x5F, 0xAA, 0xD8, 0x77, 0x0C, 0x93, 0x71, 0xA2, 0x92, 0xF9, 0x89, 0xA2, 0xB4, 0x69, 0x9B, 0x46, 0x8A, 0x03, 0xF1, 0x50 }; +static const size_t verify_vector_12_n_vec_len = 0; +static const unsigned char verify_vector_12_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar verify_vector_12_c_vec[1]; +static const unsigned char verify_vector_12_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char verify_vector_12_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int verify_vector_12_result = 0; diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 61895b56..3e10d1cd 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -496,7 +496,7 @@ void norm_arg_zero(void) { secp256k1_bppp_generators_destroy(ctx, gs); } - /* Verify vectors of length 0 */ + /* Verify |c| = 0 */ { unsigned int n_vec_len = 1; unsigned int c_vec_len = 1; @@ -508,7 +508,6 @@ void norm_arg_zero(void) { CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); - CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, 0, c_vec, c_vec_len, &commit)); CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, 0, &commit)); secp256k1_bppp_generators_destroy(ctx, gs); @@ -636,6 +635,7 @@ void norm_arg_verify_vectors(void) { CHECK(IDX_TO_TEST(9)); CHECK(IDX_TO_TEST(10)); CHECK(IDX_TO_TEST(11)); + CHECK(IDX_TO_TEST(12)); CHECK(alloc == scratch->alloc_size); secp256k1_scratch_space_destroy(ctx, scratch); From bf7bf8a64fa7a7256ad64f75ae0bcb9fccbd0ea4 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 1 Mar 2023 14:16:29 +0000 Subject: [PATCH 256/381] norm arg: split norm_arg_zero into prove_edge and verify_zero_len One function tests prover edge cases, the other tests verifier edge cases. --- src/modules/bppp/tests_impl.h | 44 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 3e10d1cd..d42f4a27 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -436,7 +436,7 @@ static int secp256k1_norm_arg_verify( return res; } -void norm_arg_zero(void) { +void norm_arg_prove_edge(void) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; secp256k1_scalar rho, mu; secp256k1_ge commit; @@ -495,23 +495,32 @@ void norm_arg_zero(void) { CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); secp256k1_bppp_generators_destroy(ctx, gs); } +} - /* Verify |c| = 0 */ - { - unsigned int n_vec_len = 1; - unsigned int c_vec_len = 1; - secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); - size_t plen = sizeof(proof); - random_scalar_order(&n_vec[0]); - random_scalar_order(&c_vec[0]); - random_scalar_order(&l_vec[0]); - CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); - CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); - CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); - CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, 0, &commit)); +/* Verify |c| = 0 */ +void norm_arg_verify_zero_len(void) { + secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; + secp256k1_scalar rho, mu; + secp256k1_ge commit; + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*10); /* shouldn't need much */ + unsigned char proof[1000]; + unsigned int n_vec_len = 1; + unsigned int c_vec_len = 1; + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); + size_t plen = sizeof(proof); - secp256k1_bppp_generators_destroy(ctx, gs); - } + random_scalar_order(&rho); + secp256k1_scalar_sqr(&mu, &rho); + + random_scalar_order(&n_vec[0]); + random_scalar_order(&c_vec[0]); + random_scalar_order(&l_vec[0]); + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, 0, &commit)); + + secp256k1_bppp_generators_destroy(ctx, gs); secp256k1_scratch_space_destroy(ctx, scratch); } @@ -650,7 +659,8 @@ void run_bppp_tests(void) { test_bppp_generators_fixed(); test_bppp_tagged_hash(); - norm_arg_zero(); + norm_arg_prove_edge(); + norm_arg_verify_zero_len(); norm_arg_test(1, 1); norm_arg_test(1, 64); norm_arg_test(64, 1); From 4eca406f4c71646d1812389d684219b481098b7d Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 11:00:37 +0200 Subject: [PATCH 257/381] Use relative #include paths in library (as in upstream) --- src/modules/bppp/bppp_norm_product_impl.h | 16 ++++++++-------- src/modules/bppp/bppp_transcript_impl.h | 4 ++-- src/modules/bppp/bppp_util.h | 8 ++++---- src/modules/bppp/main_impl.h | 14 +++++++------- src/modules/bppp/tests_impl.h | 2 +- src/modules/generator/main_impl.h | 2 +- src/modules/rangeproof/main_impl.h | 6 +++--- src/modules/rangeproof/rangeproof_impl.h | 6 +++--- src/modules/surjection/main_impl.h | 2 +- src/secp256k1.c | 6 +++--- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index 6b7521a1..6ff48f7f 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -7,15 +7,15 @@ #ifndef _SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_ #define _SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_ -#include "group.h" -#include "scalar.h" -#include "ecmult.h" -#include "ecmult_gen.h" -#include "hash.h" +#include "../../group.h" +#include "../../scalar.h" +#include "../../ecmult.h" +#include "../../ecmult_gen.h" +#include "../../hash.h" -#include "modules/bppp/main.h" -#include "modules/bppp/bppp_util.h" -#include "modules/bppp/bppp_transcript_impl.h" +#include "../bppp/main.h" +#include "../bppp/bppp_util.h" +#include "../bppp/bppp_transcript_impl.h" /* Computes the inner product of two vectors of scalars * with elements starting from offset a and offset b diff --git a/src/modules/bppp/bppp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h index a734ea2d..1b51b22e 100644 --- a/src/modules/bppp/bppp_transcript_impl.h +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -6,8 +6,8 @@ #ifndef _SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_ #define _SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_ -#include "group.h" -#include "scalar.h" +#include "../../group.h" +#include "../../scalar.h" #include "bppp_util.h" /* Initializes SHA256 with fixed midstate. This midstate was computed by applying diff --git a/src/modules/bppp/bppp_util.h b/src/modules/bppp/bppp_util.h index fd04ae69..3d810400 100644 --- a/src/modules/bppp/bppp_util.h +++ b/src/modules/bppp/bppp_util.h @@ -7,10 +7,10 @@ #ifndef _SECP256K1_MODULE_BPPP_UTIL_ #define _SECP256K1_MODULE_BPPP_UTIL_ -#include "field.h" -#include "group.h" -#include "hash.h" -#include "eckey.h" +#include "../../field.h" +#include "../../group.h" +#include "../../hash.h" +#include "../../eckey.h" /* Outputs a pair of points, amortizing the parity byte between them * Assumes both points' coordinates have been normalized. diff --git a/src/modules/bppp/main_impl.h b/src/modules/bppp/main_impl.h index dfebde10..b8ac117d 100644 --- a/src/modules/bppp/main_impl.h +++ b/src/modules/bppp/main_impl.h @@ -7,13 +7,13 @@ #ifndef _SECP256K1_MODULE_BPPP_MAIN_ #define _SECP256K1_MODULE_BPPP_MAIN_ -#include "include/secp256k1_bppp.h" -#include "include/secp256k1_generator.h" -#include "modules/generator/main_impl.h" /* for generator_{load, save} */ -#include "hash.h" -#include "util.h" -#include "modules/bppp/main.h" -#include "modules/bppp/bppp_norm_product_impl.h" +#include "../../../include/secp256k1_bppp.h" +#include "../../../include/secp256k1_generator.h" +#include "../generator/main_impl.h" /* for generator_{load, save} */ +#include "../../hash.h" +#include "../../util.h" +#include "../bppp/main.h" +#include "../bppp/bppp_norm_product_impl.h" secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_context *ctx, size_t n) { secp256k1_bppp_generators *ret; diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index bdb261a0..fb383f5a 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -9,7 +9,7 @@ #include -#include "include/secp256k1_bppp.h" +#include "../../../include/secp256k1_bppp.h" #include "bppp_norm_product_impl.h" #include "bppp_util.h" #include "bppp_transcript_impl.h" diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index e60ecdc2..4fba00ca 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -14,7 +14,7 @@ #include "../../hash.h" #include "../../scalar.h" -#include "modules/generator/pedersen_impl.h" +#include "../generator/pedersen_impl.h" /** Alternative generator for secp256k1. * This is the sha256 of 'g' after standard encoding (without compression), diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index b1af2a5e..ced0d5cd 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -9,9 +9,9 @@ #include "../../group.h" -#include "modules/generator/main_impl.h" -#include "modules/rangeproof/borromean_impl.h" -#include "modules/rangeproof/rangeproof_impl.h" +#include "../generator/main_impl.h" +#include "../rangeproof/borromean_impl.h" +#include "../rangeproof/rangeproof_impl.h" int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index dd79b6ad..12684b9c 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -13,9 +13,9 @@ #include "../../hash_impl.h" #include "../../util.h" -#include "modules/generator/pedersen.h" -#include "modules/rangeproof/borromean.h" -#include "modules/rangeproof/rangeproof.h" +#include "../generator/pedersen.h" +#include "../rangeproof/borromean.h" +#include "../rangeproof/rangeproof.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index a214f90b..42041573 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -10,7 +10,7 @@ #include #if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" +#include "../../libsecp256k1-config.h" #endif #include "../../../include/secp256k1_rangeproof.h" diff --git a/src/secp256k1.c b/src/secp256k1.c index c147eae5..a9730f5e 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -33,15 +33,15 @@ #endif #ifdef ENABLE_MODULE_GENERATOR -# include "include/secp256k1_generator.h" +# include "../include/secp256k1_generator.h" #endif #ifdef ENABLE_MODULE_RANGEPROOF -# include "include/secp256k1_rangeproof.h" +# include "../include/secp256k1_rangeproof.h" #endif #ifdef ENABLE_MODULE_ECDSA_S2C -# include "include/secp256k1_ecdsa_s2c.h" +# include "../include/secp256k1_ecdsa_s2c.h" static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge); #else typedef void secp256k1_ecdsa_s2c_opening; From c565827c1a332c96253d35b85accae9cda4ef00d Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 11:08:17 +0200 Subject: [PATCH 258/381] Use relative #include paths in ctime_test (as in upstream) --- src/valgrind_ctime_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 92ebac22..ee180116 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -29,15 +29,15 @@ #endif #ifdef ENABLE_MODULE_ECDSA_S2C -#include "include/secp256k1_ecdsa_s2c.h" +#include "../include/secp256k1_ecdsa_s2c.h" #endif #ifdef ENABLE_MODULE_ECDSA_ADAPTOR -#include "include/secp256k1_ecdsa_adaptor.h" +#include "../include/secp256k1_ecdsa_adaptor.h" #endif #ifdef ENABLE_MODULE_MUSIG -#include "include/secp256k1_musig.h" +#include "../include/secp256k1_musig.h" #endif void run_tests(secp256k1_context *ctx, unsigned char *key); From c690d6df700fe63529f7dc2fc02166d107c6b686 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 11:10:47 +0200 Subject: [PATCH 259/381] Use relative #include paths in benchmarks (as in upstream) --- src/bench_bppp.c | 2 +- src/bench_generator.c | 2 +- src/bench_rangeproof.c | 2 +- src/bench_whitelist.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bench_bppp.c b/src/bench_bppp.c index c2846182..83e46443 100644 --- a/src/bench_bppp.c +++ b/src/bench_bppp.c @@ -6,7 +6,7 @@ #include -#include "include/secp256k1_bppp.h" +#include "../include/secp256k1_bppp.h" #include "util.h" #include "bench.h" diff --git a/src/bench_generator.c b/src/bench_generator.c index d3b251e4..175137e9 100644 --- a/src/bench_generator.c +++ b/src/bench_generator.c @@ -7,7 +7,7 @@ #include #include -#include "include/secp256k1_generator.h" +#include "../include/secp256k1_generator.h" #include "util.h" #include "bench.h" diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 14e22e8a..14f5f875 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -6,7 +6,7 @@ #include -#include "include/secp256k1_rangeproof.h" +#include "../include/secp256k1_rangeproof.h" #include "util.h" #include "bench.h" diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c index e4908306..18bfa144 100644 --- a/src/bench_whitelist.c +++ b/src/bench_whitelist.c @@ -5,9 +5,9 @@ **********************************************************************/ #include -#include "include/secp256k1.h" +#include "../include/secp256k1.h" -#include "include/secp256k1_whitelist.h" +#include "../include/secp256k1_whitelist.h" #include "util.h" #include "bench.h" #include "hash_impl.h" From 0eea7d97abba0cc07515368981c6a30b96ab2428 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 11:21:55 +0200 Subject: [PATCH 260/381] Use relative #include paths in tests (as in upstream) --- src/modules/extrakeys/tests_exhaustive_impl.h | 2 +- src/modules/recovery/tests_exhaustive_impl.h | 2 +- src/modules/schnorrsig/tests_exhaustive_impl.h | 2 +- src/tests_exhaustive.c | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/extrakeys/tests_exhaustive_impl.h b/src/modules/extrakeys/tests_exhaustive_impl.h index d4a2f5bd..5ecc90d5 100644 --- a/src/modules/extrakeys/tests_exhaustive_impl.h +++ b/src/modules/extrakeys/tests_exhaustive_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H #define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H -#include "src/modules/extrakeys/main_impl.h" #include "../../../include/secp256k1_extrakeys.h" +#include "main_impl.h" static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) { secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1]; diff --git a/src/modules/recovery/tests_exhaustive_impl.h b/src/modules/recovery/tests_exhaustive_impl.h index 590a972e..ed9386b6 100644 --- a/src/modules/recovery/tests_exhaustive_impl.h +++ b/src/modules/recovery/tests_exhaustive_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H #define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H -#include "src/modules/recovery/main_impl.h" +#include "main_impl.h" #include "../../../include/secp256k1_recovery.h" void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { diff --git a/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/modules/schnorrsig/tests_exhaustive_impl.h index d8df9dd2..55f9028a 100644 --- a/src/modules/schnorrsig/tests_exhaustive_impl.h +++ b/src/modules/schnorrsig/tests_exhaustive_impl.h @@ -8,7 +8,7 @@ #define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H #include "../../../include/secp256k1_schnorrsig.h" -#include "src/modules/schnorrsig/main_impl.h" +#include "main_impl.h" static const unsigned char invalid_pubkey_bytes[][32] = { /* 0 */ diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c index 6a4e2340..225bbddf 100644 --- a/src/tests_exhaustive.c +++ b/src/tests_exhaustive.c @@ -342,15 +342,15 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou } #ifdef ENABLE_MODULE_RECOVERY -#include "src/modules/recovery/tests_exhaustive_impl.h" +#include "modules/recovery/tests_exhaustive_impl.h" #endif #ifdef ENABLE_MODULE_EXTRAKEYS -#include "src/modules/extrakeys/tests_exhaustive_impl.h" +#include "modules/extrakeys/tests_exhaustive_impl.h" #endif #ifdef ENABLE_MODULE_SCHNORRSIG -#include "src/modules/schnorrsig/tests_exhaustive_impl.h" +#include "modules/schnorrsig/tests_exhaustive_impl.h" #endif int main(int argc, char** argv) { From e444d24bcad57091746784fcea6d07e95d058cd3 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 11:47:54 +0200 Subject: [PATCH 261/381] Fix include guards: No _ prefix/suffix but _H suffix (as in upstream) --- include/secp256k1_bppp.h | 4 ++-- include/secp256k1_generator.h | 4 ++-- include/secp256k1_rangeproof.h | 4 ++-- include/secp256k1_surjectionproof.h | 4 ++-- include/secp256k1_whitelist.h | 4 ++-- src/modules/bppp/bppp_norm_product_impl.h | 4 ++-- src/modules/bppp/bppp_transcript_impl.h | 4 ++-- src/modules/bppp/bppp_util.h | 4 ++-- src/modules/bppp/main_impl.h | 4 ++-- src/modules/bppp/tests_impl.h | 4 ++-- src/modules/extrakeys/hsort.h | 4 ++-- src/modules/extrakeys/hsort_impl.h | 4 ++-- src/modules/generator/main_impl.h | 4 ++-- src/modules/generator/pedersen.h | 4 ++-- src/modules/generator/pedersen_impl.h | 4 ++-- src/modules/generator/tests_impl.h | 4 ++-- src/modules/musig/main_impl.h | 4 ++-- src/modules/rangeproof/borromean.h | 4 ++-- src/modules/rangeproof/borromean_impl.h | 4 ++-- src/modules/rangeproof/main_impl.h | 4 ++-- src/modules/rangeproof/rangeproof.h | 4 ++-- src/modules/rangeproof/rangeproof_impl.h | 4 ++-- src/modules/rangeproof/tests_impl.h | 4 ++-- src/modules/surjection/main_impl.h | 4 ++-- src/modules/surjection/surjection.h | 4 ++-- src/modules/surjection/surjection_impl.h | 4 ++-- src/modules/surjection/tests_impl.h | 4 ++-- src/modules/whitelist/main_impl.h | 4 ++-- src/modules/whitelist/tests_impl.h | 4 ++-- src/modules/whitelist/whitelist_impl.h | 4 ++-- 30 files changed, 60 insertions(+), 60 deletions(-) diff --git a/include/secp256k1_bppp.h b/include/secp256k1_bppp.h index c880ee48..8ed82c4b 100644 --- a/include/secp256k1_bppp.h +++ b/include/secp256k1_bppp.h @@ -1,5 +1,5 @@ -#ifndef _SECP256K1_BPPP_ -# define _SECP256K1_BPPP_ +#ifndef SECP256K1_BPPP_H +# define SECP256K1_BPPP_H # include "secp256k1.h" diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index 5479fc81..f0570dcb 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -1,5 +1,5 @@ -#ifndef _SECP256K1_GENERATOR_ -# define _SECP256K1_GENERATOR_ +#ifndef SECP256K1_GENERATOR_H +# define SECP256K1_GENERATOR_H # include "secp256k1.h" diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 2d86ab06..80a4f967 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -1,5 +1,5 @@ -#ifndef _SECP256K1_RANGEPROOF_ -# define _SECP256K1_RANGEPROOF_ +#ifndef SECP256K1_RANGEPROOF_H +# define SECP256K1_RANGEPROOF_H # include "secp256k1.h" # include "secp256k1_generator.h" diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index ab7a4a9e..4939e327 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -1,5 +1,5 @@ -#ifndef _SECP256K1_SURJECTIONPROOF_ -#define _SECP256K1_SURJECTIONPROOF_ +#ifndef SECP256K1_SURJECTIONPROOF_H +#define SECP256K1_SURJECTIONPROOF_H #include "secp256k1.h" #include "secp256k1_rangeproof.h" diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index 5b14df7c..83a053dd 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_WHITELIST_ -#define _SECP256K1_WHITELIST_ +#ifndef SECP256K1_WHITELIST_H +#define SECP256K1_WHITELIST_H #include "secp256k1.h" diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index 6ff48f7f..76cf5471 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_ -#define _SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_ +#ifndef SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_H +#define SECP256K1_MODULE_BPPP_PP_NORM_PRODUCT_H #include "../../group.h" #include "../../scalar.h" diff --git a/src/modules/bppp/bppp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h index 1b51b22e..5fe9b96c 100644 --- a/src/modules/bppp/bppp_transcript_impl.h +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -3,8 +3,8 @@ * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_ -#define _SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_ +#ifndef SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_H +#define SECP256K1_MODULE_BPPP_PP_TRANSCRIPT_IMPL_H #include "../../group.h" #include "../../scalar.h" diff --git a/src/modules/bppp/bppp_util.h b/src/modules/bppp/bppp_util.h index 3d810400..055d53f5 100644 --- a/src/modules/bppp/bppp_util.h +++ b/src/modules/bppp/bppp_util.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BPPP_UTIL_ -#define _SECP256K1_MODULE_BPPP_UTIL_ +#ifndef SECP256K1_MODULE_BPPP_UTIL_H +#define SECP256K1_MODULE_BPPP_UTIL_H #include "../../field.h" #include "../../group.h" diff --git a/src/modules/bppp/main_impl.h b/src/modules/bppp/main_impl.h index b8ac117d..49a09447 100644 --- a/src/modules/bppp/main_impl.h +++ b/src/modules/bppp/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BPPP_MAIN_ -#define _SECP256K1_MODULE_BPPP_MAIN_ +#ifndef SECP256K1_MODULE_BPPP_MAIN_IMPL_H +#define SECP256K1_MODULE_BPPP_MAIN_IMPL_H #include "../../../include/secp256k1_bppp.h" #include "../../../include/secp256k1_generator.h" diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index fb383f5a..ce4e05ac 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_BPPP_TEST_ -#define _SECP256K1_MODULE_BPPP_TEST_ +#ifndef SECP256K1_MODULE_BPPP_TEST_H +#define SECP256K1_MODULE_BPPP_TEST_H #include diff --git a/src/modules/extrakeys/hsort.h b/src/modules/extrakeys/hsort.h index 0f57227c..5352ef1e 100644 --- a/src/modules/extrakeys/hsort.h +++ b/src/modules/extrakeys/hsort.h @@ -4,8 +4,8 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ -#ifndef SECP256K1_HSORT_H_ -#define SECP256K1_HSORT_H_ +#ifndef SECP256K1_HSORT_H +#define SECP256K1_HSORT_H #include #include diff --git a/src/modules/extrakeys/hsort_impl.h b/src/modules/extrakeys/hsort_impl.h index a5a94023..e05aefdf 100644 --- a/src/modules/extrakeys/hsort_impl.h +++ b/src/modules/extrakeys/hsort_impl.h @@ -4,8 +4,8 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ -#ifndef SECP256K1_HSORT_IMPL_H_ -#define SECP256K1_HSORT_IMPL_H_ +#ifndef SECP256K1_HSORT_IMPL_H +#define SECP256K1_HSORT_IMPL_H #include "hsort.h" diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 4fba00ca..17517847 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_GENERATOR_MAIN -#define SECP256K1_MODULE_GENERATOR_MAIN +#ifndef SECP256K1_MODULE_GENERATOR_MAIN_H +#define SECP256K1_MODULE_GENERATOR_MAIN_H #include diff --git a/src/modules/generator/pedersen.h b/src/modules/generator/pedersen.h index ce42d521..a09f4a5e 100644 --- a/src/modules/generator/pedersen.h +++ b/src/modules/generator/pedersen.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_PEDERSEN_H_ -#define _SECP256K1_PEDERSEN_H_ +#ifndef SECP256K1_PEDERSEN_H +#define SECP256K1_PEDERSEN_H #include "../../ecmult_gen.h" #include "../../group.h" diff --git a/src/modules/generator/pedersen_impl.h b/src/modules/generator/pedersen_impl.h index 6ebffc4f..24f96bd2 100644 --- a/src/modules/generator/pedersen_impl.h +++ b/src/modules/generator/pedersen_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php. * ***********************************************************************/ -#ifndef _SECP256K1_PEDERSEN_IMPL_H_ -#define _SECP256K1_PEDERSEN_IMPL_H_ +#ifndef SECP256K1_PEDERSEN_IMPL_H +#define SECP256K1_PEDERSEN_IMPL_H #include diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index b49ecae9..e81612fd 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_GENERATOR_TESTS -#define SECP256K1_MODULE_GENERATOR_TESTS +#ifndef SECP256K1_MODULE_GENERATOR_TESTS_H +#define SECP256K1_MODULE_GENERATOR_TESTS_H #include #include diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 53a62979..e69afed5 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_MUSIG_MAIN -#define SECP256K1_MODULE_MUSIG_MAIN +#ifndef SECP256K1_MODULE_MUSIG_MAIN_H +#define SECP256K1_MODULE_MUSIG_MAIN_H #include "keyagg_impl.h" #include "session_impl.h" diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h index b9a762bf..c3ce76c9 100644 --- a/src/modules/rangeproof/borromean.h +++ b/src/modules/rangeproof/borromean.h @@ -5,8 +5,8 @@ **********************************************************************/ -#ifndef _SECP256K1_BORROMEAN_H_ -#define _SECP256K1_BORROMEAN_H_ +#ifndef SECP256K1_BORROMEAN_H +#define SECP256K1_BORROMEAN_H #include "../../scalar.h" #include "../../field.h" diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index f8ee11a4..fb5e44db 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -5,8 +5,8 @@ **********************************************************************/ -#ifndef _SECP256K1_BORROMEAN_IMPL_H_ -#define _SECP256K1_BORROMEAN_IMPL_H_ +#ifndef SECP256K1_BORROMEAN_IMPL_H +#define SECP256K1_BORROMEAN_IMPL_H #include "../../scalar.h" #include "../../field.h" diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index ced0d5cd..32614caa 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_RANGEPROOF_MAIN -#define SECP256K1_MODULE_RANGEPROOF_MAIN +#ifndef SECP256K1_MODULE_RANGEPROOF_MAIN_H +#define SECP256K1_MODULE_RANGEPROOF_MAIN_H #include "../../group.h" diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index 5aadce0e..fa118e66 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_RANGEPROOF_H_ -#define _SECP256K1_RANGEPROOF_H_ +#ifndef SECP256K1_RANGEPROOF_H +#define SECP256K1_RANGEPROOF_H #include "../../scalar.h" #include "../../group.h" diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 12684b9c..c08f817e 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_RANGEPROOF_IMPL_H_ -#define _SECP256K1_RANGEPROOF_IMPL_H_ +#ifndef SECP256K1_RANGEPROOF_IMPL_H +#define SECP256K1_RANGEPROOF_IMPL_H #include "../../eckey.h" #include "../../scalar.h" diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 61d0492e..33b67ed1 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_RANGEPROOF_TESTS -#define SECP256K1_MODULE_RANGEPROOF_TESTS +#ifndef SECP256K1_MODULE_RANGEPROOF_TESTS_H +#define SECP256K1_MODULE_RANGEPROOF_TESTS_H #include diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 42041573..c6bdea2e 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -3,8 +3,8 @@ * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_SURJECTION_MAIN -#define SECP256K1_MODULE_SURJECTION_MAIN +#ifndef SECP256K1_MODULE_SURJECTION_MAIN_H +#define SECP256K1_MODULE_SURJECTION_MAIN_H #include #include diff --git a/src/modules/surjection/surjection.h b/src/modules/surjection/surjection.h index 55d320a0..ac7407d7 100644 --- a/src/modules/surjection/surjection.h +++ b/src/modules/surjection/surjection.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SURJECTION_H_ -#define _SECP256K1_SURJECTION_H_ +#ifndef SECP256K1_SURJECTION_H +#define SECP256K1_SURJECTION_H #include "../../group.h" #include "../../scalar.h" diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index 90649e97..f5adc7eb 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SURJECTION_IMPL_H_ -#define _SECP256K1_SURJECTION_IMPL_H_ +#ifndef SECP256K1_SURJECTION_IMPL_H +#define SECP256K1_SURJECTION_IMPL_H #include #include diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index a792bb5f..cf9e39ae 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS -#define SECP256K1_MODULE_SURJECTIONPROOF_TESTS +#ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS_H +#define SECP256K1_MODULE_SURJECTIONPROOF_TESTS_H #include "../../testrand.h" #include "../../group.h" diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 9a50ce7f..da631522 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_WHITELIST_MAIN -#define SECP256K1_MODULE_WHITELIST_MAIN +#ifndef SECP256K1_MODULE_WHITELIST_MAIN_H +#define SECP256K1_MODULE_WHITELIST_MAIN_H #include "../../../include/secp256k1_whitelist.h" #include "whitelist_impl.h" diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 184fb032..19c1d6e0 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef SECP256K1_MODULE_WHITELIST_TESTS -#define SECP256K1_MODULE_WHITELIST_TESTS +#ifndef SECP256K1_MODULE_WHITELIST_TESTS_H +#define SECP256K1_MODULE_WHITELIST_TESTS_H #include "../../../include/secp256k1_whitelist.h" diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h index fc1d489c..8d691127 100644 --- a/src/modules/whitelist/whitelist_impl.h +++ b/src/modules/whitelist/whitelist_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_WHITELIST_IMPL_H_ -#define _SECP256K1_WHITELIST_IMPL_H_ +#ifndef SECP256K1_WHITELIST_IMPL_H +#define SECP256K1_WHITELIST_IMPL_H static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_gej* pubkey) { unsigned char h[32]; From 4d9d8f92d411edc81f971cdf90696ae2952f4231 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 12:38:34 +0200 Subject: [PATCH 262/381] Simple dedicated -zkp README --- README.md | 79 +++++++++++-------------------------------------------- 1 file changed, 15 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 7ac1e0a0..b0ce01e3 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,24 @@ -libsecp256k1 -============ +libsecp256k1-zkp +================ -[![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/secp256k1.svg?branch=master)](https://cirrus-ci.com/github/bitcoin-core/secp256k1) +[![Build Status](https://api.cirrus-ci.com/github/BlockstreamResearch/secp256k1-zkp.svg?branch=master)](https://cirrus-ci.com/github/BlockstreamResearch/secp256k1-zkp) -Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1. +A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features such as Confidential Assets and MuSig2 -This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose. +Added features: +* Experimental module for ECDSA adaptor signatures. +* Experimental module for ECDSA sign-to-contract. +* Experimental module for [MuSig2](src/modules/musig/musig.md). +* Experimental module for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)). +* Experimental module for Bulletproofs++ range proofs. +* Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md). -Features: -* secp256k1 ECDSA signing/verification and key generation. -* Additive and multiplicative tweaking of secret/public keys. -* Serialization/parsing of secret keys, public keys, signatures. -* Constant time, constant memory access signing and public key generation. -* Derandomized ECDSA (via RFC6979 or with a caller provided function.) -* Very efficient implementation. -* Suitable for embedded systems. -* Optional module for public key recovery. -* Optional module for ECDH key exchange. -* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). -* Optional module for ECDSA adaptor signatures (experimental). - -Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable. - -Implementation details ----------------------- - -* General - * No runtime heap allocation. - * Extensive testing infrastructure. - * Structured to facilitate review and analysis. - * Intended to be portable to any system with a C89 compiler and uint64_t support. - * No use of floating types. - * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") -* Field operations - * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). - * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). - * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan). - * This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community. -* Scalar operations - * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. - * Using 4 64-bit limbs (relying on __int128 support in the compiler). - * Using 8 32-bit limbs. -* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman). -* Group operations - * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). - * Use addition between points in Jacobian and affine coordinates where possible. - * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. - * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. -* Point multiplication for verification (a*P + b*G). - * Use wNAF notation for point multiplicands. - * Use a much larger window for multiples of G, using precomputed multiples. - * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. - * Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. -* Point multiplication for signing - * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. - * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains) - * Access the table with branch-free conditional moves so memory access is uniform. - * No data-dependent branches - * Optional runtime blinding which attempts to frustrate differential power analysis. - * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. +Experimental features are made available for testing and review by the community. The APIs of these features should not be considered stable. Build steps ----------- -libsecp256k1 is built using autotools: +libsecp256k1-zkp is built using autotools: $ ./autogen.sh $ ./configure @@ -71,15 +26,11 @@ libsecp256k1 is built using autotools: $ make check # run the test suite $ sudo make install # optional -To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. +To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-musig`. Usage examples ----------- Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. - * [ECDSA example](examples/ecdsa.c) - * [Schnorr signatures example](examples/schnorr.c) - * [Deriving a shared secret (ECDH) example](examples/ecdh.c) - To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`. Test coverage ----------- @@ -105,7 +56,7 @@ To create a HTML report with coloured and annotated source code: Benchmark ------------ -If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1 functions will be present in the root directory after the build. +If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1-zkp functions will be present in the root directory after the build. To print the benchmark result to the command line: From da7702844e212f0cc165d1560a4dc09a05811dae Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 15:56:09 +0200 Subject: [PATCH 263/381] extrakeys: Clarify comparison order of compare/sort functions Note that the touched functions don't exist upstream currently. --- include/secp256k1_extrakeys.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index deb8dc8b..d8e05398 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -242,7 +242,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Compare two public keys using lexicographic order +/** Compare two public keys using lexicographic order of their compressed + * serialization. * * Returns: <0 if the first public key is less than the second * >0 if the first public key is greater than the second @@ -257,7 +258,8 @@ SECP256K1_API int secp256k1_pubkey_cmp( const secp256k1_pubkey* pk2 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Sorts public keys using lexicographic order +/** Sort public keys using lexicographic order of their compressed + * serialization. * * Returns: 0 if the arguments are invalid. 1 otherwise. * From a0b51afc01dfc8dbdc43e4c36825fadf760daa3c Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Apr 2023 16:18:19 +0200 Subject: [PATCH 264/381] musig: VERIFY_CHECK preconditions of _musig_keyaggcoef_internal() --- src/modules/musig/keyagg_impl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 85bbacfa..114c831a 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -166,6 +166,12 @@ static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, secp256k1_ge *pk, const secp256k1_ge *second_pk) { secp256k1_sha256 sha; + VERIFY_CHECK(!secp256k1_ge_is_infinity(pk)); +#ifdef VERIFY + VERIFY_CHECK(pk->x.normalized && pk->y.normalized); + VERIFY_CHECK(secp256k1_ge_is_infinity(second_pk) || (second_pk->x.normalized && second_pk->y.normalized)); +#endif + if (!secp256k1_ge_is_infinity(second_pk) && secp256k1_fe_equal(&pk->x, &second_pk->x) && secp256k1_fe_is_odd(&pk->y) == secp256k1_fe_is_odd(&second_pk->y)) { From 095c1e749c106285e8252d6490073974dd4d8fcc Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 25 Apr 2023 14:27:53 +0000 Subject: [PATCH 265/381] norm arg: add prove_const to tests --- src/modules/bppp/tests_impl.h | 85 +++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index d42f4a27..dbacace7 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -347,6 +347,50 @@ static void copy_vectors_into_scratch(secp256k1_scratch_space* scratch, memcpy(*gs, gens_vec, (g_len + h_len) * sizeof(secp256k1_ge)); } +/* Same as secp256k1_bppp_rangeproof_norm_product_prove but does not modify the inputs */ +static int secp256k1_bppp_rangeproof_norm_product_prove_const( + secp256k1_scratch_space* scratch, + unsigned char* proof, + size_t *proof_len, + secp256k1_sha256 *transcript, + const secp256k1_scalar* rho, + const secp256k1_ge* g_vec, + size_t g_vec_len, + const secp256k1_scalar* n_vec, + size_t n_vec_len, + const secp256k1_scalar* l_vec, + size_t l_vec_len, + const secp256k1_scalar* c_vec, + size_t c_vec_len +) { + secp256k1_scalar *ns, *ls, *cs; + secp256k1_ge *gs; + size_t scratch_checkpoint; + size_t g_len = n_vec_len, h_len = l_vec_len; + int res; + + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, g_vec, g_len, h_len); + res = secp256k1_bppp_rangeproof_norm_product_prove( + ctx, + scratch, + proof, + proof_len, + transcript, /* Transcript hash of the parent protocol */ + rho, + gs, + g_vec_len, + ns, + n_vec_len, + ls, + l_vec_len, + cs, + c_vec_len + ); + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return res; +} + /* A complete norm argument. In contrast to secp256k1_bppp_rangeproof_norm_product_prove, this is meant to be used as a standalone norm argument. This is a simple wrapper around secp256k1_bppp_rangeproof_norm_product_prove @@ -367,38 +411,10 @@ static int secp256k1_norm_arg_prove( size_t c_vec_len, const secp256k1_ge* commit ) { - secp256k1_scalar *ns, *ls, *cs; - secp256k1_ge *gs, comm = *commit; - size_t scratch_checkpoint; - size_t g_len = n_vec_len, h_len = l_vec_len; - int res; secp256k1_sha256 transcript; + secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, n_vec_len, c_vec, c_vec_len, commit); - scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - - copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens_vec->gens, g_len, h_len); - - /* Commit to the initial public values */ - secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, g_len, c_vec, c_vec_len, &comm); - - res = secp256k1_bppp_rangeproof_norm_product_prove( - ctx, - scratch, - proof, - proof_len, - &transcript, /* Transcript hash of the parent protocol */ - rho, - gs, - gens_vec->n, - ns, - n_vec_len, - ls, - l_vec_len, - cs, - c_vec_len - ); - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); - return res; + return secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, proof, proof_len, &transcript, rho, gens_vec->gens, gens_vec->n, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len); } /* Verify the proof */ @@ -461,14 +477,7 @@ void norm_arg_prove_edge(void) { secp256k1_sha256_initialize(&transcript); /* No challenges used in n = 1, l = 1, but we set transcript as a good practice*/ CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); - { - secp256k1_scalar *ns, *ls, *cs; - secp256k1_ge *gs; - size_t scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); - copy_vectors_into_scratch(scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, gens->gens, n_vec_len, c_vec_len); - CHECK(secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, proof, &plen, &transcript, &rho, gs, gens->n, ns, n_vec_len, ls, c_vec_len, cs, c_vec_len)); - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); - } + CHECK(secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, proof, &plen, &transcript, &rho, gens->gens, gens->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len)); secp256k1_sha256_initialize(&transcript); CHECK(secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &rho, gens, c_vec_len, c_vec, c_vec_len, &commit)); From cf797ed2a4ccc7422de2f4081a6d6bcf536d72c8 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 25 Apr 2023 13:57:24 +0000 Subject: [PATCH 266/381] norm arg: add prove test vectors --- src/modules/bppp/test_vectors/prove.h | 47 ++++++++++++++++++++ src/modules/bppp/tests_impl.h | 63 +++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/modules/bppp/test_vectors/prove.h diff --git a/src/modules/bppp/test_vectors/prove.h b/src/modules/bppp/test_vectors/prove.h new file mode 100644 index 00000000..06559b38 --- /dev/null +++ b/src/modules/bppp/test_vectors/prove.h @@ -0,0 +1,47 @@ +static const unsigned char prove_vector_gens[264] = { 0x03, 0xAF, 0x2C, 0x40, 0xAD, 0x03, 0xCD, 0xC5, 0x76, 0x8C, 0x07, 0x1E, 0x58, 0xD6, 0x8C, 0x73, 0x45, 0xBA, 0xEB, 0xB5, 0x3F, 0x40, 0xFA, 0x8B, 0xBF, 0x73, 0x6E, 0x7B, 0x4A, 0x54, 0x06, 0xED, 0x32, 0x03, 0xCC, 0x11, 0x19, 0x22, 0x2C, 0xA1, 0x0A, 0x45, 0x23, 0xAF, 0x9B, 0x40, 0x0D, 0xA4, 0x5E, 0x06, 0x24, 0xF4, 0x5F, 0x07, 0x89, 0x88, 0xCD, 0x71, 0xAE, 0x77, 0xC1, 0xF5, 0x87, 0x4E, 0xFC, 0xA5, 0x03, 0xDE, 0x61, 0xB1, 0x8F, 0x2C, 0xAC, 0x18, 0xF5, 0xE4, 0x06, 0x8F, 0x65, 0x55, 0xA1, 0x30, 0x5E, 0xF5, 0xF4, 0x84, 0xED, 0x6B, 0xDD, 0xC2, 0xCC, 0xE8, 0x51, 0x38, 0xB8, 0xA5, 0x4C, 0x43, 0xBD, 0x02, 0xA5, 0xF9, 0x8C, 0x1F, 0x82, 0x2D, 0xC6, 0xF3, 0x0F, 0x53, 0xDB, 0x74, 0x77, 0xC7, 0x91, 0x04, 0xB0, 0xB1, 0xA6, 0x17, 0xB2, 0x91, 0xF4, 0x8B, 0x93, 0x3E, 0xBB, 0x73, 0x15, 0x3E, 0x5A, 0xD1, 0x02, 0x44, 0xF5, 0xC6, 0x4E, 0x77, 0x60, 0x81, 0x83, 0xFF, 0xC2, 0x8E, 0x06, 0xFE, 0x67, 0x0C, 0x9A, 0x4B, 0xF2, 0x34, 0xB9, 0xEA, 0xE9, 0x37, 0xDA, 0x30, 0xE2, 0x32, 0x27, 0xF3, 0x88, 0x5F, 0x2A, 0x02, 0x1D, 0x49, 0x5D, 0x04, 0xED, 0x61, 0x95, 0x37, 0xDD, 0x95, 0xB1, 0x4F, 0x64, 0x0E, 0x1E, 0xFB, 0x47, 0x9F, 0xA7, 0xD7, 0xE0, 0x7A, 0xB1, 0x02, 0x81, 0x95, 0xD1, 0xA5, 0x7E, 0xB2, 0x74, 0x8F, 0x03, 0x26, 0xA5, 0xEC, 0xE9, 0x71, 0x46, 0x37, 0xAC, 0x3D, 0x74, 0x84, 0x26, 0xCB, 0x7C, 0xE8, 0xFE, 0x4E, 0xB0, 0x6D, 0x70, 0x3D, 0x00, 0x10, 0x1A, 0x3A, 0x5B, 0xB8, 0xAA, 0x29, 0x59, 0x93, 0x15, 0x03, 0xE1, 0xA5, 0x39, 0x44, 0x75, 0x16, 0x28, 0x5F, 0xBA, 0x69, 0xA2, 0x4A, 0x2A, 0xC3, 0x5B, 0x63, 0x1F, 0x40, 0x10, 0x36, 0xF9, 0x4C, 0xD2, 0x76, 0x0F, 0xCF, 0x7F, 0x50, 0x30, 0x6E, 0x2B, 0x1D }; +static const unsigned char prove_vector_0_n_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F } }; +static secp256k1_scalar prove_vector_0_n_vec[1]; +static const unsigned char prove_vector_0_l_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E } }; +static secp256k1_scalar prove_vector_0_l_vec[1]; +static const unsigned char prove_vector_0_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_0_c_vec[1]; +static const unsigned char prove_vector_0_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char prove_vector_0_proof[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int prove_vector_0_result = 1; +static const unsigned char prove_vector_1_n_vec32[2][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }; +static secp256k1_scalar prove_vector_1_n_vec[2]; +static const unsigned char prove_vector_1_l_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B } }; +static secp256k1_scalar prove_vector_1_l_vec[4]; +static const unsigned char prove_vector_1_c_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x30 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D } }; +static secp256k1_scalar prove_vector_1_c_vec[4]; +static const unsigned char prove_vector_1_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const unsigned char prove_vector_1_proof[] = { 0x00, 0xD2, 0xEC, 0xE2, 0x53, 0x97, 0x28, 0x68, 0x22, 0x59, 0x34, 0xEF, 0xE4, 0x7B, 0x87, 0x4D, 0xE9, 0x57, 0xD5, 0xB7, 0xC7, 0x72, 0xF4, 0xC9, 0xEA, 0x66, 0x14, 0x59, 0xE1, 0xA9, 0xD5, 0xB2, 0x10, 0xDF, 0xE2, 0xFF, 0xF5, 0xA4, 0x38, 0x6B, 0xFE, 0x36, 0x89, 0xE4, 0x9D, 0x90, 0x9F, 0x71, 0x19, 0xE6, 0xA3, 0x1E, 0xAA, 0xAA, 0x4E, 0xFE, 0xC2, 0xD3, 0x37, 0xBB, 0xDE, 0xDB, 0x46, 0x43, 0xC2, 0x01, 0x42, 0x5F, 0xFC, 0xC6, 0x25, 0xA0, 0xB4, 0xF0, 0x76, 0x99, 0xF4, 0x7C, 0xE9, 0x83, 0x82, 0xED, 0x7C, 0x95, 0xBA, 0xD0, 0xE6, 0x5B, 0x88, 0xFD, 0x38, 0xEA, 0x23, 0x54, 0xD4, 0xBD, 0xD4, 0x37, 0xB8, 0x2B, 0x49, 0xAF, 0x81, 0xFD, 0xBE, 0x88, 0xB2, 0xE5, 0x3F, 0xF4, 0x30, 0x52, 0x00, 0x63, 0x9D, 0xAE, 0x82, 0x44, 0xE9, 0x62, 0x87, 0x2A, 0x23, 0x89, 0x10, 0xE4, 0x9A, 0x64, 0x9F, 0x71, 0xD9, 0x32, 0x57, 0x3B, 0xCB, 0xAC, 0x30, 0xAE, 0x71, 0x61, 0xE9, 0x50, 0x1F, 0xCB, 0x49, 0x9C, 0x52, 0xBA, 0x0C, 0xC4, 0x00, 0x58, 0x73, 0x63, 0xD3, 0x42, 0xDE, 0x42, 0x5E, 0xC5, 0x97, 0xE5, 0xDA, 0x88, 0x76, 0x49, 0x6C, 0x8B, 0x92, 0x99, 0xEE, 0xD0, 0xA9, 0xEB, 0x6E, 0xCA, 0xE1, 0x93, 0x81, 0x56, 0x2E, 0xCA, 0xF3, 0x8E, 0xF0, 0x04, 0xD2, 0x96, 0xD8, 0xDB, 0xEE, 0xEE, 0x1C, 0x44 }; +static const int prove_vector_1_result = 1; +static const unsigned char prove_vector_2_n_vec32[4][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3F }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B } }; +static secp256k1_scalar prove_vector_2_n_vec[4]; +static const unsigned char prove_vector_2_l_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E } }; +static secp256k1_scalar prove_vector_2_l_vec[1]; +static const unsigned char prove_vector_2_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_2_c_vec[1]; +static const unsigned char prove_vector_2_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char prove_vector_2_proof[] = { 0x00, 0xBC, 0x4C, 0x42, 0x67, 0x71, 0x69, 0x52, 0x6A, 0x65, 0xFE, 0xA0, 0xCB, 0x3F, 0x58, 0x8B, 0x48, 0x48, 0x6E, 0x59, 0xFC, 0x55, 0x51, 0x10, 0xB9, 0xBF, 0x6A, 0x7D, 0xBF, 0x32, 0x34, 0x4E, 0x7D, 0xBA, 0xD5, 0xCB, 0xCC, 0x19, 0xED, 0xAA, 0x9F, 0x8D, 0x93, 0x26, 0x5E, 0x3F, 0x3E, 0xAA, 0xDF, 0x0B, 0x1C, 0xB3, 0xDC, 0x37, 0xB6, 0xDB, 0xAE, 0x43, 0x63, 0x92, 0xB5, 0xFF, 0x0D, 0x1C, 0x77, 0x02, 0x7E, 0x2B, 0xB8, 0x87, 0x85, 0x81, 0x13, 0x70, 0x1F, 0x03, 0x65, 0x7D, 0xD8, 0x91, 0x83, 0xE5, 0x7E, 0x8B, 0x9E, 0x6F, 0x1C, 0x08, 0x9C, 0x9C, 0x5F, 0xA4, 0x12, 0x5F, 0xD3, 0xEE, 0xE2, 0x74, 0x7A, 0x2C, 0x58, 0x3A, 0x29, 0x4F, 0x64, 0x10, 0xE7, 0x89, 0xBF, 0xB2, 0xE5, 0xD9, 0xD5, 0xC5, 0x62, 0x83, 0x0C, 0xA8, 0xDD, 0x1E, 0x24, 0x6D, 0xD1, 0x58, 0x8D, 0x80, 0x74, 0xF3, 0xD9, 0x3A, 0x68, 0x7B, 0xF5, 0x12, 0xC6, 0xC2, 0x3F, 0x71, 0x47, 0xDF, 0xCF, 0xC8, 0xE2, 0xC4, 0x59, 0xDF, 0x4F, 0xEC, 0x86, 0xE9, 0xF9, 0x31, 0x94, 0x6A, 0x5F, 0xD9, 0x1E, 0x6B, 0x09, 0xCD, 0xCF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3E }; +static const int prove_vector_2_result = 1; +static const unsigned char prove_vector_3_n_vec32[1][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static secp256k1_scalar prove_vector_3_n_vec[1]; +static const unsigned char prove_vector_3_l_vec32[1][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static secp256k1_scalar prove_vector_3_l_vec[1]; +static const unsigned char prove_vector_3_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_3_c_vec[1]; +static const unsigned char prove_vector_3_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char prove_vector_3_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const int prove_vector_3_result = 1; +static const unsigned char prove_vector_4_n_vec32[2][32] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static secp256k1_scalar prove_vector_4_n_vec[2]; +static const unsigned char prove_vector_4_l_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A } }; +static secp256k1_scalar prove_vector_4_l_vec[1]; +static const unsigned char prove_vector_4_c_vec32[1][32] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3C } }; +static secp256k1_scalar prove_vector_4_c_vec[1]; +static const unsigned char prove_vector_4_r32[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x34 }; +static const unsigned char prove_vector_4_proof[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x3A }; +static const int prove_vector_4_result = 1; + diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index dbacace7..ff7ce209 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -14,6 +14,7 @@ #include "bppp_util.h" #include "bppp_transcript_impl.h" #include "test_vectors/verify.h" +#include "test_vectors/prove.h" static void test_bppp_generators_api(void) { /* The BP generator API requires no precomp */ @@ -660,6 +661,66 @@ void norm_arg_verify_vectors(void) { } #undef IDX_TO_TEST +void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, const unsigned char n_vec32[][32], secp256k1_scalar *n_vec, size_t n_vec_len, const unsigned char l_vec32[][32], secp256k1_scalar *l_vec, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, int result) { + secp256k1_sha256 transcript; + secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); + secp256k1_scalar rho; + unsigned char myproof[1024]; + size_t myplen = sizeof(myproof); + int overflow; + int i; + + CHECK(gs != NULL); + secp256k1_sha256_initialize(&transcript); + + secp256k1_scalar_set_b32(&rho, r32, &overflow); + CHECK(!overflow); + + for (i = 0; i < (int)n_vec_len; i++) { + secp256k1_scalar_set_b32(&n_vec[i], n_vec32[i], &overflow); + CHECK(!overflow); + } + + for (i = 0; i < (int)c_vec_len; i++) { + secp256k1_scalar_set_b32(&l_vec[i], l_vec32[i], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&c_vec[i], c_vec32[i], &overflow); + CHECK(!overflow); + } + + CHECK(secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, myproof, &myplen, &transcript, &rho, gs->gens, gs->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len) == result); + if (!result) { + secp256k1_bppp_generators_destroy(ctx, gs); + return; + } + CHECK(plen == myplen); + CHECK(secp256k1_memcmp_var(proof, myproof, plen) == 0); + secp256k1_bppp_generators_destroy(ctx, gs); +} + + +#define IDX_TO_TEST(i) (norm_arg_prove_vectors_helper(scratch, prove_vector_gens, prove_vector_##i##_proof, sizeof(prove_vector_##i##_proof), prove_vector_##i##_r32,\ + prove_vector_##i##_n_vec32, prove_vector_##i##_n_vec, sizeof(prove_vector_##i##_n_vec)/sizeof(secp256k1_scalar),\ + prove_vector_##i##_l_vec32, prove_vector_##i##_l_vec,\ + prove_vector_##i##_c_vec32, prove_vector_##i##_c_vec, sizeof(prove_vector_##i##_c_vec)/sizeof(secp256k1_scalar), \ + prove_vector_##i##_result)) + +void norm_arg_prove_vectors(void) { + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*1000); /* shouldn't need much */ + size_t alloc = scratch->alloc_size; + + IDX_TO_TEST(0); + IDX_TO_TEST(1); + IDX_TO_TEST(2); + IDX_TO_TEST(3); + IDX_TO_TEST(4); + + CHECK(alloc == scratch->alloc_size); + secp256k1_scratch_space_destroy(ctx, scratch); +} + +#undef IDX_TO_TEST + void run_bppp_tests(void) { test_log_exp(); test_norm_util_helpers(); @@ -677,7 +738,9 @@ void run_bppp_tests(void) { norm_arg_test(32, 64); norm_arg_test(64, 32); norm_arg_test(64, 64); + norm_arg_verify_vectors(); + norm_arg_prove_vectors(); } #endif From 847ed9ecb2233f1e233fae1791b5adcdeb3be52b Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 25 Apr 2023 14:35:12 +0000 Subject: [PATCH 267/381] norm arg: add verification to prove vectors --- src/modules/bppp/tests_impl.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index ff7ce209..acb16009 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -664,7 +664,8 @@ void norm_arg_verify_vectors(void) { void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsigned char *gens, const unsigned char *proof, size_t plen, const unsigned char *r32, const unsigned char n_vec32[][32], secp256k1_scalar *n_vec, size_t n_vec_len, const unsigned char l_vec32[][32], secp256k1_scalar *l_vec, const unsigned char c_vec32[][32], secp256k1_scalar *c_vec, size_t c_vec_len, int result) { secp256k1_sha256 transcript; secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); - secp256k1_scalar rho; + secp256k1_scalar rho, mu; + secp256k1_ge commit; unsigned char myproof[1024]; size_t myplen = sizeof(myproof); int overflow; @@ -672,15 +673,14 @@ void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsigned ch CHECK(gs != NULL); secp256k1_sha256_initialize(&transcript); - secp256k1_scalar_set_b32(&rho, r32, &overflow); CHECK(!overflow); + secp256k1_scalar_sqr(&mu, &rho); for (i = 0; i < (int)n_vec_len; i++) { secp256k1_scalar_set_b32(&n_vec[i], n_vec32[i], &overflow); CHECK(!overflow); } - for (i = 0; i < (int)c_vec_len; i++) { secp256k1_scalar_set_b32(&l_vec[i], l_vec32[i], &overflow); CHECK(!overflow); @@ -688,13 +688,17 @@ void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsigned ch CHECK(!overflow); } - CHECK(secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, myproof, &myplen, &transcript, &rho, gs->gens, gs->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len) == result); + CHECK(secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, myproof, &myplen, &transcript, &rho, gs->gens, gs->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len) == result); if (!result) { secp256k1_bppp_generators_destroy(ctx, gs); return; } CHECK(plen == myplen); CHECK(secp256k1_memcmp_var(proof, myproof, plen) == 0); + + CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + secp256k1_sha256_initialize(&transcript); + CHECK(secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); secp256k1_bppp_generators_destroy(ctx, gs); } From f3126fdfec7c4dbfab3acf01714325b027110aff Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 25 Apr 2023 14:52:42 +0000 Subject: [PATCH 268/381] norm arg: remove prove edge tests which are now covered by vectors --- src/modules/bppp/tests_impl.h | 55 ----------------------------------- 1 file changed, 55 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index acb16009..3d7fc4c5 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -453,60 +453,6 @@ static int secp256k1_norm_arg_verify( return res; } -void norm_arg_prove_edge(void) { - secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; - secp256k1_scalar rho, mu; - secp256k1_ge commit; - size_t i; - secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 1000*10); /* shouldn't need much */ - unsigned char proof[1000]; - secp256k1_sha256 transcript; - - random_scalar_order(&rho); - secp256k1_scalar_sqr(&mu, &rho); - - /* l is zero vector and n is zero vectors of length 1 each. */ - { - size_t plen = sizeof(proof); - unsigned int n_vec_len = 1; - unsigned int c_vec_len = 1; - secp256k1_bppp_generators *gens = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); - - secp256k1_scalar_set_int(&n_vec[0], 0); - secp256k1_scalar_set_int(&l_vec[0], 0); - random_scalar_order(&c_vec[0]); - - secp256k1_sha256_initialize(&transcript); /* No challenges used in n = 1, l = 1, but we set transcript as a good practice*/ - CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); - CHECK(secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, proof, &plen, &transcript, &rho, gens->gens, gens->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len)); - secp256k1_sha256_initialize(&transcript); - CHECK(secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, plen, &transcript, &rho, gens, c_vec_len, c_vec, c_vec_len, &commit)); - - secp256k1_bppp_generators_destroy(ctx, gens); - } - - /* l is the zero vector and longer than n. This results in one of the - * internal commitments X or R to be the point at infinity. */ - { - unsigned int n_vec_len = 1; - unsigned int c_vec_len = 2; - secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(ctx, n_vec_len + c_vec_len); - size_t plen = sizeof(proof); - for (i = 0; i < n_vec_len; i++) { - random_scalar_order(&n_vec[i]); - } - for (i = 0; i < c_vec_len; i++) { - secp256k1_scalar_set_int(&l_vec[i], 0); - random_scalar_order(&c_vec[i]); - } - CHECK(secp256k1_bppp_commit(ctx, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); - CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); - secp256k1_sha256_initialize(&transcript); - CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); - secp256k1_bppp_generators_destroy(ctx, gs); - } -} - /* Verify |c| = 0 */ void norm_arg_verify_zero_len(void) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; @@ -733,7 +679,6 @@ void run_bppp_tests(void) { test_bppp_generators_fixed(); test_bppp_tagged_hash(); - norm_arg_prove_edge(); norm_arg_verify_zero_len(); norm_arg_test(1, 1); norm_arg_test(1, 64); From f50ad760049bb86e81e288456e01fc38ea289776 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 11 May 2023 17:20:10 +0000 Subject: [PATCH 269/381] musig: update version number of BIP --- include/secp256k1_musig.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 38b8c0b8..dadef182 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -9,9 +9,11 @@ extern "C" { #include -/** This module implements BIP MuSig2 v1.0.0-rc.3, a multi-signature scheme - * compatible with BIP-340 ("Schnorr"). You can find an example demonstrating - * the musig module in examples/musig.c. +/** This module implements BIP 327 "MuSig2 for BIP340-compatible + * Multi-Signatures" + * (https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki) + * v1.0.0. You can find an example demonstrating the musig module in + * examples/musig.c. * * The module also supports BIP-341 ("Taproot") public key tweaking and adaptor * signatures as described in From 4ab4ec38a04c8cc820294704bb5d8958bc222787 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 11 May 2023 17:22:27 +0000 Subject: [PATCH 270/381] musig: add note about missing verification to partial_sign to doc --- include/secp256k1_musig.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index dadef182..fdf60e51 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -442,6 +442,11 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process( * created by calling musig_nonce_gen with that pubkey. Otherwise, the * illegal_callback is called. * + * This function does not verify the output partial signature, deviating from + * the BIP 327 specification. It is recommended to verify the output partial + * signature with `secp256k1_musig_partial_sig_verify` to prevent random or + * adversarially provoked computation errors. + * * 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 From 3e9428996698257aa2a3b4e974f574b8479f1261 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 10 May 2023 15:19:38 +0200 Subject: [PATCH 271/381] ct: Use volatile trick in scalar_cond_negate --- src/scalar_4x64_impl.h | 3 ++- src/scalar_8x32_impl.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 60aca8c1..a48d58c2 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -180,7 +180,8 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { /* If we are flag = 0, mask = 00...00 and this is a no-op; * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint64_t mask = !flag - 1; + volatile int vflag = flag; + uint64_t mask = -vflag; uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); r->d[0] = t & nonzero; t >>= 64; diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index ad025cff..d960a9bd 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -253,7 +253,8 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { /* If we are flag = 0, mask = 00...00 and this is a no-op; * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint32_t mask = !flag - 1; + volatile int vflag = flag; + uint32_t mask = -vflag; uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); r->d[0] = t & nonzero; t >>= 32; From c8c0f55a1132c0fc9a726f0a4a1417288163b904 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 10 May 2023 16:25:37 +0200 Subject: [PATCH 272/381] ct: Be cautious and use volatile trick in more "conditional" paths - secp256k1_scalar_cadd_bit - secp256k1_modinvXX_normalize_YY - secp256k1_modinvXX_divsteps_ZZ - ECMULT_CONST_TABLE_GET_GE Even though those code loations are not problematic right now (with current compilers). --- src/ecmult_const_impl.h | 2 +- src/modinv32_impl.h | 33 ++++++++++++++++++--------------- src/modinv64_impl.h | 31 +++++++++++++++++-------------- src/scalar_4x64_impl.h | 3 ++- src/scalar_8x32_impl.h | 3 ++- 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h index 12dbcc6c..c92b2a04 100644 --- a/src/ecmult_const_impl.h +++ b/src/ecmult_const_impl.h @@ -29,7 +29,7 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p #define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ int m = 0; \ /* Extract the sign-bit for a constant time absolute-value. */ \ - int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \ + int volatile mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \ int abs_n = ((n) + mask) ^ mask; \ int idx_n = abs_n >> 1; \ secp256k1_fe neg_y; \ diff --git a/src/modinv32_impl.h b/src/modinv32_impl.h index 661c5fc0..fc16eaaa 100644 --- a/src/modinv32_impl.h +++ b/src/modinv32_impl.h @@ -64,7 +64,7 @@ static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int3 const int32_t M30 = (int32_t)(UINT32_MAX >> 2); int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4], r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8]; - int32_t cond_add, cond_negate; + volatile int32_t cond_add, cond_negate; #ifdef VERIFY /* Verify that all limbs are in range (-2^30,2^30). */ @@ -186,7 +186,8 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ * being inside [-2^31,2^31) means that casting to signed works correctly. */ uint32_t u = 1, v = 0, q = 0, r = 1; - uint32_t c1, c2, f = f0, g = g0, x, y, z; + volatile uint32_t c1, c2; + uint32_t mask1, mask2, f = f0, g = g0, x, y, z; int i; for (i = 0; i < 30; ++i) { @@ -195,23 +196,25 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ VERIFY_CHECK((q * f0 + r * g0) == g << i); /* Compute conditional masks for (zeta < 0) and for (g & 1). */ c1 = zeta >> 31; - c2 = -(g & 1); + mask1 = c1; + c2 = g & 1; + mask2 = -c2; /* Compute x,y,z, conditionally negated versions of f,u,v. */ - x = (f ^ c1) - c1; - y = (u ^ c1) - c1; - z = (v ^ c1) - c1; + x = (f ^ mask1) - mask1; + y = (u ^ mask1) - mask1; + z = (v ^ mask1) - mask1; /* Conditionally add x,y,z to g,q,r. */ - g += x & c2; - q += y & c2; - r += z & c2; - /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ - c1 &= c2; + g += x & mask2; + q += y & mask2; + r += z & mask2; + /* In what follows, mask1 is a condition mask for (zeta < 0) and (g & 1). */ + mask1 &= mask2; /* Conditionally change zeta into -zeta-2 or zeta-1. */ - zeta = (zeta ^ c1) - 1; + zeta = (zeta ^ mask1) - 1; /* Conditionally add g,q,r to f,u,v. */ - f += g & c1; - u += q & c1; - v += r & c1; + f += g & mask1; + u += q & mask1; + v += r & mask1; /* Shifts */ g >>= 1; u <<= 1; diff --git a/src/modinv64_impl.h b/src/modinv64_impl.h index 0743a9c8..905ef47b 100644 --- a/src/modinv64_impl.h +++ b/src/modinv64_impl.h @@ -69,7 +69,7 @@ static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, i static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) { const int64_t M62 = (int64_t)(UINT64_MAX >> 2); int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4]; - int64_t cond_add, cond_negate; + volatile int64_t cond_add, cond_negate; #ifdef VERIFY /* Verify that all limbs are in range (-2^62,2^62). */ @@ -165,7 +165,8 @@ static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_ * being inside [-2^63,2^63) means that casting to signed works correctly. */ uint64_t u = 8, v = 0, q = 0, r = 8; - uint64_t c1, c2, f = f0, g = g0, x, y, z; + volatile uint64_t c1, c2; + uint64_t mask1, mask2, f = f0, g = g0, x, y, z; int i; for (i = 3; i < 62; ++i) { @@ -174,23 +175,25 @@ static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_ VERIFY_CHECK((q * f0 + r * g0) == g << i); /* Compute conditional masks for (zeta < 0) and for (g & 1). */ c1 = zeta >> 63; - c2 = -(g & 1); + mask1 = c1; + c2 = g & 1; + mask2 = -c2; /* Compute x,y,z, conditionally negated versions of f,u,v. */ - x = (f ^ c1) - c1; - y = (u ^ c1) - c1; - z = (v ^ c1) - c1; + x = (f ^ mask1) - mask1; + y = (u ^ mask1) - mask1; + z = (v ^ mask1) - mask1; /* Conditionally add x,y,z to g,q,r. */ - g += x & c2; - q += y & c2; - r += z & c2; + g += x & mask2; + q += y & mask2; + r += z & mask2; /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ - c1 &= c2; + mask1 &= mask2; /* Conditionally change zeta into -zeta-2 or zeta-1. */ - zeta = (zeta ^ c1) - 1; + zeta = (zeta ^ mask1) - 1; /* Conditionally add g,q,r to f,u,v. */ - f += g & c1; - u += q & c1; - v += r & c1; + f += g & mask1; + u += q & mask1; + v += r & mask1; /* Shifts */ g >>= 1; u <<= 1; diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index a48d58c2..4403e8a8 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -110,8 +110,9 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { uint128_t t; + volatile int vflag = flag; VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + bit += ((uint32_t) vflag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index d960a9bd..b96e0335 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -153,8 +153,9 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { uint64_t t; + volatile int vflag = flag; VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + bit += ((uint32_t) vflag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); From 56a5d41429a4daed2b02b59c45022044c3575955 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 12 May 2023 05:15:05 -0400 Subject: [PATCH 273/381] Bugfix: mark outputs as early clobber in scalar x86_64 asm In the existing code, the compiler is allowed to allocate the RSI register for outputs m0, m1, or m2, which are written to before the input in RSI is read from. Fix this by marking them as early clobber. Reported by ehoffman2 in https://github.com/bitcoin-core/secp256k1/issues/766 --- src/scalar_4x64_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 4403e8a8..426c41f1 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -389,7 +389,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) "movq %%r10, %q5\n" /* extract m6 */ "movq %%r8, %q6\n" - : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "=&g"(m0), "=&g"(m1), "=&g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); From 39407c3f5999aa10e1470bc9eae8f63800a63e51 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 12 May 2023 05:17:11 -0400 Subject: [PATCH 274/381] Mark stack variables as early clobber for technical correctness In the field 5x52 asm for x86_64, stack variables are provided as outputs. The existing inputs are all forcibly allocated to registers, so cannot coincide, but mark them as early clobber anyway to make this clearer. --- src/field_5x52_asm_impl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field_5x52_asm_impl.h b/src/field_5x52_asm_impl.h index a2118044..51e35c96 100644 --- a/src/field_5x52_asm_impl.h +++ b/src/field_5x52_asm_impl.h @@ -278,7 +278,7 @@ __asm__ __volatile__( "addq %%rsi,%%r8\n" /* r[4] = c */ "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "+S"(a), "=&m"(tmp1), "=&m"(tmp2), "=&m"(tmp3) : "b"(b), "D"(r) : "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" ); @@ -493,7 +493,7 @@ __asm__ __volatile__( "addq %%rsi,%%r8\n" /* r[4] = c */ "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "+S"(a), "=&m"(tmp1), "=&m"(tmp2), "=&m"(tmp3) : "D"(r) : "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" ); From 05b207e969f9b4181061dd3fba749b6df06de718 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 17 Jul 2023 13:29:59 +0000 Subject: [PATCH 275/381] sync-upstream: allows providing the local branch via cli --- contrib/sync-upstream.sh | 43 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index c64acbbd..1489227d 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -3,12 +3,13 @@ set -eou pipefail help() { - echo "$0 range [end]" - echo " merges every merge commit present in upstream and missing locally." + echo "$0 [-b ] range [end]" + echo " merges every merge commit present in upstream and missing in (default: master)." echo " If the optional [end] commit is provided, only merges up to [end]." + echo " If the optional [-b branch] provided, then ." echo - echo "$0 select ... " - echo " merges every selected merge commit" + echo "$0 [-b ] select ... " + echo " merges every selected merge commit into (default: master)" echo echo "This tool creates a branch and a script that can be executed to create the" echo "PR automatically. The script requires the github-cli tool (aka gh)." @@ -17,12 +18,9 @@ help() { exit 1 } -if [ "$#" -lt 1 ]; then - help -fi - REMOTE=upstream REMOTE_BRANCH="$REMOTE/master" +LOCAL_BRANCH="master" # Makes sure you have a remote "upstream" that is up-to-date setup() { ret=0 @@ -41,7 +39,7 @@ setup() { } range() { - RANGESTART_COMMIT=$(git merge-base "$REMOTE_BRANCH" master) + RANGESTART_COMMIT=$(git merge-base "$REMOTE_BRANCH" "$LOCAL_BRANCH") RANGEEND_COMMIT=$(git rev-parse "$REMOTE_BRANCH") if [ "$#" = 1 ]; then RANGEEND_COMMIT=$1 @@ -57,18 +55,37 @@ range() { esac } +# Process -b argument +while getopts "b:" opt; do + case $opt in + b) + LOCAL_BRANCH=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + ;; + esac +done + +# Shift off the processed options +shift $((OPTIND -1)) + +if [ "$#" -lt 1 ]; then + help +fi + case $1 in range) shift setup range "$@" - REPRODUCE_COMMAND="$0 range $RANGEEND_COMMIT" + REPRODUCE_COMMAND="$0 range -b $LOCAL_BRANCH $RANGEEND_COMMIT" ;; select) shift setup COMMITS=$* - REPRODUCE_COMMAND="$0 select $@" + REPRODUCE_COMMAND="$0 select -b $LOCAL_BRANCH $@" ;; help) help @@ -96,7 +113,7 @@ echo "-----------------------------------" echo "$BODY" echo "-----------------------------------" # Create branch from PR commit and create PR -git checkout master +git checkout "$LOCAL_BRANCH" git pull --autostash git checkout -b temp-merge-"$PRNUM" @@ -115,7 +132,7 @@ cat < "$FNAME" #!/bin/sh gh pr create -t '$TITLE' -b '$BODY' --web # Remove temporary branch -git checkout master +git checkout "$LOCAL_BRANCH" git branch -D temp-merge-"$PRNUM" EOT chmod +x "$FNAME" From 9b6a1c384d00cc61bf9160e5df858ca6c52e6af3 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 17 Jul 2023 18:42:25 +0200 Subject: [PATCH 276/381] sync-upstream.sh: Fix position of "-b" option in reproduce command --- contrib/sync-upstream.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 1489227d..1f021bbb 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -79,13 +79,13 @@ case $1 in shift setup range "$@" - REPRODUCE_COMMAND="$0 range -b $LOCAL_BRANCH $RANGEEND_COMMIT" + REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH range $RANGEEND_COMMIT" ;; select) shift setup COMMITS=$* - REPRODUCE_COMMAND="$0 select -b $LOCAL_BRANCH $@" + REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH select $@" ;; help) help From 0a9915687191cabba0241bd45c3a3416b7f36e29 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 18 Jul 2023 15:05:27 +0200 Subject: [PATCH 277/381] sync-upstream.sh: Add "git show --remerge-diff" tip --- contrib/sync-upstream.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 1f021bbb..b910a599 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -104,8 +104,7 @@ do done # Remove trailing "," TITLE=${TITLE%?} - -BODY=$(printf "%s\n\n%s" "$BODY" "This PR can be recreated with \`$REPRODUCE_COMMAND\`.") +BODY=$(printf "%s\n\n%s\n%s" "$BODY" "This PR can be recreated with \`$REPRODUCE_COMMAND\`." "Tip: Use \`git show --remerge-diff\` to show the changes manually added to the merge commit.") echo "-----------------------------------" echo "$TITLE" From 7e9193666f840eaec024d1dad968361b79a14753 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 20 Jul 2023 23:02:38 +0200 Subject: [PATCH 278/381] ci: Always define EXPERIMENTAL variable --- .cirrus.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.cirrus.yml b/.cirrus.yml index 48c9be8c..31501f4d 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -14,6 +14,7 @@ env: WITH_VALGRIND: yes EXTRAFLAGS: ### secp256k1 modules + EXPERIMENTAL: no ECDH: no RECOVERY: no SCHNORRSIG: no From 3372993060bfe914d828e13499fcd2763ef83dd5 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 20 Jul 2023 15:40:12 +0200 Subject: [PATCH 279/381] bppp: Fix test for invalid sign byte The test is supposed to create an invalid sign byte. Before this PR, the generated sign byte could in fact be valid due to an overflow. Co-authored-by: Jonas Nick --- src/modules/bppp/tests_impl.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index eddb5240..694435ef 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -257,7 +257,11 @@ void test_serialize_two_points(void) { random_group_element_test(&X); random_group_element_test(&R); secp256k1_bppp_serialize_points(buf, &X, &R); - buf[0] |= 4 + (unsigned char)secp256k1_testrandi64(4, 255); + + buf[0] = 4 + (unsigned char)secp256k1_testrandi64(0, 253); + /* Assert that buf[0] is actually invalid. */ + CHECK(buf[0] != 0x02 && buf[0] != 0x03); + CHECK(!secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 0)); } From 3970a7292aedc64c13983d3e48d7485fce44f928 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Jul 2023 13:05:25 +0200 Subject: [PATCH 280/381] rangeproof: Use util functions for writing big endian --- src/modules/rangeproof/borromean_impl.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index fb5e44db..3a3b74e2 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -20,24 +20,18 @@ #include #include -#if defined(SECP256K1_BIG_ENDIAN) -#define BE32(x) (x) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#endif - SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, size_t ridx, size_t eidx) { - uint32_t ring; - uint32_t epos; + unsigned char ring[4]; + unsigned char epos[4]; secp256k1_sha256 sha256_en; secp256k1_sha256_initialize(&sha256_en); - ring = BE32((uint32_t)ridx); - epos = BE32((uint32_t)eidx); + secp256k1_write_be32(ring, (uint32_t)ridx); + secp256k1_write_be32(epos, (uint32_t)eidx); secp256k1_sha256_write(&sha256_en, e, elen); secp256k1_sha256_write(&sha256_en, m, mlen); - secp256k1_sha256_write(&sha256_en, (unsigned char*)&ring, 4); - secp256k1_sha256_write(&sha256_en, (unsigned char*)&epos, 4); + secp256k1_sha256_write(&sha256_en, ring, 4); + secp256k1_sha256_write(&sha256_en, epos, 4); secp256k1_sha256_finalize(&sha256_en, hash); } From 860360eed448b846ef0212499f5507a996488d16 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 19 Jul 2023 11:43:08 +0200 Subject: [PATCH 281/381] scalar: Remove unused secp256k1_scalar_chacha20 Unused since a11250330b24b3dffdf11d2de5d496397b4e4410. --- src/scalar.h | 3 -- src/scalar_4x64_impl.h | 87 -------------------------------- src/scalar_8x32_impl.h | 95 ----------------------------------- src/scalar_low_impl.h | 5 -- src/tests.c | 110 ----------------------------------------- src/util.h | 25 ---------- 6 files changed, 325 deletions(-) diff --git a/src/scalar.h b/src/scalar.h index 227913cb..42fdaf6e 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -108,7 +108,4 @@ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_ /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); -/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); - #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 426c41f1..9c403e7a 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -970,93 +970,6 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); } -#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) -#define QUARTERROUND(a,b,c,d) \ - a += b; d = ROTL32(d ^ a, 16); \ - c += d; b = ROTL32(b ^ c, 12); \ - a += b; d = ROTL32(d ^ a, 8); \ - c += d; b = ROTL32(b ^ c, 7); - -#if defined(SECP256K1_BIG_ENDIAN) -#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define LE32(p) (p) -#endif - -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { - size_t n; - size_t over_count = 0; - uint32_t seed32[8]; - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int over1, over2; - - memcpy((void *) seed32, (const void *) seed, 32); - do { - x0 = 0x61707865; - x1 = 0x3320646e; - x2 = 0x79622d32; - x3 = 0x6b206574; - x4 = LE32(seed32[0]); - x5 = LE32(seed32[1]); - x6 = LE32(seed32[2]); - x7 = LE32(seed32[3]); - x8 = LE32(seed32[4]); - x9 = LE32(seed32[5]); - x10 = LE32(seed32[6]); - x11 = LE32(seed32[7]); - x12 = idx; - x13 = idx >> 32; - x14 = 0; - x15 = over_count; - - n = 10; - while (n--) { - QUARTERROUND(x0, x4, x8,x12) - QUARTERROUND(x1, x5, x9,x13) - QUARTERROUND(x2, x6,x10,x14) - QUARTERROUND(x3, x7,x11,x15) - QUARTERROUND(x0, x5,x10,x15) - QUARTERROUND(x1, x6,x11,x12) - QUARTERROUND(x2, x7, x8,x13) - QUARTERROUND(x3, x4, x9,x14) - } - - x0 += 0x61707865; - x1 += 0x3320646e; - x2 += 0x79622d32; - x3 += 0x6b206574; - x4 += LE32(seed32[0]); - x5 += LE32(seed32[1]); - x6 += LE32(seed32[2]); - x7 += LE32(seed32[3]); - x8 += LE32(seed32[4]); - x9 += LE32(seed32[5]); - x10 += LE32(seed32[6]); - x11 += LE32(seed32[7]); - x12 += idx; - x13 += idx >> 32; - x14 += 0; - x15 += over_count; - - r1->d[3] = (((uint64_t) x0) << 32) | x1; - r1->d[2] = (((uint64_t) x2) << 32) | x3; - r1->d[1] = (((uint64_t) x4) << 32) | x5; - r1->d[0] = (((uint64_t) x6) << 32) | x7; - r2->d[3] = (((uint64_t) x8) << 32) | x9; - r2->d[2] = (((uint64_t) x10) << 32) | x11; - r2->d[1] = (((uint64_t) x12) << 32) | x13; - r2->d[0] = (((uint64_t) x14) << 32) | x15; - - over1 = secp256k1_scalar_check_overflow(r1); - over2 = secp256k1_scalar_check_overflow(r2); - over_count++; - } while (over1 | over2); -} - -#undef ROTL32 -#undef QUARTERROUND -#undef LE32 - static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) { const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index b96e0335..1cb390f6 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -749,101 +749,6 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); } -#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) -#define QUARTERROUND(a,b,c,d) \ - a += b; d = ROTL32(d ^ a, 16); \ - c += d; b = ROTL32(b ^ c, 12); \ - a += b; d = ROTL32(d ^ a, 8); \ - c += d; b = ROTL32(b ^ c, 7); - -#if defined(SECP256K1_BIG_ENDIAN) -#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define LE32(p) (p) -#endif - -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { - size_t n; - size_t over_count = 0; - uint32_t seed32[8]; - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int over1, over2; - - memcpy((void *) seed32, (const void *) seed, 32); - do { - x0 = 0x61707865; - x1 = 0x3320646e; - x2 = 0x79622d32; - x3 = 0x6b206574; - x4 = LE32(seed32[0]); - x5 = LE32(seed32[1]); - x6 = LE32(seed32[2]); - x7 = LE32(seed32[3]); - x8 = LE32(seed32[4]); - x9 = LE32(seed32[5]); - x10 = LE32(seed32[6]); - x11 = LE32(seed32[7]); - x12 = idx; - x13 = idx >> 32; - x14 = 0; - x15 = over_count; - - n = 10; - while (n--) { - QUARTERROUND(x0, x4, x8,x12) - QUARTERROUND(x1, x5, x9,x13) - QUARTERROUND(x2, x6,x10,x14) - QUARTERROUND(x3, x7,x11,x15) - QUARTERROUND(x0, x5,x10,x15) - QUARTERROUND(x1, x6,x11,x12) - QUARTERROUND(x2, x7, x8,x13) - QUARTERROUND(x3, x4, x9,x14) - } - - x0 += 0x61707865; - x1 += 0x3320646e; - x2 += 0x79622d32; - x3 += 0x6b206574; - x4 += LE32(seed32[0]); - x5 += LE32(seed32[1]); - x6 += LE32(seed32[2]); - x7 += LE32(seed32[3]); - x8 += LE32(seed32[4]); - x9 += LE32(seed32[5]); - x10 += LE32(seed32[6]); - x11 += LE32(seed32[7]); - x12 += idx; - x13 += idx >> 32; - x14 += 0; - x15 += over_count; - - r1->d[7] = x0; - r1->d[6] = x1; - r1->d[5] = x2; - r1->d[4] = x3; - r1->d[3] = x4; - r1->d[2] = x5; - r1->d[1] = x6; - r1->d[0] = x7; - r2->d[7] = x8; - r2->d[6] = x9; - r2->d[5] = x10; - r2->d[4] = x11; - r2->d[3] = x12; - r2->d[2] = x13; - r2->d[1] = x14; - r2->d[0] = x15; - - over1 = secp256k1_scalar_check_overflow(r1); - over2 = secp256k1_scalar_check_overflow(r2); - over_count++; - } while (over1 | over2); -} - -#undef ROTL32 -#undef QUARTERROUND -#undef LE32 - static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) { const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 4005cc8c..e98ec31f 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -127,11 +127,6 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se *r = (*r & mask0) | (*a & mask1); } -SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { - *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; - *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; -} - static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { int i; *r = 0; diff --git a/src/tests.c b/src/tests.c index 9e7efe3a..d9d8cae7 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1949,114 +1949,6 @@ void run_scalar_set_b32_seckey_tests(void) { CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } -void scalar_chacha_tests(void) { - /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A - * Note that scalar_set_b32 and scalar_get_b32 represent integers - * underlying the scalar in big-endian format. */ - unsigned char expected1[64] = { - 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, - 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, - 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, - 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, - 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, - 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, - 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, - 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 - }; - unsigned char expected2[64] = { - 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, - 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, - 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, - 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, - 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, - 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, - 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, - 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b - }; - unsigned char seed3[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - unsigned char expected3[64] = { - 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, - 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, - 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, - 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, - 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, - 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, - 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, - 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 - }; - unsigned char seed4[32] = { - 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - unsigned char expected4[64] = { - 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, - 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, - 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, - 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, - 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, - 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, - 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, - 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e - }; - unsigned char seed5[32] = { - 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, - 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, - 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, - 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 - }; - unsigned char expected5[64] = { - 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, - 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, - 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, - 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, - 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, - 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, - 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, - 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 - }; - - secp256k1_scalar exp_r1, exp_r2; - secp256k1_scalar r1, r2; - unsigned char seed0[32] = { 0 }; - - secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); - secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); - secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); - secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); - secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); - secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); -} - void run_scalar_tests(void) { int i; for (i = 0; i < 128 * count; i++) { @@ -2066,8 +1958,6 @@ void run_scalar_tests(void) { run_scalar_set_b32_seckey_tests(); } - scalar_chacha_tests(); - { /* (-1)+1 should be zero. */ secp256k1_scalar s, o; diff --git a/src/util.h b/src/util.h index fa65aade..c02dac15 100644 --- a/src/util.h +++ b/src/util.h @@ -199,31 +199,6 @@ SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { # define SECP256K1_GNUC_EXT #endif -/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */ -#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN) -/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */ -# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ - defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \ - defined(__i486__) || defined(__i586__) || defined(__i686__) || \ - defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \ - defined(__ARMEL__) || defined(__AARCH64EL__) || \ - (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \ - (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \ - defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */ -# define SECP256K1_LITTLE_ENDIAN -# endif -# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ - defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \ - defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \ - (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \ - (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1) -# define SECP256K1_BIG_ENDIAN -# endif -#endif -#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN) -# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h. -#endif - /* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { unsigned char *p = (unsigned char *)s; From ea478beec666dc38729f4927a005875f079fa914 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Jul 2023 18:10:55 +0000 Subject: [PATCH 282/381] musig: change test vector generation code shebang from python to python3 --- contrib/musig2-vectors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/musig2-vectors.py b/contrib/musig2-vectors.py index 8df3870f..97424419 100755 --- a/contrib/musig2-vectors.py +++ b/contrib/musig2-vectors.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import json From e593ed568572e49b668555e98db1d426952923d5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 25 Jul 2023 07:28:33 +0000 Subject: [PATCH 283/381] musig: ensure point_load output is normalized This is similar to the upstream commit "Normalize ge produced from secp256k1_pubkey_load". --- src/modules/musig/keyagg_impl.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 0419a151..aeb942ac 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -41,8 +41,10 @@ static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data) { } else { /* Otherwise, fall back to 32-byte big endian for X and Y. */ secp256k1_fe x, y; - secp256k1_fe_set_b32_mod(&x, data); - secp256k1_fe_set_b32_mod(&y, data + 32); + int ret = 1; + ret &= secp256k1_fe_set_b32_limit(&x, data); + ret &= secp256k1_fe_set_b32_limit(&y, data + 32); + VERIFY_CHECK(ret); secp256k1_ge_set_xy(ge, &x, &y); } } From b160486766653015e05f94b6c8742d76850e2556 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 27 Jul 2023 08:14:04 +0000 Subject: [PATCH 284/381] ecdsa_adaptor: add missing include --- include/secp256k1_ecdsa_adaptor.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index d887dab4..8b5baad0 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -5,6 +5,8 @@ extern "C" { #endif +#include "secp256k1.h" + /** This module implements single signer ECDSA adaptor signatures following * "One-Time Verifiably Encrypted Signatures A.K.A. Adaptor Signatures" by * Lloyd Fournier From 579999b4252083afc8fb59fbec9e027ca7691194 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 27 Jul 2023 10:14:06 +0000 Subject: [PATCH 285/381] scalar: adjust muladd2 to new int128 interface --- src/scalar_4x64_impl.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index d43fcc01..9d02e70f 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -254,9 +254,10 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { #define muladd2(a,b) { \ uint64_t tl, th, th2, tl2; \ { \ - uint128_t t = (uint128_t)a * b; \ - th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ - tl = t; \ + secp256k1_uint128 t; \ + secp256k1_u128_mul(&t, a, b); \ + th = secp256k1_u128_hi_u64(&t); /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = secp256k1_u128_to_u64(&t); \ } \ th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ From 4c70cc9bf56ab36f20cba5695d4f728a84779f91 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 27 Jul 2023 16:12:47 +0200 Subject: [PATCH 286/381] Suppress wrong/buggy warning in MSVC <19.33 For background, see: https://developercommunity.visualstudio.com/t/c-compiler-incorrect-propagation-of-const-qualifie/390711 --- src/modules/extrakeys/main_impl.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 4fb18771..7a7015e1 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -328,7 +328,19 @@ int secp256k1_pubkey_sort(const secp256k1_context* ctx, const secp256k1_pubkey * ARG_CHECK(pubkeys != NULL); cmp_data.ctx = ctx; + + /* Suppress wrong warning (fixed in MSVC 19.33) */ + #if defined(_MSC_VER) && (_MSC_VER < 1933) + #pragma warning(push) + #pragma warning(disable: 4090) + #endif + secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_pubkey_sort_cmp, &cmp_data); + + #if defined(_MSC_VER) && (_MSC_VER < 1933) + #pragma warning(pop) + #endif + return 1; } From 525b661f83554281707182dc0756f26cca325915 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 27 Jul 2023 17:36:21 +0200 Subject: [PATCH 287/381] bppp/build: Fix linkage of benchmark --- src/modules/bppp/Makefile.am.include | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/bppp/Makefile.am.include b/src/modules/bppp/Makefile.am.include index 13e8ea03..de90e06e 100644 --- a/src/modules/bppp/Makefile.am.include +++ b/src/modules/bppp/Makefile.am.include @@ -8,6 +8,6 @@ noinst_HEADERS += src/modules/bppp/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_bppp bench_bppp_SOURCES = src/bench_bppp.c -bench_bppp_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_bppp_LDFLAGS = -static +bench_bppp_LDADD = libsecp256k1.la +bench_bppp_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif From 9e96a2e9d80d66ac2ef1a73c33ac9d1647403248 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 28 Jul 2023 10:52:25 +0200 Subject: [PATCH 288/381] hsort tests: Don't call secp256k1_testrand_int(0) --- src/modules/extrakeys/tests_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index bd7d4e7d..ff37e02e 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -580,7 +580,7 @@ static void test_hsort(void) { * [-interval/2, interval/2] */ for (i = 0; i < COUNT; i++) { int n = secp256k1_testrand_int(NUM); - int interval = secp256k1_testrand_int(64); + int interval = secp256k1_testrand_int(63) + 1; for (j = 0; j < n; j++) { ints[j] = secp256k1_testrand_int(interval) - interval/2; } From 54b37db953f9feebae89e43f618c4859cd36acdb Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 28 Jul 2023 11:28:58 +0200 Subject: [PATCH 289/381] build: Fix linkage of extra binaries in -zkp modules --- Makefile.am | 2 +- src/modules/generator/Makefile.am.include | 4 ++-- src/modules/rangeproof/Makefile.am.include | 4 ++-- src/modules/whitelist/Makefile.am.include | 5 ++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Makefile.am b/Makefile.am index 7e10ccbd..0ca6c57c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -187,7 +187,7 @@ endif if ENABLE_MODULE_MUSIG noinst_PROGRAMS += musig_example musig_example_SOURCES = examples/musig.c -musig_example_CPPFLAGS = -I$(top_srcdir)/include +musig_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC musig_example_LDADD = libsecp256k1.la musig_example_LDFLAGS = -static if BUILD_WINDOWS diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include index 4119966c..f1bee7b3 100644 --- a/src/modules/generator/Makefile.am.include +++ b/src/modules/generator/Makefile.am.include @@ -6,6 +6,6 @@ noinst_HEADERS += src/modules/generator/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_generator bench_generator_SOURCES = src/bench_generator.c -bench_generator_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_generator_LDFLAGS = -static +bench_generator_LDADD = libsecp256k1.la +bench_generator_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include index 5272f229..bc727d5b 100644 --- a/src/modules/rangeproof/Makefile.am.include +++ b/src/modules/rangeproof/Makefile.am.include @@ -8,6 +8,6 @@ noinst_HEADERS += src/modules/rangeproof/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_rangeproof bench_rangeproof_SOURCES = src/bench_rangeproof.c -bench_rangeproof_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_rangeproof_LDFLAGS = -static +bench_rangeproof_LDADD = libsecp256k1.la +bench_rangeproof_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include index f43e3e4b..41445112 100644 --- a/src/modules/whitelist/Makefile.am.include +++ b/src/modules/whitelist/Makefile.am.include @@ -5,7 +5,6 @@ noinst_HEADERS += src/modules/whitelist/tests_impl.h if USE_BENCHMARK noinst_PROGRAMS += bench_whitelist bench_whitelist_SOURCES = src/bench_whitelist.c -bench_whitelist_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) -bench_whitelist_LDADD = libsecp256k1.la $(SECP_LIBS) -bench_generator_LDFLAGS = -static +bench_whitelist_LDADD = libsecp256k1.la +bench_generator_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif From 82777bba349b6bda24a4f22a5bdc4e31877cd8a2 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 20 Jul 2023 15:40:12 +0200 Subject: [PATCH 290/381] bppp: Fix test for invalid sign byte The test is supposed to create an invalid sign byte. Before this PR, the generated sign byte could in fact be valid due to an overflow. Co-authored-by: Jonas Nick --- src/modules/bppp/tests_impl.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 4913cd4f..ce6cc318 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -256,7 +256,11 @@ static void test_serialize_two_points(void) { random_group_element_test(&X); random_group_element_test(&R); secp256k1_bppp_serialize_points(buf, &X, &R); - buf[0] |= 4 + (unsigned char)secp256k1_testrandi64(4, 255); + + buf[0] = 4 + (unsigned char)secp256k1_testrandi64(0, 253); + /* Assert that buf[0] is actually invalid. */ + CHECK(buf[0] != 0x02 && buf[0] != 0x03); + CHECK(!secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 0)); } From 167194bede0697ed6862cc138028eee7ae509246 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 21 Jul 2023 13:05:25 +0200 Subject: [PATCH 291/381] rangeproof: Use util functions for writing big endian --- src/modules/rangeproof/borromean_impl.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index fb5e44db..3a3b74e2 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -20,24 +20,18 @@ #include #include -#if defined(SECP256K1_BIG_ENDIAN) -#define BE32(x) (x) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#endif - SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, size_t ridx, size_t eidx) { - uint32_t ring; - uint32_t epos; + unsigned char ring[4]; + unsigned char epos[4]; secp256k1_sha256 sha256_en; secp256k1_sha256_initialize(&sha256_en); - ring = BE32((uint32_t)ridx); - epos = BE32((uint32_t)eidx); + secp256k1_write_be32(ring, (uint32_t)ridx); + secp256k1_write_be32(epos, (uint32_t)eidx); secp256k1_sha256_write(&sha256_en, e, elen); secp256k1_sha256_write(&sha256_en, m, mlen); - secp256k1_sha256_write(&sha256_en, (unsigned char*)&ring, 4); - secp256k1_sha256_write(&sha256_en, (unsigned char*)&epos, 4); + secp256k1_sha256_write(&sha256_en, ring, 4); + secp256k1_sha256_write(&sha256_en, epos, 4); secp256k1_sha256_finalize(&sha256_en, hash); } From aa3edea1199a7f741ed189d648c483409a7fcd6a Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 19 Jul 2023 11:43:08 +0200 Subject: [PATCH 292/381] scalar: Remove unused secp256k1_scalar_chacha20 Unused since a11250330b24b3dffdf11d2de5d496397b4e4410. --- src/scalar.h | 3 -- src/scalar_4x64_impl.h | 87 -------------------------------- src/scalar_8x32_impl.h | 95 ----------------------------------- src/scalar_low_impl.h | 5 -- src/tests.c | 110 ----------------------------------------- src/util.h | 25 ---------- 6 files changed, 325 deletions(-) diff --git a/src/scalar.h b/src/scalar.h index ce480362..c9193ffa 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -105,7 +105,4 @@ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_ /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); -/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); - #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 9d02e70f..7cd33476 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -999,93 +999,6 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); } -#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) -#define QUARTERROUND(a,b,c,d) \ - a += b; d = ROTL32(d ^ a, 16); \ - c += d; b = ROTL32(b ^ c, 12); \ - a += b; d = ROTL32(d ^ a, 8); \ - c += d; b = ROTL32(b ^ c, 7); - -#if defined(SECP256K1_BIG_ENDIAN) -#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define LE32(p) (p) -#endif - -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { - size_t n; - size_t over_count = 0; - uint32_t seed32[8]; - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int over1, over2; - - memcpy((void *) seed32, (const void *) seed, 32); - do { - x0 = 0x61707865; - x1 = 0x3320646e; - x2 = 0x79622d32; - x3 = 0x6b206574; - x4 = LE32(seed32[0]); - x5 = LE32(seed32[1]); - x6 = LE32(seed32[2]); - x7 = LE32(seed32[3]); - x8 = LE32(seed32[4]); - x9 = LE32(seed32[5]); - x10 = LE32(seed32[6]); - x11 = LE32(seed32[7]); - x12 = idx; - x13 = idx >> 32; - x14 = 0; - x15 = over_count; - - n = 10; - while (n--) { - QUARTERROUND(x0, x4, x8,x12) - QUARTERROUND(x1, x5, x9,x13) - QUARTERROUND(x2, x6,x10,x14) - QUARTERROUND(x3, x7,x11,x15) - QUARTERROUND(x0, x5,x10,x15) - QUARTERROUND(x1, x6,x11,x12) - QUARTERROUND(x2, x7, x8,x13) - QUARTERROUND(x3, x4, x9,x14) - } - - x0 += 0x61707865; - x1 += 0x3320646e; - x2 += 0x79622d32; - x3 += 0x6b206574; - x4 += LE32(seed32[0]); - x5 += LE32(seed32[1]); - x6 += LE32(seed32[2]); - x7 += LE32(seed32[3]); - x8 += LE32(seed32[4]); - x9 += LE32(seed32[5]); - x10 += LE32(seed32[6]); - x11 += LE32(seed32[7]); - x12 += idx; - x13 += idx >> 32; - x14 += 0; - x15 += over_count; - - r1->d[3] = (((uint64_t) x0) << 32) | x1; - r1->d[2] = (((uint64_t) x2) << 32) | x3; - r1->d[1] = (((uint64_t) x4) << 32) | x5; - r1->d[0] = (((uint64_t) x6) << 32) | x7; - r2->d[3] = (((uint64_t) x8) << 32) | x9; - r2->d[2] = (((uint64_t) x10) << 32) | x11; - r2->d[1] = (((uint64_t) x12) << 32) | x13; - r2->d[0] = (((uint64_t) x14) << 32) | x15; - - over1 = secp256k1_scalar_check_overflow(r1); - over2 = secp256k1_scalar_check_overflow(r2); - over_count++; - } while (over1 | over2); -} - -#undef ROTL32 -#undef QUARTERROUND -#undef LE32 - static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) { const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 448ef0c8..e7091857 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -751,101 +751,6 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); } -#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) -#define QUARTERROUND(a,b,c,d) \ - a += b; d = ROTL32(d ^ a, 16); \ - c += d; b = ROTL32(b ^ c, 12); \ - a += b; d = ROTL32(d ^ a, 8); \ - c += d; b = ROTL32(b ^ c, 7); - -#if defined(SECP256K1_BIG_ENDIAN) -#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define LE32(p) (p) -#endif - -static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { - size_t n; - size_t over_count = 0; - uint32_t seed32[8]; - uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - int over1, over2; - - memcpy((void *) seed32, (const void *) seed, 32); - do { - x0 = 0x61707865; - x1 = 0x3320646e; - x2 = 0x79622d32; - x3 = 0x6b206574; - x4 = LE32(seed32[0]); - x5 = LE32(seed32[1]); - x6 = LE32(seed32[2]); - x7 = LE32(seed32[3]); - x8 = LE32(seed32[4]); - x9 = LE32(seed32[5]); - x10 = LE32(seed32[6]); - x11 = LE32(seed32[7]); - x12 = idx; - x13 = idx >> 32; - x14 = 0; - x15 = over_count; - - n = 10; - while (n--) { - QUARTERROUND(x0, x4, x8,x12) - QUARTERROUND(x1, x5, x9,x13) - QUARTERROUND(x2, x6,x10,x14) - QUARTERROUND(x3, x7,x11,x15) - QUARTERROUND(x0, x5,x10,x15) - QUARTERROUND(x1, x6,x11,x12) - QUARTERROUND(x2, x7, x8,x13) - QUARTERROUND(x3, x4, x9,x14) - } - - x0 += 0x61707865; - x1 += 0x3320646e; - x2 += 0x79622d32; - x3 += 0x6b206574; - x4 += LE32(seed32[0]); - x5 += LE32(seed32[1]); - x6 += LE32(seed32[2]); - x7 += LE32(seed32[3]); - x8 += LE32(seed32[4]); - x9 += LE32(seed32[5]); - x10 += LE32(seed32[6]); - x11 += LE32(seed32[7]); - x12 += idx; - x13 += idx >> 32; - x14 += 0; - x15 += over_count; - - r1->d[7] = x0; - r1->d[6] = x1; - r1->d[5] = x2; - r1->d[4] = x3; - r1->d[3] = x4; - r1->d[2] = x5; - r1->d[1] = x6; - r1->d[0] = x7; - r2->d[7] = x8; - r2->d[6] = x9; - r2->d[5] = x10; - r2->d[4] = x11; - r2->d[3] = x12; - r2->d[2] = x13; - r2->d[1] = x14; - r2->d[0] = x15; - - over1 = secp256k1_scalar_check_overflow(r1); - over2 = secp256k1_scalar_check_overflow(r2); - over_count++; - } while (over1 | over2); -} - -#undef ROTL32 -#undef QUARTERROUND -#undef LE32 - static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) { const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 17ebc53a..f7807556 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -129,11 +129,6 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se *r = (*r & mask0) | (*a & mask1); } -SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { - *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; - *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; -} - static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { int i; *r = 0; diff --git a/src/tests.c b/src/tests.c index 3ee0232e..300a0324 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2368,114 +2368,6 @@ static void run_scalar_set_b32_seckey_tests(void) { CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } -static void scalar_chacha_tests(void) { - /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A - * Note that scalar_set_b32 and scalar_get_b32 represent integers - * underlying the scalar in big-endian format. */ - unsigned char expected1[64] = { - 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, - 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, - 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, - 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, - 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, - 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, - 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, - 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 - }; - unsigned char expected2[64] = { - 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, - 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, - 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, - 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, - 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, - 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, - 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, - 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b - }; - unsigned char seed3[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - unsigned char expected3[64] = { - 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, - 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, - 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, - 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, - 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, - 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, - 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, - 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 - }; - unsigned char seed4[32] = { - 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - unsigned char expected4[64] = { - 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, - 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, - 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, - 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, - 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, - 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, - 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, - 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e - }; - unsigned char seed5[32] = { - 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, - 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, - 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, - 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 - }; - unsigned char expected5[64] = { - 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, - 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, - 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, - 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, - 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, - 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, - 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, - 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 - }; - - secp256k1_scalar exp_r1, exp_r2; - secp256k1_scalar r1, r2; - unsigned char seed0[32] = { 0 }; - - secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); - secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); - secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); - secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); - secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); - - secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); - secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); - secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); - CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); - CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); -} - static void run_scalar_tests(void) { int i; for (i = 0; i < 128 * COUNT; i++) { @@ -2485,8 +2377,6 @@ static void run_scalar_tests(void) { run_scalar_set_b32_seckey_tests(); } - scalar_chacha_tests(); - { /* Check that the scalar constants secp256k1_scalar_zero and secp256k1_scalar_one contain the expected values. */ diff --git a/src/util.h b/src/util.h index cc36bc58..9c0eb0fd 100644 --- a/src/util.h +++ b/src/util.h @@ -220,31 +220,6 @@ SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { # define SECP256K1_GNUC_EXT #endif -/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */ -#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN) -/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */ -# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ - defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \ - defined(__i486__) || defined(__i586__) || defined(__i686__) || \ - defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \ - defined(__ARMEL__) || defined(__AARCH64EL__) || \ - (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \ - (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \ - defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */ -# define SECP256K1_LITTLE_ENDIAN -# endif -# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ - defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \ - defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \ - (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \ - (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1) -# define SECP256K1_BIG_ENDIAN -# endif -#endif -#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN) -# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h. -#endif - /* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { unsigned char *p = (unsigned char *)s; From 394e09ee84dbf88a8911db455d42da57254180d3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Jul 2023 18:10:55 +0000 Subject: [PATCH 293/381] musig: change test vector generation code shebang from python to python3 --- contrib/musig2-vectors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/musig2-vectors.py b/contrib/musig2-vectors.py index 8df3870f..97424419 100755 --- a/contrib/musig2-vectors.py +++ b/contrib/musig2-vectors.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import json From 5bf57590bf2685fe46e6041faaaa5726585b5916 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 4 Aug 2023 15:00:49 +0000 Subject: [PATCH 294/381] bppp: Fix test for invalid sign byte again The first byte provided to secp256k1_bppp_parse_one_of_points is allowed to be 0, 1, 2, or 3 since it encodes the Y coordinate of two points. In a previous fix we wrongly assumed it can only be 2 or 3. --- src/modules/bppp/tests_impl.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index ce6cc318..b55f158e 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -257,10 +257,8 @@ static void test_serialize_two_points(void) { random_group_element_test(&R); secp256k1_bppp_serialize_points(buf, &X, &R); - buf[0] = 4 + (unsigned char)secp256k1_testrandi64(0, 253); - /* Assert that buf[0] is actually invalid. */ - CHECK(buf[0] != 0x02 && buf[0] != 0x03); - + /* buf is valid if 0 <= buf[0] < 4. */ + buf[0] = (unsigned char)secp256k1_testrandi64(4, 255); CHECK(!secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 0)); } From e9d522fc6443a81a613fbb89c72f790d181e8d77 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Oct 2023 07:59:47 +0000 Subject: [PATCH 295/381] ci: turn on -zkp modules in macos-native job --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c56e8f39..7d1f765e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -678,15 +678,15 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: From 6a3aae8f1de9d693cfcaa583a558148e1aa3b0a3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Oct 2023 11:18:21 +0000 Subject: [PATCH 296/381] group_parse: use secp256k1_memcmp_var instead of memcmp --- src/secp256k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secp256k1.c b/src/secp256k1.c index 7f7fb52e..701b436b 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -865,7 +865,7 @@ static void secp256k1_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { unsigned char zeros[33] = { 0 }; - if (memcmp(in33, zeros, sizeof(zeros)) == 0) { + if (secp256k1_memcmp_var(in33, zeros, sizeof(zeros)) == 0) { secp256k1_ge_set_infinity(ge); return 1; } From b41caaafd2f7308f99245bc833158cdc5836c52d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Oct 2023 13:10:05 +0000 Subject: [PATCH 297/381] bppp: replace memcmp in tests with secp256k1_memcmp_var --- src/modules/bppp/tests_impl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 8a9b72dc..fa4727a2 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -93,11 +93,11 @@ static void test_bppp_generators_fixed(void) { len = 99; CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); - CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + CHECK(secp256k1_memcmp_var(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); len = sizeof(gens_ser); CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); - CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + CHECK(secp256k1_memcmp_var(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); secp256k1_bppp_generators_destroy(CTX, gens); } @@ -124,7 +124,7 @@ static void test_bppp_tagged_hash(void) { secp256k1_bppp_sha256_tagged_commitment_init(&sha); secp256k1_bppp_challenge_scalar(&s, &sha, 0); secp256k1_scalar_get_b32(output, &s); - CHECK(memcmp(output, expected, sizeof(output)) == 0); + CHECK(secp256k1_memcmp_var(output, expected, sizeof(output)) == 0); } { @@ -136,7 +136,7 @@ static void test_bppp_tagged_hash(void) { secp256k1_sha256_write(&sha, tmp, sizeof(tmp)); secp256k1_bppp_challenge_scalar(&s, &sha, 0); secp256k1_scalar_get_b32(output, &s); - CHECK(memcmp(output, expected, sizeof(output)) == 0); + CHECK(secp256k1_memcmp_var(output, expected, sizeof(output)) == 0); } } From fcc0299fa50c19438d13a34c9281c9ca437633f3 Mon Sep 17 00:00:00 2001 From: Jon Griffiths Date: Tue, 31 Oct 2023 16:43:13 +1300 Subject: [PATCH 298/381] surjectionproof: remove unused include Following the merge of b627ba7050b608e869515a8ef622d71bf8c13b54 from upstream, this include should have been deleted as well. --- src/modules/surjection/main_impl.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index c6bdea2e..f1d7d42f 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -9,10 +9,6 @@ #include #include -#if defined HAVE_CONFIG_H -#include "../../libsecp256k1-config.h" -#endif - #include "../../../include/secp256k1_rangeproof.h" #include "../../../include/secp256k1_surjectionproof.h" #include "../rangeproof/borromean.h" From c33d2241cb6ac8d0e04d4f4de912ee560fd14305 Mon Sep 17 00:00:00 2001 From: roconnor-blockstream Date: Fri, 17 Nov 2023 14:56:47 -0500 Subject: [PATCH 299/381] Typo in shallue_van_de_woestijne description --- src/modules/generator/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 63318594..a00b22f4 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -114,7 +114,7 @@ static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { x1d = wd x2n = -(x1n + wd) x2d = wd - x3n = wd^2 + c^2 + t^2 + x3n = wd^2 + c^2 * t^2 x3d = (c * t)^2 The joint denominator j = wd * c^2 * t^2, and From e2eb3fae407f0a081a19baeb2ea22eb965fa9674 Mon Sep 17 00:00:00 2001 From: Sanket Kanjalkar Date: Sun, 7 Jan 2024 07:44:11 -0800 Subject: [PATCH 300/381] Make *key_cache const in musig_pubkey_get --- include/secp256k1_musig.h | 2 +- src/modules/musig/keyagg_impl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index a064ed33..3aba53a3 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -234,7 +234,7 @@ SECP256K1_API int secp256k1_musig_pubkey_agg( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( const secp256k1_context *ctx, secp256k1_pubkey *agg_pk, - secp256k1_musig_keyagg_cache *keyagg_cache + const secp256k1_musig_keyagg_cache *keyagg_cache ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Apply plain "EC" tweaking to a public key in a given keyagg_cache by diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index aeb942ac..95da286f 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -278,7 +278,7 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s return 1; } -int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache) { +int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *agg_pk, const secp256k1_musig_keyagg_cache *keyagg_cache) { secp256k1_keyagg_cache_internal cache_i; VERIFY_CHECK(ctx != NULL); ARG_CHECK(agg_pk != NULL); From c29f28e638599a48741a1c59599cff282f88d633 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 18 Jan 2024 11:37:18 +0100 Subject: [PATCH 301/381] include: make docs more consistent Like upstream https://github.com/bitcoin-core/secp256k1/pull/1476 . --- include/secp256k1_ecdsa_adaptor.h | 8 +++---- include/secp256k1_ecdsa_s2c.h | 20 ++++++++--------- include/secp256k1_generator.h | 24 ++++++++++----------- include/secp256k1_surjectionproof.h | 33 ++++++++++++++--------------- include/secp256k1_whitelist.h | 14 ++++++------ 5 files changed, 49 insertions(+), 50 deletions(-) diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index 225caa73..e50f94e9 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -71,7 +71,7 @@ SECP256K1_API const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_no * this file and applied the suggested countermeasures. * * Returns: 1 on success, 0 on failure - * Args: ctx: a secp256k1 context object (not secp256k1_context_static) + * Args: ctx: pointer to a context object (not secp256k1_context_static) * Out: adaptor_sig162: pointer to 162 byte to store the returned signature * In: seckey32: pointer to 32 byte secret key that will be used for * signing @@ -101,7 +101,7 @@ SECP256K1_API int secp256k1_ecdsa_adaptor_encrypt( * and the completed ECDSA signature. * * Returns: 1 on success, 0 on failure - * Args: ctx: a secp256k1 context object + * Args: ctx: pointer to a context object * In: adaptor_sig162: pointer to 162-byte signature to verify * pubkey: pointer to the public key corresponding to the secret key * used for signing @@ -121,7 +121,7 @@ SECP256K1_API int secp256k1_ecdsa_adaptor_verify( * Derives an ECDSA signature from an adaptor signature and an adaptor decryption key. * * Returns: 1 on success, 0 on failure - * Args: ctx: a secp256k1 context object + * Args: ctx: pointer to a context object * Out: sig: pointer to the ECDSA signature to create * In: deckey32: pointer to 32-byte decryption secret key for the adaptor * encryption public key @@ -140,7 +140,7 @@ SECP256K1_API int secp256k1_ecdsa_adaptor_decrypt( * signature. * * Returns: 1 on success, 0 on failure - * Args: ctx: a secp256k1 context object (not secp256k1_context_static) + * Args: ctx: pointer to a context object (not secp256k1_context_static) * Out: deckey32: pointer to 32-byte adaptor decryption key for the adaptor * encryption public key * In: sig: pointer to ECDSA signature to recover the adaptor decryption diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h index 02b50513..ea4219fe 100644 --- a/include/secp256k1_ecdsa_s2c.h +++ b/include/secp256k1_ecdsa_s2c.h @@ -33,7 +33,7 @@ typedef struct { * * Returns: 1 if the opening could be parsed * 0 if the opening could not be parsed - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object * Out: opening: pointer to an opening object. If 1 is returned, it is set to a * parsed version of input. If not, its value is unspecified. * In: input33: pointer to 33-byte array with a serialized opening @@ -49,9 +49,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_parse * * Returns: 1 if the opening was successfully serialized. * 0 if the opening could not be serialized - * Args: ctx: a secp256k1 context object + * Args: ctx: pointer to a context object * Out: output33: pointer to a 33-byte array to place the serialized opening in - * In: opening: a pointer to an initialized `secp256k1_ecdsa_s2c_opening` + * In: opening: pointer to an initialized `secp256k1_ecdsa_s2c_opening` */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_serialize( const secp256k1_context *ctx, @@ -63,9 +63,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_seria * * Returns: 1: signature created * 0: the nonce generation function failed, or the private key was invalid. - * Args: ctx: pointer to a context object (not secp256k1_context_static) - * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) - * s2c_opening: if non-NULL, pointer to an secp256k1_ecdsa_s2c_opening structure to populate + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * s2c_opening: if non-NULL, pointer to an secp256k1_ecdsa_s2c_opening structure to populate * In: msg32: the 32-byte message hash being signed (cannot be NULL) * seckey: pointer to a 32-byte secret key (cannot be NULL) * s2c_data32: pointer to a 32-byte data to commit to in the nonce (cannot be NULL) @@ -84,7 +84,7 @@ SECP256K1_API int secp256k1_ecdsa_s2c_sign( * Returns: 1: the signature contains a commitment to data32 (though it does * not necessarily need to be a valid siganture!) * 0: incorrect opening - * Args: ctx: a secp256k1 context object + * Args: ctx: pointer to a context object * In: sig: the signature containing the sign-to-contract commitment (cannot be NULL) * data32: the 32-byte data that was committed to (cannot be NULL) * opening: pointer to the opening created during signing (cannot be NULL) @@ -193,8 +193,8 @@ SECP256K1_API int secp256k1_ecdsa_anti_exfil_signer_commit( * * Returns: 1: signature created * 0: the nonce generation function failed, or the private key was invalid. - * Args: ctx: pointer to a context object (not secp256k1_context_static) - * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) * In: msg32: the 32-byte message hash being signed (cannot be NULL) * seckey: pointer to a 32-byte secret key (cannot be NULL) * host_data32: pointer to 32-byte host-provided randomness (cannot be NULL) @@ -211,7 +211,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_anti_exfil_sign( * * Returns: 1: the signature is valid and contains a commitment to host_data32 * 0: incorrect opening - * Args: ctx: a secp256k1 context object + * Args: ctx: pointer to a context object * In: sig: the signature produced by the signer (cannot be NULL) * msghash32: the 32-byte message hash being verified (cannot be NULL) * pubkey: pointer to the signer's public key (cannot be NULL) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index eacefad8..a0f9fb85 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -29,7 +29,7 @@ SECP256K1_API const secp256k1_generator *secp256k1_generator_h; /** Parse a 33-byte generator byte sequence into a generator object. * * Returns: 1 if input contains a valid generator. - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object * Out: gen: pointer to the output generator object * In: input: pointer to a 33-byte serialized generator */ @@ -42,9 +42,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse( /** Serialize a 33-byte generator into a serialized byte sequence. * * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 33-byte byte array - * In: gen: a pointer to a generator + * Args: ctx: pointer to a context object + * Out: output: pointer to a 33-byte byte array + * In: gen: pointer to a generator object */ SECP256K1_API int secp256k1_generator_serialize( const secp256k1_context *ctx, @@ -56,8 +56,8 @@ SECP256K1_API int secp256k1_generator_serialize( * * Returns: 0 in the highly unlikely case the seed is not acceptable, * 1 otherwise. - * Args: ctx: a secp256k1 context object - * Out: gen: a generator object + * Args: ctx: pointer to a context object + * Out: gen: pointer to a the new generator object * In: seed32: a 32-byte seed * * If successful a valid generator will be placed in gen. The produced @@ -75,8 +75,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( * * Returns: 0 in the highly unlikely case the seed is not acceptable or when * blind is out of range. 1 otherwise. - * Args: ctx: a secp256k1 context object (not secp256k1_context_static) - * Out: gen: a generator object + * Args: ctx: pointer to a context object (not secp256k1_context_static) + * Out: gen: pointer to a generator object * In: seed32: a 32-byte seed * blind32: a 32-byte secret value to blind the generator with. * @@ -107,7 +107,7 @@ typedef struct { /** Parse a 33-byte commitment into a commitment object. * * Returns: 1 if input contains a valid commitment. - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object * Out: commit: pointer to the output commitment object * In: input: pointer to a 33-byte serialized commitment key */ @@ -120,9 +120,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_par /** Serialize a commitment object into a serialized byte sequence. * * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 33-byte byte array - * In: commit: a pointer to a secp256k1_pedersen_commitment containing an + * Args: ctx: pointer to a context object + * Out: output: pointer to a 33-byte byte array + * In: commit: pointer to a secp256k1_pedersen_commitment containing an * initialized commitment */ SECP256K1_API int secp256k1_pedersen_commitment_serialize( diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index 6fee1e60..c9a4aaee 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -56,9 +56,9 @@ typedef struct { /** Parse a surjection proof * * Returns: 1 when the proof could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: proof: a pointer to a proof object - * In: input: a pointer to the array to parse + * Args: ctx: pointer to a context object + * Out: proof: pointer to a proof object + * In: input: pointer to the array to parse * inputlen: length of the array pointed to by input * * The proof must consist of: @@ -79,12 +79,11 @@ SECP256K1_API int secp256k1_surjectionproof_parse( /** Serialize a surjection proof * * Returns: 1 if enough space was available to serialize, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: output: a pointer to an array to store the serialization - * In/Out: outputlen: a pointer to an integer which is initially set to the - * size of output, and is overwritten with the written - * size. - * In: proof: a pointer to an initialized proof object + * Args: ctx: pointer to a context object + * Out: output: pointer to an array to store the serialization + * In/Out: outputlen: pointer to an integer which is initially set to the size + * of output, and is overwritten with the written size. + * In: proof: pointer to an initialized proof object * * See secp256k1_surjectionproof_parse for details about the encoding. */ @@ -109,7 +108,7 @@ typedef struct { * * Returns: the number of inputs for the given proof * In: ctx: pointer to a context object - * proof: a pointer to a proof object + * proof: pointer to a proof object */ SECP256K1_API size_t secp256k1_surjectionproof_n_total_inputs( const secp256k1_context *ctx, @@ -120,7 +119,7 @@ SECP256K1_API size_t secp256k1_surjectionproof_n_total_inputs( * * Returns: the number of inputs for the given proof * In: ctx: pointer to a context object - * proof: a pointer to a proof object + * proof: pointer to a proof object */ SECP256K1_API size_t secp256k1_surjectionproof_n_used_inputs( const secp256k1_context *ctx, @@ -131,7 +130,7 @@ SECP256K1_API size_t secp256k1_surjectionproof_n_used_inputs( * * Returns: the total size * In: ctx: pointer to a context object - * proof: a pointer to a proof object + * proof: pointer to a proof object */ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( const secp256k1_context *ctx, @@ -156,7 +155,7 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( * limited to 256 the probability of giving up is smaller than * (255/256)^(n_input_tags_to_use*max_n_iterations). * - * random_seed32: a random seed to be used for input selection + * random_seed32: random seed to be used for input selection * Out: proof: The proof whose bitvector will be initialized. In case of failure, * the state of the proof is undefined. * input_index: The index of the actual input that is secretly mapped to the output @@ -179,8 +178,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initial * n: inputs were selected after n iterations of random selection * * In: ctx: pointer to a context object - * proof_out_p: a pointer to a pointer to `secp256k1_surjectionproof*`. - * the newly-allocated struct pointer will be saved here. + * proof_out_p: pointer to a pointer to `secp256k1_surjectionproof*`. + * The newly-allocated struct pointer will be saved here. * fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known, * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; * this won't match the output tag but might be used in the anonymity set.) @@ -192,8 +191,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initial * limited to 256 the probability of giving up is smaller than * (255/256)^(n_input_tags_to_use*max_n_iterations). * - * random_seed32: a random seed to be used for input selection - * Out: proof_out_p: The pointer to newly-allocated proof whose bitvector will be initialized. + * random_seed32: random seed to be used for input selection + * Out: proof_out_p: pointer to newly-allocated proof whose bitvector will be initialized. * In case of failure, the pointer will be NULL. * input_index: The index of the actual input that is secretly mapped to the output */ diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index 096a0ee4..9f9decce 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -40,9 +40,9 @@ typedef struct { /** Parse a whitelist signature * * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input: a pointer to the array to parse + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input: pointer to the array to parse * input_len: the length of the above array * * The signature must consist of a 1-byte n_keys value, followed by a 32-byte @@ -67,7 +67,7 @@ SECP256K1_API int secp256k1_whitelist_signature_parse( /** Returns the number of keys a signature expects to have. * * Returns: the number of keys for the given signature - * In: sig: a pointer to a signature object + * In: sig: pointer to a signature object */ SECP256K1_API size_t secp256k1_whitelist_signature_n_keys( const secp256k1_whitelist_signature *sig @@ -76,10 +76,10 @@ SECP256K1_API size_t secp256k1_whitelist_signature_n_keys( /** Serialize a whitelist signature * * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: output64: a pointer to an array to store the serialization + * Args: ctx: pointer to a context object + * Out: output64: pointer to an array to store the serialization * In/Out: output_len: length of the above array, updated with the actual serialized length - * In: sig: a pointer to an initialized signature object + * In: sig: pointer to an initialized signature object * * See secp256k1_whitelist_signature_parse for details about the encoding. */ From 4f656988650006a779c898bdf6303e469b4a8b01 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 18 Jan 2024 11:38:26 +0100 Subject: [PATCH 302/381] extrakeys: Remove redundant secp256k1_pubkey_cmp It was a verbatim copy of secp256k1_ec_pubkey_cmp. --- include/secp256k1_extrakeys.h | 16 ------------- include/secp256k1_generator.h | 6 ++--- src/modules/extrakeys/main_impl.h | 29 +---------------------- src/modules/extrakeys/tests_impl.h | 37 ------------------------------ 4 files changed, 4 insertions(+), 84 deletions(-) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index c75bd7bd..9f091163 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -240,22 +240,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Compare two public keys using lexicographic order of their compressed - * serialization. - * - * Returns: <0 if the first public key is less than the second - * >0 if the first public key is greater than the second - * 0 if the two public keys are equal - * Args: ctx: a secp256k1 context object. - * In: pubkey1: first public key to compare - * pubkey2: second public key to compare - */ -SECP256K1_API int secp256k1_pubkey_cmp( - const secp256k1_context *ctx, - const secp256k1_pubkey *pk1, - const secp256k1_pubkey *pk2 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - /** Sort public keys using lexicographic order of their compressed * serialization. * diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index a0f9fb85..0a59c363 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -58,7 +58,7 @@ SECP256K1_API int secp256k1_generator_serialize( * 1 otherwise. * Args: ctx: pointer to a context object * Out: gen: pointer to a the new generator object - * In: seed32: a 32-byte seed + * In: seed32: 32-byte seed * * If successful a valid generator will be placed in gen. The produced * generators are distributed uniformly over the curve, and will not have a @@ -77,8 +77,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate( * blind is out of range. 1 otherwise. * Args: ctx: pointer to a context object (not secp256k1_context_static) * Out: gen: pointer to a generator object - * In: seed32: a 32-byte seed - * blind32: a 32-byte secret value to blind the generator with. + * In: seed32: 32-byte seed + * blind32: 32-byte secret value to blind the generator with. * * The result is equivalent to first calling secp256k1_generator_generate, * converting the result to a public key, calling secp256k1_ec_pubkey_tweak_add, diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 7a7015e1..2ba41465 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -283,33 +283,6 @@ int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_ke return ret; } -int secp256k1_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* pk0, const secp256k1_pubkey* pk1) { - unsigned char out[2][33]; - const secp256k1_pubkey* pk[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - pk[0] = pk0; pk[1] = pk1; - for (i = 0; i < 2; i++) { - size_t outputlen = sizeof(out[i]); - /* If the public key is NULL or invalid, pubkey_serialize will - * call the illegal_callback and return 0. In that case we will - * serialize the key as all zeros which is less than any valid public - * key. This results in consistent comparisons even if NULL or invalid - * pubkeys are involved and prevents edge cases such as sorting - * algorithms that use this function and do not terminate as a - * result. */ - if (!secp256k1_ec_pubkey_serialize(ctx, out[i], &outputlen, pk[i], SECP256K1_EC_COMPRESSED)) { - /* Note that pubkey_serialize should already set the output to - * zero in that case, but it's not guaranteed by the API, we can't - * test it and writing a VERIFY_CHECK is more complex than - * explicitly memsetting (again). */ - memset(out[i], 0, sizeof(out[i])); - } - } - return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1])); -} - /* This struct wraps a const context pointer to satisfy the secp256k1_hsort api * which expects a non-const cmp_data pointer. */ typedef struct { @@ -317,7 +290,7 @@ typedef struct { } secp256k1_pubkey_sort_cmp_data; static int secp256k1_pubkey_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) { - return secp256k1_pubkey_cmp(((secp256k1_pubkey_sort_cmp_data*)cmp_data)->ctx, + return secp256k1_ec_pubkey_cmp(((secp256k1_pubkey_sort_cmp_data*)cmp_data)->ctx, *(secp256k1_pubkey **)pk1, *(secp256k1_pubkey **)pk2); } diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index dc531e05..60299ce0 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -507,42 +507,6 @@ static void test_hsort(void) { } #undef NUM -static void test_pubkey_comparison(void) { - unsigned char pk1_ser[33] = { - 0x02, - 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11, - 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23 - }; - const unsigned char pk2_ser[33] = { - 0x03, - 0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d, - 0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c - }; - secp256k1_pubkey pk1; - secp256k1_pubkey pk2; - - CHECK(secp256k1_ec_pubkey_parse(CTX, &pk1, pk1_ser, sizeof(pk1_ser)) == 1); - CHECK(secp256k1_ec_pubkey_parse(CTX, &pk2, pk2_ser, sizeof(pk2_ser)) == 1); - - CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, NULL, &pk2) < 0)); - CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, &pk1, NULL) > 0)); - CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk2) < 0); - CHECK(secp256k1_pubkey_cmp(CTX, &pk2, &pk1) > 0); - CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk1) == 0); - CHECK(secp256k1_pubkey_cmp(CTX, &pk2, &pk2) == 0); - memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */ - CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk2) < 0)); - { - int32_t ecount = 0; - secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount); - CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk1) == 0); - CHECK(ecount == 2); - secp256k1_context_set_illegal_callback(CTX, NULL, NULL); - } - CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, &pk2, &pk1) > 0)); - -} - static void test_sort_helper(secp256k1_pubkey *pk, size_t *pk_order, size_t n_pk) { size_t i; const secp256k1_pubkey *pk_test[5]; @@ -704,7 +668,6 @@ static void run_extrakeys_tests(void) { test_keypair_add(); test_hsort(); - test_pubkey_comparison(); test_sort_api(); test_sort(); test_sort_vectors(); From de54a1eff741917b734ad64e1bc914025a97325d Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 5 Jan 2024 13:09:11 +0000 Subject: [PATCH 303/381] musig2: clean up ctx doc in include file --- include/secp256k1_musig.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 3aba53a3..28ecf1ef 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -103,7 +103,7 @@ typedef struct { /** Parse a signer's public nonce. * * Returns: 1 when the nonce could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object + * 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 */ @@ -116,7 +116,7 @@ SECP256K1_API int secp256k1_musig_pubnonce_parse( /** Serialize a signer's public nonce * * Returns: 1 when the nonce could be serialized, 0 otherwise - * Args: ctx: a secp256k1 context object + * 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 */ @@ -129,7 +129,7 @@ SECP256K1_API int secp256k1_musig_pubnonce_serialize( /** Parse an aggregate public nonce. * * Returns: 1 when the nonce could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object + * 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 */ @@ -142,7 +142,7 @@ SECP256K1_API int secp256k1_musig_aggnonce_parse( /** Serialize an aggregate public nonce * * Returns: 1 when the nonce could be serialized, 0 otherwise - * Args: ctx: a secp256k1 context object + * 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 */ @@ -155,7 +155,7 @@ SECP256K1_API int secp256k1_musig_aggnonce_serialize( /** Serialize a MuSig partial signature * * Returns: 1 when the signature could be serialized, 0 otherwise - * Args: ctx: a secp256k1 context object + * 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 */ @@ -168,7 +168,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_serialize( /** Parse a MuSig partial signature. * * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object + * 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 * From 33db8edb2760ac86c693db48c85f78930b3c239f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 6 Jan 2024 15:53:30 +0000 Subject: [PATCH 304/381] group: add ge_to_bytes and ge_from_bytes --- src/group.h | 8 ++++++++ src/group_impl.h | 22 ++++++++++++++++++++++ src/secp256k1.c | 16 ++-------------- src/tests.c | 15 +++++++++++++++ 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/group.h b/src/group.h index 463102d5..81b159c8 100644 --- a/src/group.h +++ b/src/group.h @@ -183,6 +183,14 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); +/** Convert a group element that is not infinity to a 64-byte array. The output + * array is platform-dependent. */ +static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *a); + +/** Convert a 64-byte array into group element. This function assumes that the + * provided buffer correctly encodes a group element. */ +static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf); + /** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve. * * In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the diff --git a/src/group_impl.h b/src/group_impl.h index 1368e14a..f27b7d99 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_GROUP_IMPL_H #define SECP256K1_GROUP_IMPL_H +#include + #include "field.h" #include "group.h" #include "util.h" @@ -963,4 +965,24 @@ static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp25 return secp256k1_fe_is_square_var(&r); } +static void secp256k1_ge_to_bytes(unsigned char *buf, secp256k1_ge *a) { + secp256k1_ge_storage s; + + /* We require that the secp256k1_ge_storage type is exactly 64 bytes. + * This is formally not guaranteed by the C standard, but should hold on any + * sane compiler in the real world. */ + STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); + VERIFY_CHECK(!secp256k1_ge_is_infinity(a)); + secp256k1_ge_to_storage(&s, a); + memcpy(buf, &s, 64); +} + +static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf) { + secp256k1_ge_storage s; + + STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); + memcpy(&s, buf, 64); + secp256k1_ge_from_storage(r, &s); +} + #endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index 56758f11..0acf9f86 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -258,25 +258,13 @@ static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, } static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { - secp256k1_ge_storage s; - - /* We require that the secp256k1_ge_storage type is exactly 64 bytes. - * This is formally not guaranteed by the C standard, but should hold on any - * sane compiler in the real world. */ - STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); - memcpy(&s, &pubkey->data[0], 64); - secp256k1_ge_from_storage(ge, &s); + secp256k1_ge_from_bytes(ge, pubkey->data); ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); return 1; } static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { - secp256k1_ge_storage s; - - STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_ge_to_storage(&s, ge); - memcpy(&pubkey->data[0], &s, 64); + secp256k1_ge_to_bytes(pubkey->data, ge); } int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { diff --git a/src/tests.c b/src/tests.c index a171e316..2d6e1062 100644 --- a/src/tests.c +++ b/src/tests.c @@ -4072,6 +4072,20 @@ static void test_add_neg_y_diff_x(void) { CHECK(secp256k1_gej_eq_ge_var(&sumj, &res)); } +static void test_ge_bytes(void) { + int i; + + for (i = 0; i < COUNT; i++) { + unsigned char buf[64]; + secp256k1_ge p, q; + + random_group_element_test(&p); + secp256k1_ge_to_bytes(buf, &p); + secp256k1_ge_from_bytes(&q, buf); + CHECK(secp256k1_ge_eq_var(&p, &q)); + } +} + static void run_ge(void) { int i; for (i = 0; i < COUNT * 32; i++) { @@ -4079,6 +4093,7 @@ static void run_ge(void) { } test_add_neg_y_diff_x(); test_intialized_inf(); + test_ge_bytes(); } static void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) { From cd173688fb94d4a4acdde2304321064a162254e2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 6 Jan 2024 14:44:36 +0000 Subject: [PATCH 305/381] musig: replace point_{save,load} with ge_{to,from}_bytes --- src/modules/musig/keyagg.h | 9 ------- src/modules/musig/keyagg_impl.h | 40 ++++---------------------------- src/modules/musig/session_impl.h | 8 +++---- 3 files changed, 8 insertions(+), 49 deletions(-) diff --git a/src/modules/musig/keyagg.h b/src/modules/musig/keyagg.h index 9ccea847..620522fe 100644 --- a/src/modules/musig/keyagg.h +++ b/src/modules/musig/keyagg.h @@ -27,15 +27,6 @@ typedef struct { int parity_acc; } secp256k1_keyagg_cache_internal; -/* Save and load points to and from byte arrays, similar to - * secp256k1_pubkey_{save,load}. */ -static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge); - -/* In contrast to pubkey_load, point_load does not attempt to check that data - * has been initialized, since it is assumed that this check already happened - * (e.g. by comparing magic bytes) */ -static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data); - /* point_save_ext and point_load_ext are identical to point_save and point_load * except that they allow saving and loading the point at infinity */ static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge); diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 9e4808f8..81338f5d 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -17,43 +17,11 @@ #include "../../hash.h" #include "../../util.h" -static void secp256k1_point_save(unsigned char *data, secp256k1_ge *ge) { - if (sizeof(secp256k1_ge_storage) == 64) { - secp256k1_ge_storage s; - secp256k1_ge_to_storage(&s, ge); - memcpy(data, &s, sizeof(s)); - } else { - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_normalize_var(&ge->y); - secp256k1_fe_get_b32(data, &ge->x); - secp256k1_fe_get_b32(data + 32, &ge->y); - } -} - -static void secp256k1_point_load(secp256k1_ge *ge, const unsigned char *data) { - if (sizeof(secp256k1_ge_storage) == 64) { - /* When the secp256k1_ge_storage type is exactly 64 byte, use its - * representation as conversion is very fast. */ - secp256k1_ge_storage s; - memcpy(&s, data, sizeof(s)); - secp256k1_ge_from_storage(ge, &s); - } else { - /* Otherwise, fall back to 32-byte big endian for X and Y. */ - secp256k1_fe x, y; - int ret = 1; - ret &= secp256k1_fe_set_b32_limit(&x, data); - ret &= secp256k1_fe_set_b32_limit(&y, data + 32); - VERIFY_CHECK(ret); - secp256k1_ge_set_xy(ge, &x, &y); - } -} - static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) { if (secp256k1_ge_is_infinity(ge)) { memset(data, 0, 64); } else { - secp256k1_point_save(data, ge); + secp256k1_ge_to_bytes(data, ge); } } @@ -62,7 +30,7 @@ static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) { secp256k1_ge_set_infinity(ge); } else { - secp256k1_point_load(ge, data); + secp256k1_ge_from_bytes(ge, data); } } @@ -82,7 +50,7 @@ static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, sec unsigned char *ptr = cache->data; memcpy(ptr, secp256k1_musig_keyagg_cache_magic, 4); ptr += 4; - secp256k1_point_save(ptr, &cache_i->pk); + secp256k1_ge_to_bytes(ptr, &cache_i->pk); ptr += 64; secp256k1_point_save_ext(ptr, &cache_i->second_pk); ptr += 64; @@ -97,7 +65,7 @@ static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_k const unsigned char *ptr = cache->data; ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_keyagg_cache_magic, 4) == 0); ptr += 4; - secp256k1_point_load(&cache_i->pk, ptr); + secp256k1_ge_from_bytes(&cache_i->pk, ptr); ptr += 64; secp256k1_point_load_ext(&cache_i->second_pk, ptr); ptr += 64; diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 092bcfd5..880ecab5 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -26,7 +26,7 @@ static void secp256k1_musig_secnonce_save(secp256k1_musig_secnonce *secnonce, co 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_point_save(&secnonce->data[68], pk); + secp256k1_ge_to_bytes(&secnonce->data[68], pk); } static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_ge *pk, secp256k1_musig_secnonce *secnonce) { @@ -34,7 +34,7 @@ static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1 ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0); secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL); secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL); - secp256k1_point_load(pk, &secnonce->data[68]); + secp256k1_ge_from_bytes(pk, &secnonce->data[68]); /* We make very sure that the nonce isn't invalidated by checking the values * in addition to the magic. */ is_zero = secp256k1_scalar_is_zero(&k[0]) & secp256k1_scalar_is_zero(&k[1]); @@ -62,7 +62,7 @@ static void secp256k1_musig_pubnonce_save(secp256k1_musig_pubnonce* nonce, secp2 int i; memcpy(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4); for (i = 0; i < 2; i++) { - secp256k1_point_save(nonce->data + 4+64*i, &ge[i]); + secp256k1_ge_to_bytes(nonce->data + 4+64*i, &ge[i]); } } @@ -73,7 +73,7 @@ static int secp256k1_musig_pubnonce_load(const secp256k1_context* ctx, secp256k1 ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_pubnonce_magic, 4) == 0); for (i = 0; i < 2; i++) { - secp256k1_point_load(&ge[i], nonce->data + 4 + 64*i); + secp256k1_ge_from_bytes(&ge[i], nonce->data + 4 + 64*i); } return 1; } From b673a43090df39190084860cee385f9099b77e76 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 6 Jan 2024 16:49:16 +0000 Subject: [PATCH 306/381] musig: new upstream def of VERIFY_CHECK (empty in non-VERIFY) Remove explicity VERIFY_CHECKs in keyaggcoef_internal since normalization should be checked in the fe_* functions. --- src/modules/musig/keyagg_impl.h | 12 +++++++++--- src/modules/musig/session_impl.h | 32 ++++++++++++++------------------ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 81338f5d..aff95542 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -131,14 +131,12 @@ static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { /* Compute KeyAgg coefficient which is constant 1 for the second pubkey and * otherwise tagged_hash(pk_hash, x) where pk_hash is the hash of public keys. * second_pk is the point at infinity in case there is no second_pk. Assumes - * that pk is not the point at infinity and that the coordinates of pk and + * that pk is not the point at infinity and that the Y-coordinates of pk and * second_pk are normalized. */ static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, secp256k1_ge *pk, const secp256k1_ge *second_pk) { secp256k1_sha256 sha; VERIFY_CHECK(!secp256k1_ge_is_infinity(pk)); - VERIFY_CHECK(pk->x.normalized && pk->y.normalized); - VERIFY_CHECK(secp256k1_ge_is_infinity(second_pk) || (second_pk->x.normalized && second_pk->y.normalized)); if (!secp256k1_ge_is_infinity(second_pk) && secp256k1_fe_equal(&pk->x, &second_pk->x) @@ -151,9 +149,13 @@ static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsig secp256k1_musig_keyaggcoef_sha256(&sha); secp256k1_sha256_write(&sha, pk_hash, 32); ret = secp256k1_eckey_pubkey_serialize(pk, buf, &buflen, 1); +#ifdef VERIFY /* Serialization does not fail since the pk is not the point at infinity * (according to this function's precondition). */ VERIFY_CHECK(ret && buflen == sizeof(buf)); +#else + (void) ret; +#endif secp256k1_sha256_write(&sha, buf, sizeof(buf)); secp256k1_sha256_finalize(&sha, buf); secp256k1_scalar_set_b32(r, buf, NULL); @@ -178,9 +180,13 @@ static int secp256k1_musig_pubkey_agg_callback(secp256k1_scalar *sc, secp256k1_g secp256k1_musig_pubkey_agg_ecmult_data *ctx = (secp256k1_musig_pubkey_agg_ecmult_data *) data; int ret; ret = secp256k1_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); +#ifdef VERIFY /* pubkey_load can't fail because the same pks have already been loaded in * `musig_compute_pk_hash` (and we test this). */ VERIFY_CHECK(ret); +#else + (void) ret; +#endif secp256k1_musig_keyaggcoef_internal(sc, ctx->pk_hash, pt, &ctx->second_pk); return 1; } diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 880ecab5..ff87f2fd 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -174,8 +174,12 @@ int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned ch int ret; size_t size = 33; ret = secp256k1_eckey_pubkey_serialize(&ge[i], &out66[33*i], &size, 1); +#ifdef VERIFY /* serialize must succeed because the point was just loaded */ VERIFY_CHECK(ret && size == 33); +#else + (void) ret; +#endif } return 1; } @@ -258,16 +262,6 @@ int secp256k1_musig_partial_sig_parse(const secp256k1_context* ctx, secp256k1_mu return 1; } -/* Normalizes the x-coordinate of the given group element. */ -static int secp256k1_xonly_ge_serialize(unsigned char *output32, secp256k1_ge *ge) { - if (secp256k1_ge_is_infinity(ge)) { - return 0; - } - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_get_b32(output32, &ge->x); - return 1; -} - /* Write optional inputs into the hash */ static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data, unsigned char len) { unsigned char zero[7] = { 0 }; @@ -364,22 +358,25 @@ int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secn } if (keyagg_cache != NULL) { - int ret_tmp; if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { return 0; } - ret_tmp = secp256k1_xonly_ge_serialize(aggpk_ser, &cache_i.pk); - /* Serialization can not fail because the loaded point can not be infinity. */ - VERIFY_CHECK(ret_tmp); + /* The loaded point cache_i.pk can not be the point at infinity. */ + secp256k1_fe_get_b32(aggpk_ser, &cache_i.pk.x); aggpk_ser_ptr = aggpk_ser; } if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) { return 0; } pk_serialize_success = secp256k1_eckey_pubkey_serialize(&pk, pk_ser, &pk_ser_len, SECP256K1_EC_COMPRESSED); + +#ifdef VERIFY /* A pubkey cannot be the point at infinity */ VERIFY_CHECK(pk_serialize_success); VERIFY_CHECK(pk_ser_len == sizeof(pk_ser)); +#else + (void) pk_serialize_success; +#endif secp256k1_nonce_function_musig(k, session_id32, msg32, seckey, pk_ser, aggpk_ser_ptr, extra_input32); VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); @@ -460,7 +457,6 @@ static int secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigne secp256k1_ge fin_nonce_pt; secp256k1_gej fin_nonce_ptj; secp256k1_ge aggnonce[2]; - int ret; secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); @@ -476,9 +472,9 @@ static int secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigne if (secp256k1_ge_is_infinity(&fin_nonce_pt)) { fin_nonce_pt = secp256k1_ge_const_g; } - ret = secp256k1_xonly_ge_serialize(fin_nonce, &fin_nonce_pt); - /* Can't fail since fin_nonce_pt is not infinity */ - VERIFY_CHECK(ret); + /* fin_nonce_pt is not the point at infinity */ + secp256k1_fe_normalize_var(&fin_nonce_pt.x); + secp256k1_fe_get_b32(fin_nonce, &fin_nonce_pt.x); secp256k1_fe_normalize_var(&fin_nonce_pt.y); *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); return 1; From 5d87e80c6928694bc9d2fe3bf8fd89343ba16f83 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Thu, 18 Jan 2024 10:33:12 -0500 Subject: [PATCH 307/381] shallue_van_de_woestijne rewrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation returns an off-curve point for the input t=0. This rewrite addresses that issue by implicity returning the on-curve point (d, sqrt(1 + b)), which is the point that the paper Indifferentiable Hashing to Barreto–Naehrig Curves suggests returning in this case. Note: At the moment it is cryptographically impossible for the input t to be 0. --- src/modules/generator/main_impl.h | 63 +++++++++++++------------------ 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index a00b22f4..2804a691 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -107,61 +107,50 @@ static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { x2 = -(x1 + 1) x3 = 1 + 1/w^2 - To avoid the 2 divisions, compute the above in numerator/denominator form: - wn = c * t - wd = 1 + 7 + t^2 - x1n = d*wd - t*wn - x1d = wd - x2n = -(x1n + wd) - x2d = wd - x3n = wd^2 + c^2 * t^2 - x3d = (c * t)^2 + To avoid the 2 divisions, compute the joint denominator j = wd * x3d, where + wd = 1 + b + t^2 + x3d = c^2 * t^2 = -3 * t^2 - The joint denominator j = wd * c^2 * t^2, and - 1 / x1d = 1/j * c^2 * t^2 - 1 / x2d = x3d = 1/j * wd + so that + + 1 / wd = 1/j * x3d + 1 / x3d = 1/j * wd */ - static const secp256k1_fe c = SECP256K1_FE_CONST(0x0a2d2ba9, 0x3507f1df, 0x233770c2, 0xa797962c, 0xc61f6d15, 0xda14ecd4, 0x7d8d27ae, 0x1cd5f852); + static const secp256k1_fe negc = SECP256K1_FE_CONST(0xf5d2d456, 0xcaf80e20, 0xdcc88f3d, 0x586869d3, 0x39e092ea, 0x25eb132b, 0x8272d850, 0xe32a03dd); static const secp256k1_fe d = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40); - static const secp256k1_fe b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7); - static const secp256k1_fe b_plus_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 8); - secp256k1_fe wn, wd, x1n, x2n, x3n, x3d, jinv, tmp, x1, x2, x3, alphain, betain, gammain, y1, y2, y3; + secp256k1_fe wd, x3d, jinv, tmp, x1, x2, x3, alphain, betain, gammain, y1, y2, y3; int alphaquad, betaquad; - secp256k1_fe_mul(&wn, &c, t); /* mag 1 */ secp256k1_fe_sqr(&wd, t); /* mag 1 */ - secp256k1_fe_add(&wd, &b_plus_one); /* mag 2 */ - secp256k1_fe_mul(&tmp, t, &wn); /* mag 1 */ - secp256k1_fe_negate(&tmp, &tmp, 1); /* mag 2 */ - secp256k1_fe_mul(&x1n, &d, &wd); /* mag 1 */ - secp256k1_fe_add(&x1n, &tmp); /* mag 3 */ - x2n = x1n; /* mag 3 */ - secp256k1_fe_add(&x2n, &wd); /* mag 5 */ - secp256k1_fe_negate(&x2n, &x2n, 5); /* mag 6 */ - secp256k1_fe_mul(&x3d, &c, t); /* mag 1 */ - secp256k1_fe_sqr(&x3d, &x3d); /* mag 1 */ - secp256k1_fe_sqr(&x3n, &wd); /* mag 1 */ - secp256k1_fe_add(&x3n, &x3d); /* mag 2 */ - secp256k1_fe_mul(&jinv, &x3d, &wd); /* mag 1 */ + secp256k1_fe_mul(&x1, &negc, &wd); /* mag 1 */ + x3d = wd; /* mag 1 */ + secp256k1_fe_mul_int(&x3d, 3); /* mag 3 */ + secp256k1_fe_negate(&x3d, &x3d, 3); /* mag 4 */ + secp256k1_fe_add_int(&wd, SECP256K1_B + 1); /* mag 2 */ + secp256k1_fe_mul(&jinv, &wd, &x3d); /* mag 1 */ secp256k1_fe_inv(&jinv, &jinv); /* mag 1 */ - secp256k1_fe_mul(&x1, &x1n, &x3d); /* mag 1 */ + secp256k1_fe_mul(&x1, &x1, &x3d); /* mag 1 */ secp256k1_fe_mul(&x1, &x1, &jinv); /* mag 1 */ - secp256k1_fe_mul(&x2, &x2n, &x3d); /* mag 1 */ - secp256k1_fe_mul(&x2, &x2, &jinv); /* mag 1 */ - secp256k1_fe_mul(&x3, &x3n, &wd); /* mag 1 */ + secp256k1_fe_add(&x1, &d); /* mag 2 */ + x2 = x1; /* mag 2 */ + secp256k1_fe_add_int(&x2, 1); /* mag 3 */ + secp256k1_fe_negate(&x2, &x2, 3); /* mag 4 */ + secp256k1_fe_sqr(&x3, &wd); /* mag 1 */ + secp256k1_fe_mul(&x3, &x3, &wd); /* mag 1 */ secp256k1_fe_mul(&x3, &x3, &jinv); /* mag 1 */ + secp256k1_fe_add_int(&x3, 1); /* mag 2 */ secp256k1_fe_sqr(&alphain, &x1); /* mag 1 */ secp256k1_fe_mul(&alphain, &alphain, &x1); /* mag 1 */ - secp256k1_fe_add(&alphain, &b); /* mag 2 */ + secp256k1_fe_add_int(&alphain, SECP256K1_B); /* mag 2 */ secp256k1_fe_sqr(&betain, &x2); /* mag 1 */ secp256k1_fe_mul(&betain, &betain, &x2); /* mag 1 */ - secp256k1_fe_add(&betain, &b); /* mag 2 */ + secp256k1_fe_add_int(&betain, SECP256K1_B); /* mag 2 */ secp256k1_fe_sqr(&gammain, &x3); /* mag 1 */ secp256k1_fe_mul(&gammain, &gammain, &x3); /* mag 1 */ - secp256k1_fe_add(&gammain, &b); /* mag 2 */ + secp256k1_fe_add_int(&gammain, SECP256K1_B); /* mag 2 */ alphaquad = secp256k1_fe_sqrt(&y1, &alphain); betaquad = secp256k1_fe_sqrt(&y2, &betain); From 26522241b407a04825442dea839af82d97372daf Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 19 Jan 2024 19:47:21 +0000 Subject: [PATCH 308/381] generators: shallue_van_de_woestijne improve comments --- src/modules/generator/main_impl.h | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 2804a691..28536694 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -111,10 +111,20 @@ static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { wd = 1 + b + t^2 x3d = c^2 * t^2 = -3 * t^2 - so that + so that if j != 0, then 1 / wd = 1/j * x3d 1 / x3d = 1/j * wd + + x1 = d - c * t^2 * x3d / j + x3 = 1 + wd^3 / j + + If j = 0, the function outputs the point (d, f(d)). This point is equal + to (x1, f(x1)) as defined above if division by 0 is defined to be 0. In + below code this is not special-cased because secp256k1_fe_inv returns 0 + on input 0. + + j = 0 happens only when t = 0 (since wd != 0 as -8 is not a square). */ static const secp256k1_fe negc = SECP256K1_FE_CONST(0xf5d2d456, 0xcaf80e20, 0xdcc88f3d, 0x586869d3, 0x39e092ea, 0x25eb132b, 0x8272d850, 0xe32a03dd); @@ -123,23 +133,41 @@ static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) { secp256k1_fe wd, x3d, jinv, tmp, x1, x2, x3, alphain, betain, gammain, y1, y2, y3; int alphaquad, betaquad; + /* wd = t^2 */ secp256k1_fe_sqr(&wd, t); /* mag 1 */ + /* x1 = -c * t^2 */ secp256k1_fe_mul(&x1, &negc, &wd); /* mag 1 */ + /* x3d = t^2 */ x3d = wd; /* mag 1 */ + /* x3d = 3 * t^2 */ secp256k1_fe_mul_int(&x3d, 3); /* mag 3 */ + /* x3d = -3 * t^2 */ secp256k1_fe_negate(&x3d, &x3d, 3); /* mag 4 */ + /* wd = 1 + b + t^2 */ secp256k1_fe_add_int(&wd, SECP256K1_B + 1); /* mag 2 */ + /* jinv = wd * x3d */ secp256k1_fe_mul(&jinv, &wd, &x3d); /* mag 1 */ + /* jinv = 1/(wd * x3d) */ secp256k1_fe_inv(&jinv, &jinv); /* mag 1 */ + /* x1 = -c * t^2 * x3d */ secp256k1_fe_mul(&x1, &x1, &x3d); /* mag 1 */ + /* x1 = -c * t^2 * x3d * 1/j */ secp256k1_fe_mul(&x1, &x1, &jinv); /* mag 1 */ + /* x1 = d + -c * t^2 * x3d * 1/j */ secp256k1_fe_add(&x1, &d); /* mag 2 */ + /* x2 = x1 */ x2 = x1; /* mag 2 */ + /* x2 = x1 + 1 */ secp256k1_fe_add_int(&x2, 1); /* mag 3 */ + /* x2 = - (x1 + 1) */ secp256k1_fe_negate(&x2, &x2, 3); /* mag 4 */ + /* x3 = wd^2 */ secp256k1_fe_sqr(&x3, &wd); /* mag 1 */ + /* x3 = wd^3 */ secp256k1_fe_mul(&x3, &x3, &wd); /* mag 1 */ + /* x3 = wd^3 * 1/j */ secp256k1_fe_mul(&x3, &x3, &jinv); /* mag 1 */ + /* x3 = 1 + (wd^3 * 1/j) */ secp256k1_fe_add_int(&x3, 1); /* mag 2 */ secp256k1_fe_sqr(&alphain, &x1); /* mag 1 */ From 6b9d335ef641b77884fdb0e65f95c5ac4dd2209f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 19 Jan 2024 20:05:10 +0000 Subject: [PATCH 309/381] generator: add shallue_van_de_woestijne test for t = 0 --- src/modules/generator/tests_impl.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 2b8d0bcc..14a993b9 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -48,7 +48,9 @@ static void test_generator_api(void) { static void test_shallue_van_de_woestijne(void) { /* Matches with the output of the shallue_van_de_woestijne.sage SAGE program */ - static const secp256k1_ge_storage results[32] = { + static const secp256k1_ge_storage results[34] = { + SECP256K1_GE_STORAGE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40, 0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), + SECP256K1_GE_STORAGE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40, 0x4218f20a, 0xe6c646b3, 0x63db6860, 0x5822fb14, 0x264ca8d2, 0x587fdd6f, 0xbc750d58, 0x7e76a7ee), SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a), SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705), SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97), @@ -87,7 +89,7 @@ static void test_shallue_van_de_woestijne(void) { secp256k1_fe fe; secp256k1_ge_storage ges; int i, s; - for (i = 1; i <= 16; i++) { + for (i = 0; i <= 16; i++) { secp256k1_fe_set_int(&fe, i); for (s = 0; s < 2; s++) { @@ -96,9 +98,9 @@ static void test_shallue_van_de_woestijne(void) { secp256k1_fe_normalize(&fe); } shallue_van_de_woestijne(&ge, &fe); + CHECK(secp256k1_ge_is_valid_var(&ge)); secp256k1_ge_to_storage(&ges, &ge); - - CHECK(secp256k1_memcmp_var(&ges, &results[i * 2 + s - 2], sizeof(secp256k1_ge_storage)) == 0); + CHECK(secp256k1_memcmp_var(&ges, &results[i * 2 + s], sizeof(secp256k1_ge_storage)) == 0); } } } From 4228fd1124281af57c9872f1de3f4eaac73973f4 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 25 Jan 2024 15:57:16 +0100 Subject: [PATCH 310/381] cmake: Add support for -zkp modules Co-authored-by: lightyear15 --- CMakeLists.txt | 70 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 37 ++++++++++++++++++++++++ configure.ac | 9 +++--- src/CMakeLists.txt | 25 +++++++++++++++++ 4 files changed, 137 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ef7defe..2d9d9d2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,8 +61,70 @@ option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) +option(SECP256K1_ENABLE_MODULE_GENERATOR "Enable NUMS generator module." ON) +option(SECP256K1_ENABLE_MODULE_RANGEPROOF "Enable Range proof module." ON) +option(SECP256K1_ENABLE_MODULE_SURJECTIONPROOF "Enable Surjection proof module." ON) +option(SECP256K1_ENABLE_MODULE_WHITELIST "Enable key whitelist module." ON) +option(SECP256K1_ENABLE_MODULE_MUSIG "Enable MuSig module." ON) +option(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR "Enable ecdsa adaptor signatures module." ON) +option(SECP256K1_ENABLE_MODULE_ECDSA_S2C "Enable ECDSA sign-to-contract module." ON) +option(SECP256K1_ENABLE_MODULE_BPPP "Enable Bulletproofs++ module." ON) + # Processing must be done in a topological sorting of the dependency graph # (dependent module first). +if(SECP256K1_ENABLE_MODULE_BPPP) + if(DEFINED SECP256K1_ENABLE_MODULE_GENERATOR AND NOT SECP256K1_ENABLE_MODULE_GENERATOR) + message(FATAL_ERROR "Module dependency error: You have disabled the generator module explicitly, but it is required by the bppp module.") + endif() + set(SECP256K1_ENABLE_MODULE_GENERATOR ON) + add_compile_definitions(ENABLE_MODULE_BPPP=1) +endif() + +if(SECP256K1_ENABLE_MODULE_ECDSA_S2C) + add_compile_definitions(ENABLE_MODULE_ECDSA_S2C=1) +endif() + +if(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR) + add_compile_definitions(ENABLE_MODULE_ECDSA_ADAPTOR=1) +endif() + +if(SECP256K1_ENABLE_MODULE_MUSIG) + if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG) + message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.") + endif() + set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON) + add_compile_definitions(ENABLE_MODULE_MUSIG=1) +endif() + +if(SECP256K1_ENABLE_MODULE_WHITELIST) + if(DEFINED SECP256K1_ENABLE_MODULE_RANGEPROOF AND NOT SECP256K1_ENABLE_MODULE_RANGEPROOF) + message(FATAL_ERROR "Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the whitelist module.") + endif() + set(SECP256K1_ENABLE_MODULE_RANGEPROOF ON) + add_compile_definitions(ENABLE_MODULE_WHITELIST=1) +endif() + +if(SECP256K1_ENABLE_MODULE_SURJECTIONPROOF) + if(DEFINED SECP256K1_ENABLE_MODULE_RANGEPROOF AND NOT SECP256K1_ENABLE_MODULE_RANGEPROOF) + message(FATAL_ERROR "Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the surjectionproof module.") + endif() + set(SECP256K1_ENABLE_MODULE_RANGEPROOF ON) + add_compile_definitions(ENABLE_MODULE_SURJECTIONPROOF=1) +endif() + +if(SECP256K1_ENABLE_MODULE_RANGEPROOF) + if(DEFINED SECP256K1_ENABLE_MODULE_GENERATOR AND NOT SECP256K1_ENABLE_MODULE_GENERATOR) + message(FATAL_ERROR "Module dependency error: You have disabled the generator module explicitly, but it is required by the rangeproof module.") + endif() + set(SECP256K1_ENABLE_MODULE_GENERATOR ON) + add_compile_definitions(ENABLE_MODULE_RANGEPROOF=1) +endif() + +if(SECP256K1_ENABLE_MODULE_GENERATOR) + add_compile_definitions(ENABLE_MODULE_GENERATOR=1) +endif() + + if(SECP256K1_ENABLE_MODULE_ELLSWIFT) add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) endif() @@ -292,6 +354,14 @@ message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOV message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") +message(" generator ........................... ${SECP256K1_ENABLE_MODULE_GENERATOR}") +message(" rangeproof .......................... ${SECP256K1_ENABLE_MODULE_RANGEPROOF}") +message(" surjectionproof ..................... ${SECP256K1_ENABLE_MODULE_SURJECTIONPROOF}") +message(" whitelist ........................... ${SECP256K1_ENABLE_MODULE_WHITELIST}") +message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}") +message(" ecdsa-s2c ........................... ${SECP256K1_ENABLE_MODULE_ECDSA_S2C}") +message(" ecdsa-adaptor ....................... ${SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR}") +message(" bppp ................................ ${SECP256K1_ENABLE_MODULE_BPPP}") message("Parameters:") message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") message(" ecmult gen precision bits ........... ${SECP256K1_ECMULT_GEN_PREC_BITS}") diff --git a/README.md b/README.md index 6d9676ad..88bdb2ba 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,43 @@ Building with Autotools To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-musig`. +Building with CMake (experimental) +---------------------------------- + +To maintain a pristine source tree, CMake encourages to perform an out-of-source build by using a separate dedicated build tree. + +### Building on POSIX systems + + $ mkdir build && cd build + $ cmake .. + $ cmake --build . + $ ctest # run the test suite + $ sudo cmake --build . --target install # optional + +To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake .. -LH` to see the full list of available flags. + +### Cross compiling + +To alleviate issues with cross compiling, preconfigured toolchain files are available in the `cmake` directory. +For example, to cross compile for Windows: + + $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/x86_64-w64-mingw32.toolchain.cmake + +To cross compile for Android with [NDK](https://developer.android.com/ndk/guides/cmake) (using NDK's toolchain file, and assuming the `ANDROID_NDK_ROOT` environment variable has been set): + + $ cmake .. -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28 + +### Building on Windows + +To build on Windows with Visual Studio, a proper [generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) must be specified for a new build tree. + +The following example assumes using of Visual Studio 2022 and CMake v3.21+. + +In "Developer Command Prompt for VS 2022": + + >cmake -G "Visual Studio 17 2022" -A x64 -S . -B build + >cmake --build build --config RelWithDebInfo + Usage examples ----------- diff --git a/configure.ac b/configure.ac index 98da1335..fe2cb970 100644 --- a/configure.ac +++ b/configure.ac @@ -454,14 +454,14 @@ if test x"$enable_module_bppp" = x"yes"; then enable_module_generator=yes fi -if test x"$enable_module_ecdsa_adaptor" = x"yes"; then - SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1" -fi - if test x"$enable_module_ecdsa_s2c" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_S2C=1" fi +if test x"$enable_module_ecdsa_adaptor" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1" +fi + if test x"$enable_module_musig" = x"yes"; then if test x"$enable_module_schnorrsig" = x"no"; then AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.]) @@ -498,6 +498,7 @@ if test x"$enable_module_generator" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_GENERATOR=1" fi + if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cbaeb91..27e90204 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,31 @@ if(SECP256K1_INSTALL) "${PROJECT_SOURCE_DIR}/include/secp256k1.h" "${PROJECT_SOURCE_DIR}/include/secp256k1_preallocated.h" ) + if(SECP256K1_ENABLE_MODULE_BPPP) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_bppp.h") + endif() + if(SECP256K1_ENABLE_MODULE_ECDSA_S2C) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdsa_s2c.h") + endif() + if(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdsa_adaptor.h") + endif() + if(SECP256K1_ENABLE_MODULE_MUSIG) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h") + endif() + if(SECP256K1_ENABLE_MODULE_WHITELIST) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_whitelist.h") + endif() + if(SECP256K1_ENABLE_MODULE_SURJECTIONPROOF) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_surjectionproof.h") + endif() + if(SECP256K1_ENABLE_MODULE_RANGEPROOF) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_rangeproof.h") + endif() + if(SECP256K1_ENABLE_MODULE_GENERATOR) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_generator.h") + endif() + if(SECP256K1_ENABLE_MODULE_ECDH) list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdh.h") endif() From 9de973f61376754e4125d8d2a5f1711ce421bd4b Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 16 Feb 2024 10:58:02 +0100 Subject: [PATCH 311/381] configure: Document canonical order of modules --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index fe2cb970..5fcce97d 100644 --- a/configure.ac +++ b/configure.ac @@ -610,6 +610,10 @@ AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE) AC_OUTPUT +# The order in which all modules are listed here should be considered the +# canonical order. This order (or, when appropriate, its reserve) should be used +# everywhere we list or process modules, i.e., here and in other build system +# files and docs. echo echo "Build Options:" echo " with external callbacks = $enable_external_default_callbacks" From 0873358f774d913c4e370d0d0218b61f92c9d171 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 5 Feb 2024 17:00:18 +0100 Subject: [PATCH 312/381] configure: Reorder modules also for AC_ARG_ENABLE --- configure.ac | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/configure.ac b/configure.ac index 5fcce97d..24c46090 100644 --- a/configure.ac +++ b/configure.ac @@ -168,39 +168,14 @@ AC_ARG_ENABLE(examples, AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) -AC_ARG_ENABLE(module_bppp, - AS_HELP_STRING([--enable-module-bppp],[enable Bulletproofs++ module (experimental)]), - [], - [SECP_SET_DEFAULT([enable_module_bppp], [no], [yes])]) - AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_ecdh], [yes], [yes])]) -AC_ARG_ENABLE(module_musig, - AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), - [], - [SECP_SET_DEFAULT([enable_module_musig], [no], [yes])]) - AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])]) -AC_ARG_ENABLE(module_generator, - AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]), - [], - [SECP_SET_DEFAULT([enable_module_generator], [no], [yes])]) - -AC_ARG_ENABLE(module_rangeproof, - AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]), - [], - [SECP_SET_DEFAULT([enable_module_rangeproof], [no], [yes])]) - -AC_ARG_ENABLE(module_whitelist, - AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), - [], - [SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])]) - AC_ARG_ENABLE(module_extrakeys, AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_extrakeys], [yes], [yes])]) @@ -213,11 +188,45 @@ AC_ARG_ENABLE(module_ellswift, AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) +AC_ARG_ENABLE(module_generator, + AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_generator], [no], [yes])]) + +AC_ARG_ENABLE(module_rangeproof, + AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_rangeproof], [no], [yes])]) + +AC_ARG_ENABLE(module_surjectionproof, + AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_surjectionproof], [no], [yes])]) +AC_ARG_ENABLE(reduced_surjection_proof_size, + AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), + [], + [SECP_SET_DEFAULT([use_reduced_surjection_proof_size], [no], [no])]) + +AC_ARG_ENABLE(module_whitelist, + AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), + [], + [SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])]) + +AC_ARG_ENABLE(module_musig, + AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), + [], + [SECP_SET_DEFAULT([enable_module_musig], [no], [yes])]) + AC_ARG_ENABLE(module_ecdsa_s2c, AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_ecdsa_s2c], [no], [yes])]) +AC_ARG_ENABLE(module_bppp, + AS_HELP_STRING([--enable-module-bppp],[enable Bulletproofs++ module (experimental)]), + [], + [SECP_SET_DEFAULT([enable_module_bppp], [no], [yes])]) + AC_ARG_ENABLE(module_ecdsa-adaptor, AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]), [], @@ -227,16 +236,6 @@ 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])]) -AC_ARG_ENABLE(module_surjectionproof, - AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), - [], - [SECP_SET_DEFAULT([enable_module_surjectionproof], [no], [yes])]) - -AC_ARG_ENABLE(reduced_surjection_proof_size, - AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), - [], - [SECP_SET_DEFAULT([use_reduced_surjection_proof_size], [no], [no])]) - # Test-only override of the (autodetected by the C code) "widemul" setting. # Legal values are: # * int64 (for [u]int64_t), @@ -627,6 +626,7 @@ echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" echo " module ellswift = $enable_module_ellswift" +# libsecp256k1-zkp modules, in the order they were added to the libsecp256k1-zkp echo " module generator = $enable_module_generator" echo " module rangeproof = $enable_module_rangeproof" echo " module surjectionproof = $enable_module_surjectionproof" From 860e3bb29461c05b495c6f027b6c5a62532738b5 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 5 Feb 2024 17:01:12 +0100 Subject: [PATCH 313/381] configure: Fix reduced surjection proof size The variable set automatically by AC_ARG_ENABLE is called enable_... --- configure.ac | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 24c46090..0f9a87df 100644 --- a/configure.ac +++ b/configure.ac @@ -205,7 +205,7 @@ AC_ARG_ENABLE(module_surjectionproof, AC_ARG_ENABLE(reduced_surjection_proof_size, AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), [], - [SECP_SET_DEFAULT([use_reduced_surjection_proof_size], [no], [no])]) + [SECP_SET_DEFAULT([enable_reduced_surjection_proof_size], [no], [no])]) AC_ARG_ENABLE(module_whitelist, AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), @@ -526,7 +526,7 @@ if test x"$enable_external_default_callbacks" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1" fi -if test x"$use_reduced_surjection_proof_size" = x"yes"; then +if test x"$enable_reduced_surjection_proof_size" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_REDUCED_SURJECTION_PROOF_SIZE=1" fi @@ -630,6 +630,9 @@ echo " module ellswift = $enable_module_ellswift" echo " module generator = $enable_module_generator" echo " module rangeproof = $enable_module_rangeproof" echo " module surjectionproof = $enable_module_surjectionproof" +if test x"$enable_module_surjectionproof" = x"yes" && test x"$enable_reduced_surjection_proof_size" = x"yes"; then +echo " reduced proof size = $enable_reduced_surjection_proof_size" +fi echo " module whitelist = $enable_module_whitelist" echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" From 3a9b1d46a31e8ca93a94752e08b514c06ebc2c0c Mon Sep 17 00:00:00 2001 From: Benedikt Date: Sun, 26 Nov 2023 16:44:23 +0100 Subject: [PATCH 314/381] New Experimental Module: Incremental Half-Aggregation for Schnorr Signatures --- .github/workflows/ci.yml | 33 +- Makefile.am | 4 + ci/ci.sh | 3 +- configure.ac | 35 +- include/secp256k1_schnorrsig_halfagg.h | 107 ++++++ .../schnorrsig_halfagg/Makefile.am.include | 3 + src/modules/schnorrsig_halfagg/main_impl.h | 202 +++++++++++ src/modules/schnorrsig_halfagg/tests_impl.h | 338 ++++++++++++++++++ src/secp256k1.c | 4 + src/tests.c | 8 + 10 files changed, 714 insertions(+), 23 deletions(-) create mode 100644 include/secp256k1_schnorrsig_halfagg.h create mode 100644 src/modules/schnorrsig_halfagg/Makefile.am.include create mode 100644 src/modules/schnorrsig_halfagg/main_impl.h create mode 100644 src/modules/schnorrsig_halfagg/tests_impl.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d1f765e..36293f13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ env: MUSIG: 'no' ECDSAADAPTOR: 'no' BPPP: 'no' + SCHNORRSIG_HALFAGG: 'no' ### test options SECP256K1_TEST_ITERS: BENCH: 'yes' @@ -78,14 +79,14 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} - env_vars: { WIDEMUL: 'int128' } - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes'} + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } - - env_vars: { RECOVERY: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes'} - - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { RECOVERY: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } @@ -156,6 +157,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CC: ${{ matrix.cc }} steps: @@ -208,6 +210,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' steps: @@ -267,6 +270,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' steps: @@ -320,6 +324,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' strategy: @@ -383,6 +388,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' steps: @@ -443,6 +449,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' SECP256K1_TEST_ITERS: 2 @@ -502,6 +509,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' CFLAGS: '-fsanitize=undefined,address -g' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' @@ -567,6 +575,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'yes' CC: 'clang' SECP256K1_TEST_ITERS: 32 @@ -622,6 +631,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' CTIMETESTS: 'no' strategy: @@ -678,15 +688,15 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: @@ -805,6 +815,7 @@ jobs: MUSIG: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' + SCHNORRSIG_HALFAGG: 'yes' steps: - name: Checkout diff --git a/Makefile.am b/Makefile.am index 565860fc..329f86ca 100644 --- a/Makefile.am +++ b/Makefile.am @@ -265,6 +265,10 @@ EXTRA_DIST += src/wycheproof/WYCHEPROOF_COPYING EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json EXTRA_DIST += tools/tests_wycheproof_generate.py +if ENABLE_MODULE_SCHNORRSIG_HALFAGG +include src/modules/schnorrsig_halfagg/Makefile.am.include +endif + if ENABLE_MODULE_BPPP include src/modules/bppp/Makefile.am.include endif diff --git a/ci/ci.sh b/ci/ci.sh index f246d732..47c4ae67 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 ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ - EXPERIMENTAL ECDH RECOVERY SCHNORRSIG ELLSWIFT \ + EXPERIMENTAL ECDH RECOVERY SCHNORRSIG SCHNORRSIG_HALFAGG ELLSWIFT \ ECDSA_S2C GENERATOR RANGEPROOF WHITELIST MUSIG ECDSAADAPTOR BPPP \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ EXAMPLES \ @@ -82,6 +82,7 @@ esac --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ --with-valgrind="$WITH_VALGRIND" \ diff --git a/configure.ac b/configure.ac index 0f9a87df..4d2a6e67 100644 --- a/configure.ac +++ b/configure.ac @@ -184,6 +184,10 @@ AC_ARG_ENABLE(module_schnorrsig, AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])]) +AC_ARG_ENABLE(module_schnorrsig_halfagg, + AS_HELP_STRING([--enable-module-schnorrsig-halfagg],[enable schnorrsig half-aggregation module (experimental) [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_schnorrsig_halfagg], [no], [yes])]) + AC_ARG_ENABLE(module_ellswift, AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) @@ -445,6 +449,11 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" # Processing must be done in a reverse topological sorting of the dependency graph # (dependent module first). +if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG_HALFAGG=1" + enable_module_schnorrsig=yes +fi + if test x"$enable_module_bppp" = x"yes"; then if test x"$enable_module_generator" = x"no"; then AC_MSG_ERROR([Module dependency error: You have disabled the generator module explicitly, but it is required by the bppp module.]) @@ -497,7 +506,6 @@ if test x"$enable_module_generator" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_GENERATOR=1" fi - if test x"$enable_module_ellswift" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi @@ -544,6 +552,9 @@ else # module (which automatically enables the module dependencies) we want to # print an error for the dependent module, not the module dependency. Hence, # we first test dependent modules. + if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then + AC_MSG_ERROR([Schnorrsig Half-Aggregation module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_bppp" = x"yes"; then AC_MSG_ERROR([Bulletproofs++ module is experimental. Use --enable-experimental to allow.]) fi @@ -599,6 +610,7 @@ AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_BPPP], [test x"$enable_module_bppp" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG_HALFAGG], [test x"$enable_module_schnorrsig_halfagg" = x"yes"]) AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) @@ -638,18 +650,19 @@ echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" echo " module bppp = $enable_module_bppp" +echo " module schnorrsig-halfagg = $enable_module_schnorrsig_halfagg" echo -echo " asm = $set_asm" -echo " ecmult window size = $set_ecmult_window" -echo " ecmult gen prec. bits = $set_ecmult_gen_precision" +echo " asm = $set_asm" +echo " ecmult window size = $set_ecmult_window" +echo " ecmult gen prec. bits = $set_ecmult_gen_precision" # 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" diff --git a/include/secp256k1_schnorrsig_halfagg.h b/include/secp256k1_schnorrsig_halfagg.h new file mode 100644 index 00000000..39eb5080 --- /dev/null +++ b/include/secp256k1_schnorrsig_halfagg.h @@ -0,0 +1,107 @@ +#ifndef SECP256K1_SCHNORRSIG_HALFAGG_H +#define SECP256K1_SCHNORRSIG_HALFAGG_H + +#include "secp256k1.h" +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** Incrementally (Half-)Aggregate a sequence of Schnorr + * signatures to an existing half-aggregate signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: a secp256k1 context object. + * In/Out: aggsig: pointer to the serialized aggregate signature + * that is input. The first 32*(n_before+1) of this + * array should hold the input aggsig. It will be + * overwritten by the new serialized aggregate signature. + * It should be large enough for that, see aggsig_len. + * aggsig_len: size of aggsig array in bytes. + * Should be large enough to hold the new + * serialized aggregate signature, i.e., + * should satisfy aggsig_size >= 32*(n_before+n_new+1). + * It will be overwritten to be the exact size of the + * resulting aggsig. + * In: all_pubkeys: Array of (n_before + n_new) many x-only public keys, + * including both the ones for the already aggregated signature + * and the ones for the signatures that should be added. + * Can only be NULL if n_before + n_new is 0. + * all_msgs32: Array of (n_before + n_new) many 32-byte messages, + * including both the ones for the already aggregated signature + * and the ones for the signatures that should be added. + * Can only be NULL if n_before + n_new is 0. + * new_sigs64: Array of n_new many 64-byte signatures, containing the new + * signatures that should be added. Can only be NULL if n_new is 0. + * n_before: Number of signatures that have already been aggregated + * in the input aggregate signature. + * n_new: Number of signatures that should now be added + * to the aggregate signature. + */ +SECP256K1_API int secp256k1_schnorrsig_inc_aggregate( + const secp256k1_context *ctx, + unsigned char *aggsig, + size_t *aggsig_len, + const secp256k1_xonly_pubkey* all_pubkeys, + const unsigned char *all_msgs32, + const unsigned char *new_sigs64, + size_t n_before, + size_t n_new +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** (Half-)Aggregate a sequence of Schnorr signatures. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: a secp256k1 context object. + * Out: aggsig: pointer to an array of aggsig_len many bytes to + * store the serialized aggregate signature. + * In/Out: aggsig_len: size of the aggsig array that is passed in bytes; + * will be overwritten to be the exact size of aggsig. + * In: pubkeys: Array of n many x-only public keys. + * Can only be NULL if n is 0. + * msgs32: Array of n many 32-byte messages. + * Can only be NULL if n is 0. + * sigs64: Array of n many 64-byte signatures. + * Can only be NULL if n is 0. + * n: number of signatures to be aggregated. + */ +SECP256K1_API int secp256k1_schnorrsig_aggregate( + const secp256k1_context *ctx, + unsigned char *aggsig, + size_t *aggsig_len, + const secp256k1_xonly_pubkey *pubkeys, + const unsigned char *msgs32, + const unsigned char *sigs64, + size_t n +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a (Half-)aggregate Schnorr signature. + * + * Returns: 1: correct signature. + * 0: incorrect signature. + * Args: ctx: a secp256k1 context object. + * In: pubkeys: Array of n many x-only public keys. Can only be NULL if n is 0. + * msgs32: Array of n many 32-byte messages. Can only be NULL if n is 0. + * n: number of signatures to that have been aggregated. + * aggsig: Pointer to an array of aggsig_size many bytes + * containing the serialized aggregate + * signature to be verified. + * aggsig_len: Size of the aggregate signature in bytes. + * Should be aggsig_len = 32*(n+1) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_aggverify( + const secp256k1_context *ctx, + const secp256k1_xonly_pubkey *pubkeys, + const unsigned char *msgs32, + size_t n, + const unsigned char *aggsig, + size_t aggsig_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_HALFAGG_H */ diff --git a/src/modules/schnorrsig_halfagg/Makefile.am.include b/src/modules/schnorrsig_halfagg/Makefile.am.include new file mode 100644 index 00000000..8cc04074 --- /dev/null +++ b/src/modules/schnorrsig_halfagg/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_schnorrsig_halfagg.h +noinst_HEADERS += src/modules/schnorrsig_halfagg/main_impl.h +noinst_HEADERS += src/modules/schnorrsig_halfagg/tests_impl.h diff --git a/src/modules/schnorrsig_halfagg/main_impl.h b/src/modules/schnorrsig_halfagg/main_impl.h new file mode 100644 index 00000000..7eac1079 --- /dev/null +++ b/src/modules/schnorrsig_halfagg/main_impl.h @@ -0,0 +1,202 @@ +#ifndef SECP256K1_MODULE_SCHNORRSIG_HALFAGG_MAIN_H +#define SECP256K1_MODULE_SCHNORRSIG_HALFAGG_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_schnorrsig.h" +#include "../../../include/secp256k1_schnorrsig_halfagg.h" +#include "../../hash.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("HalfAgg/randomizer")||SHA256("HalfAgg/randomizer"). */ +void secp256k1_schnorrsig_sha256_tagged_aggregation(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xd11f5532ul; + sha->s[1] = 0xfa57f70ful; + sha->s[2] = 0x5db0d728ul; + sha->s[3] = 0xf806ffe1ul; + sha->s[4] = 0x1d4db069ul; + sha->s[5] = 0xb4d587e1ul; + sha->s[6] = 0x50451c2aul; + sha->s[7] = 0x10fb63e9ul; + + sha->bytes = 64; +} + +int secp256k1_schnorrsig_inc_aggregate(const secp256k1_context *ctx, unsigned char *aggsig, size_t *aggsig_len, const secp256k1_xonly_pubkey *all_pubkeys, const unsigned char *all_msgs32, const unsigned char *new_sigs64, size_t n_before, size_t n_new) { + size_t i; + size_t n; + secp256k1_sha256 hash; + secp256k1_scalar s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(aggsig != NULL); + ARG_CHECK(aggsig_len != NULL); + ARG_CHECK(new_sigs64 != NULL || n_new == 0); + + /* Check that aggsig_len is large enough, i.e. aggsig_len >= 32*(n+1) */ + n = n_before + n_new; + ARG_CHECK(n >= n_before); + ARG_CHECK(all_pubkeys != NULL || n == 0); + ARG_CHECK(all_msgs32 != NULL || n == 0); + if ((*aggsig_len / 32) <= 0 || ((*aggsig_len / 32) - 1) < n) { + return 0; + } + + /* Prepare hash with common prefix. The prefix is the tag and */ + /* r_0 || pk_0 || m_0 || .... || r_{n'-1} || pk_{n'-1} || m_{n'-1} */ + /* where n' = n_before */ + secp256k1_schnorrsig_sha256_tagged_aggregation(&hash); + for (i = 0; i < n_before; ++i) { + /* serialize pk_i */ + unsigned char pk_ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &all_pubkeys[i])) { + return 0; + } + /* write r_i */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + /* write pk_i */ + secp256k1_sha256_write(&hash, pk_ser, 32); + /* write m_i*/ + secp256k1_sha256_write(&hash, &all_msgs32[i*32], 32); + } + + /* Compute s = s_old + sum_{i = n_before}^{n} z_i*s_i */ + /* where s_old = 0 if n_before = 0 */ + secp256k1_scalar_set_int(&s, 0); + if (n_before > 0) secp256k1_scalar_set_b32(&s, &aggsig[n_before*32], NULL); + for (i = n_before; i < n; ++i) { + unsigned char pk_ser[32]; + unsigned char hashoutput[32]; + secp256k1_sha256 hashcopy; + secp256k1_scalar si; + secp256k1_scalar zi; + + /* Step 0: Serialize pk_i into pk_ser */ + if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, &all_pubkeys[i])) { + return 0; + } + + /* Step 1: z_i = TaggedHash(...) */ + /* 1.a) Write into hash r_i, pk_i, m_i, r_i */ + secp256k1_sha256_write(&hash, &new_sigs64[(i-n_before)*64], 32); + secp256k1_sha256_write(&hash, pk_ser, 32); + secp256k1_sha256_write(&hash, &all_msgs32[i*32], 32); + /* 1.b) Copy the hash */ + hashcopy = hash; + /* 1.c) Finalize the copy to get zi*/ + secp256k1_sha256_finalize(&hashcopy, hashoutput); + /* Note: No need to check overflow, comes from hash */ + secp256k1_scalar_set_b32(&zi, hashoutput, NULL); + + /* Step 2: s := s + zi*si */ + /* except if i == 0, then zi = 1 implicitly */ + secp256k1_scalar_set_b32(&si, &new_sigs64[(i-n_before)*64+32], NULL); + if (i != 0) secp256k1_scalar_mul(&si, &si, &zi); + secp256k1_scalar_add(&s, &s, &si); + } + + /* copy new rs into aggsig */ + for (i = n_before; i < n; ++i) { + memcpy(&aggsig[i*32], &new_sigs64[(i-n_before)*64], 32); + } + /* copy new s into aggsig */ + secp256k1_scalar_get_b32(&aggsig[n*32], &s); + *aggsig_len = 32 * (1 + n); + return 1; +} + +int secp256k1_schnorrsig_aggregate(const secp256k1_context *ctx, unsigned char *aggsig, size_t *aggsig_len, const secp256k1_xonly_pubkey *pubkeys, const unsigned char *msgs32, const unsigned char *sigs64, size_t n) { + return secp256k1_schnorrsig_inc_aggregate(ctx, aggsig, aggsig_len, pubkeys, msgs32, sigs64, 0, n); +} + +int secp256k1_schnorrsig_aggverify(const secp256k1_context *ctx, const secp256k1_xonly_pubkey *pubkeys, const unsigned char *msgs32, size_t n, const unsigned char *aggsig, size_t aggsig_len) { + size_t i; + secp256k1_gej lhs, rhs; + secp256k1_scalar s; + secp256k1_sha256 hash; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkeys != NULL || n == 0); + ARG_CHECK(msgs32 != NULL || n == 0); + ARG_CHECK(aggsig != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + + /* Check that aggsig_len is correct, i.e., aggsig_len = 32*(n+1) */ + if ((aggsig_len / 32) <= 0 || ((aggsig_len / 32)-1) != n || (aggsig_len % 32) != 0) { + return 0; + } + + /* Compute the rhs: */ + /* Set rhs = 0 */ + /* For each i in 0,.., n-1, do: */ + /* (1) z_i = TaggedHash(...) */ + /* (2) T_i = R_i+e_i*P_i */ + /* (3) rhs = rhs + z_i*T_i */ + secp256k1_gej_set_infinity(&rhs); + secp256k1_schnorrsig_sha256_tagged_aggregation(&hash); + for (i = 0; i < n; ++i) { + secp256k1_fe rx; + secp256k1_ge rp, pp; + secp256k1_scalar ei; + secp256k1_gej ppj, ti; + + unsigned char pk_ser[32]; + unsigned char hashoutput[32]; + secp256k1_sha256 hashcopy; + secp256k1_scalar zi; + + /* Step 0: Serialize pk_i into pk_ser */ + /* We need that in Step 1 and in Step 2 */ + if (!secp256k1_xonly_pubkey_load(ctx, &pp, &pubkeys[i])) { + return 0; + } + secp256k1_fe_get_b32(pk_ser, &pp.x); + + /* Step 1: z_i = TaggedHash(...) */ + /* 1.a) Write into hash r_i, pk_i, m_i, r_i */ + secp256k1_sha256_write(&hash, &aggsig[i*32], 32); + secp256k1_sha256_write(&hash, pk_ser, 32); + secp256k1_sha256_write(&hash, &msgs32[i*32], 32); + /* 1.b) Copy the hash */ + hashcopy = hash; + /* 1.c) Finalize the copy to get zi*/ + secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_scalar_set_b32(&zi, hashoutput, NULL); + + /* Step 2: T_i = R_i+e_i*P_i */ + /* 2.a) R_i = lift_x(int(r_i)); fail if that fails */ + if (!secp256k1_fe_set_b32_limit(&rx, &aggsig[i*32])) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&rp, &rx, 0)) { + return 0; + } + + /* 2.b) e_i = int(hash_{BIP0340/challenge}(bytes(r_i) || pk_i || m_i)) mod n */ + secp256k1_schnorrsig_challenge(&ei, &aggsig[i*32], &msgs32[i*32], 32, pk_ser); + secp256k1_gej_set_ge(&ppj, &pp); + /* 2.c) T_i = R_i + e_i*P_i */ + secp256k1_ecmult(&ti, &ppj, &ei, NULL); + secp256k1_gej_add_ge_var(&ti, &ti, &rp, NULL); + + /* Step 3: rhs = rhs + zi*T_i */ + /* Note that if i == 0, then zi = 1 implicitly */ + if (i != 0) secp256k1_ecmult(&ti, &ti, &zi, NULL); + secp256k1_gej_add_var(&rhs, &rhs, &ti, NULL); + } + + /* Compute the lhs as lhs = s*G */ + secp256k1_scalar_set_b32(&s, &aggsig[n*32], &overflow); + if (overflow) { + return 0; + } + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &lhs, &s); + + /* Check that lhs == rhs */ + secp256k1_gej_neg(&lhs, &lhs); + secp256k1_gej_add_var(&lhs, &lhs, &rhs, NULL); + return secp256k1_gej_is_infinity(&lhs); +} + +#endif diff --git a/src/modules/schnorrsig_halfagg/tests_impl.h b/src/modules/schnorrsig_halfagg/tests_impl.h new file mode 100644 index 00000000..16ac3ab3 --- /dev/null +++ b/src/modules/schnorrsig_halfagg/tests_impl.h @@ -0,0 +1,338 @@ +#ifndef SECP256K1_MODULE_SCHNORRSIG_HALFAGG_TESTS_H +#define SECP256K1_MODULE_SCHNORRSIG_HALFAGG_TESTS_H + +#include "../../../include/secp256k1_schnorrsig_halfagg.h" + +#define N_MAX 50 + +/* We test that the hash initialized by secp256k1_schnorrsig_sha256_tagged_aggregate + * has the expected state. */ +void test_schnorrsig_sha256_tagged_aggregate(void) { + unsigned char tag[18] = "HalfAgg/randomizer"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_schnorrsig_sha256_tagged_aggregation(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); +} + +/* Create n many x-only pubkeys and sigs for random messages */ +void test_schnorrsig_aggregate_input_helper(secp256k1_xonly_pubkey *pubkeys, unsigned char *msgs32, unsigned char *sigs64, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_testrand256(sk); + secp256k1_testrand256(&msgs32[i*32]); + + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pubkeys[i], NULL, &keypair)); + CHECK(secp256k1_schnorrsig_sign(CTX, &sigs64[i*64], &msgs32[i*32], &keypair, NULL)); + } +} + +/* In this test we create a bunch of Schnorr signatures, + * aggregate some of them in one shot, and then + * aggregate the others incrementally to the already aggregated ones. + * The aggregate signature should verify after both steps. */ +void test_schnorrsig_aggregate(void) { + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1) + 17]; + size_t aggsig_len = sizeof(aggsig); + + size_t n = secp256k1_testrand_int(N_MAX + 1); + size_t n_initial = secp256k1_testrand_int(n + 1); + size_t n_new = n - n_initial; + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + + /* Aggregate the first n_initial of them */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n_initial)); + /* Make sure that the aggregate signature verifies */ + CHECK(aggsig_len == 32*(n_initial + 1)); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n_initial, aggsig, aggsig_len)); + /* Aggregate the remaining n_new many signatures to the already existing ones */ + aggsig_len = sizeof(aggsig); + secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new); + /* Make sure that the aggregate signature verifies */ + CHECK(aggsig_len == 32*(n + 1)); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len)); + + /* Check that a direct aggregation of the n sigs yields an identical aggsig */ + { + unsigned char aggsig2[sizeof(aggsig)]; + size_t aggsig_len2 = sizeof(aggsig2); + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig2, &aggsig_len2, pubkeys, msgs32, sigs64, n)); + CHECK(aggsig_len == aggsig_len2); + CHECK(secp256k1_memcmp_var(aggsig, aggsig2, aggsig_len) == 0); + } +} + +/* This tests the verification test vectors from + * https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/hacspec-halfagg/tests/tests.rs#L78 . */ +void test_schnorrsig_aggverify_spec_vectors(void) { + /* Test vector 0 */ + { + size_t n = 0; + const unsigned char aggsig[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + size_t aggsig_len = sizeof(aggsig); + CHECK(secp256k1_schnorrsig_aggverify(CTX, NULL, NULL, n, aggsig, aggsig_len)); + } + /* Test vector 1 */ + { + size_t n = 1; + const unsigned char pubkeys_ser[1*32] = { + 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, + 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, + 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, + 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f + }; + secp256k1_xonly_pubkey pubkeys[1]; + const unsigned char msgs32[1*32] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 + }; + const unsigned char aggsig[1*32+32] = { + 0xb0, 0x70, 0xaa, 0xfc, 0xea, 0x43, 0x9a, 0x4f, + 0x6f, 0x1b, 0xbf, 0xc2, 0xeb, 0x66, 0xd2, 0x9d, + 0x24, 0xb0, 0xca, 0xb7, 0x4d, 0x6b, 0x74, 0x5c, + 0x3c, 0xfb, 0x00, 0x9c, 0xc8, 0xfe, 0x4a, 0xa8, + 0x0e, 0x06, 0x6c, 0x34, 0x81, 0x99, 0x36, 0x54, + 0x9f, 0xf4, 0x9b, 0x6f, 0xd4, 0xd4, 0x1e, 0xdf, + 0xc4, 0x01, 0xa3, 0x67, 0xb8, 0x7d, 0xdd, 0x59, + 0xfe, 0xe3, 0x81, 0x77, 0x96, 0x1c, 0x22, 0x5f, + }; + size_t aggsig_len = sizeof(aggsig); + size_t i; + for (i = 0; i < n; ++i) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pubkeys[i], &pubkeys_ser[i*32])); + } + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len)); + } + /* Test vector 2 */ + { + size_t n = 2; + const unsigned char pubkeys_ser[2*32] = { + 0x1b, 0x84, 0xc5, 0x56, 0x7b, 0x12, 0x64, 0x40, + 0x99, 0x5d, 0x3e, 0xd5, 0xaa, 0xba, 0x05, 0x65, + 0xd7, 0x1e, 0x18, 0x34, 0x60, 0x48, 0x19, 0xff, + 0x9c, 0x17, 0xf5, 0xe9, 0xd5, 0xdd, 0x07, 0x8f, + + 0x46, 0x27, 0x79, 0xad, 0x4a, 0xad, 0x39, 0x51, + 0x46, 0x14, 0x75, 0x1a, 0x71, 0x08, 0x5f, 0x2f, + 0x10, 0xe1, 0xc7, 0xa5, 0x93, 0xe4, 0xe0, 0x30, + 0xef, 0xb5, 0xb8, 0x72, 0x1c, 0xe5, 0x5b, 0x0b, + }; + secp256k1_xonly_pubkey pubkeys[2]; + const unsigned char msgs32[2*32] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + }; + const unsigned char aggsig[2*32+32] = { + 0xb0, 0x70, 0xaa, 0xfc, 0xea, 0x43, 0x9a, 0x4f, + 0x6f, 0x1b, 0xbf, 0xc2, 0xeb, 0x66, 0xd2, 0x9d, + 0x24, 0xb0, 0xca, 0xb7, 0x4d, 0x6b, 0x74, 0x5c, + 0x3c, 0xfb, 0x00, 0x9c, 0xc8, 0xfe, 0x4a, 0xa8, + 0xa3, 0xaf, 0xbd, 0xb4, 0x5a, 0x6a, 0x34, 0xbf, + 0x7c, 0x8c, 0x00, 0xf1, 0xb6, 0xd7, 0xe7, 0xd3, + 0x75, 0xb5, 0x45, 0x40, 0xf1, 0x37, 0x16, 0xc8, + 0x7b, 0x62, 0xe5, 0x1e, 0x2f, 0x4f, 0x22, 0xff, + 0xbf, 0x89, 0x13, 0xec, 0x53, 0x22, 0x6a, 0x34, + 0x89, 0x2d, 0x60, 0x25, 0x2a, 0x70, 0x52, 0x61, + 0x4c, 0xa7, 0x9a, 0xe9, 0x39, 0x98, 0x68, 0x28, + 0xd8, 0x1d, 0x23, 0x11, 0x95, 0x73, 0x71, 0xad, + }; + size_t aggsig_len = sizeof(aggsig); + size_t i; + for (i = 0; i < n; ++i) { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pubkeys[i], &pubkeys_ser[i*32])); + } + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len)); + } +} + +static void test_schnorrsig_aggregate_api(void) { + size_t n = secp256k1_testrand_int(N_MAX + 1); + size_t n_initial = secp256k1_testrand_int(n + 1); + size_t n_new = n - n_initial; + + /* Test preparation. */ + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1)]; + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + + /* Test body 1: Check API of function aggregate. */ + { + /* Should not accept NULL for aggsig or aggsig length */ + size_t aggsig_len = sizeof(aggsig); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, NULL, &aggsig_len, pubkeys, msgs32, sigs64, n_initial)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, NULL, pubkeys, msgs32, sigs64, n_initial)); + /* Should not accept NULL for keys, messages, or signatures if n_initial is not 0 */ + if (n_initial != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, NULL, msgs32, sigs64, n_initial)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, NULL, sigs64, n_initial)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, NULL, n_initial)); + } + } + + /* Test body 2: Check API of function inc_aggregate. */ + { + size_t aggsig_len = sizeof(aggsig); + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n_initial)); + aggsig_len = 32*(n+1); + /* Should not accept NULL for aggsig or aggsig length */ + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, NULL, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, NULL, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + /* Should not accept NULL for keys or messages if n is not 0 */ + if (n != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, NULL, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, NULL, &sigs64[n_initial*64], n_initial, n_new)); + } + /* Should not accept NULL for new_sigs64 if n_new is not 0 */ + if (n_new != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, NULL, n_initial, n_new)); + } + /* Should not accept overflowing number of sigs. */ + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], SIZE_MAX, SIZE_MAX)); + if (n_initial > 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, SIZE_MAX)); + } + /* Should reject if aggsig_len is too small. */ + aggsig_len = 32*n; + CHECK(secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new) == 0); + aggsig_len = 32*(n+1) - 1; + CHECK(secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new) == 0); + } + + /* Test body 3: Check API of function aggverify. */ + { + size_t aggsig_len = sizeof(aggsig); + CHECK(secp256k1_schnorrsig_inc_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, &sigs64[n_initial*64], n_initial, n_new)); + /* Should not accept NULL for keys or messages if n is not 0 */ + if (n != 0) { + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggverify(CTX, NULL, msgs32, n, aggsig, aggsig_len)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggverify(CTX, pubkeys, NULL, n, aggsig, aggsig_len)); + } + /* Should never accept NULL the aggsig */ + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, NULL, aggsig_len)); + /* Should reject for invalid aggsig_len. */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len + 1) == 0); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len - 1) == 0); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len + 32) == 0); + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len - 32) == 0); + } +} + +/* In this test, we make sure that trivial attempts to break + * the security of verification do not work. */ +static void test_schnorrsig_aggregate_unforge(void) { + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1)]; + + size_t n = secp256k1_testrand_int(N_MAX + 1); + + /* Test 1: We fix a set of n messages and compute + * a random aggsig for them. This should not verify. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + { + size_t aggsig_len = sizeof(aggsig); + size_t i; + /* Sample aggsig randomly */ + for (i = 0; i < n + 1; ++i) { + secp256k1_testrand256(&aggsig[i*32]); + } + /* Make sure that it does not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } + + /* Test 2: We fix a set of n messages and compute valid + * signatures for all but one. The resulting aggregate signature + * should not verify. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + if (n > 0) { + size_t aggsig_len = sizeof(aggsig); + /* Replace a randomly chosen real sig with a random one. */ + size_t k = secp256k1_testrand_int(n); + secp256k1_testrand256(&sigs64[k*64]); + secp256k1_testrand256(&sigs64[k*64+32]); + /* Aggregate the n signatures */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n)); + /* Make sure the result does not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } + + /* Test 3: We generate a valid aggregate signature and then + * change one of the messages. This should not verify. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + if (n > 0) { + size_t aggsig_len = sizeof(aggsig); + size_t k; + /* Aggregate the n signatures */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n)); + /* Change one of the messages */ + k = secp256k1_testrand_int(32*n); + msgs32[k] = msgs32[k]^0xff; + /* Make sure the result does not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } +} + +/* In this test, we make sure that the algorithms properly reject + * for overflowing and non parseable values. */ +static void test_schnorrsig_aggregate_overflow(void) { + secp256k1_xonly_pubkey pubkeys[N_MAX]; + unsigned char msgs32[N_MAX*32]; + unsigned char sigs64[N_MAX*64]; + unsigned char aggsig[32*(N_MAX + 1)]; + size_t n = secp256k1_testrand_int(N_MAX + 1); + + /* We check that verification returns 0 if the s in aggsig overflows. */ + test_schnorrsig_aggregate_input_helper(pubkeys, msgs32, sigs64, n); + { + size_t aggsig_len = sizeof(aggsig); + /* Aggregate */ + CHECK(secp256k1_schnorrsig_aggregate(CTX, aggsig, &aggsig_len, pubkeys, msgs32, sigs64, n)); + /* Make s in the aggsig overflow */ + memset(&aggsig[n*32], 0xFF, 32); + /* Should not verify */ + CHECK(secp256k1_schnorrsig_aggverify(CTX, pubkeys, msgs32, n, aggsig, aggsig_len) == 0); + } +} + +static void run_schnorrsig_halfagg_tests(void) { + int i; + + test_schnorrsig_sha256_tagged_aggregate(); + test_schnorrsig_aggverify_spec_vectors(); + + for (i = 0; i < COUNT; i++) { + test_schnorrsig_aggregate(); + test_schnorrsig_aggregate_api(); + test_schnorrsig_aggregate_unforge(); + test_schnorrsig_aggregate_overflow(); + } +} + +#undef N_MAX + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 0acf9f86..4c578269 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -873,6 +873,10 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { # include "modules/schnorrsig/main_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG +# include "modules/schnorrsig_halfagg/main_impl.h" +#endif + #ifdef ENABLE_MODULE_ELLSWIFT # include "modules/ellswift/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index 2d6e1062..7c2f30e3 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7446,6 +7446,10 @@ static void run_ecdsa_wycheproof(void) { test_ecdsa_wycheproof(); } +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG +# include "modules/schnorrsig_halfagg/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_BPPP # include "modules/bppp/tests_impl.h" #endif @@ -7818,6 +7822,10 @@ int main(int argc, char **argv) { /* EC key arithmetic test */ run_eckey_negate_test(); +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG + run_schnorrsig_halfagg_tests(); +#endif + #ifdef ENABLE_MODULE_BPPP run_bppp_tests(); #endif From 7040a2024795a7e3758c7ab604ab440652c9772f Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Tue, 7 May 2024 19:38:51 +0200 Subject: [PATCH 315/381] doc: fix sage code for deriving alternative generator H The expression `G.decode('hex')` fails with the following error message on Sage 9.5: AttributeError: 'str' object has no attribute 'decode' Fix that by converting the hex-string to bytes using `bytes.fromhex`. --- src/modules/generator/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 28536694..f1cd0779 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -24,7 +24,7 @@ import hashlib F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' - H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) + H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(bytes.fromhex(G)).hexdigest(),16))) print('%x %x' % H.xy()) */ static const secp256k1_generator secp256k1_generator_h_internal = {{ From 5e7c2c178dc22779ad0f23d39aea39fba0746687 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 10 May 2024 13:25:56 +0000 Subject: [PATCH 316/381] generator: massively speed up serialization `secp256k1_pedersen_commit_serialize` would call `_load` (which does a sqrt to fully decompress the key, then a conditional negation based on the flag), then check the Jacobian symbol of the resulting y-coordinate, then re-serialize based on this. Instead, don't do any of this stuff. Copy the flag directly out of the internal representation and copy the x-coordinate directly out of the internal representation. Checked that none of the other _serialize methods in the modules do this. Fixes #293 --- src/modules/generator/main_impl.h | 8 +------- src/modules/generator/tests_impl.h | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index f1cd0779..c20d4cc3 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -296,17 +296,11 @@ int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_ } int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { - secp256k1_ge ge; - VERIFY_CHECK(ctx != NULL); ARG_CHECK(output != NULL); ARG_CHECK(commit != NULL); - secp256k1_pedersen_commitment_load(&ge, commit); - - output[0] = 9 ^ secp256k1_fe_is_square_var(&ge.y); - secp256k1_fe_normalize_var(&ge.x); - secp256k1_fe_get_b32(&output[1], &ge.x); + memcpy(output, commit->data, 33); return 1; } diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 14a993b9..f5a34bc6 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -264,7 +264,13 @@ static void test_pedersen(void) { } CHECK(secp256k1_pedersen_blind_sum(CTX, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); for (i = 0; i < total; i++) { + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + CHECK(secp256k1_pedersen_commit(CTX, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commitment_serialize(CTX, result, &commits[i])); + CHECK(secp256k1_pedersen_commitment_parse(CTX, &parse, result)); + CHECK(secp256k1_memcmp_var(&commits[i], &parse, 33) == 0); } CHECK(secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[inputs], outputs, cptr, inputs)); From 6361266013ad14428c89334013c74f8dec6f8e9d Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 16 May 2024 14:10:52 +0000 Subject: [PATCH 317/381] generator: speed up parsing Similar to speeding up serialization; in our parsing logic we did a bunch of expensive stuff then expensively inverted it. Drop everything except the essential checks and then memcpy. --- src/modules/generator/main_impl.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index c20d4cc3..d3dce9fa 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -276,7 +276,6 @@ static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* co int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { secp256k1_fe x; - secp256k1_ge ge; VERIFY_CHECK(ctx != NULL); ARG_CHECK(commit != NULL); @@ -285,13 +284,11 @@ int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_ if ((input[0] & 0xFE) != 8 || !secp256k1_fe_set_b32_limit(&x, &input[1]) || - !secp256k1_ge_set_xquad(&ge, &x)) { + !secp256k1_ge_x_on_curve_var(&x)) { return 0; } - if (input[0] & 1) { - secp256k1_ge_neg(&ge, &ge); - } - secp256k1_pedersen_commitment_save(commit, &ge); + + memcpy(commit->data, input, 33); return 1; } From 3a1c39625e7f9c05234df71c35bee3dae5b785e6 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 19 Jun 2024 15:27:06 +0000 Subject: [PATCH 318/381] rangeproof: add unit test for malleating single-value proofs I was a bit confused reading `secp256k1_rangeproof_getheader_impl` because in the case of single-value proofs (`has_nz_range == 0`) some bits of the header are unconstrained. At first I thought this was a malleability vector. And I think I've had this same confusion in the past. But in fact it is not a malleability vector because the whole header gets hashed into the proof. Add a unit test to confirm this to reduce future confusion. --- src/modules/rangeproof/tests_impl.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index d1abe204..49cc16cf 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -422,6 +422,7 @@ static void test_single_value_proof(uint64_t val) { uint64_t val_out = 0; size_t m_len_out = 0; + size_t i; secp256k1_testrand256(blind); secp256k1_testrand256(nonce); @@ -463,6 +464,30 @@ static void test_single_value_proof(uint64_t val) { CHECK(plen == 73); } + /* Test if trailing bytes are rejected. */ + proof[plen] = 0; + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_val_out, &max_val_out, + &commit, + proof, plen + 1, + NULL, 0, + secp256k1_generator_h + ) == 0); + /* Test if single-bit malleation is caught */ + for (i = 0; i < plen*8; i++) { + proof[i >> 3] ^= 1 << (i & 7); + CHECK(secp256k1_rangeproof_verify( + CTX, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + ) == 0); + proof[i >> 3] ^= 1 << (i & 7); + } + /* Test if unchanged proof is accepted. */ CHECK(secp256k1_rangeproof_verify( CTX, &min_val_out, &max_val_out, From 83d0fa25a826d716f809e00ef0a67385b776aec9 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 8 Sep 2025 19:18:39 +0000 Subject: [PATCH 319/381] extrakeys: fix pubkey_sort_cmp test Instead of providing CTX directly, pass a cmp_data object containing CTX. Otherwise, memory sanitizer fails with "use-of-uninitialized-value". --- src/modules/extrakeys/tests_impl.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 60299ce0..1f10ceed 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -605,7 +605,9 @@ static void test_sort(void) { } secp256k1_pubkey_sort(CTX, pk_ptr, 5); for (j = 1; j < 5; j++) { - CHECK(secp256k1_pubkey_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], CTX) <= 0); + secp256k1_pubkey_sort_cmp_data cmp_data; + cmp_data.ctx = CTX; + CHECK(secp256k1_pubkey_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], &cmp_data) <= 0); } } } From 654a8c327c6a22989007a3e864a8c917336fc783 Mon Sep 17 00:00:00 2001 From: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> Date: Thu, 15 Aug 2024 17:43:29 +0200 Subject: [PATCH 320/381] refactor: Use array initialization for unterminated strings The previous code is correct and harmless to initialize an array with a non-terminated character sequence using a string literal. However, it requires exactly specifying the array size, which can be cumbersome. Also, GCC-15 may issue the -Wunterminated-string-initialization warning. [1] Fix both issues by using array initialization. This refactoring commit does not change behavior. [1] Example warning: src/modules/schnorrsig/main_impl.h:48:46: error: initializer-string for array of 'unsigned char' is too long [-Werror=unterminated-string-initialization] 48 | static const unsigned char bip340_algo[13] = "BIP0340/nonce"; | ^~~~~~~~~~~~~~~ (cherry picked from commit fa67b6752d8ba3e4c41f6c36b1c6b94a21770419) Conflicts: src/testrand_impl.h (kept local name `secp256k1_testrand_seed`) --- examples/schnorr.c | 4 ++-- src/modules/ellswift/tests_impl.h | 6 +++--- src/modules/schnorrsig/main_impl.h | 2 +- src/modules/schnorrsig/tests_impl.h | 10 +++++----- src/testrand_impl.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/schnorr.c b/examples/schnorr.c index 4c0dd1c1..f9bab637 100644 --- a/examples/schnorr.c +++ b/examples/schnorr.c @@ -18,9 +18,9 @@ #include "examples_util.h" int main(void) { - unsigned char msg[12] = "Hello World!"; + unsigned char msg[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'}; unsigned char msg_hash[32]; - unsigned char tag[17] = "my_fancy_protocol"; + unsigned char tag[] = {'m', 'y', '_', 'f', 'a', 'n', 'c', 'y', '_', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l'}; unsigned char seckey[32]; unsigned char randomize[32]; unsigned char auxiliary_rand[32]; diff --git a/src/modules/ellswift/tests_impl.h b/src/modules/ellswift/tests_impl.h index 7d1efbc4..fc163f39 100644 --- a/src/modules/ellswift/tests_impl.h +++ b/src/modules/ellswift/tests_impl.h @@ -406,9 +406,9 @@ void run_ellswift_tests(void) { /* Test hash initializers. */ { secp256k1_sha256 sha, sha_optimized; - static const unsigned char encode_tag[25] = "secp256k1_ellswift_encode"; - static const unsigned char create_tag[25] = "secp256k1_ellswift_create"; - static const unsigned char bip324_tag[26] = "bip324_ellswift_xonly_ecdh"; + static const unsigned char encode_tag[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'e', 'n', 'c', 'o', 'd', 'e'}; + static const unsigned char create_tag[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'c', 'r', 'e', 'a', 't', 'e'}; + static const unsigned char bip324_tag[] = {'b', 'i', 'p', '3', '2', '4', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'x', 'o', 'n', 'l', 'y', '_', 'e', 'c', 'd', 'h'}; /* Check that hash initialized by * secp256k1_ellswift_sha256_init_encode has the expected diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 26727e46..57f7eadd 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -45,7 +45,7 @@ static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 * /* algo argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340 * by using the correct tagged hash function. */ -static const unsigned char bip340_algo[13] = "BIP0340/nonce"; +static const unsigned char bip340_algo[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'n', 'o', 'n', 'c', 'e'}; static const unsigned char schnorrsig_extraparams_magic[4] = SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC; diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 8ada90a8..f1f4b608 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -21,9 +21,9 @@ static void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, s } static void run_nonce_function_bip340_tests(void) { - unsigned char tag[13] = "BIP0340/nonce"; - unsigned char aux_tag[11] = "BIP0340/aux"; - unsigned char algo[13] = "BIP0340/nonce"; + unsigned char tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'n', 'o', 'n', 'c', 'e'}; + unsigned char aux_tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'a', 'u', 'x'}; + unsigned char algo[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'n', 'o', 'n', 'c', 'e'}; size_t algolen = sizeof(algo); secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; @@ -158,7 +158,7 @@ static void test_schnorrsig_api(void) { /* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the * expected state. */ static void test_schnorrsig_sha256_tagged(void) { - unsigned char tag[17] = "BIP0340/challenge"; + unsigned char tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'c', 'h', 'a', 'l', 'l', 'e', 'n', 'g', 'e'}; secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; @@ -806,7 +806,7 @@ static void test_schnorrsig_sign(void) { unsigned char sk[32]; secp256k1_xonly_pubkey pk; secp256k1_keypair keypair; - const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + const unsigned char msg[] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'm', 's', 'g', ' ', 'f', 'o', 'r', ' ', 'a', ' ', 's', 'c', 'h', 'n', 'o', 'r', 'r', 's', 'i', 'g', '.', '.'}; unsigned char sig[64]; unsigned char sig2[64]; unsigned char zeros64[64] = { 0 }; diff --git a/src/testrand_impl.h b/src/testrand_impl.h index cff82a45..48645206 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -19,7 +19,7 @@ static uint64_t secp256k1_test_state[4]; SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) { - static const unsigned char PREFIX[19] = "secp256k1 test init"; + static const unsigned char PREFIX[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', ' ', 't', 'e', 's', 't', ' ', 'i', 'n', 'i', 't'}; unsigned char out32[32]; secp256k1_sha256 hash; int i; From c4570307ecb814d326c52cd79b58d1b027af8355 Mon Sep 17 00:00:00 2001 From: BEULAHEVANJALIN Date: Sat, 6 Sep 2025 19:25:24 +0530 Subject: [PATCH 321/381] test/fix: refactor unterminated string initializers to brace arrays - Mirror upstream fix (bitcoin-core/secp256k1 fa67b675) - Convert tags, test vectors, and constants in -zkp-only modules (ecdsa_adaptor, ecdsa_s2c, musig, bppp, rangeproof, schnorrsig_halfagg) - Avoid -Wunterminated-string-initialization without changing behavior --- src/modules/bppp/tests_impl.h | 2 +- src/modules/ecdsa_adaptor/dleq_impl.h | 2 +- src/modules/ecdsa_adaptor/main_impl.h | 2 +- src/modules/ecdsa_adaptor/tests_impl.h | 8 +++---- src/modules/ecdsa_s2c/tests_impl.h | 26 ++++++++++----------- src/modules/musig/tests_impl.h | 8 +++---- src/modules/rangeproof/tests_impl.h | 2 +- src/modules/schnorrsig_halfagg/tests_impl.h | 2 +- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 2ee2ac98..a2e29b84 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -103,7 +103,7 @@ static void test_bppp_generators_fixed(void) { } static void test_bppp_tagged_hash(void) { - unsigned char tag_data[29] = "Bulletproofs_pp/v0/commitment"; + unsigned char tag_data[] = {'B', 'u', 'l', 'l', 'e', 't', 'p', 'r', 'o', 'o', 'f', 's', '_', 'p', 'p', '/', 'v', '0', '/', 'c', 'o', 'm', 'm', 'i', 't', 'm', 'e', 'n', 't'}; secp256k1_sha256 sha; secp256k1_sha256 sha_cached; unsigned char output[32]; diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index 2660328c..ff946c2c 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -18,7 +18,7 @@ static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { } /* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ -static const unsigned char dleq_algo[4] = "DLEQ"; +static const unsigned char dleq_algo[] = {'D','L','E','Q'}; static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { unsigned char buf[33]; diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 77f1627b..d75764f4 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -98,7 +98,7 @@ static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(secp256k1_s } /* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ -static const unsigned char ecdsa_adaptor_algo[16] = "ECDSAadaptor/non"; +static const unsigned char ecdsa_adaptor_algo[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; /* Modified BIP-340 nonce function */ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *pk33, const unsigned char *algo, size_t algolen, void *data) { diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index c3b3d43d..46aec3d7 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -730,11 +730,11 @@ static void ecdsa_adaptor_test_sha256_eq(const secp256k1_sha256 *sha1, const sec } static void run_nonce_function_ecdsa_adaptor_tests(void) { - unsigned char tag[16] = "ECDSAadaptor/non"; - unsigned char aux_tag[16] = "ECDSAadaptor/aux"; - unsigned char algo[16] = "ECDSAadaptor/non"; + unsigned char tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; + unsigned char aux_tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'a', 'u', 'x'}; + unsigned char algo[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; size_t algolen = sizeof(algo); - unsigned char dleq_tag[4] = "DLEQ"; + unsigned char dleq_tag[] = {'D', 'L', 'E', 'Q'}; secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; unsigned char nonce[32]; diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index 7bbd8770..48f3a3a2 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -10,8 +10,8 @@ #include "../../../include/secp256k1_ecdsa_s2c.h" static void test_ecdsa_s2c_tagged_hash(void) { - unsigned char tag_data[14] = "s2c/ecdsa/data"; - unsigned char tag_point[15] = "s2c/ecdsa/point"; + unsigned char tag_data[] = {'s', '2', 'c', '/', 'e', 'c', 'd', 's', 'a', '/', 'd', 'a', 't', 'a'}; + unsigned char tag_point[] = {'s', '2', 'c', '/', 'e', 'c', 'd', 's', 'a', '/', 'p', 'o', 'i', 'n', 't'}; secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; unsigned char output[32]; @@ -78,10 +78,10 @@ static void run_s2c_opening_test(void) { static void test_ecdsa_s2c_api(void) { secp256k1_ecdsa_s2c_opening s2c_opening; secp256k1_ecdsa_signature sig; - const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"; - const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss"; - const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd"; - const unsigned char hostrand[32] = "hrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhr"; + const unsigned char msg[] = {'m', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm', 'm'}; + const unsigned char sec[] = {'s', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's', 's'}; + const unsigned char s2c_data[] = {'d', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd'}; + const unsigned char hostrand[] = {'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r', 'h', 'r'}; unsigned char hostrand_commitment[32]; secp256k1_pubkey pk; @@ -148,14 +148,14 @@ typedef struct { static ecdsa_s2c_test ecdsa_s2c_tests[] = { { - "\x1b\xf6\xfb\x42\xf4\x1e\xb8\x76\xc4\xd7\xaa\x0d\x67\x24\x2b\x00\xba\xab\x99\xdc\x20\x84\x49\x3e\x4e\x63\x27\x7f\xa1\xf7\x7f\x22", - "\x03\xf0\x30\xde\xf3\x18\x8c\x0f\x56\xfc\xea\x87\x43\x5b\x30\x76\x43\xf4\x5d\xaf\xe2\x2c\xbc\x82\xfd\x56\x03\x4f\xae\x97\x41\x7d\x3a", - "\x02\xdf\x63\x75\x5d\x1f\x32\x92\xbf\xfe\xd8\x29\x86\xb1\x06\x49\x7c\x93\xb1\xf8\xbd\xc0\x45\x4b\x6b\x0b\x0a\x47\x79\xc0\xef\x71\x88", + {0x1b, 0xf6, 0xfb, 0x42, 0xf4, 0x1e, 0xb8, 0x76, 0xc4, 0xd7, 0xaa, 0x0d, 0x67, 0x24, 0x2b, 0x00, 0xba, 0xab, 0x99, 0xdc, 0x20, 0x84, 0x49, 0x3e, 0x4e, 0x63, 0x27, 0x7f, 0xa1, 0xf7, 0x7f, 0x22}, + {0x03, 0xf0, 0x30, 0xde, 0xf3, 0x18, 0x8c, 0x0f, 0x56, 0xfc, 0xea, 0x87, 0x43, 0x5b, 0x30, 0x76, 0x43, 0xf4, 0x5d, 0xaf, 0xe2, 0x2c, 0xbc, 0x82, 0xfd, 0x56, 0x03, 0x4f, 0xae, 0x97, 0x41, 0x7d, 0x3a}, + {0x02, 0xdf, 0x63, 0x75, 0x5d, 0x1f, 0x32, 0x92, 0xbf, 0xfe, 0xd8, 0x29, 0x86, 0xb1, 0x06, 0x49, 0x7c, 0x93, 0xb1, 0xf8, 0xbd, 0xc0, 0x45, 0x4b, 0x6b, 0x0b, 0x0a, 0x47, 0x79, 0xc0, 0xef, 0x71, 0x88}, }, { - "\x35\x19\x9a\x8f\xbf\x84\xad\x6e\xf6\x9a\x18\x4c\x1b\x19\x28\x5b\xef\xbe\x06\xe6\x0b\x62\x64\xe6\xd3\x73\x89\x3f\x68\x55\xe2\x4a", - "\x03\x90\x17\x17\xce\x7c\x74\x84\xa2\xce\x1b\x7d\xc7\x40\x3b\x14\xe0\x35\x49\x71\x39\x3e\xc0\x92\xa7\xf3\xe0\xc8\xe4\xe2\xd2\x63\x9d", - "\x02\xc0\x4a\xc7\xf7\x71\xe8\xeb\xdb\xf3\x15\xff\x5e\x58\xb7\xfe\x95\x16\x10\x21\x03\x50\x00\x66\x17\x2c\x4f\xac\x5b\x20\xf9\xe0\xea", + {0x35, 0x19, 0x9a, 0x8f, 0xbf, 0x84, 0xad, 0x6e, 0xf6, 0x9a, 0x18, 0x4c, 0x1b, 0x19, 0x28, 0x5b, 0xef, 0xbe, 0x06, 0xe6, 0x0b, 0x62, 0x64, 0xe6, 0xd3, 0x73, 0x89, 0x3f, 0x68, 0x55, 0xe2, 0x4a}, + {0x03, 0x90, 0x17, 0x17, 0xce, 0x7c, 0x74, 0x84, 0xa2, 0xce, 0x1b, 0x7d, 0xc7, 0x40, 0x3b, 0x14, 0xe0, 0x35, 0x49, 0x71, 0x39, 0x3e, 0xc0, 0x92, 0xa7, 0xf3, 0xe0, 0xc8, 0xe4, 0xe2, 0xd2, 0x63, 0x9d}, + {0x02, 0xc0, 0x4a, 0xc7, 0xf7, 0x71, 0xe8, 0xeb, 0xdb, 0xf3, 0x15, 0xff, 0x5e, 0x58, 0xb7, 0xfe, 0x95, 0x16, 0x10, 0x21, 0x03, 0x50, 0x00, 0x66, 0x17, 0x2c, 0x4f, 0xac, 0x5b, 0x20, 0xf9, 0xe0, 0xea}, }, }; @@ -207,7 +207,7 @@ static void test_ecdsa_s2c_sign_verify(void) { { /* invalid privkeys */ unsigned char zero_privkey[32] = {0}; - unsigned char overflow_privkey[32] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; + unsigned char overflow_privkey[32] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, NULL, message, zero_privkey, s2c_data) == 0); CHECK(secp256k1_ecdsa_s2c_sign(CTX, &signature, NULL, message, overflow_privkey, s2c_data) == 0); } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 753b8ac5..d77aed66 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -579,8 +579,8 @@ static void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { int nonce_parity_b; unsigned char seed_a[2][32] = { "a0", "a1" }; unsigned char seed_b[2][32] = { "b0", "b1" }; - const unsigned char msg32_a[32] = "this is the message blockchain a"; - const unsigned char msg32_b[32] = "this is the message blockchain b"; + const unsigned char msg32_a[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'a'}; + const unsigned char msg32_b[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'b'}; int i; /* Step 1: key setup */ @@ -676,12 +676,12 @@ static void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char static void sha256_tag_test(void) { secp256k1_sha256 sha_tagged; { - char tag[11] = "KeyAgg list"; + char tag[] = {'K', 'e', 'y', 'A', 'g', 'g', ' ', 'l', 'i', 's', 't'}; secp256k1_musig_keyagglist_sha256(&sha_tagged); sha256_tag_test_internal(&sha_tagged, (unsigned char*)tag, sizeof(tag)); } { - char tag[18] = "KeyAgg coefficient"; + char tag[] = {'K', 'e', 'y', 'A', 'g', 'g', ' ', 'c', 'o', 'e', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 't'}; secp256k1_musig_keyaggcoef_sha256(&sha_tagged); sha256_tag_test_internal(&sha_tagged, (unsigned char*)tag, sizeof(tag)); } diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 49cc16cf..f2ca3214 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -414,7 +414,7 @@ static void test_single_value_proof(uint64_t val) { unsigned char blind[32]; unsigned char blind_out[32]; unsigned char nonce[32]; - const unsigned char message[1] = " "; /* no message will fit into a single-value proof */ + const unsigned char message[] = { ' ' }; /* no message will fit into a single-value proof */ unsigned char message_out[sizeof(proof)] = { 0 }; size_t plen = sizeof(proof); uint64_t min_val_out = 0; diff --git a/src/modules/schnorrsig_halfagg/tests_impl.h b/src/modules/schnorrsig_halfagg/tests_impl.h index 16ac3ab3..37099849 100644 --- a/src/modules/schnorrsig_halfagg/tests_impl.h +++ b/src/modules/schnorrsig_halfagg/tests_impl.h @@ -8,7 +8,7 @@ /* We test that the hash initialized by secp256k1_schnorrsig_sha256_tagged_aggregate * has the expected state. */ void test_schnorrsig_sha256_tagged_aggregate(void) { - unsigned char tag[18] = "HalfAgg/randomizer"; + unsigned char tag[] = {'H', 'a', 'l', 'f', 'A', 'g', 'g', '/', 'r', 'a', 'n', 'd', 'o', 'm', 'i', 'z', 'e', 'r'}; secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; From 40bd8549dd0a7dfcb885537cdbd2b1bc4a10b8a6 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 21 Jul 2025 14:47:57 +0200 Subject: [PATCH 322/381] musig/test: Remove dead code This avoids a compiler warning on clang-snapshot about &keypair being uninitialized. (cherry picked from commit 8d967a602b14c86d0f9e43a31fdfe76f39f32091) --- src/modules/musig/tests_impl.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index d77aed66..ed744e5b 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -1022,13 +1022,10 @@ static void musig_test_vectors_signverify(void) { CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); - musig_test_set_secnonce(&secnonce, vector->secnonces[c->secnonce_index], &pubkey); expected = c->error != MUSIG_SECNONCE; - if (expected) { - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); - } else { - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); - } + CHECK(!expected); + musig_test_set_secnonce(&secnonce, vector->secnonces[c->secnonce_index], &pubkey); + CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); } for (i = 0; i < sizeof(vector->verify_fail_case)/sizeof(vector->verify_fail_case[0]); i++) { const struct musig_verify_fail_error_case *c = &vector->verify_fail_case[i]; From 53fd89b6354842786aadf103a69ba5236ba7ac49 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 21 Jul 2025 14:08:23 +0000 Subject: [PATCH 323/381] musig/tests: initialize keypair The keypair is unused in musig_partial_sign, but clang-snapshot gives a compiler warning anyway. --- src/modules/musig/tests_impl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ed744e5b..ce99b367 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -1013,6 +1013,8 @@ static void musig_test_vectors_signverify(void) { if (!expected) { continue; } + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); + CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); expected = c->error != MUSIG_AGGNONCE; CHECK(expected == secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonces[c->aggnonce_index])); From 7d779c6d9d3a4f31b0ad69d6bb6b1b2ed6610218 Mon Sep 17 00:00:00 2001 From: BEULAHEVANJALIN Date: Mon, 8 Sep 2025 13:33:36 +0530 Subject: [PATCH 324/381] examples/musig: use brace-enclosed initializer for 32-byte msg Switch msg initialization from a string literal to a brace-enclosed array to avoid -Wunterminated-string-initialization. Upstream removed the trailing '!' from the message; this change retains it. --- examples/musig.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/musig.c b/examples/musig.c index 16dd12f3..d4a02195 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -169,7 +169,7 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr const secp256k1_pubkey *pubkeys_ptr[N_SIGNERS]; secp256k1_xonly_pubkey agg_pk; secp256k1_musig_keyagg_cache cache; - unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; + unsigned char msg[] = {'t', 'h', 'i', 's', '_', 'c', 'o', 'u', 'l', 'd', ' ', 'b', 'e', ' ', 't', 'h', 'e', '_', 'h', 'a', 's', 'h', '_', 'o', 'f', '_', 'a', '_', 'm', 's', 'g', '!'}; unsigned char sig[64]; /* Create a secp256k1 context */ From e639e6caa981f1b2461ff1c794ab13c39e51827e Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 15 Apr 2024 17:07:41 +0200 Subject: [PATCH 325/381] autotools: Disable eager MSan in ctime_tests Co-authored-by: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> --- build-aux/m4/bitcoin_secp.m4 | 12 ++++++++++++ configure.ac | 33 +++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/build-aux/m4/bitcoin_secp.m4 b/build-aux/m4/bitcoin_secp.m4 index 11adef4f..fee2d7b4 100644 --- a/build-aux/m4/bitcoin_secp.m4 +++ b/build-aux/m4/bitcoin_secp.m4 @@ -45,6 +45,18 @@ fi AC_MSG_RESULT($has_valgrind) ]) +AC_DEFUN([SECP_MSAN_CHECK], [ +AC_MSG_CHECKING(whether MemorySanitizer is enabled) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #if defined(__has_feature) + # if __has_feature(memory_sanitizer) + # error "MemorySanitizer is enabled." + # endif + #endif + ]])], [msan_enabled=no], [msan_enabled=yes]) +AC_MSG_RESULT([$msan_enabled]) +]) + dnl SECP_TRY_APPEND_CFLAGS(flags, VAR) dnl Append flags to VAR if CC accepts them. AC_DEFUN([SECP_TRY_APPEND_CFLAGS], [ diff --git a/configure.ac b/configure.ac index 4d2a6e67..a9c33550 100644 --- a/configure.ac +++ b/configure.ac @@ -296,6 +296,20 @@ if test x"$enable_ctime_tests" = x"auto"; then enable_ctime_tests=$enable_valgrind fi +print_msan_notice=no +if test x"$enable_ctime_tests" = x"yes" && test x"$GCC" = x"yes"; then + SECP_MSAN_CHECK + # MSan on Clang >=16 reports unitialized memory in function parameters and return values, even if + # the uninitalized variable is never actually "used". This is called "eager" checking, and it's + # sounds like good idea for normal use of MSan. However, it yields many false positives in the + # ctime_tests because many return values depend on secret (i.e., "uninitialized") values, and + # we're only interested in detecting branches (which count as "uses") on secret data. + if test x"$msan_enabled" = x"yes"; then + SECP_TRY_APPEND_CFLAGS([-fno-sanitize-memory-param-retval], SECP_CFLAGS) + print_msan_notice=yes + fi +fi + if test x"$enable_coverage" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1" SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS" @@ -660,9 +674,16 @@ if test x"$set_widemul" != xauto; then 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 + echo "Note:" + echo " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to SECP_CFLAGS" + echo " to avoid false positives in ctime_tests. Pass --disable-ctime-tests to avoid this." +fi From 76b3396516eef38251a1389f7fa982d46b922aab Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 22 May 2024 15:10:33 +0200 Subject: [PATCH 326/381] configure: Move "experimental" warning to bottom to make it more promiment --- configure.ac | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index a9c33550..673d6fbc 100644 --- a/configure.ac +++ b/configure.ac @@ -556,12 +556,7 @@ fi ### Check for --enable-experimental if necessary ### -if test x"$enable_experimental" = x"yes"; then - AC_MSG_NOTICE([******]) - AC_MSG_NOTICE([WARNING: experimental build]) - AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) - AC_MSG_NOTICE([******]) -else +if test x"$enable_experimental" = x"no"; then # The order of the following tests matters. If the user enables a dependent # module (which automatically enables the module dependencies) we want to # print an error for the dependent module, not the module dependency. Hence, @@ -687,3 +682,10 @@ if test x"$print_msan_notice" = x"yes"; then echo " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to SECP_CFLAGS" echo " to avoid false positives in ctime_tests. Pass --disable-ctime-tests to avoid this." fi + +if test x"$enable_experimental" = x"yes"; then + echo + echo "WARNING: Experimental build" + echo " Experimental features do not have stable APIs or properties, and may not be safe for" + echo " production use." +fi From fd259fe9ad320af4ea10ccf60f545f7f75ac18d4 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 22 May 2024 16:51:51 +0200 Subject: [PATCH 327/381] ci: Add job with -fsanitize-memory-param-retval --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36293f13..5dc8859e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -556,11 +556,18 @@ jobs: matrix: configuration: - env_vars: + CTIMETESTS: 'yes' CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -g' - env_vars: ECMULTGENPRECISION: 2 ECMULTWINDOW: 2 + CTIMETESTS: 'yes' CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -g -O3' + - env_vars: + # -fsanitize-memory-param-retval is clang's default, but our build system disables it + # when ctime_tests when enabled. + CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -fsanitize-memory-param-retval -g' + CTIMETESTS: 'no' env: ECDH: 'yes' @@ -576,7 +583,6 @@ jobs: ECDSAADAPTOR: 'yes' BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' - CTIMETESTS: 'yes' CC: 'clang' SECP256K1_TEST_ITERS: 32 ASM: 'no' From a1be8ed1b1e9f9ce2935a095bfa2b140a3971e1b Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:03:44 +0100 Subject: [PATCH 328/381] ci: Silent Homebrew's noisy reinstall warnings --- .github/workflows/ci.yml | 58 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5dc8859e..e6cde1a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -711,7 +711,7 @@ jobs: - name: Install Homebrew packages run: | - brew install automake libtool gcc + brew install --quiet automake libtool gcc ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc - name: Install and cache Valgrind @@ -739,6 +739,62 @@ jobs: run: env if: ${{ always() }} + arm64-macos-native: + name: "ARM64: macOS Sonoma" + # See: https://github.com/actions/runner-images#available-images. + runs-on: macos-14 + + env: + CC: 'clang' + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + WITH_VALGRIND: 'no' + CTIMETESTS: 'no' + + strategy: + fail-fast: false + matrix: + env_vars: + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' } + - BUILD: 'distcheck' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Homebrew packages + run: | + brew install --quiet automake libtool gcc + ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc + + - name: CI script + env: ${{ matrix.env_vars }} + run: ./ci/ci.sh + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + win64-native: name: ${{ matrix.configuration.job_name }} # See: https://github.com/actions/runner-images#available-images. From b9a82b481fc35c5db3a127185ffada77fe107d46 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Fri, 25 Oct 2024 08:18:02 +0100 Subject: [PATCH 329/381] ci: Update macOS image The macOS 12 GHA image has been deprecated since 2024-10-07. See: https://github.com/actions/runner-images/issues/10721 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6cde1a3..9a4f44b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -680,10 +680,10 @@ jobs: run: env if: ${{ always() }} - macos-native: - name: "x86_64: macOS Monterey" + x86_64-macos-native: + name: "x86_64: macOS Ventura, Valgrind" # See: https://github.com/actions/runner-images#available-images. - runs-on: macos-12 # Use M1 once available https://github.com/github/roadmap/issues/528 + runs-on: macos-13 env: CC: 'clang' From db8750de466aba8c141f5a960e6a91362a260c22 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 22 Jan 2026 09:09:27 +0000 Subject: [PATCH 330/381] sync-upstream: improve help text --- contrib/sync-upstream.sh | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index b910a599..26a842c0 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -3,18 +3,30 @@ set -eou pipefail help() { - echo "$0 [-b ] range [end]" - echo " merges every merge commit present in upstream and missing in (default: master)." - echo " If the optional [end] commit is provided, only merges up to [end]." - echo " If the optional [-b branch] provided, then ." + echo "Sync merge commits from bitcoin-core/secp256k1 into secp256k1-zkp." echo - echo "$0 [-b ] select ... " - echo " merges every selected merge commit into (default: master)" + echo "Usage:" + echo " $0 [-b ] range [end]" + echo " Merges every merge commit present in upstream/master and missing in " + echo " (default: master). If the optional [end] commit is provided, only merges" + echo " up to and including [end]." echo - echo "This tool creates a branch and a script that can be executed to create the" - echo "PR automatically. The script requires the github-cli tool (aka gh)." - echo "" - echo "Tip: \`git log --oneline upstream/master --merges\` shows merge commits." + echo " $0 [-b ] select ... " + echo " Merges every selected merge commit into (default: master)." + echo + echo "This tool creates a temporary branch and attempts to merge the upstream commits." + echo "If there are merge conflicts, resolve them and run tests, then use the generated" + echo "script contrib/gh-pr-create.sh to create the PR (requires the gh tool)." + echo + echo "Setup:" + echo " Requires a remote named 'upstream' pointing to bitcoin-core/secp256k1." + echo " The script will fetch it automatically, and offer to create it if missing." + echo " To add manually: git remote add upstream git@github.com:bitcoin-core/secp256k1.git" + echo + echo "Listing upstream merge commits:" + echo " To list merge commits in upstream/master that are missing from (oldest first):" + echo " git log --oneline --merges \$(git merge-base upstream/master )..upstream/master | tac" + echo " Use these for [end] in 'range' or as arguments to 'select'." exit 1 } From f9cf003d9b3c58e26de4ba6ed3d7f7d9b3e36328 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 26 Jan 2026 14:30:31 +0200 Subject: [PATCH 331/381] scalar: Port bitcoin-core/secp256k1#1512 to zkp-specific code --- src/scalar_4x64_impl.h | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 7ef726f7..11622b0f 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -882,7 +882,7 @@ static void secp256k1_scalar_mul_512(uint64_t *l8, const secp256k1_scalar *a, co #endif } -static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +static void secp256k1_scalar_sqr_512(uint64_t *l8, const secp256k1_scalar *a) { #ifdef USE_ASM_X86_64 __asm__ __volatile__( /* Preload */ @@ -893,7 +893,7 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { /* (rax,rdx) = a0 * a0 */ "movq %%r11, %%rax\n" "mulq %%r11\n" - /* Extract l0 */ + /* Extract l8[0] */ "movq %%rax, 0(%%rsi)\n" /* (r8,r9,r10) = (rdx,0) */ "movq %%rdx, %%r8\n" @@ -908,7 +908,7 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { "addq %%rax, %%r8\n" "adcq %%rdx, %%r9\n" "adcq $0, %%r10\n" - /* Extract l1 */ + /* Extract l8[1] */ "movq %%r8, 8(%%rsi)\n" "xorq %%r8, %%r8\n" /* (r9,r10,r8) += 2 * a0 * a2 */ @@ -926,7 +926,7 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { "addq %%rax, %%r9\n" "adcq %%rdx, %%r10\n" "adcq $0, %%r8\n" - /* Extract l2 */ + /* Extract l8[2] */ "movq %%r9, 16(%%rsi)\n" "xorq %%r9, %%r9\n" /* (r10,r8,r9) += 2 * a0 * a3 */ @@ -947,7 +947,7 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { "addq %%rax, %%r10\n" "adcq %%rdx, %%r8\n" "adcq $0, %%r9\n" - /* Extract l3 */ + /* Extract l8[3] */ "movq %%r10, 24(%%rsi)\n" "xorq %%r10, %%r10\n" /* (r8,r9,r10) += 2 * a1 * a3 */ @@ -965,7 +965,7 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { "addq %%rax, %%r8\n" "adcq %%rdx, %%r9\n" "adcq $0, %%r10\n" - /* Extract l4 */ + /* Extract l8[4] */ "movq %%r8, 32(%%rsi)\n" "xorq %%r8, %%r8\n" /* (r9,r10,r8) += 2 * a2 * a3 */ @@ -977,45 +977,48 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { "addq %%rax, %%r9\n" "adcq %%rdx, %%r10\n" "adcq $0, %%r8\n" - /* Extract l5 */ + /* Extract l8[5] */ "movq %%r9, 40(%%rsi)\n" /* (r10,r8) += a3 * a3 */ "movq %%r14, %%rax\n" "mulq %%r14\n" "addq %%rax, %%r10\n" "adcq %%rdx, %%r8\n" - /* Extract l6 */ + /* Extract l8[6] */ "movq %%r10, 48(%%rsi)\n" - /* Extract l7 */ + /* Extract l8[7] */ "movq %%r8, 56(%%rsi)\n" : - : "S"(l), "D"(a->d) + : "S"(l8), "D"(a->d) : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); + + SECP256K1_CHECKMEM_MSAN_DEFINE(l8, sizeof(*l8) * 8); + #else /* 160 bit accumulator. */ uint64_t c0 = 0, c1 = 0; uint32_t c2 = 0; - /* l[0..7] = a[0..3] * b[0..3]. */ + /* l8[0..7] = a[0..3] * b[0..3]. */ muladd_fast(a->d[0], a->d[0]); - extract_fast(l[0]); + extract_fast(l8[0]); muladd2(a->d[0], a->d[1]); - extract(l[1]); + extract(l8[1]); muladd2(a->d[0], a->d[2]); muladd(a->d[1], a->d[1]); - extract(l[2]); + extract(l8[2]); muladd2(a->d[0], a->d[3]); muladd2(a->d[1], a->d[2]); - extract(l[3]); + extract(l8[3]); muladd2(a->d[1], a->d[3]); muladd(a->d[2], a->d[2]); - extract(l[4]); + extract(l8[4]); muladd2(a->d[2], a->d[3]); - extract(l[5]); + extract(l8[5]); muladd_fast(a->d[3], a->d[3]); - extract_fast(l[6]); + extract_fast(l8[6]); VERIFY_CHECK(c1 == 0); - l[7] = c0; + l8[7] = c0; #endif } From 41cead8a0bb2fdccc93a04973ef6859dad93c150 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Feb 2026 15:50:58 +0100 Subject: [PATCH 332/381] sync-upstream: Extend git usage tips --- contrib/sync-upstream.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 26a842c0..c1ad8e0a 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -116,7 +116,17 @@ do done # Remove trailing "," TITLE=${TITLE%?} -BODY=$(printf "%s\n\n%s\n%s" "$BODY" "This PR can be recreated with \`$REPRODUCE_COMMAND\`." "Tip: Use \`git show --remerge-diff\` to show the changes manually added to the merge commit.") +BODY+=$(cat <\` to show the conflict resolution in the merge commit. + * Use \`git read-tree --reset -u \` to replay these resolutions during the conflict resolution stage when recreating the PR branch locally. + Be aware that this may discard your index as well as the uncommitted changes and untracked files in your worktree. +EOF +) echo "-----------------------------------" echo "$TITLE" From 96a415b1c0cdd7570c9d1cc80330c124daf557fd Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Thu, 5 Feb 2026 17:29:53 +0200 Subject: [PATCH 333/381] scalar: Port bitcoin-core/secp256k1#1393 to zkp-specific code --- src/scalar_4x64_impl.h | 2 ++ src/scalar_8x32_impl.h | 2 ++ src/scalar_low_impl.h | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index fb8d59e1..e797de0c 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -51,6 +51,8 @@ SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint6 r->d[1] = 0; r->d[2] = 0; r->d[3] = 0; + + SECP256K1_SCALAR_VERIFY(r); } SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index e541f211..4ca85c99 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -73,6 +73,8 @@ SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint6 r->d[5] = 0; r->d[6] = 0; r->d[7] = 0; + + SECP256K1_SCALAR_VERIFY(r); } SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 0e867815..04c4770d 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -30,7 +30,7 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { *r = v % EXHAUSTIVE_TEST_ORDER; - secp256k1_scalar_verify(r); + SECP256K1_SCALAR_VERIFY(r); } SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { From 2cb2e312e9e2e29725b449afd732b8b73074fffe Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Thu, 5 Feb 2026 19:02:49 +0200 Subject: [PATCH 334/381] extrakeys: Migrate to bitcoin-core/secp256k1#1518 secp256k1_ec_pubkey_sort --- include/secp256k1_extrakeys.h | 15 -- include/secp256k1_musig.h | 2 +- src/modules/extrakeys/Makefile.am.include | 4 +- src/modules/extrakeys/hsort.h | 22 --- src/modules/extrakeys/hsort_impl.h | 116 ------------- src/modules/extrakeys/main_impl.h | 35 ---- src/modules/extrakeys/tests_impl.h | 195 ---------------------- 7 files changed, 2 insertions(+), 387 deletions(-) delete mode 100644 src/modules/extrakeys/hsort.h delete mode 100644 src/modules/extrakeys/hsort_impl.h diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index 9f091163..ad70b92f 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -240,21 +240,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Sort public keys using lexicographic order of their compressed - * serialization. - * - * Returns: 0 if the arguments are invalid. 1 otherwise. - * - * Args: ctx: pointer to a context object - * In: pubkeys: array of pointers to pubkeys to sort - * n_pubkeys: number of elements in the pubkeys array - */ -SECP256K1_API int secp256k1_pubkey_sort( - const secp256k1_context *ctx, - const secp256k1_pubkey **pubkeys, - size_t n_pubkeys -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - #ifdef __cplusplus } #endif diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 28ecf1ef..0c539e8a 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -186,7 +186,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_parse( * * Different orders of `pubkeys` result in different `agg_pk`s. * - * Before aggregating, the pubkeys can be sorted with `secp256k1_pubkey_sort` + * Before aggregating, the pubkeys can be sorted with `secp256k1_ec_pubkey_sort` * which ensures the same `agg_pk` result for the same multiset of pubkeys. * This is useful to do before `pubkey_agg`, such that the order of pubkeys * does not affect the aggregate public key. diff --git a/src/modules/extrakeys/Makefile.am.include b/src/modules/extrakeys/Makefile.am.include index fe496fd2..622d8bb4 100644 --- a/src/modules/extrakeys/Makefile.am.include +++ b/src/modules/extrakeys/Makefile.am.include @@ -1,6 +1,4 @@ include_HEADERS += include/secp256k1_extrakeys.h noinst_HEADERS += src/modules/extrakeys/tests_impl.h noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h -noinst_HEADERS += src/modules/extrakeys/main_impl.h -noinst_HEADERS += src/modules/extrakeys/hsort.h -noinst_HEADERS += src/modules/extrakeys/hsort_impl.h +noinst_HEADERS += src/modules/extrakeys/main_impl.h \ No newline at end of file diff --git a/src/modules/extrakeys/hsort.h b/src/modules/extrakeys/hsort.h deleted file mode 100644 index 5352ef1e..00000000 --- a/src/modules/extrakeys/hsort.h +++ /dev/null @@ -1,22 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Russell O'Connor, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_HSORT_H -#define SECP256K1_HSORT_H - -#include -#include - -/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This - * is preferred over standard library implementations because they generally - * make no guarantee about being fast for malicious inputs. - * - * See the qsort_r manpage for a description of the interface. - */ -static void secp256k1_hsort(void *ptr, size_t count, size_t size, - int (*cmp)(const void *, const void *, void *), - void *cmp_data); -#endif diff --git a/src/modules/extrakeys/hsort_impl.h b/src/modules/extrakeys/hsort_impl.h deleted file mode 100644 index e05aefdf..00000000 --- a/src/modules/extrakeys/hsort_impl.h +++ /dev/null @@ -1,116 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Russell O'Connor, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_HSORT_IMPL_H -#define SECP256K1_HSORT_IMPL_H - -#include "hsort.h" - -/* An array is a heap when, for all non-zero indexes i, the element at index i - * compares as less than or equal to the element at index parent(i) = (i-1)/2. - */ - -static SECP256K1_INLINE size_t child1(size_t i) { - VERIFY_CHECK(i <= (SIZE_MAX - 1)/2); - return 2*i + 1; -} - -static SECP256K1_INLINE size_t child2(size_t i) { - VERIFY_CHECK(i <= SIZE_MAX/2 - 1); - return child1(i)+1; -} - -static SECP256K1_INLINE void heap_swap64(unsigned char *a, size_t i, size_t j, size_t stride) { - unsigned char tmp[64]; - VERIFY_CHECK(stride <= 64); - memcpy(tmp, a + i*stride, stride); - memmove(a + i*stride, a + j*stride, stride); - memcpy(a + j*stride, tmp, stride); -} - -static SECP256K1_INLINE void heap_swap(unsigned char *a, size_t i, size_t j, size_t stride) { - while (64 < stride) { - heap_swap64(a + (stride - 64), i, j, 64); - stride -= 64; - } - heap_swap64(a, i, j, stride); -} - -static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_size, size_t stride, - int (*cmp)(const void *, const void *, void *), void *cmp_data) { - while (i < heap_size/2) { - VERIFY_CHECK(i <= SIZE_MAX/2 - 1); - /* Proof: - * i < heap_size/2 - * i + 1 <= heap_size/2 - * 2*i + 2 <= heap_size <= SIZE_MAX - * 2*i <= SIZE_MAX - 2 - */ - - VERIFY_CHECK(child1(i) < heap_size); - /* Proof: - * i < heap_size/2 - * i + 1 <= heap_size/2 - * 2*i + 2 <= heap_size - * 2*i + 1 < heap_size - * child1(i) < heap_size - */ - - /* Let [x] be notation for the contents at a[x*stride]. - * - * If [child1(i)] > [i] and [child2(i)] > [i], - * swap [i] with the larger child to ensure the new parent is larger - * than both children. When [child1(i)] == [child2(i)], swap [i] with - * [child2(i)]. - * Else if [child1(i)] > [i], swap [i] with [child1(i)]. - * Else if [child2(i)] > [i], swap [i] with [child2(i)]. - */ - if (child2(i) < heap_size - && 0 <= cmp(a + child2(i)*stride, a + child1(i)*stride, cmp_data)) { - if (0 < cmp(a + child2(i)*stride, a + i*stride, cmp_data)) { - heap_swap(a, i, child2(i), stride); - i = child2(i); - } else { - /* At this point we have [child2(i)] >= [child1(i)] and we have - * [child2(i)] <= [i], and thus [child1(i)] <= [i] which means - * that the next comparison can be skipped. */ - return; - } - } else if (0 < cmp(a + child1(i)*stride, a + i*stride, cmp_data)) { - heap_swap(a, i, child1(i), stride); - i = child1(i); - } else { - return; - } - } - /* heap_size/2 <= i - * heap_size/2 < i + 1 - * heap_size < 2*i + 2 - * heap_size <= 2*i + 1 - * heap_size <= child1(i) - * Thus child1(i) and child2(i) are now out of bounds and we are at a leaf. - */ -} - -/* In-place heap sort. */ -static void secp256k1_hsort(void *ptr, size_t count, size_t size, - int (*cmp)(const void *, const void *, void *), - void *cmp_data ) { - size_t i; - - for(i = count/2; 0 < i; --i) { - heap_down(ptr, i-1, count, size, cmp, cmp_data); - } - for(i = count; 1 < i; --i) { - /* Extract the largest value from the heap */ - heap_swap(ptr, 0, i-1, size); - - /* Repair the heap condition */ - heap_down(ptr, 0, i-1, size, cmp, cmp_data); - } -} - -#endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 2ba41465..0c7e2667 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -9,7 +9,6 @@ #include "../../../include/secp256k1.h" #include "../../../include/secp256k1_extrakeys.h" -#include "hsort_impl.h" #include "../../util.h" static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) { @@ -283,38 +282,4 @@ int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_ke return ret; } -/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api - * which expects a non-const cmp_data pointer. */ -typedef struct { - const secp256k1_context *ctx; -} secp256k1_pubkey_sort_cmp_data; - -static int secp256k1_pubkey_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) { - return secp256k1_ec_pubkey_cmp(((secp256k1_pubkey_sort_cmp_data*)cmp_data)->ctx, - *(secp256k1_pubkey **)pk1, - *(secp256k1_pubkey **)pk2); -} - -int secp256k1_pubkey_sort(const secp256k1_context* ctx, const secp256k1_pubkey **pubkeys, size_t n_pubkeys) { - secp256k1_pubkey_sort_cmp_data cmp_data; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(pubkeys != NULL); - - cmp_data.ctx = ctx; - - /* Suppress wrong warning (fixed in MSVC 19.33) */ - #if defined(_MSC_VER) && (_MSC_VER < 1933) - #pragma warning(push) - #pragma warning(disable: 4090) - #endif - - secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_pubkey_sort_cmp, &cmp_data); - - #if defined(_MSC_VER) && (_MSC_VER < 1933) - #pragma warning(pop) - #endif - - return 1; -} - #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 1f10ceed..45521d17 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -467,196 +467,6 @@ static void test_keypair_add(void) { } } -static void test_hsort_is_sorted(int *ints, size_t n) { - size_t i; - for (i = 1; i < n; i++) { - CHECK(ints[i-1] <= ints[i]); - } -} - -static int test_hsort_cmp(const void *i1, const void *i2, void *counter) { - *(size_t*)counter += 1; - return *(int*)i1 - *(int*)i2; -} - -#define NUM 64 -static void test_hsort(void) { - int ints[NUM] = { 0 }; - size_t counter = 0; - int i, j; - - secp256k1_hsort(ints, 0, sizeof(ints[0]), test_hsort_cmp, &counter); - CHECK(counter == 0); - secp256k1_hsort(ints, 1, sizeof(ints[0]), test_hsort_cmp, &counter); - CHECK(counter == 0); - secp256k1_hsort(ints, NUM, sizeof(ints[0]), test_hsort_cmp, &counter); - CHECK(counter > 0); - test_hsort_is_sorted(ints, NUM); - - /* Test hsort with length n array and random elements in - * [-interval/2, interval/2] */ - for (i = 0; i < COUNT; i++) { - int n = secp256k1_testrand_int(NUM); - int interval = secp256k1_testrand_int(63) + 1; - for (j = 0; j < n; j++) { - ints[j] = secp256k1_testrand_int(interval) - interval/2; - } - secp256k1_hsort(ints, n, sizeof(ints[0]), test_hsort_cmp, &counter); - test_hsort_is_sorted(ints, n); - } -} -#undef NUM - -static void test_sort_helper(secp256k1_pubkey *pk, size_t *pk_order, size_t n_pk) { - size_t i; - const secp256k1_pubkey *pk_test[5]; - - for (i = 0; i < n_pk; i++) { - pk_test[i] = &pk[pk_order[i]]; - } - secp256k1_pubkey_sort(CTX, pk_test, n_pk); - for (i = 0; i < n_pk; i++) { - CHECK(secp256k1_memcmp_var(pk_test[i], &pk[i], sizeof(*pk_test[i])) == 0); - } -} - -static void permute(size_t *arr, size_t n) { - size_t i; - for (i = n - 1; i >= 1; i--) { - size_t tmp, j; - j = secp256k1_testrand_int(i + 1); - tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; - } -} - -static void rand_pk(secp256k1_pubkey *pk) { - unsigned char seckey[32]; - secp256k1_keypair keypair; - secp256k1_testrand256(seckey); - CHECK(secp256k1_keypair_create(CTX, &keypair, seckey) == 1); - CHECK(secp256k1_keypair_pub(CTX, pk, &keypair) == 1); -} - -static void test_sort_api(void) { - secp256k1_pubkey pks[2]; - const secp256k1_pubkey *pks_ptr[2]; - - pks_ptr[0] = &pks[0]; - pks_ptr[1] = &pks[1]; - - rand_pk(&pks[0]); - rand_pk(&pks[1]); - - CHECK(secp256k1_pubkey_sort(CTX, pks_ptr, 2) == 1); - CHECK_ILLEGAL(CTX, secp256k1_pubkey_sort(CTX, NULL, 2)); - CHECK(secp256k1_pubkey_sort(CTX, pks_ptr, 0) == 1); - /* Test illegal public keys */ - memset(&pks[0], 0, sizeof(pks[0])); - CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_sort(CTX, pks_ptr, 2) == 1)); - memset(&pks[1], 0, sizeof(pks[1])); - { - int32_t ecount = 0; - secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount); - CHECK(secp256k1_pubkey_sort(CTX, pks_ptr, 2) == 1); - CHECK(ecount == 2); - secp256k1_context_set_illegal_callback(CTX, NULL, NULL); - } -} - -static void test_sort(void) { - secp256k1_pubkey pk[5]; - unsigned char pk_ser[5][33] = { - { 0x02, 0x08 }, - { 0x02, 0x0b }, - { 0x02, 0x0c }, - { 0x03, 0x05 }, - { 0x03, 0x0a }, - }; - int i; - size_t pk_order[5] = { 0, 1, 2, 3, 4 }; - - for (i = 0; i < 5; i++) { - CHECK(secp256k1_ec_pubkey_parse(CTX, &pk[i], pk_ser[i], sizeof(pk_ser[i]))); - } - - permute(pk_order, 1); - test_sort_helper(pk, pk_order, 1); - permute(pk_order, 2); - test_sort_helper(pk, pk_order, 2); - permute(pk_order, 3); - test_sort_helper(pk, pk_order, 3); - for (i = 0; i < COUNT; i++) { - permute(pk_order, 4); - test_sort_helper(pk, pk_order, 4); - } - for (i = 0; i < COUNT; i++) { - permute(pk_order, 5); - test_sort_helper(pk, pk_order, 5); - } - /* Check that sorting also works for random pubkeys */ - for (i = 0; i < COUNT; i++) { - int j; - const secp256k1_pubkey *pk_ptr[5]; - for (j = 0; j < 5; j++) { - rand_pk(&pk[j]); - pk_ptr[j] = &pk[j]; - } - secp256k1_pubkey_sort(CTX, pk_ptr, 5); - for (j = 1; j < 5; j++) { - secp256k1_pubkey_sort_cmp_data cmp_data; - cmp_data.ctx = CTX; - CHECK(secp256k1_pubkey_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], &cmp_data) <= 0); - } - } -} - -/* Test vectors from BIP-MuSig2 */ -static void test_sort_vectors(void) { - enum { N_PUBKEYS = 6 }; - unsigned char pk_ser[N_PUBKEYS][33] = { - { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, - 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, - 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 }, - { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, - 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, - 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, - { 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, - 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, - 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }, - { 0x02, 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, - 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, - 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 }, - { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, - 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, - 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xFF }, - { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, - 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, - 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 } - }; - secp256k1_pubkey pubkeys[N_PUBKEYS]; - secp256k1_pubkey *sorted[N_PUBKEYS]; - const secp256k1_pubkey *pks_ptr[N_PUBKEYS]; - int i; - - sorted[0] = &pubkeys[3]; - sorted[1] = &pubkeys[0]; - sorted[2] = &pubkeys[0]; - sorted[3] = &pubkeys[4]; - sorted[4] = &pubkeys[1]; - sorted[5] = &pubkeys[2]; - - for (i = 0; i < N_PUBKEYS; i++) { - CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkeys[i], pk_ser[i], sizeof(pk_ser[i]))); - pks_ptr[i] = &pubkeys[i]; - } - CHECK(secp256k1_pubkey_sort(CTX, pks_ptr, N_PUBKEYS) == 1); - for (i = 0; i < N_PUBKEYS; i++) { - CHECK(secp256k1_memcmp_var(pks_ptr[i], sorted[i], sizeof(secp256k1_pubkey)) == 0); - } -} - static void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); @@ -668,11 +478,6 @@ static void run_extrakeys_tests(void) { /* keypair tests */ test_keypair(); test_keypair_add(); - - test_hsort(); - test_sort_api(); - test_sort(); - test_sort_vectors(); } #endif From 040673bd44de8b98be3abf682e6e19ba6db33e8e Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sun, 1 Feb 2026 18:51:16 +0000 Subject: [PATCH 335/381] ci, docker: Fix LLVM repository signature failure The LLVM apt repository uses legacy SHA1 signatures which are now rejected by the stricter Sequoia PGP policy. This change extends the 'sha1.second_preimage_resistance' cutoff date to 9999-01-01 in the default Sequoia config. This effectively whitelists the legacy signature algorithm, preventing "OpenPGP signature verification failed" errors during `apt-get update`. See https://github.com/llvm/llvm-project/issues/153385. --- ci/linux-debian.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index 5ce715b4..aa50951b 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -66,6 +66,9 @@ RUN \ wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ # Add repository for this Debian release . /etc/os-release && echo "deb http://apt.llvm.org/${VERSION_CODENAME} llvm-toolchain-${VERSION_CODENAME} main" >> /etc/apt/sources.list && \ + # Temporarily work around Sequoia PGP policy deadline for legacy repositories. + # See https://github.com/llvm/llvm-project/issues/153385. + sed -i 's/\(sha1\.second_preimage_resistance =\).*/\1 9999-01-01/' /usr/share/apt/default-sequoia.config && \ apt-get update && \ # Determine the version number of the LLVM development branch LLVM_VERSION=$(apt-cache search --names-only '^clang-[0-9]+$' | sort -V | tail -1 | cut -f1 -d" " | cut -f2 -d"-" ) && \ From 91b2deab7bd7365dc7901fd4d850bcb10fe90ca2 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 6 Feb 2026 14:37:11 +0200 Subject: [PATCH 336/381] ci: Add zkp modules to arm64-macos-native job --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17e82601..f2b38101 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -755,13 +755,13 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', MUSIG: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } - BUILD: 'distcheck' steps: From 21c24fdc7aef67af6f83ae92226aef5084c3c6f3 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 13 Feb 2026 11:36:52 +0200 Subject: [PATCH 337/381] musig: Remove module in preparation for upstream merge --- .gitignore | 3 - CMakeLists.txt | 10 - Makefile.am | 15 - README.md | 8 +- ci/ci.sh | 4 +- configure.ac | 18 - contrib/musig2-vectors.py | 656 -------------- doc/musig-spec.mediawiki | 1 - examples/musig.c | 214 ----- include/secp256k1_musig.h | 609 ------------- src/CMakeLists.txt | 3 - src/ctime_tests.c | 72 -- src/modules/musig/Makefile.am.include | 8 - src/modules/musig/adaptor_impl.h | 101 --- src/modules/musig/keyagg.h | 40 - src/modules/musig/keyagg_impl.h | 311 ------- src/modules/musig/main_impl.h | 14 - src/modules/musig/musig.md | 63 -- src/modules/musig/session.h | 25 - src/modules/musig/session_impl.h | 705 --------------- src/modules/musig/tests_impl.h | 1193 ------------------------- src/modules/musig/vectors.h | 346 ------- src/secp256k1.c | 4 - src/tests.c | 8 - 24 files changed, 5 insertions(+), 4426 deletions(-) delete mode 100755 contrib/musig2-vectors.py delete mode 100644 doc/musig-spec.mediawiki delete mode 100644 examples/musig.c delete mode 100644 include/secp256k1_musig.h delete mode 100644 src/modules/musig/Makefile.am.include delete mode 100644 src/modules/musig/adaptor_impl.h delete mode 100644 src/modules/musig/keyagg.h delete mode 100644 src/modules/musig/keyagg_impl.h delete mode 100644 src/modules/musig/main_impl.h delete mode 100644 src/modules/musig/musig.md delete mode 100644 src/modules/musig/session.h delete mode 100644 src/modules/musig/session_impl.h delete mode 100644 src/modules/musig/tests_impl.h delete mode 100644 src/modules/musig/vectors.h diff --git a/.gitignore b/.gitignore index 82bdcf81..e24dcda4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ bench_internal bench_whitelist noverify_tests tests -example_musig exhaustive_tests precompute_ecmult_gen precompute_ecmult @@ -66,8 +65,6 @@ build-aux/test-driver libsecp256k1.pc contrib/gh-pr-create.sh -musig_example - ### CMake /CMakeUserPresets.json # Default CMake build directory. diff --git a/CMakeLists.txt b/CMakeLists.txt index 44e8b0e8..76a9e09e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,6 @@ option(SECP256K1_ENABLE_MODULE_GENERATOR "Enable NUMS generator module." ON) option(SECP256K1_ENABLE_MODULE_RANGEPROOF "Enable Range proof module." ON) option(SECP256K1_ENABLE_MODULE_SURJECTIONPROOF "Enable Surjection proof module." ON) option(SECP256K1_ENABLE_MODULE_WHITELIST "Enable key whitelist module." ON) -option(SECP256K1_ENABLE_MODULE_MUSIG "Enable MuSig module." ON) option(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR "Enable ecdsa adaptor signatures module." ON) option(SECP256K1_ENABLE_MODULE_ECDSA_S2C "Enable ECDSA sign-to-contract module." ON) option(SECP256K1_ENABLE_MODULE_BPPP "Enable Bulletproofs++ module." ON) @@ -89,14 +88,6 @@ if(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR) add_compile_definitions(ENABLE_MODULE_ECDSA_ADAPTOR=1) endif() -if(SECP256K1_ENABLE_MODULE_MUSIG) - if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG) - message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.") - endif() - set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON) - add_compile_definitions(ENABLE_MODULE_MUSIG=1) -endif() - if(SECP256K1_ENABLE_MODULE_WHITELIST) if(DEFINED SECP256K1_ENABLE_MODULE_RANGEPROOF AND NOT SECP256K1_ENABLE_MODULE_RANGEPROOF) message(FATAL_ERROR "Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the whitelist module.") @@ -374,7 +365,6 @@ message(" generator ........................... ${SECP256K1_ENABLE_MODULE_GENER message(" rangeproof .......................... ${SECP256K1_ENABLE_MODULE_RANGEPROOF}") message(" surjectionproof ..................... ${SECP256K1_ENABLE_MODULE_SURJECTIONPROOF}") message(" whitelist ........................... ${SECP256K1_ENABLE_MODULE_WHITELIST}") -message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}") message(" ecdsa-s2c ........................... ${SECP256K1_ENABLE_MODULE_ECDSA_S2C}") message(" ecdsa-adaptor ....................... ${SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR}") message(" bppp ................................ ${SECP256K1_ENABLE_MODULE_BPPP}") diff --git a/Makefile.am b/Makefile.am index 04bc4c51..e142c9a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -197,17 +197,6 @@ ellswift_example_LDFLAGS += -lbcrypt endif TESTS += ellswift_example endif -if ENABLE_MODULE_MUSIG -noinst_PROGRAMS += musig_example -musig_example_SOURCES = examples/musig.c -musig_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC -musig_example_LDADD = libsecp256k1.la -musig_example_LDFLAGS = -static -if BUILD_WINDOWS -musig_example_LDFLAGS += -lbcrypt -endif -TESTS += musig_example -endif endif ### Precomputed tables @@ -290,10 +279,6 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif -if ENABLE_MODULE_MUSIG -include src/modules/musig/Makefile.am.include -endif - if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif diff --git a/README.md b/README.md index eddd67a7..488044b4 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,11 @@ libsecp256k1-zkp ![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) -A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features such as Confidential Assets and MuSig2 +A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features such as Confidential Assets and Bulletproofs++ range proofs Added features: * Experimental module for ECDSA adaptor signatures. * Experimental module for ECDSA sign-to-contract. -* Experimental module for [MuSig2](src/modules/musig/musig.md). * Experimental module for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)). * Experimental module for Bulletproofs++ range proofs. * Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md). @@ -27,7 +26,7 @@ Building with Autotools $ make check # run the test suite $ sudo make install # optional -To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-musig`. +To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-rangeproof`. Building with CMake (experimental) ---------------------------------- @@ -74,9 +73,8 @@ Usage examples can be found in the [examples](examples) directory. To compile th * [Schnorr signatures example](examples/schnorr.c) * [Deriving a shared secret (ECDH) example](examples/ecdh.c) * [ElligatorSwift key exchange example](examples/ellswift.c) - * [MuSig example](examples/musig.c) -To compile the Schnorr signature, ECDH and MuSig examples, you need to enable the corresponding module by providing a flag to the `configure` script, for example `--enable-module-schnorrsig`. +To compile the Schnorr signature and ECDH examples, you need to enable the corresponding module by providing a flag to the `configure` script, for example `--enable-module-schnorrsig`. Benchmark ------------ diff --git a/ci/ci.sh b/ci/ci.sh index 828ff4b6..7c786bfe 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -14,7 +14,7 @@ print_environment() { for var in WERROR_CFLAGS MAKEFLAGS BUILD \ ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG SCHNORRSIG_HALFAGG ELLSWIFT \ - ECDSA_S2C GENERATOR RANGEPROOF WHITELIST MUSIG ECDSAADAPTOR BPPP \ + ECDSA_S2C GENERATOR RANGEPROOF WHITELIST ECDSAADAPTOR BPPP \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ EXAMPLES \ HOST WRAPPER_CMD \ @@ -82,7 +82,7 @@ esac --enable-module-ecdsa-s2c="$ECDSA_S2C" \ --enable-module-bppp="$BPPP" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \ --enable-examples="$EXAMPLES" \ diff --git a/configure.ac b/configure.ac index e0007537..37e36760 100644 --- a/configure.ac +++ b/configure.ac @@ -216,11 +216,6 @@ AC_ARG_ENABLE(module_whitelist, [], [SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])]) -AC_ARG_ENABLE(module_musig, - AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), - [], - [SECP_SET_DEFAULT([enable_module_musig], [no], [yes])]) - AC_ARG_ENABLE(module_ecdsa_s2c, AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), [], @@ -475,14 +470,6 @@ if test x"$enable_module_ecdsa_adaptor" = x"yes"; then SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDSA_ADAPTOR=1" fi -if test x"$enable_module_musig" = x"yes"; then - if test x"$enable_module_schnorrsig" = x"no"; then - AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.]) - fi - SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_MUSIG=1" - enable_module_schnorrsig=yes -fi - if test x"$enable_module_whitelist" = x"yes"; then if test x"$enable_module_rangeproof" = x"no"; then AC_MSG_ERROR([Module dependency error: You have disabled the rangeproof module explicitly, but it is required by the whitelist module.]) @@ -564,9 +551,6 @@ if test x"$enable_experimental" = x"no"; then if test x"$enable_module_ecdsa_s2c" = x"yes"; then AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.]) fi - if test x"$enable_module_musig" = x"yes"; then - AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) - fi if test x"$enable_module_whitelist" = x"yes"; then AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) fi @@ -606,7 +590,6 @@ AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x" AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"]) -AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_BPPP], [test x"$enable_module_bppp" = x"yes"]) @@ -646,7 +629,6 @@ if test x"$enable_module_surjectionproof" = x"yes" && test x"$enable_reduced_sur echo " reduced proof size = $enable_reduced_surjection_proof_size" fi echo " module whitelist = $enable_module_whitelist" -echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" echo " module bppp = $enable_module_bppp" diff --git a/contrib/musig2-vectors.py b/contrib/musig2-vectors.py deleted file mode 100755 index 97424419..00000000 --- a/contrib/musig2-vectors.py +++ /dev/null @@ -1,656 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import json -import textwrap - -max_pubkeys = 0 - -if len(sys.argv) < 2: - print( - "This script converts BIP MuSig2 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 hexstr_to_intarray(str): - return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)]) - - -def create_init(name): - return """ -static const struct musig_%s_vector musig_%s_vector = { -""" % ( - name, - name, - ) - - -def init_array(key): - return textwrap.indent("{ %s },\n" % hexstr_to_intarray(data[key]), 4 * " ") - - -def init_arrays(key): - s = textwrap.indent("{\n", 4 * " ") - s += textwrap.indent( - ",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in data[key]]), 8 * " " - ) - s += textwrap.indent("\n},\n", 4 * " ") - return s - - -def init_indices(array): - return " %d, { %s }" % ( - len(array), - ", ".join(map(str, array) if len(array) > 0 else "0"), - ) - - -def init_is_xonly(case): - if len(case["tweak_indices"]) > 0: - return ", ".join(map(lambda x: "1" if x else "0", case["is_xonly"])) - return "0" - - -def init_optional_expected(case): - return hexstr_to_intarray(case["expected"]) if "expected" in case else 0 - - -def init_cases(cases, f): - s = textwrap.indent("{\n", 4 * " ") - for (i, case) in enumerate(cases): - s += textwrap.indent("%s\n" % f(case), 8 * " ") - s += textwrap.indent("},\n", 4 * " ") - return s - - -def finish_init(): - return "};\n" - - -s = ( - """/** - * Automatically generated by %s. - * - * The test vectors for the KeySort function are included in this file. They can - * be found in src/modules/extrakeys/tests_impl.h. */ -""" - % sys.argv[0] -) - - -s += """ -enum MUSIG_ERROR { - MUSIG_PUBKEY, - MUSIG_TWEAK, - MUSIG_PUBNONCE, - MUSIG_AGGNONCE, - MUSIG_SECNONCE, - MUSIG_SIG, - MUSIG_SIG_VERIFY, - MUSIG_OTHER -}; -""" - -# key agg vectors -with open(sys.argv[1] + "/key_agg_vectors.json", "r") as f: - data = json.load(f) - - max_key_indices = max( - len(test_case["key_indices"]) for test_case in data["valid_test_cases"] - ) - max_tweak_indices = max( - len(test_case["tweak_indices"]) for test_case in data["error_test_cases"] - ) - num_pubkeys = len(data["pubkeys"]) - max_pubkeys = max(num_pubkeys, max_pubkeys) - num_tweaks = len(data["tweaks"]) - num_valid_cases = len(data["valid_test_cases"]) - num_error_cases = len(data["error_test_cases"]) - - # Add structures for valid and error cases - s += ( - """ -struct musig_key_agg_valid_test_case { - size_t key_indices_len; - size_t key_indices[%d]; - unsigned char expected[32]; -}; -""" - % max_key_indices - ) - s += """ -struct musig_key_agg_error_test_case { - size_t key_indices_len; - size_t key_indices[%d]; - size_t tweak_indices_len; - size_t tweak_indices[%d]; - int is_xonly[%d]; - enum MUSIG_ERROR error; -}; -""" % ( - max_key_indices, - max_tweak_indices, - max_tweak_indices, - ) - - # Add structure for entire vector - s += """ -struct musig_key_agg_vector { - unsigned char pubkeys[%d][33]; - unsigned char tweaks[%d][32]; - struct musig_key_agg_valid_test_case valid_case[%d]; - struct musig_key_agg_error_test_case error_case[%d]; -}; -""" % ( - num_pubkeys, - num_tweaks, - num_valid_cases, - num_error_cases, - ) - - s += create_init("key_agg") - # Add pubkeys and tweaks to the vector - s += init_arrays("pubkeys") - s += init_arrays("tweaks") - - # Add valid cases to the vector - s += init_cases( - data["valid_test_cases"], - lambda case: "{ %s, { %s }}," - % (init_indices(case["key_indices"]), hexstr_to_intarray(case["expected"])), - ) - - def comment_to_error(case): - comment = case["comment"] - if "public key" in comment.lower(): - return "MUSIG_PUBKEY" - elif "tweak" in comment.lower(): - return "MUSIG_TWEAK" - else: - sys.exit("Unknown error") - - # Add error cases to the vector - s += init_cases( - data["error_test_cases"], - lambda case: "{ %s, %s, { %s }, %s }," - % ( - init_indices(case["key_indices"]), - init_indices(case["tweak_indices"]), - init_is_xonly(case), - comment_to_error(case), - ), - ) - - s += finish_init() - -# nonce gen vectors -with open(sys.argv[1] + "/nonce_gen_vectors.json", "r") as f: - data = json.load(f) - - # The MuSig2 implementation only allows messages of length 32 - data["test_cases"] = list( - filter(lambda c: c["msg"] is None or len(c["msg"]) == 64, data["test_cases"]) - ) - - num_tests = len(data["test_cases"]) - - s += """ -struct musig_nonce_gen_test_case { - unsigned char rand_[32]; - int has_sk; - unsigned char sk[32]; - unsigned char pk[33]; - int has_aggpk; - unsigned char aggpk[32]; - int has_msg; - unsigned char msg[32]; - int has_extra_in; - unsigned char extra_in[32]; - unsigned char expected_secnonce[97]; - unsigned char expected_pubnonce[66]; -}; -""" - - s += ( - """ -struct musig_nonce_gen_vector { - struct musig_nonce_gen_test_case test_case[%d]; -}; -""" - % num_tests - ) - - s += create_init("nonce_gen") - - def init_array_maybe(array): - return "%d , { %s }" % ( - 0 if array is None else 1, - hexstr_to_intarray(array) if array is not None else 0, - ) - - s += init_cases( - data["test_cases"], - lambda case: "{ { %s }, %s, { %s }, %s, %s, %s, { %s }, { %s } }," - % ( - hexstr_to_intarray(case["rand_"]), - init_array_maybe(case["sk"]), - hexstr_to_intarray(case["pk"]), - init_array_maybe(case["aggpk"]), - init_array_maybe(case["msg"]), - init_array_maybe(case["extra_in"]), - hexstr_to_intarray(case["expected_secnonce"]), - hexstr_to_intarray(case["expected_pubnonce"]), - ), - ) - - s += finish_init() - -# nonce agg vectors -with open(sys.argv[1] + "/nonce_agg_vectors.json", "r") as f: - data = json.load(f) - - num_pnonces = len(data["pnonces"]) - num_valid_cases = len(data["valid_test_cases"]) - num_error_cases = len(data["error_test_cases"]) - - pnonce_indices_len = 2 - for case in data["valid_test_cases"] + data["error_test_cases"]: - assert len(case["pnonce_indices"]) == pnonce_indices_len - - # Add structures for valid and error cases - s += """ -struct musig_nonce_agg_test_case { - size_t pnonce_indices[2]; - /* if valid case */ - unsigned char expected[66]; - /* if error case */ - int invalid_nonce_idx; -}; -""" - # Add structure for entire vector - s += """ -struct musig_nonce_agg_vector { - unsigned char pnonces[%d][66]; - struct musig_nonce_agg_test_case valid_case[%d]; - struct musig_nonce_agg_test_case error_case[%d]; -}; -""" % ( - num_pnonces, - num_valid_cases, - num_error_cases, - ) - - s += create_init("nonce_agg") - s += init_arrays("pnonces") - - for cases in (data["valid_test_cases"], data["error_test_cases"]): - s += init_cases( - cases, - lambda case: "{ { %s }, { %s }, %d }," - % ( - ", ".join(map(str, case["pnonce_indices"])), - init_optional_expected(case), - case["error"]["signer"] if "error" in case else 0, - ), - ) - s += finish_init() - -# sign/verify vectors -with open(sys.argv[1] + "/sign_verify_vectors.json", "r") as f: - data = json.load(f) - - # The MuSig2 implementation only allows messages of length 32 - assert list(filter(lambda x: len(x) == 64, data["msgs"]))[0] == data["msgs"][0] - data["msgs"] = [data["msgs"][0]] - - def filter_msg32(k): - return list(filter(lambda x: x["msg_index"] == 0, data[k])) - - data["valid_test_cases"] = filter_msg32("valid_test_cases") - data["sign_error_test_cases"] = filter_msg32("sign_error_test_cases") - data["verify_error_test_cases"] = filter_msg32("verify_error_test_cases") - data["verify_fail_test_cases"] = filter_msg32("verify_fail_test_cases") - - num_pubkeys = len(data["pubkeys"]) - max_pubkeys = max(num_pubkeys, max_pubkeys) - num_secnonces = len(data["secnonces"]) - num_pubnonces = len(data["pnonces"]) - num_aggnonces = len(data["aggnonces"]) - num_msgs = len(data["msgs"]) - num_valid_cases = len(data["valid_test_cases"]) - num_sign_error_cases = len(data["sign_error_test_cases"]) - num_verify_fail_cases = len(data["verify_fail_test_cases"]) - num_verify_error_cases = len(data["verify_error_test_cases"]) - - all_cases = ( - data["valid_test_cases"] - + data["sign_error_test_cases"] - + data["verify_error_test_cases"] - + data["verify_fail_test_cases"] - ) - max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases) - max_nonce_indices = max( - len(test_case["nonce_indices"]) if "nonce_indices" in test_case else 0 - for test_case in all_cases - ) - # Add structures for valid and error cases - s += ( - """ -/* Omit pubnonces in the test vectors because our partial signature verification - * implementation is able to accept the aggnonce directly. */ -struct musig_valid_case { - size_t key_indices_len; - size_t key_indices[%d]; - size_t aggnonce_index; - size_t msg_index; - size_t signer_index; - unsigned char expected[32]; -}; -""" - % max_key_indices - ) - - s += ( - """ -struct musig_sign_error_case { - size_t key_indices_len; - size_t key_indices[%d]; - size_t aggnonce_index; - size_t msg_index; - size_t secnonce_index; - enum MUSIG_ERROR error; -}; -""" - % max_key_indices - ) - - s += """ -struct musig_verify_fail_error_case { - unsigned char sig[32]; - size_t key_indices_len; - size_t key_indices[%d]; - size_t nonce_indices_len; - size_t nonce_indices[%d]; - size_t msg_index; - size_t signer_index; - enum MUSIG_ERROR error; -}; -""" % ( - max_key_indices, - max_nonce_indices, - ) - - # Add structure for entire vector - s += """ -struct musig_sign_verify_vector { - unsigned char sk[32]; - unsigned char pubkeys[%d][33]; - unsigned char secnonces[%d][194]; - unsigned char pubnonces[%d][194]; - unsigned char aggnonces[%d][66]; - unsigned char msgs[%d][32]; - struct musig_valid_case valid_case[%d]; - struct musig_sign_error_case sign_error_case[%d]; - struct musig_verify_fail_error_case verify_fail_case[%d]; - struct musig_verify_fail_error_case verify_error_case[%d]; -}; -""" % ( - num_pubkeys, - num_secnonces, - num_pubnonces, - num_aggnonces, - num_msgs, - num_valid_cases, - num_sign_error_cases, - num_verify_fail_cases, - num_verify_error_cases, - ) - - s += create_init("sign_verify") - s += init_array("sk") - s += init_arrays("pubkeys") - s += init_arrays("secnonces") - s += init_arrays("pnonces") - s += init_arrays("aggnonces") - s += init_arrays("msgs") - - s += init_cases( - data["valid_test_cases"], - lambda case: "{ %s, %d, %d, %d, { %s }}," - % ( - init_indices(case["key_indices"]), - case["aggnonce_index"], - case["msg_index"], - case["signer_index"], - init_optional_expected(case), - ), - ) - - def sign_error(case): - comment = case["comment"] - if "pubkey" in comment or "public key" in comment: - return "MUSIG_PUBKEY" - elif "Aggregate nonce" in comment: - return "MUSIG_AGGNONCE" - elif "Secnonce" in comment: - return "MUSIG_SECNONCE" - else: - sys.exit("Unknown sign error") - - s += init_cases( - data["sign_error_test_cases"], - lambda case: "{ %s, %d, %d, %d, %s }," - % ( - init_indices(case["key_indices"]), - case["aggnonce_index"], - case["msg_index"], - case["secnonce_index"], - sign_error(case), - ), - ) - - def verify_error(case): - comment = case["comment"] - if "exceeds" in comment: - return "MUSIG_SIG" - elif "Wrong signer" in comment or "Wrong signature" in comment: - return "MUSIG_SIG_VERIFY" - elif "pubnonce" in comment: - return "MUSIG_PUBNONCE" - elif "pubkey" in comment: - return "MUSIG_PUBKEY" - else: - sys.exit("Unknown verify error") - - for cases in ("verify_fail_test_cases", "verify_error_test_cases"): - s += init_cases( - data[cases], - lambda case: "{ { %s }, %s, %s, %d, %d, %s }," - % ( - hexstr_to_intarray(case["sig"]), - init_indices(case["key_indices"]), - init_indices(case["nonce_indices"]), - case["msg_index"], - case["signer_index"], - verify_error(case), - ), - ) - - s += finish_init() - -# tweak vectors -with open(sys.argv[1] + "/tweak_vectors.json", "r") as f: - data = json.load(f) - - num_pubkeys = len(data["pubkeys"]) - max_pubkeys = max(num_pubkeys, max_pubkeys) - num_pubnonces = len(data["pnonces"]) - num_tweaks = len(data["tweaks"]) - num_valid_cases = len(data["valid_test_cases"]) - num_error_cases = len(data["error_test_cases"]) - - all_cases = data["valid_test_cases"] + data["error_test_cases"] - max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases) - max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases) - max_nonce_indices = max(len(test_case["nonce_indices"]) for test_case in all_cases) - # Add structures for valid and error cases - s += """ -struct musig_tweak_case { - size_t key_indices_len; - size_t key_indices[%d]; - size_t nonce_indices_len; - size_t nonce_indices[%d]; - size_t tweak_indices_len; - size_t tweak_indices[%d]; - int is_xonly[%d]; - size_t signer_index; - unsigned char expected[32]; -}; -""" % ( - max_key_indices, - max_nonce_indices, - max_tweak_indices, - max_tweak_indices, - ) - - # Add structure for entire vector - s += """ -struct musig_tweak_vector { - unsigned char sk[32]; - unsigned char secnonce[97]; - unsigned char aggnonce[66]; - unsigned char msg[32]; - unsigned char pubkeys[%d][33]; - unsigned char pubnonces[%d][194]; - unsigned char tweaks[%d][32]; - struct musig_tweak_case valid_case[%d]; - struct musig_tweak_case error_case[%d]; -}; -""" % ( - num_pubkeys, - num_pubnonces, - num_tweaks, - num_valid_cases, - num_error_cases, - ) - s += create_init("tweak") - s += init_array("sk") - s += init_array("secnonce") - s += init_array("aggnonce") - s += init_array("msg") - s += init_arrays("pubkeys") - s += init_arrays("pnonces") - s += init_arrays("tweaks") - - s += init_cases( - data["valid_test_cases"], - lambda case: "{ %s, %s, %s, { %s }, %d, { %s }}," - % ( - init_indices(case["key_indices"]), - init_indices(case["nonce_indices"]), - init_indices(case["tweak_indices"]), - init_is_xonly(case), - case["signer_index"], - init_optional_expected(case), - ), - ) - - s += init_cases( - data["error_test_cases"], - lambda case: "{ %s, %s, %s, { %s }, %d, { %s }}," - % ( - init_indices(case["key_indices"]), - init_indices(case["nonce_indices"]), - init_indices(case["tweak_indices"]), - init_is_xonly(case), - case["signer_index"], - init_optional_expected(case), - ), - ) - - s += finish_init() - -# sigagg vectors -with open(sys.argv[1] + "/sig_agg_vectors.json", "r") as f: - data = json.load(f) - - num_pubkeys = len(data["pubkeys"]) - max_pubkeys = max(num_pubkeys, max_pubkeys) - num_tweaks = len(data["tweaks"]) - num_psigs = len(data["psigs"]) - num_valid_cases = len(data["valid_test_cases"]) - num_error_cases = len(data["error_test_cases"]) - - all_cases = data["valid_test_cases"] + data["error_test_cases"] - max_key_indices = max(len(test_case["key_indices"]) for test_case in all_cases) - max_tweak_indices = max(len(test_case["tweak_indices"]) for test_case in all_cases) - max_psig_indices = max(len(test_case["psig_indices"]) for test_case in all_cases) - - # Add structures for valid and error cases - s += """ -/* Omit pubnonces in the test vectors because they're only needed for - * implementations that do not directly accept an aggnonce. */ -struct musig_sig_agg_case { - size_t key_indices_len; - size_t key_indices[%d]; - size_t tweak_indices_len; - size_t tweak_indices[%d]; - int is_xonly[%d]; - unsigned char aggnonce[66]; - size_t psig_indices_len; - size_t psig_indices[%d]; - /* if valid case */ - unsigned char expected[64]; - /* if error case */ - int invalid_sig_idx; -}; -""" % ( - max_key_indices, - max_tweak_indices, - max_tweak_indices, - max_psig_indices, - ) - - # Add structure for entire vector - s += """ -struct musig_sig_agg_vector { - unsigned char pubkeys[%d][33]; - unsigned char tweaks[%d][32]; - unsigned char psigs[%d][32]; - unsigned char msg[32]; - struct musig_sig_agg_case valid_case[%d]; - struct musig_sig_agg_case error_case[%d]; -}; -""" % ( - num_pubkeys, - num_tweaks, - num_psigs, - num_valid_cases, - num_error_cases, - ) - - s += create_init("sig_agg") - s += init_arrays("pubkeys") - s += init_arrays("tweaks") - s += init_arrays("psigs") - s += init_array("msg") - - for cases in (data["valid_test_cases"], data["error_test_cases"]): - s += init_cases( - cases, - lambda case: "{ %s, %s, { %s }, { %s }, %s, { %s }, %d }," - % ( - init_indices(case["key_indices"]), - init_indices(case["tweak_indices"]), - init_is_xonly(case), - hexstr_to_intarray(case["aggnonce"]), - init_indices(case["psig_indices"]), - init_optional_expected(case), - case["error"]["signer"] if "error" in case else 0, - ), - ) - s += finish_init() -s += "enum { MUSIG_VECTORS_MAX_PUBKEYS = %d };" % max_pubkeys -print(s) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki deleted file mode 100644 index 017a0c3e..00000000 --- a/doc/musig-spec.mediawiki +++ /dev/null @@ -1 +0,0 @@ -This document was moved to [https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki]. \ No newline at end of file diff --git a/examples/musig.c b/examples/musig.c deleted file mode 100644 index d4a02195..00000000 --- a/examples/musig.c +++ /dev/null @@ -1,214 +0,0 @@ -/************************************************************************* - * Written in 2018 by Jonas Nick * - * 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 MuSig module to create a - * 3-of-3 multisignature. Additionally, see the documentation in - * include/secp256k1_musig.h and src/modules/musig/musig.md. - */ - -#include -#include -#include - -#include -#include -#include - -#include "examples_util.h" - -struct signer_secrets { - secp256k1_keypair keypair; - secp256k1_musig_secnonce secnonce; -}; - -struct signer { - secp256k1_pubkey pubkey; - secp256k1_musig_pubnonce pubnonce; - secp256k1_musig_partial_sig partial_sig; -}; - - /* Number of public keys involved in creating the aggregate signature */ -#define N_SIGNERS 3 -/* 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]; - while (1) { - if (!fill_random(seckey, sizeof(seckey))) { - printf("Failed to generate randomness\n"); - return 1; - } - if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { - break; - } - } - if (!secp256k1_keypair_pub(ctx, &signer->pubkey, &signer_secrets->keypair)) { - return 0; - } - return 1; -} - -/* Tweak the pubkey corresponding to the provided keyagg cache, update the cache - * and return the tweaked aggregate pk. */ -static int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *cache) { - secp256k1_pubkey output_pk; - unsigned char plain_tweak[32] = "this could be a BIP32 tweak...."; - unsigned char xonly_tweak[32] = "this could be a taproot tweak.."; - - - /* Plain tweaking which, for example, allows deriving multiple child - * public keys from a single aggregate key using BIP32 */ - if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, plain_tweak)) { - return 0; - } - /* Note that we did not provided an output_pk argument, because the - * resulting pk is also saved in the cache and so if one is just interested - * in signing the output_pk argument is unnecessary. On the other hand, if - * one is not interested in signing, the same output_pk can be obtained by - * calling `secp256k1_musig_pubkey_get` right after key aggregation to get - * the full pubkey and then call `secp256k1_ec_pubkey_tweak_add`. */ - - /* Xonly tweaking which, for example, allows creating taproot commitments */ - if (!secp256k1_musig_pubkey_xonly_tweak_add(ctx, &output_pk, cache, xonly_tweak)) { - return 0; - } - /* Note that if we wouldn't care about signing, we can arrive at the same - * output_pk by providing the untweaked public key to - * `secp256k1_xonly_pubkey_tweak_add` (after converting it to an xonly pubkey - * if necessary with `secp256k1_xonly_pubkey_from_pubkey`). */ - - /* Now we convert the output_pk to an xonly pubkey to allow to later verify - * the Schnorr signature against it. For this purpose we can ignore the - * `pk_parity` output argument; we would need it if we would have to open - * the taproot commitment. */ - if (!secp256k1_xonly_pubkey_from_pubkey(ctx, agg_pk, NULL, &output_pk)) { - return 0; - } - return 1; -} - -/* Sign a message hash with the given key pairs and store the result in sig */ -static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const secp256k1_musig_keyagg_cache *cache, const unsigned char *msg32, unsigned char *sig64) { - int i; - const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS]; - const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS]; - /* The same for all signers */ - secp256k1_musig_session session; - - for (i = 0; i < N_SIGNERS; i++) { - unsigned char seckey[32]; - unsigned char session_id[32]; - /* Create random session ID. It is absolutely necessary that the session ID - * is unique for every call of secp256k1_musig_nonce_gen. Otherwise - * it's trivial for an attacker to extract the secret key! */ - if (!fill_random(session_id, sizeof(session_id))) { - 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 other signers. */ - if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, &signer[i].pubkey, msg32, NULL, NULL)) { - return 0; - } - pubnonces[i] = &signer[i].pubnonce; - } - /* Communication round 1: A production system would exchange public nonces - * here before moving on. */ - for (i = 0; i < N_SIGNERS; i++) { - secp256k1_musig_aggnonce agg_pubnonce; - - /* Create aggregate nonce and initialize the session */ - if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) { - return 0; - } - if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache, NULL)) { - return 0; - } - /* partial_sign will clear the secnonce by setting it to 0. That's because - * you must _never_ reuse the secnonce (or use the same session_id to - * create a secnonce). If you do, you effectively reuse the nonce and - * leak the secret key. */ - if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, cache, &session)) { - return 0; - } - partial_sigs[i] = &signer[i].partial_sig; - } - /* Communication round 2: A production system would exchange - * partial signatures here before moving on. */ - for (i = 0; i < N_SIGNERS; i++) { - /* To check whether signing was successful, it suffices to either verify - * the aggregate signature with the aggregate public key using - * secp256k1_schnorrsig_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_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, cache, &session)) { - return 0; - } - } - return secp256k1_musig_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_pubkey *pubkeys_ptr[N_SIGNERS]; - secp256k1_xonly_pubkey agg_pk; - secp256k1_musig_keyagg_cache cache; - unsigned char msg[] = {'t', 'h', 'i', 's', '_', 'c', 'o', 'u', 'l', 'd', ' ', 'b', 'e', ' ', 't', 'h', 'e', '_', 'h', 'a', 's', 'h', '_', 'o', 'f', '_', 'a', '_', 'm', 's', 'g', '!'}; - unsigned char sig[64]; - - /* Create a secp256k1 context */ - ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - printf("Creating key pairs......"); - for (i = 0; i < N_SIGNERS; i++) { - if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) { - printf("FAILED\n"); - return 1; - } - pubkeys_ptr[i] = &signers[i].pubkey; - } - printf("ok\n"); - printf("Combining public keys..."); - /* If you just want to aggregate and not sign the cache can be NULL */ - if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pubkeys_ptr, N_SIGNERS)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - printf("Tweaking................"); - /* Optionally tweak the aggregate key */ - if (!tweak(ctx, &agg_pk, &cache)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - printf("Signing message........."); - if (!sign(ctx, signer_secrets, signers, &cache, msg, sig)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - printf("Verifying signature....."); - if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) { - printf("FAILED\n"); - return 1; - } - printf("ok\n"); - secp256k1_context_destroy(ctx); - return 0; -} diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h deleted file mode 100644 index 0c539e8a..00000000 --- a/include/secp256k1_musig.h +++ /dev/null @@ -1,609 +0,0 @@ -#ifndef SECP256K1_MUSIG_H -#define SECP256K1_MUSIG_H - -#include "secp256k1_extrakeys.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** This module implements BIP 327 "MuSig2 for BIP340-compatible - * Multi-Signatures" - * (https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki) - * v1.0.0. You can find an example demonstrating the musig module in - * examples/musig.c. - * - * The module also supports BIP-341 ("Taproot") public key tweaking and adaptor - * signatures as described in - * https://github.com/ElementsProject/scriptless-scripts/pull/24. - * - * It is recommended to read the documentation in this include file carefully. - * Further notes on API usage can be found in src/modules/musig/musig.md - * - * Since the first version of MuSig is essentially replaced by MuSig2, we use - * MuSig, musig and MuSig2 synonymously unless noted otherwise. - */ - -/** Opaque data structures - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. 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 caches information about public key aggregation. - * - * Guaranteed to be 197 bytes in size. It can be safely copied/moved. No - * serialization and parsing functions (yet). - */ -typedef struct { - unsigned char data[197]; -} secp256k1_musig_keyagg_cache; - -/** Opaque data structure that holds a signer's _secret_ nonce. - * - * 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. See - * https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for - * more details about the risks associated with serializing or deserializing - * this structure. - * - * We repeat, copying this data structure can result in nonce reuse which will - * leak the secret signing key. - */ -typedef struct { - unsigned char data[132]; -} secp256k1_musig_secnonce; - -/** Opaque data structure that holds a signer's public nonce. -* -* Guaranteed to be 132 bytes in size. It can be safely copied/moved. Serialized -* and parsed with `musig_pubnonce_serialize` and `musig_pubnonce_parse`. -*/ -typedef struct { - unsigned char data[132]; -} secp256k1_musig_pubnonce; - -/** Opaque data structure that holds an aggregate public nonce. - * - * Guaranteed to be 132 bytes in size. It can be safely copied/moved. - * Serialized and parsed with `musig_aggnonce_serialize` and - * `musig_aggnonce_parse`. - */ -typedef struct { - unsigned char data[132]; -} secp256k1_musig_aggnonce; - -/** Opaque data structure that holds a MuSig session. - * - * This structure is not required to be kept secret for the signing protocol to - * be secure. Guaranteed to be 133 bytes in size. It can be safely - * copied/moved. No serialization and parsing functions (yet). - */ -typedef struct { - unsigned char data[133]; -} secp256k1_musig_session; - -/** Opaque data structure that holds a partial MuSig signature. - * - * Guaranteed to be 36 bytes in size. Serialized and parsed with - * `musig_partial_sig_serialize` and `musig_partial_sig_parse`. - */ -typedef struct { - unsigned char data[36]; -} secp256k1_musig_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 int secp256k1_musig_pubnonce_parse( - const secp256k1_context *ctx, - secp256k1_musig_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 when the nonce could be serialized, 0 otherwise - * 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_musig_pubnonce_serialize( - const secp256k1_context *ctx, - unsigned char *out66, - const secp256k1_musig_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 int secp256k1_musig_aggnonce_parse( - const secp256k1_context *ctx, - secp256k1_musig_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 when the nonce could be serialized, 0 otherwise - * 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_musig_aggnonce_serialize( - const secp256k1_context *ctx, - unsigned char *out66, - const secp256k1_musig_aggnonce *nonce -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize a MuSig partial signature - * - * Returns: 1 when the signature could be serialized, 0 otherwise - * 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_musig_partial_sig_serialize( - const secp256k1_context *ctx, - unsigned char *out32, - const secp256k1_musig_partial_sig *sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse a MuSig 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 - * - * After the call, sig will always be initialized. If parsing failed or the - * encoded numbers are out of range, signature verification with it is - * guaranteed to fail for every message and public key. - */ -SECP256K1_API int secp256k1_musig_partial_sig_parse( - const secp256k1_context *ctx, - secp256k1_musig_partial_sig *sig, - const unsigned char *in32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Computes an aggregate public key and uses it to initialize a keyagg_cache - * - * Different orders of `pubkeys` result in different `agg_pk`s. - * - * Before aggregating, the pubkeys can be sorted with `secp256k1_ec_pubkey_sort` - * which ensures the same `agg_pk` result for the same multiset of pubkeys. - * This is useful to do before `pubkey_agg`, such that the order of pubkeys - * does not affect the aggregate public key. - * - * Returns: 0 if the arguments are invalid, 1 otherwise - * Args: ctx: pointer to a context object - * scratch: should be NULL because it is not yet implemented. If it - * was implemented then the scratch space would be used to - * compute the aggregate pubkey by multiexponentiation. - * Generally, the larger the scratch space, the faster this - * function. However, the returns of providing a larger - * scratch space are diminishing. If NULL, an inefficient - * algorithm is used. - * Out: agg_pk: the MuSig-aggregated x-only public key. If you do not need it, - * this arg can be NULL. - * keyagg_cache: if non-NULL, pointer to a musig_keyagg_cache struct that - * is required for signing (or observing the signing session - * and verifying partial signatures). - * In: pubkeys: input array of pointers to public keys to aggregate. The order - * is important; a different order will result in a different - * aggregate public key. - * n_pubkeys: length of pubkeys array. Must be greater than 0. - */ -SECP256K1_API int secp256k1_musig_pubkey_agg( - const secp256k1_context *ctx, - secp256k1_scratch_space *scratch, - secp256k1_xonly_pubkey *agg_pk, - secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_pubkey * const *pubkeys, - size_t n_pubkeys -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); - -/** Obtain the aggregate public key from a keyagg_cache. - * - * This is only useful if you need the non-xonly public key, in particular for - * plain (non-xonly) tweaking or batch-verifying multiple key aggregations - * (not implemented). - * - * Returns: 0 if the arguments are invalid, 1 otherwise - * Args: ctx: pointer to a context object - * Out: agg_pk: the MuSig-aggregated public key. - * In: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by - * `musig_pubkey_agg` - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( - const secp256k1_context *ctx, - secp256k1_pubkey *agg_pk, - const secp256k1_musig_keyagg_cache *keyagg_cache -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Apply plain "EC" tweaking to a public key in a given keyagg_cache by - * adding the generator multiplied with `tweak32` to it. This is useful for - * deriving child keys from an aggregate public key via BIP32. - * - * The tweaking method is the same as `secp256k1_ec_pubkey_tweak_add`. So after - * the following pseudocode buf and buf2 have identical contents (absent - * earlier failures). - * - * secp256k1_musig_pubkey_agg(..., keyagg_cache, pubkeys, ...) - * secp256k1_musig_pubkey_get(..., agg_pk, keyagg_cache) - * secp256k1_musig_pubkey_ec_tweak_add(..., output_pk, tweak32, keyagg_cache) - * secp256k1_ec_pubkey_serialize(..., buf, output_pk) - * secp256k1_ec_pubkey_tweak_add(..., agg_pk, tweak32) - * secp256k1_ec_pubkey_serialize(..., buf2, agg_pk) - * - * This function is required if you want to _sign_ for a tweaked aggregate key. - * On the other hand, if you are only computing a public key, but not intending - * to create a signature for it, you can just use - * `secp256k1_ec_pubkey_tweak_add`. - * - * Returns: 0 if the arguments are invalid or the resulting public key would be - * invalid (only when the tweak is the negation of the corresponding - * secret key). 1 otherwise. - * Args: ctx: pointer to a context object - * Out: output_pubkey: pointer to a public key to store the result. Will be set - * to an invalid value if this function returns 0. If you - * do not need it, this arg can be NULL. - * In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by - * `musig_pubkey_agg` - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid - * according to `secp256k1_ec_seckey_verify`, this function - * returns 0. For uniformly random 32-byte arrays the - * chance of being invalid is negligible (around 1 in - * 2^128). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_ec_tweak_add( - const secp256k1_context *ctx, - secp256k1_pubkey *output_pubkey, - secp256k1_musig_keyagg_cache *keyagg_cache, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Apply x-only tweaking to a public key in a given keyagg_cache by adding the - * generator multiplied with `tweak32` to it. This is useful for creating - * Taproot outputs. - * - * The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in - * the following pseudocode xonly_pubkey_tweak_add_check (absent earlier - * failures) returns 1. - * - * secp256k1_musig_pubkey_agg(..., agg_pk, keyagg_cache, pubkeys, ...) - * secp256k1_musig_pubkey_xonly_tweak_add(..., output_pk, tweak32, keyagg_cache) - * secp256k1_xonly_pubkey_serialize(..., buf, output_pk) - * secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32) - * - * This function is required if you want to _sign_ for a tweaked aggregate key. - * On the other hand, if you are only computing a public key, but not intending - * to create a signature for it, you can just use - * `secp256k1_xonly_pubkey_tweak_add`. - * - * Returns: 0 if the arguments are invalid or the resulting public key would be - * invalid (only when the tweak is the negation of the corresponding - * secret key). 1 otherwise. - * Args: ctx: pointer to a context object - * Out: output_pubkey: pointer to a public key to store the result. Will be set - * to an invalid value if this function returns 0. If you - * do not need it, this arg can be NULL. - * In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by - * `musig_pubkey_agg` - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid - * according to secp256k1_ec_seckey_verify, this function - * returns 0. For uniformly random 32-byte arrays the - * chance of being invalid is negligible (around 1 in - * 2^128). - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_tweak_add( - const secp256k1_context *ctx, - secp256k1_pubkey *output_pubkey, - secp256k1_musig_keyagg_cache *keyagg_cache, - const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** 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 other signers. - * - * MuSig differs from regular Schnorr 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_id32 that must NOT BE - * REUSED in subsequent calls to this function. - * If you do not provide a seckey, session_id32 _must_ be UNIFORMLY RANDOM - * AND KEPT SECRET (even from other signers). If you do provide a seckey, - * session_id32 can instead be a counter (that must never repeat!). However, - * it is recommended to always choose session_id32 uniformly at random. - * 2. If you already know the seckey, message or aggregate public key - * cache, they 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. - * - * Remember that nonce reuse will leak the secret key! - * Note that using the same seckey for multiple MuSig 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 - * pubnonce: pointer to a structure to store the public nonce - * In: session_id32: a 32-byte session_id32 as explained above. Must be unique to this - * call to secp256k1_musig_nonce_gen and must be uniformly random - * unless you really know what you are doing. - * seckey: the 32-byte secret key that will later be used for signing, if - * already known (can be NULL) - * pubkey: public key of the signer creating the nonce. The secnonce - * output of this function cannot be used to sign for any - * other public key. - * msg32: the 32-byte message that will later be signed, if already known - * (can be NULL) - * keyagg_cache: pointer to the keyagg_cache that was used to create the aggregate - * (and potentially tweaked) public key if already known - * (can be NULL) - * extra_input32: an optional 32-byte array that is input to the nonce - * derivation function (can be NULL) - */ -SECP256K1_API int secp256k1_musig_nonce_gen( - const secp256k1_context *ctx, - secp256k1_musig_secnonce *secnonce, - secp256k1_musig_pubnonce *pubnonce, - const unsigned char *session_id32, - const unsigned char *seckey, - const secp256k1_pubkey *pubkey, - const unsigned char *msg32, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const unsigned char *extra_input32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6); - -/** Aggregates the nonces of all signers into a single nonce - * - * This can be done by an untrusted party to reduce the communication - * between signers. Instead of everyone sending nonces to everyone else, there - * can be one party receiving all nonces, aggregating the nonces with this - * function and then sending only the aggregate nonce back to the signers. - * - * Returns: 0 if the arguments are invalid, 1 otherwise - * Args: ctx: pointer to a context object - * Out: aggnonce: pointer to an aggregate public nonce object for - * musig_nonce_process - * In: pubnonces: array of pointers to public nonces sent by the - * signers - * n_pubnonces: number of elements in the pubnonces array. Must be - * greater than 0. - */ -SECP256K1_API int secp256k1_musig_nonce_agg( - const secp256k1_context *ctx, - secp256k1_musig_aggnonce *aggnonce, - const secp256k1_musig_pubnonce * const *pubnonces, - size_t n_pubnonces -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Takes the public nonces of all signers and computes a session that is - * required for signing and verification of partial signatures. - * - * If the adaptor argument is non-NULL, then the output of - * musig_partial_sig_agg will be a pre-signature which is not a valid Schnorr - * signature. In order to create a valid signature, the pre-signature and the - * secret adaptor must be provided to `musig_adapt`. - * - * Returns: 0 if the arguments are invalid or if some signer sent invalid - * pubnonces, 1 otherwise - * 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 musig_nonce_agg - * msg32: the 32-byte message to sign - * keyagg_cache: pointer to the keyagg_cache that was used to create the - * aggregate (and potentially tweaked) pubkey - * adaptor: optional pointer to an adaptor point encoded as a public - * key if this signing session is part of an adaptor - * signature protocol (can be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process( - const secp256k1_context *ctx, - secp256k1_musig_session *session, - const secp256k1_musig_aggnonce *aggnonce, - const unsigned char *msg32, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_pubkey *adaptor -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - -/** Produces a partial signature - * - * 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 means that when signing for a - * keypair consisting of a seckey and pubkey, the secnonce must have been - * created by calling musig_nonce_gen with that pubkey. Otherwise, the - * illegal_callback is called. - * - * This function does not verify the output partial signature, deviating from - * the BIP 327 specification. It is recommended to verify the output partial - * signature with `secp256k1_musig_partial_sig_verify` to prevent random or - * adversarially provoked computation errors. - * - * 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 - * Out: partial_sig: pointer to struct to store the partial signature - * In/Out: secnonce: pointer to the secnonce struct created in - * musig_nonce_gen that has been never used in a - * partial_sign call before and has been created for the - * keypair - * In: keypair: pointer to keypair to sign the message with - * keyagg_cache: pointer to the keyagg_cache that was output when the - * aggregate public key for this session - * session: pointer to the session that was created with - * musig_nonce_process - */ -SECP256K1_API 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_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); - -/** Verifies an individual signer's partial signature - * - * The signature is verified for a specific signing session. In order to avoid - * accidentally verifying a signature from a different or non-existing signing - * session, you must ensure the following: - * 1. The `keyagg_cache` argument is identical to the one used to create the - * `session` with `musig_nonce_process`. - * 2. The `pubkey` argument must be identical to the one sent by the signer - * before aggregating it with `musig_pubkey_agg` to create the - * `keyagg_cache`. - * 3. The `pubnonce` argument must be identical to the one sent by the signer - * before aggregating it with `musig_nonce_agg` and using the result to - * create the `session` with `musig_nonce_process`. - * - * This function is essential when using protocols with adaptor signatures. - * However, it is not essential for regular MuSig sessions, in the sense that if any - * partial signature does not verify, the full signature will not verify either, so the - * problem will be caught. But this function allows determining the specific party - * who produced an invalid signature. - * - * 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, sent by - * the signer associated with `pubnonce` and `pubkey` - * pubnonce: public nonce of the signer in the signing session - * pubkey: public key of the signer in the signing session - * keyagg_cache: pointer to the keyagg_cache that was output when the - * aggregate public key for this signing session - * session: pointer to the session that was created with - * `musig_nonce_process` - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( - const secp256k1_context *ctx, - const secp256k1_musig_partial_sig *partial_sig, - const secp256k1_musig_pubnonce *pubnonce, - const secp256k1_pubkey *pubkey, - const secp256k1_musig_keyagg_cache *keyagg_cache, - const secp256k1_musig_session *session -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); - -/** Aggregates 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) Schnorr signature - * In: session: pointer to the session that was created with - * musig_nonce_process - * 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. - */ -SECP256K1_API int secp256k1_musig_partial_sig_agg( - const secp256k1_context *ctx, - unsigned char *sig64, - const secp256k1_musig_session *session, - const secp256k1_musig_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); - -/** Extracts the nonce_parity bit from a session - * - * This is used for adaptor signatures. - * - * Returns: 0 if the arguments are invalid, 1 otherwise - * Args: ctx: pointer to a context object - * Out: nonce_parity: pointer to an integer that indicates the parity - * of the aggregate public nonce. Used for adaptor - * signatures. - * In: session: pointer to the session that was created with - * musig_nonce_process - */ -SECP256K1_API int secp256k1_musig_nonce_parity( - const secp256k1_context *ctx, - int *nonce_parity, - const secp256k1_musig_session *session -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Creates a signature from a pre-signature and an adaptor. - * - * If the sec_adaptor32 argument is incorrect, the output signature will be - * invalid. This function does not verify the signature. - * - * Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain - * invalid (overflowing) values. 1 otherwise (which does NOT mean the - * signature or the adaptor are valid!) - * Args: ctx: pointer to a context object - * Out: sig64: 64-byte signature. This pointer may point to the same - * memory area as `pre_sig`. - * In: pre_sig64: 64-byte pre-signature - * sec_adaptor32: 32-byte secret adaptor to add to the pre-signature - * nonce_parity: the output of `musig_nonce_parity` called with the - * session used for producing the pre-signature - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_adapt( - const secp256k1_context *ctx, - unsigned char *sig64, - const unsigned char *pre_sig64, - const unsigned char *sec_adaptor32, - int nonce_parity -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -/** Extracts a secret adaptor from a MuSig pre-signature and corresponding - * signature - * - * This function will not fail unless given grossly invalid data; if it is - * merely given signatures that do not verify, the returned value will be - * nonsense. It is therefore important that all data be verified at earlier - * steps of any protocol that uses this function. In particular, this includes - * verifying all partial signatures that were aggregated into pre_sig64. - * - * Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain - * grossly invalid (overflowing) values. 1 otherwise (which does NOT - * mean the signatures or the adaptor are valid!) - * Args: ctx: pointer to a context object - * Out:sec_adaptor32: 32-byte secret adaptor - * In: sig64: complete, valid 64-byte signature - * pre_sig64: the pre-signature corresponding to sig64, i.e., the - * aggregate of partial signatures without the secret - * adaptor - * nonce_parity: the output of `musig_nonce_parity` called with the - * session used for producing sig64 - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_adaptor( - const secp256k1_context *ctx, - unsigned char *sec_adaptor32, - const unsigned char *sig64, - const unsigned char *pre_sig64, - int nonce_parity -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 27e90204..7c0871ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -129,9 +129,6 @@ if(SECP256K1_INSTALL) if(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR) list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdsa_adaptor.h") endif() - if(SECP256K1_ENABLE_MODULE_MUSIG) - list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_musig.h") - endif() if(SECP256K1_ENABLE_MODULE_WHITELIST) list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_whitelist.h") endif() diff --git a/src/ctime_tests.c b/src/ctime_tests.c index 407d2cc6..0b72f281 100644 --- a/src/ctime_tests.c +++ b/src/ctime_tests.c @@ -43,10 +43,6 @@ #include "../include/secp256k1_ecdsa_adaptor.h" #endif -#ifdef ENABLE_MODULE_MUSIG -#include "../include/secp256k1_musig.h" -#endif - static void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -281,72 +277,4 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { CHECK(ret == 0); } #endif - -#ifdef ENABLE_MODULE_MUSIG - { - secp256k1_pubkey pk; - const secp256k1_pubkey *pk_ptr[1]; - secp256k1_xonly_pubkey agg_pk; - unsigned char session_id[32]; - secp256k1_musig_secnonce secnonce; - secp256k1_musig_pubnonce pubnonce; - const secp256k1_musig_pubnonce *pubnonce_ptr[1]; - secp256k1_musig_aggnonce aggnonce; - secp256k1_musig_keyagg_cache cache; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig; - const secp256k1_musig_partial_sig *partial_sig_ptr[1]; - unsigned char extra_input[32]; - unsigned char sec_adaptor[32]; - secp256k1_pubkey adaptor; - unsigned char pre_sig[64]; - int nonce_parity; - - pk_ptr[0] = &pk; - pubnonce_ptr[0] = &pubnonce; - SECP256K1_CHECKMEM_DEFINE(key, 32); - memcpy(session_id, key, sizeof(session_id)); - session_id[0] = session_id[0] + 1; - memcpy(extra_input, key, sizeof(extra_input)); - extra_input[0] = extra_input[0] + 2; - memcpy(sec_adaptor, key, sizeof(sec_adaptor)); - sec_adaptor[0] = extra_input[0] + 3; - partial_sig_ptr[0] = &partial_sig; - - CHECK(secp256k1_keypair_create(ctx, &keypair, key)); - CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair)); - CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pk_ptr, 1)); - CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor)); - SECP256K1_CHECKMEM_UNDEFINE(key, 32); - SECP256K1_CHECKMEM_UNDEFINE(session_id, sizeof(session_id)); - SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input)); - SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor)); - ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_id, key, &pk, msg, &cache, extra_input); - SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); - CHECK(ret == 1); - CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1)); - /* Make sure that previous tests don't undefine msg. It's not used as a secret here. */ - SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg)); - CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache, &adaptor) == 1); - - ret = secp256k1_keypair_create(ctx, &keypair, key); - SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); - CHECK(ret == 1); - ret = secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &cache, &session); - SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); - CHECK(ret == 1); - - SECP256K1_CHECKMEM_DEFINE(&partial_sig, sizeof(partial_sig)); - CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1)); - SECP256K1_CHECKMEM_DEFINE(pre_sig, sizeof(pre_sig)); - - CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity, &session)); - ret = secp256k1_musig_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity); - SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); - CHECK(ret == 1); - ret = secp256k1_musig_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity); - SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); - CHECK(ret == 1); - } -#endif } diff --git a/src/modules/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include deleted file mode 100644 index dc2f77f1..00000000 --- a/src/modules/musig/Makefile.am.include +++ /dev/null @@ -1,8 +0,0 @@ -include_HEADERS += include/secp256k1_musig.h -noinst_HEADERS += src/modules/musig/main_impl.h -noinst_HEADERS += src/modules/musig/keyagg.h -noinst_HEADERS += src/modules/musig/keyagg_impl.h -noinst_HEADERS += src/modules/musig/session.h -noinst_HEADERS += src/modules/musig/session_impl.h -noinst_HEADERS += src/modules/musig/adaptor_impl.h -noinst_HEADERS += src/modules/musig/tests_impl.h diff --git a/src/modules/musig/adaptor_impl.h b/src/modules/musig/adaptor_impl.h deleted file mode 100644 index 3830e8a2..00000000 --- a/src/modules/musig/adaptor_impl.h +++ /dev/null @@ -1,101 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H -#define SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H - -#include - -#include "../../../include/secp256k1.h" -#include "../../../include/secp256k1_musig.h" - -#include "session.h" -#include "../../scalar.h" - -int secp256k1_musig_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_musig_session *session) { - secp256k1_musig_session_internal session_i; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(nonce_parity != NULL); - ARG_CHECK(session != NULL); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - *nonce_parity = session_i.fin_nonce_parity; - return 1; -} - -int secp256k1_musig_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) { - secp256k1_scalar s; - secp256k1_scalar t; - int overflow; - int ret = 1; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(pre_sig64 != NULL); - ARG_CHECK(sec_adaptor32 != NULL); - ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); - - secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); - ret &= !overflow; - - /* Determine if the secret adaptor should be negated. - * - * The musig_session stores the X-coordinate and the parity of the "final nonce" - * (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor. - * - * Since a BIP340 signature requires an x-only public nonce, in the case where - * (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce - * corresponding to the signature is actually (-r - t)*G. Thus adapting a - * pre-signature requires negating t in this case. - */ - if (nonce_parity) { - secp256k1_scalar_negate(&t, &t); - } - - secp256k1_scalar_add(&s, &s, &t); - secp256k1_scalar_get_b32(&sig64[32], &s); - memmove(sig64, pre_sig64, 32); - secp256k1_scalar_clear(&t); - return ret; -} - -int secp256k1_musig_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) { - secp256k1_scalar t; - secp256k1_scalar s; - int overflow; - int ret = 1; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sec_adaptor32 != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(pre_sig64 != NULL); - ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); - - secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); - ret &= !overflow; - secp256k1_scalar_negate(&t, &t); - - secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); - if (overflow) { - return 0; - } - secp256k1_scalar_add(&t, &t, &s); - - if (!nonce_parity) { - secp256k1_scalar_negate(&t, &t); - } - secp256k1_scalar_get_b32(sec_adaptor32, &t); - secp256k1_scalar_clear(&t); - return ret; -} - -#endif diff --git a/src/modules/musig/keyagg.h b/src/modules/musig/keyagg.h deleted file mode 100644 index 620522fe..00000000 --- a/src/modules/musig/keyagg.h +++ /dev/null @@ -1,40 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_H -#define SECP256K1_MODULE_MUSIG_KEYAGG_H - -#include "../../../include/secp256k1.h" -#include "../../../include/secp256k1_musig.h" - -#include "../../field.h" -#include "../../group.h" -#include "../../scalar.h" - -typedef struct { - secp256k1_ge pk; - /* If there is no "second" public key, second_pk is set to the point at - * infinity */ - secp256k1_ge second_pk; - unsigned char pk_hash[32]; - /* tweak is identical to value tacc[v] in the specification. */ - secp256k1_scalar tweak; - /* parity_acc corresponds to gacc[v] in the spec. If gacc[v] is -1, - * parity_acc is 1. Otherwise, parity_acc is 0. */ - int parity_acc; -} secp256k1_keyagg_cache_internal; - -/* point_save_ext and point_load_ext are identical to point_save and point_load - * except that they allow saving and loading the point at infinity */ -static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge); - -static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data); - -static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache); - -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk); - -#endif diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h deleted file mode 100644 index aff95542..00000000 --- a/src/modules/musig/keyagg_impl.h +++ /dev/null @@ -1,311 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_KEYAGG_IMPL_H -#define SECP256K1_MODULE_MUSIG_KEYAGG_IMPL_H - -#include - -#include "keyagg.h" -#include "../../eckey.h" -#include "../../ecmult.h" -#include "../../field.h" -#include "../../group.h" -#include "../../hash.h" -#include "../../util.h" - -static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) { - if (secp256k1_ge_is_infinity(ge)) { - memset(data, 0, 64); - } else { - secp256k1_ge_to_bytes(data, ge); - } -} - -static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data) { - unsigned char zeros[64] = { 0 }; - if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) { - secp256k1_ge_set_infinity(ge); - } else { - secp256k1_ge_from_bytes(ge, data); - } -} - -static const unsigned char secp256k1_musig_keyagg_cache_magic[4] = { 0xf4, 0xad, 0xbb, 0xdf }; - -/* A keyagg cache consists of - * - 4 byte magic set during initialization to allow detecting an uninitialized - * object. - * - 64 byte aggregate (and potentially tweaked) public key - * - 64 byte "second" public key (set to the point at infinity if not present) - * - 32 byte hash of all public keys - * - 1 byte the parity of the internal key (if tweaked, otherwise 0) - * - 32 byte tweak - */ -/* Requires that cache_i->pk is not infinity and cache_i->second_pk_x to be normalized. */ -static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, secp256k1_keyagg_cache_internal *cache_i) { - unsigned char *ptr = cache->data; - memcpy(ptr, secp256k1_musig_keyagg_cache_magic, 4); - ptr += 4; - secp256k1_ge_to_bytes(ptr, &cache_i->pk); - ptr += 64; - secp256k1_point_save_ext(ptr, &cache_i->second_pk); - ptr += 64; - memcpy(ptr, cache_i->pk_hash, 32); - ptr += 32; - *ptr = cache_i->parity_acc; - ptr += 1; - secp256k1_scalar_get_b32(ptr, &cache_i->tweak); -} - -static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache) { - const unsigned char *ptr = cache->data; - ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_keyagg_cache_magic, 4) == 0); - ptr += 4; - secp256k1_ge_from_bytes(&cache_i->pk, ptr); - ptr += 64; - secp256k1_point_load_ext(&cache_i->second_pk, ptr); - ptr += 64; - memcpy(cache_i->pk_hash, ptr, 32); - ptr += 32; - cache_i->parity_acc = *ptr & 1; - ptr += 1; - secp256k1_scalar_set_b32(&cache_i->tweak, ptr, NULL); - return 1; -} - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */ -static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - - sha->s[0] = 0xb399d5e0ul; - sha->s[1] = 0xc8fff302ul; - sha->s[2] = 0x6badac71ul; - sha->s[3] = 0x07c5b7f1ul; - sha->s[4] = 0x9701e2eful; - sha->s[5] = 0x2a72ecf8ul; - sha->s[6] = 0x201a4c7bul; - sha->s[7] = 0xab148a38ul; - sha->bytes = 64; -} - -/* Computes pk_hash = tagged_hash(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_pk_hash(const secp256k1_context *ctx, unsigned char *pk_hash, const secp256k1_pubkey * const* pk, size_t np) { - secp256k1_sha256 sha; - size_t i; - - secp256k1_musig_keyagglist_sha256(&sha); - for (i = 0; i < np; i++) { - unsigned char ser[33]; - size_t ser_len = sizeof(ser); - if (!secp256k1_ec_pubkey_serialize(ctx, ser, &ser_len, pk[i], SECP256K1_EC_COMPRESSED)) { - return 0; - } - VERIFY_CHECK(ser_len == sizeof(ser)); - secp256k1_sha256_write(&sha, ser, sizeof(ser)); - } - secp256k1_sha256_finalize(&sha, pk_hash); - return 1; -} - -/* Initializes SHA256 with fixed midstate. This midstate was computed by applying - * SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */ -static void secp256k1_musig_keyaggcoef_sha256(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - - sha->s[0] = 0x6ef02c5aul; - sha->s[1] = 0x06a480deul; - sha->s[2] = 0x1f298665ul; - sha->s[3] = 0x1d1134f2ul; - sha->s[4] = 0x56a0b063ul; - sha->s[5] = 0x52da4147ul; - sha->s[6] = 0xf280d9d4ul; - sha->s[7] = 0x4484be15ul; - sha->bytes = 64; -} - -/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and - * otherwise tagged_hash(pk_hash, x) where pk_hash is the hash of public keys. - * second_pk is the point at infinity in case there is no second_pk. Assumes - * that pk is not the point at infinity and that the Y-coordinates of pk and - * second_pk are normalized. */ -static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *pk_hash, secp256k1_ge *pk, const secp256k1_ge *second_pk) { - secp256k1_sha256 sha; - - VERIFY_CHECK(!secp256k1_ge_is_infinity(pk)); - - if (!secp256k1_ge_is_infinity(second_pk) - && secp256k1_fe_equal(&pk->x, &second_pk->x) - && secp256k1_fe_is_odd(&pk->y) == secp256k1_fe_is_odd(&second_pk->y)) { - secp256k1_scalar_set_int(r, 1); - } else { - unsigned char buf[33]; - size_t buflen = sizeof(buf); - int ret; - secp256k1_musig_keyaggcoef_sha256(&sha); - secp256k1_sha256_write(&sha, pk_hash, 32); - ret = secp256k1_eckey_pubkey_serialize(pk, buf, &buflen, 1); -#ifdef VERIFY - /* Serialization does not fail since the pk is not the point at infinity - * (according to this function's precondition). */ - VERIFY_CHECK(ret && buflen == sizeof(buf)); -#else - (void) ret; -#endif - secp256k1_sha256_write(&sha, buf, sizeof(buf)); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(r, buf, NULL); - } -} - -/* Assumes both field elements x and second_pk_x are normalized. */ -static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk) { - secp256k1_musig_keyaggcoef_internal(r, cache_i->pk_hash, pk, &cache_i->second_pk); -} - -typedef struct { - const secp256k1_context *ctx; - /* pk_hash is the hash of the public keys */ - unsigned char pk_hash[32]; - const secp256k1_pubkey * const* pks; - secp256k1_ge second_pk; -} secp256k1_musig_pubkey_agg_ecmult_data; - -/* Callback for batch EC multiplication to compute keyaggcoef_0*P0 + keyaggcoef_1*P1 + ... */ -static int secp256k1_musig_pubkey_agg_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { - secp256k1_musig_pubkey_agg_ecmult_data *ctx = (secp256k1_musig_pubkey_agg_ecmult_data *) data; - int ret; - ret = secp256k1_pubkey_load(ctx->ctx, pt, ctx->pks[idx]); -#ifdef VERIFY - /* pubkey_load can't fail because the same pks have already been loaded in - * `musig_compute_pk_hash` (and we test this). */ - VERIFY_CHECK(ret); -#else - (void) ret; -#endif - secp256k1_musig_keyaggcoef_internal(sc, ctx->pk_hash, pt, &ctx->second_pk); - return 1; -} - -int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey * const* pubkeys, size_t n_pubkeys) { - secp256k1_musig_pubkey_agg_ecmult_data ecmult_data; - secp256k1_gej pkj; - secp256k1_ge pkp; - size_t i; - (void) scratch; - - VERIFY_CHECK(ctx != NULL); - if (agg_pk != NULL) { - memset(agg_pk, 0, sizeof(*agg_pk)); - } - ARG_CHECK(pubkeys != NULL); - ARG_CHECK(n_pubkeys > 0); - - ecmult_data.ctx = ctx; - ecmult_data.pks = pubkeys; - - secp256k1_ge_set_infinity(&ecmult_data.second_pk); - for (i = 1; i < n_pubkeys; i++) { - if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) { - secp256k1_ge pk; - if (!secp256k1_pubkey_load(ctx, &pk, pubkeys[i])) { - return 0; - } - ecmult_data.second_pk = pk; - break; - } - } - - if (!secp256k1_musig_compute_pk_hash(ctx, ecmult_data.pk_hash, pubkeys, n_pubkeys)) { - return 0; - } - /* TODO: actually use optimized ecmult_multi algorithms by providing a - * scratch space */ - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, NULL, &pkj, NULL, secp256k1_musig_pubkey_agg_callback, (void *) &ecmult_data, n_pubkeys)) { - /* In order to reach this line with the current implementation of - * ecmult_multi_var one would need to provide a callback that can - * fail. */ - return 0; - } - secp256k1_ge_set_gej(&pkp, &pkj); - secp256k1_fe_normalize_var(&pkp.y); - /* The resulting public key is infinity with negligible probability */ - VERIFY_CHECK(!secp256k1_ge_is_infinity(&pkp)); - if (keyagg_cache != NULL) { - secp256k1_keyagg_cache_internal cache_i = { 0 }; - cache_i.pk = pkp; - cache_i.second_pk = ecmult_data.second_pk; - memcpy(cache_i.pk_hash, ecmult_data.pk_hash, sizeof(cache_i.pk_hash)); - secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); - } - - secp256k1_extrakeys_ge_even_y(&pkp); - if (agg_pk != NULL) { - secp256k1_xonly_pubkey_save(agg_pk, &pkp); - } - return 1; -} - -int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *agg_pk, const secp256k1_musig_keyagg_cache *keyagg_cache) { - secp256k1_keyagg_cache_internal cache_i; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(agg_pk != NULL); - memset(agg_pk, 0, sizeof(*agg_pk)); - ARG_CHECK(keyagg_cache != NULL); - - if(!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - secp256k1_pubkey_save(agg_pk, &cache_i.pk); - return 1; -} - -static int secp256k1_musig_pubkey_tweak_add_internal(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32, int xonly) { - secp256k1_keyagg_cache_internal cache_i; - int overflow = 0; - secp256k1_scalar tweak; - - VERIFY_CHECK(ctx != NULL); - if (output_pubkey != NULL) { - memset(output_pubkey, 0, sizeof(*output_pubkey)); - } - ARG_CHECK(keyagg_cache != NULL); - ARG_CHECK(tweak32 != NULL); - - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - secp256k1_scalar_set_b32(&tweak, tweak32, &overflow); - if (overflow) { - return 0; - } - if (xonly && secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { - cache_i.parity_acc ^= 1; - secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak); - } - secp256k1_scalar_add(&cache_i.tweak, &cache_i.tweak, &tweak); - if (!secp256k1_eckey_pubkey_tweak_add(&cache_i.pk, &tweak)) { - return 0; - } - /* eckey_pubkey_tweak_add fails if cache_i.pk is infinity */ - VERIFY_CHECK(!secp256k1_ge_is_infinity(&cache_i.pk)); - secp256k1_keyagg_cache_save(keyagg_cache, &cache_i); - if (output_pubkey != NULL) { - secp256k1_pubkey_save(output_pubkey, &cache_i.pk); - } - return 1; -} - -int secp256k1_musig_pubkey_ec_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { - return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 0); -} - -int secp256k1_musig_pubkey_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { - return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 1); -} - -#endif diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h deleted file mode 100644 index e69afed5..00000000 --- a/src/modules/musig/main_impl.h +++ /dev/null @@ -1,14 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra, Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_MAIN_H -#define SECP256K1_MODULE_MUSIG_MAIN_H - -#include "keyagg_impl.h" -#include "session_impl.h" -#include "adaptor_impl.h" - -#endif diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md deleted file mode 100644 index 44b0de4f..00000000 --- a/src/modules/musig/musig.md +++ /dev/null @@ -1,63 +0,0 @@ -Notes on the musig module API -=========================== - -The following sections contain additional notes on the API of the musig module (`include/secp256k1_musig.h`). -A usage example can be found in `examples/musig.c`. - -# API misuse - -The musig API is designed to be as misuse resistant as possible. -However, the MuSig protocol has some additional failure modes (mainly due to interactivity) that do not appear in single-signing. -While the results can be catastrophic (e.g. leaking of the secret key), it is unfortunately not possible for the musig implementation to rule out all such failure modes. - -Therefore, users of the musig module must take great care to make sure of the following: - -1. A unique nonce per signing session is generated in `secp256k1_musig_nonce_gen`. - See the corresponding comment in `include/secp256k1_musig.h` for how to ensure that. -2. The `secp256k1_musig_secnonce` structure is never copied or serialized. - See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`. -3. Opaque data structures are never written to or read from directly. - Instead, only the provided accessor functions are used. -4. If adaptor signatures are used, all partial signatures are verified. - -# Key Aggregation and (Taproot) Tweaking - -Given a set of public keys, the aggregate public key is computed with `secp256k1_musig_pubkey_agg`. -A (Taproot) tweak can be added to the resulting public key with `secp256k1_xonly_pubkey_tweak_add` and a plain tweak can be added with `secp256k1_ec_pubkey_tweak_add`. - -# Signing - -This is covered by `examples/musig.c`. -Essentially, the protocol proceeds in the following steps: - -1. Generate a keypair with `secp256k1_keypair_create` and obtain the public key with `secp256k1_keypair_pub`. -2. Call `secp256k1_musig_pubkey_agg` with the pubkeys of all participants. -3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and a plain tweak with `secp256k1_musig_pubkey_ec_tweak_add`. -4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers. -5. Someone (not necessarily the signer) aggregates the public nonce with `secp256k1_musig_nonce_agg` and sends it to the signers. -6. Process the aggregate nonce with `secp256k1_musig_nonce_process`. -7. Create a partial signature with `secp256k1_musig_partial_sign`. -8. Verify the partial signatures (optional in some scenarios) with `secp256k1_musig_partial_sig_verify`. -9. Someone (not necessarily the signer) obtains all partial signatures and aggregates them into the final Schnorr signature using `secp256k1_musig_partial_sig_agg`. - -The aggregate signature can be verified with `secp256k1_schnorrsig_verify`. - -Note that steps 1 to 5 can happen before the message to be signed is known to the signers. -Therefore, the communication round to exchange nonces can be viewed as a pre-processing step that is run whenever convenient to the signers. -This disables some of the defense-in-depth measures that may protect against API misuse in some cases. -Similarly, the API supports an alternative protocol flow where generating the aggregate key (steps 1 to 3) is allowed to happen after exchanging nonces (steps 4 to 5). - -# Verification - -A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7. - -# Atomic Swaps - -The signing API supports the production of "adaptor signatures", modified partial signatures -which are offset by an auxiliary secret known to one party. That is, -1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`. -2. When calling `secp256k1_musig_nonce_process`, the public adaptor `T` is provided as the `adaptor` argument. -3. The party who is going to extract the secret adaptor `t` later must verify all partial signatures. -4. Due to step 2, the signature output of `secp256k1_musig_partial_sig_agg` is a pre-signature and not a valid Schnorr signature. All parties involved extract this session's `nonce_parity` with `secp256k1_musig_nonce_parity`. -5. The party who knows `t` must "adapt" the pre-signature with `t` (and the `nonce_parity` using `secp256k1_musig_adapt` to complete the signature. -6. Any party who sees both the final signature and the pre-signature (and has the `nonce_parity`) can extract `t` with `secp256k1_musig_extract_adaptor`. diff --git a/src/modules/musig/session.h b/src/modules/musig/session.h deleted file mode 100644 index dfaa5e0d..00000000 --- a/src/modules/musig/session.h +++ /dev/null @@ -1,25 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_SESSION_H -#define SECP256K1_MODULE_MUSIG_SESSION_H - -#include "../../../include/secp256k1.h" -#include "../../../include/secp256k1_musig.h" - -#include "../../scalar.h" - -typedef struct { - int fin_nonce_parity; - unsigned char fin_nonce[32]; - secp256k1_scalar noncecoef; - secp256k1_scalar challenge; - secp256k1_scalar s_part; -} secp256k1_musig_session_internal; - -static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session); - -#endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h deleted file mode 100644 index ff87f2fd..00000000 --- a/src/modules/musig/session_impl.h +++ /dev/null @@ -1,705 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2021 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_SESSION_IMPL_H -#define SECP256K1_MODULE_MUSIG_SESSION_IMPL_H - -#include - -#include "../../../include/secp256k1.h" -#include "../../../include/secp256k1_extrakeys.h" -#include "../../../include/secp256k1_musig.h" - -#include "keyagg.h" -#include "session.h" -#include "../../eckey.h" -#include "../../hash.h" -#include "../../scalar.h" -#include "../../util.h" - -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, 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); -} - -static int secp256k1_musig_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_ge *pk, secp256k1_musig_secnonce *secnonce) { - int is_zero; - ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_musig_secnonce_magic, 4) == 0); - 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]); - /* We make very sure that the nonce isn't invalidated by checking the values - * in addition to the magic. */ - is_zero = secp256k1_scalar_is_zero(&k[0]) & secp256k1_scalar_is_zero(&k[1]); - secp256k1_declassify(ctx, &is_zero, sizeof(is_zero)); - ARG_CHECK(!is_zero); - return 1; -} - -/* If flag is true, invalidate the secnonce; otherwise leave it. Constant-time. */ -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); -} - -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, secp256k1_ge* ge) { - 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, &ge[i]); - } -} - -/* 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* ge, 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(&ge[i], nonce->data + 4 + 64*i); - } - return 1; -} - -static const unsigned char secp256k1_musig_aggnonce_magic[4] = { 0xa8, 0xb7, 0xe4, 0x67 }; - -static void secp256k1_musig_aggnonce_save(secp256k1_musig_aggnonce* nonce, secp256k1_ge* ge) { - int i; - memcpy(&nonce->data[0], secp256k1_musig_aggnonce_magic, 4); - for (i = 0; i < 2; i++) { - secp256k1_point_save_ext(&nonce->data[4 + 64*i], &ge[i]); - } -} - -static int secp256k1_musig_aggnonce_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_musig_aggnonce* nonce) { - int i; - - ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_musig_aggnonce_magic, 4) == 0); - for (i = 0; i < 2; i++) { - secp256k1_point_load_ext(&ge[i], &nonce->data[4 + 64*i]); - } - return 1; -} - -static const unsigned char secp256k1_musig_session_cache_magic[4] = { 0x9d, 0xed, 0xe9, 0x17 }; - -/* A session consists of - * - 4 byte session cache magic - * - 1 byte the parity of the final nonce - * - 32 byte serialized x-only final nonce - * - 32 byte nonce coefficient b - * - 32 byte signature challenge hash e - * - 32 byte scalar s that is added to the partial signatures of the signers - */ -static void secp256k1_musig_session_save(secp256k1_musig_session *session, const secp256k1_musig_session_internal *session_i) { - unsigned char *ptr = session->data; - - memcpy(ptr, secp256k1_musig_session_cache_magic, 4); - ptr += 4; - *ptr = session_i->fin_nonce_parity; - ptr += 1; - memcpy(ptr, session_i->fin_nonce, 32); - ptr += 32; - secp256k1_scalar_get_b32(ptr, &session_i->noncecoef); - ptr += 32; - secp256k1_scalar_get_b32(ptr, &session_i->challenge); - ptr += 32; - secp256k1_scalar_get_b32(ptr, &session_i->s_part); -} - -static int secp256k1_musig_session_load(const secp256k1_context* ctx, secp256k1_musig_session_internal *session_i, const secp256k1_musig_session *session) { - const unsigned char *ptr = session->data; - - ARG_CHECK(secp256k1_memcmp_var(ptr, secp256k1_musig_session_cache_magic, 4) == 0); - ptr += 4; - session_i->fin_nonce_parity = *ptr; - ptr += 1; - memcpy(session_i->fin_nonce, ptr, 32); - ptr += 32; - secp256k1_scalar_set_b32(&session_i->noncecoef, ptr, NULL); - ptr += 32; - secp256k1_scalar_set_b32(&session_i->challenge, ptr, NULL); - ptr += 32; - secp256k1_scalar_set_b32(&session_i->s_part, ptr, NULL); - return 1; -} - -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); -} - -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; -} - -int secp256k1_musig_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_pubnonce* nonce) { - secp256k1_ge ge[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out66 != NULL); - memset(out66, 0, 66); - ARG_CHECK(nonce != NULL); - - if (!secp256k1_musig_pubnonce_load(ctx, ge, nonce)) { - return 0; - } - for (i = 0; i < 2; i++) { - int ret; - size_t size = 33; - ret = secp256k1_eckey_pubkey_serialize(&ge[i], &out66[33*i], &size, 1); -#ifdef VERIFY - /* serialize must succeed because the point was just loaded */ - VERIFY_CHECK(ret && size == 33); -#else - (void) ret; -#endif - } - return 1; -} - -int secp256k1_musig_pubnonce_parse(const secp256k1_context* ctx, secp256k1_musig_pubnonce* nonce, const unsigned char *in66) { - secp256k1_ge ge[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(&ge[i], &in66[33*i], 33)) { - return 0; - } - if (!secp256k1_ge_is_in_correct_subgroup(&ge[i])) { - return 0; - } - } - secp256k1_musig_pubnonce_save(nonce, ge); - return 1; -} - -int secp256k1_musig_aggnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_musig_aggnonce* nonce) { - secp256k1_ge ge[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out66 != NULL); - memset(out66, 0, 66); - ARG_CHECK(nonce != NULL); - - if (!secp256k1_musig_aggnonce_load(ctx, ge, nonce)) { - return 0; - } - for (i = 0; i < 2; i++) { - secp256k1_ge_serialize_ext(&out66[33*i], &ge[i]); - } - return 1; -} - -int secp256k1_musig_aggnonce_parse(const secp256k1_context* ctx, secp256k1_musig_aggnonce* nonce, const unsigned char *in66) { - secp256k1_ge ge[2]; - int i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(nonce != NULL); - ARG_CHECK(in66 != NULL); - - for (i = 0; i < 2; i++) { - if (!secp256k1_ge_parse_ext(&ge[i], &in66[33*i])) { - return 0; - } - } - secp256k1_musig_aggnonce_save(nonce, ge); - return 1; -} - -int secp256k1_musig_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_sig* sig) { - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out32 != NULL); - ARG_CHECK(sig != NULL); - memcpy(out32, &sig->data[4], 32); - return 1; -} - -int secp256k1_musig_partial_sig_parse(const secp256k1_context* ctx, secp256k1_musig_partial_sig* sig, const unsigned char *in32) { - secp256k1_scalar tmp; - int overflow; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(in32 != NULL); - - secp256k1_scalar_set_b32(&tmp, in32, &overflow); - if (overflow) { - return 0; - } - secp256k1_musig_partial_sig_save(sig, &tmp); - return 1; -} - -/* Write optional inputs into the hash */ -static void secp256k1_nonce_function_musig_helper(secp256k1_sha256 *sha, unsigned int prefix_size, const unsigned char *data, unsigned char len) { - unsigned char zero[7] = { 0 }; - /* The spec requires length prefixes to be between 1 and 8 bytes - * (inclusive) */ - VERIFY_CHECK(prefix_size <= 8); - /* Since the length of all input data fits in a byte, we can always pad the - * length prefix with prefix_size - 1 zero bytes. */ - secp256k1_sha256_write(sha, zero, prefix_size - 1); - if (data != NULL) { - secp256k1_sha256_write(sha, &len, 1); - secp256k1_sha256_write(sha, data, len); - } else { - len = 0; - secp256k1_sha256_write(sha, &len, 1); - } -} - -static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *msg32, const unsigned char *seckey32, const unsigned char *pk33, const unsigned char *agg_pk32, const unsigned char *extra_input32) { - secp256k1_sha256 sha; - unsigned char rand[32]; - unsigned char i; - unsigned char msg_present; - - if (seckey32 != NULL) { - secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/aux", sizeof("MuSig/aux") - 1); - secp256k1_sha256_write(&sha, session_id, 32); - secp256k1_sha256_finalize(&sha, rand); - for (i = 0; i < 32; i++) { - rand[i] ^= seckey32[i]; - } - } else { - memcpy(rand, session_id, sizeof(rand)); - } - - /* Subtract one from `sizeof` to avoid hashing the implicit null byte */ - secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/nonce", sizeof("MuSig/nonce") - 1); - secp256k1_sha256_write(&sha, rand, sizeof(rand)); - secp256k1_nonce_function_musig_helper(&sha, 1, pk33, 33); - secp256k1_nonce_function_musig_helper(&sha, 1, agg_pk32, 32); - msg_present = msg32 != NULL; - secp256k1_sha256_write(&sha, &msg_present, 1); - if (msg_present) { - secp256k1_nonce_function_musig_helper(&sha, 8, msg32, 32); - } - secp256k1_nonce_function_musig_helper(&sha, 4, extra_input32, 32); - - for (i = 0; i < 2; i++) { - unsigned char buf[32]; - secp256k1_sha256 sha_tmp = sha; - secp256k1_sha256_write(&sha_tmp, &i, 1); - secp256k1_sha256_finalize(&sha_tmp, buf); - secp256k1_scalar_set_b32(&k[i], buf, NULL); - } -} - -int secp256k1_musig_nonce_gen(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *session_id32, const unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) { - secp256k1_keyagg_cache_internal cache_i; - secp256k1_scalar k[2]; - secp256k1_ge nonce_pt[2]; - int i; - unsigned char pk_ser[33]; - size_t pk_ser_len = sizeof(pk_ser); - unsigned char aggpk_ser[32]; - unsigned char *aggpk_ser_ptr = NULL; - secp256k1_ge pk; - int pk_serialize_success; - int ret = 1; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secnonce != NULL); - memset(secnonce, 0, sizeof(*secnonce)); - ARG_CHECK(pubnonce != NULL); - memset(pubnonce, 0, sizeof(*pubnonce)); - ARG_CHECK(session_id32 != NULL); - ARG_CHECK(pubkey != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - if (seckey == NULL) { - /* Check in constant time that the session_id is not 0 as a - * defense-in-depth measure that may protect against a faulty RNG. */ - unsigned char acc = 0; - for (i = 0; i < 32; i++) { - acc |= session_id32[i]; - } - ret &= !!acc; - memset(&acc, 0, sizeof(acc)); - } - - /* 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 (keyagg_cache != NULL) { - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - /* The loaded point cache_i.pk can not be the point at infinity. */ - secp256k1_fe_get_b32(aggpk_ser, &cache_i.pk.x); - aggpk_ser_ptr = aggpk_ser; - } - if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) { - return 0; - } - pk_serialize_success = secp256k1_eckey_pubkey_serialize(&pk, pk_ser, &pk_ser_len, SECP256K1_EC_COMPRESSED); - -#ifdef VERIFY - /* A pubkey cannot be the point at infinity */ - VERIFY_CHECK(pk_serialize_success); - VERIFY_CHECK(pk_ser_len == sizeof(pk_ser)); -#else - (void) pk_serialize_success; -#endif - - secp256k1_nonce_function_musig(k, session_id32, msg32, seckey, pk_ser, aggpk_ser_ptr, extra_input32); - VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0])); - VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1])); - VERIFY_CHECK(!secp256k1_scalar_eq(&k[0], &k[1])); - secp256k1_musig_secnonce_save(secnonce, k, &pk); - secp256k1_musig_secnonce_invalidate(ctx, secnonce, !ret); - - for (i = 0; i < 2; i++) { - secp256k1_gej nonce_ptj; - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj, &k[i]); - secp256k1_ge_set_gej(&nonce_pt[i], &nonce_ptj); - secp256k1_declassify(ctx, &nonce_pt[i], sizeof(nonce_pt)); - secp256k1_scalar_clear(&k[i]); - } - /* nonce_pt won't be infinity because k != 0 with overwhelming probability */ - secp256k1_musig_pubnonce_save(pubnonce, nonce_pt); - return ret; -} - -static int secp256k1_musig_sum_nonces(const secp256k1_context* ctx, secp256k1_gej *summed_nonces, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) { - size_t i; - int j; - - secp256k1_gej_set_infinity(&summed_nonces[0]); - secp256k1_gej_set_infinity(&summed_nonces[1]); - - for (i = 0; i < n_pubnonces; i++) { - secp256k1_ge nonce_pt[2]; - if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonces[i])) { - return 0; - } - for (j = 0; j < 2; j++) { - secp256k1_gej_add_ge_var(&summed_nonces[j], &summed_nonces[j], &nonce_pt[j], NULL); - } - } - return 1; -} - -int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggnonce *aggnonce, const secp256k1_musig_pubnonce * const* pubnonces, size_t n_pubnonces) { - secp256k1_gej aggnonce_ptj[2]; - secp256k1_ge aggnonce_pt[2]; - int i; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(aggnonce != NULL); - ARG_CHECK(pubnonces != NULL); - ARG_CHECK(n_pubnonces > 0); - - if (!secp256k1_musig_sum_nonces(ctx, aggnonce_ptj, pubnonces, n_pubnonces)) { - return 0; - } - for (i = 0; i < 2; i++) { - secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]); - } - secp256k1_musig_aggnonce_save(aggnonce, aggnonce_pt); - return 1; -} - -/* tagged_hash(aggnonce[0], aggnonce[1], agg_pk, msg) */ -static int secp256k1_musig_compute_noncehash(unsigned char *noncehash, secp256k1_ge *aggnonce, const unsigned char *agg_pk32, const unsigned char *msg) { - unsigned char buf[33]; - secp256k1_sha256 sha; - int i; - - secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"MuSig/noncecoef", sizeof("MuSig/noncecoef") - 1); - for (i = 0; i < 2; i++) { - secp256k1_ge_serialize_ext(buf, &aggnonce[i]); - secp256k1_sha256_write(&sha, buf, sizeof(buf)); - } - secp256k1_sha256_write(&sha, agg_pk32, 32); - secp256k1_sha256_write(&sha, msg, 32); - secp256k1_sha256_finalize(&sha, noncehash); - return 1; -} - -static int secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsigned char *fin_nonce, secp256k1_scalar *b, secp256k1_gej *aggnoncej, const unsigned char *agg_pk32, const unsigned char *msg) { - unsigned char noncehash[32]; - secp256k1_ge fin_nonce_pt; - secp256k1_gej fin_nonce_ptj; - secp256k1_ge aggnonce[2]; - - secp256k1_ge_set_gej(&aggnonce[0], &aggnoncej[0]); - secp256k1_ge_set_gej(&aggnonce[1], &aggnoncej[1]); - if (!secp256k1_musig_compute_noncehash(noncehash, aggnonce, agg_pk32, msg)) { - return 0; - } - /* fin_nonce = aggnonce[0] + b*aggnonce[1] */ - secp256k1_scalar_set_b32(b, noncehash, NULL); - secp256k1_gej_set_infinity(&fin_nonce_ptj); - secp256k1_ecmult(&fin_nonce_ptj, &aggnoncej[1], b, NULL); - secp256k1_gej_add_ge_var(&fin_nonce_ptj, &fin_nonce_ptj, &aggnonce[0], NULL); - secp256k1_ge_set_gej(&fin_nonce_pt, &fin_nonce_ptj); - if (secp256k1_ge_is_infinity(&fin_nonce_pt)) { - fin_nonce_pt = secp256k1_ge_const_g; - } - /* fin_nonce_pt is not the point at infinity */ - secp256k1_fe_normalize_var(&fin_nonce_pt.x); - secp256k1_fe_get_b32(fin_nonce, &fin_nonce_pt.x); - secp256k1_fe_normalize_var(&fin_nonce_pt.y); - *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); - return 1; -} - -int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey *adaptor) { - secp256k1_keyagg_cache_internal cache_i; - secp256k1_ge aggnonce_pt[2]; - secp256k1_gej aggnonce_ptj[2]; - unsigned char fin_nonce[32]; - secp256k1_musig_session_internal session_i; - unsigned char agg_pk32[32]; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(aggnonce != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(keyagg_cache != NULL); - - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - secp256k1_fe_get_b32(agg_pk32, &cache_i.pk.x); - - if (!secp256k1_musig_aggnonce_load(ctx, aggnonce_pt, aggnonce)) { - return 0; - } - secp256k1_gej_set_ge(&aggnonce_ptj[0], &aggnonce_pt[0]); - secp256k1_gej_set_ge(&aggnonce_ptj[1], &aggnonce_pt[1]); - /* Add public adaptor to nonce */ - if (adaptor != NULL) { - secp256k1_ge adaptorp; - if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { - return 0; - } - secp256k1_gej_add_ge_var(&aggnonce_ptj[0], &aggnonce_ptj[0], &adaptorp, NULL); - } - if (!secp256k1_musig_nonce_process_internal(&session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_ptj, agg_pk32, msg32)) { - return 0; - } - - secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, agg_pk32); - - /* If there is a tweak then set `challenge` times `tweak` to the `s`-part.*/ - secp256k1_scalar_set_int(&session_i.s_part, 0); - if (!secp256k1_scalar_is_zero(&cache_i.tweak)) { - secp256k1_scalar e_tmp; - secp256k1_scalar_mul(&e_tmp, &session_i.challenge, &cache_i.tweak); - if (secp256k1_fe_is_odd(&cache_i.pk.y)) { - secp256k1_scalar_negate(&e_tmp, &e_tmp); - } - secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &e_tmp); - } - memcpy(session_i.fin_nonce, fin_nonce, sizeof(session_i.fin_nonce)); - secp256k1_musig_session_save(session, &session_i); - 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; - secp256k1_scalar k[2]; - secp256k1_scalar mu, s; - secp256k1_keyagg_cache_internal cache_i; - secp256k1_musig_session_internal session_i; - int ret; - - VERIFY_CHECK(ctx != NULL); - - ARG_CHECK(secnonce != NULL); - /* Fails if the magic doesn't match */ - ret = secp256k1_musig_secnonce_load(ctx, k, &pk, secnonce); - /* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls - * of this function to fail */ - memset(secnonce, 0, sizeof(*secnonce)); - if (!ret) { - secp256k1_musig_partial_sign_clear(&sk, k); - return 0; - } - - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(keypair != NULL); - ARG_CHECK(keyagg_cache != NULL); - ARG_CHECK(session != NULL); - - if (!secp256k1_keypair_load(ctx, &sk, &keypair_pk, keypair)) { - secp256k1_musig_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); - return 0; - } - secp256k1_fe_normalize_var(&pk.y); - - /* Negate sk if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc. - * This corresponds to the line "Let d = g⋅gacc⋅d' mod n" in the - * specification. */ - if ((secp256k1_fe_is_odd(&cache_i.pk.y) - != cache_i.parity_acc)) { - secp256k1_scalar_negate(&sk, &sk); - } - - /* Multiply KeyAgg coefficient */ - secp256k1_fe_normalize_var(&pk.x); - /* TODO Cache mu */ - secp256k1_musig_keyaggcoef(&mu, &cache_i, &pk); - secp256k1_scalar_mul(&sk, &sk, &mu); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - secp256k1_musig_partial_sign_clear(&sk, k); - return 0; - } - - if (session_i.fin_nonce_parity) { - secp256k1_scalar_negate(&k[0], &k[0]); - secp256k1_scalar_negate(&k[1], &k[1]); - } - - /* Sign */ - secp256k1_scalar_mul(&s, &session_i.challenge, &sk); - secp256k1_scalar_mul(&k[1], &session_i.noncecoef, &k[1]); - 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); - return 1; -} - -int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_partial_sig *partial_sig, const secp256k1_musig_pubnonce *pubnonce, const secp256k1_pubkey *pubkey, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_musig_session *session) { - secp256k1_keyagg_cache_internal cache_i; - secp256k1_musig_session_internal session_i; - secp256k1_scalar mu, e, s; - secp256k1_gej pkj; - secp256k1_ge nonce_pt[2]; - secp256k1_gej rj; - secp256k1_gej tmp; - secp256k1_ge pkp; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(partial_sig != NULL); - ARG_CHECK(pubnonce != NULL); - ARG_CHECK(pubkey != NULL); - ARG_CHECK(keyagg_cache != NULL); - ARG_CHECK(session != NULL); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - - /* Compute "effective" nonce rj = aggnonce[0] + b*aggnonce[1] */ - /* TODO: use multiexp to compute -s*G + e*mu*pubkey + aggnonce[0] + b*aggnonce[1] */ - if (!secp256k1_musig_pubnonce_load(ctx, nonce_pt, pubnonce)) { - return 0; - } - secp256k1_gej_set_ge(&rj, &nonce_pt[1]); - secp256k1_ecmult(&rj, &rj, &session_i.noncecoef, NULL); - secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pt[0], NULL); - - if (!secp256k1_pubkey_load(ctx, &pkp, pubkey)) { - return 0; - } - if (!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { - return 0; - } - /* Multiplying the challenge by the KeyAgg coefficient is equivalent - * to multiplying the signer's public key by the coefficient, except - * much easier to do. */ - secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp); - secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - - /* Negate e if secp256k1_fe_is_odd(&cache_i.pk.y)) XOR cache_i.parity_acc. - * This corresponds to the line "Let g' = g⋅gacc mod n" and the multiplication "g'⋅e" - * in the specification. */ - if (secp256k1_fe_is_odd(&cache_i.pk.y) - != cache_i.parity_acc) { - secp256k1_scalar_negate(&e, &e); - } - - if (!secp256k1_musig_partial_sig_load(ctx, &s, partial_sig)) { - return 0; - } - /* Compute -s*G + e*pkj + rj (e already includes the keyagg coefficient mu) */ - secp256k1_scalar_negate(&s, &s); - secp256k1_gej_set_ge(&pkj, &pkp); - secp256k1_ecmult(&tmp, &pkj, &e, &s); - if (session_i.fin_nonce_parity) { - secp256k1_gej_neg(&rj, &rj); - } - secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL); - - return secp256k1_gej_is_infinity(&tmp); -} - -int secp256k1_musig_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, const secp256k1_musig_session *session, const secp256k1_musig_partial_sig * const* partial_sigs, size_t n_sigs) { - size_t i; - secp256k1_musig_session_internal session_i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig64 != NULL); - ARG_CHECK(session != NULL); - ARG_CHECK(partial_sigs != NULL); - ARG_CHECK(n_sigs > 0); - - if (!secp256k1_musig_session_load(ctx, &session_i, session)) { - return 0; - } - for (i = 0; i < n_sigs; i++) { - secp256k1_scalar term; - if (!secp256k1_musig_partial_sig_load(ctx, &term, partial_sigs[i])) { - return 0; - } - secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &term); - } - secp256k1_scalar_get_b32(&sig64[32], &session_i.s_part); - memcpy(&sig64[0], session_i.fin_nonce, 32); - return 1; -} - -#endif diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h deleted file mode 100644 index 13b41249..00000000 --- a/src/modules/musig/tests_impl.h +++ /dev/null @@ -1,1193 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_MODULE_MUSIG_TESTS_IMPL_H -#define SECP256K1_MODULE_MUSIG_TESTS_IMPL_H - -#include -#include - -#include "../../../include/secp256k1.h" -#include "../../../include/secp256k1_extrakeys.h" -#include "../../../include/secp256k1_musig.h" - -#include "session.h" -#include "keyagg.h" -#include "../../scalar.h" -#include "../../scratch.h" -#include "../../field.h" -#include "../../group.h" -#include "../../hash.h" -#include "../../util.h" - -#include "vectors.h" - -static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_pubkey *pk, const unsigned char *sk) { - int ret; - secp256k1_keypair keypair_tmp; - ret = secp256k1_keypair_create(CTX, &keypair_tmp, sk); - ret &= secp256k1_keypair_pub(CTX, pk, &keypair_tmp); - if (keypair != NULL) { - *keypair = keypair_tmp; - } - return ret; -} - -/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig aggregate, sign, verify - * test. */ -static void musig_simple_test(secp256k1_scratch_space *scratch) { - unsigned char sk[2][32]; - secp256k1_keypair keypair[2]; - secp256k1_musig_pubnonce pubnonce[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr[2]; - secp256k1_musig_aggnonce aggnonce; - unsigned char msg[32]; - secp256k1_xonly_pubkey agg_pk; - secp256k1_musig_keyagg_cache keyagg_cache; - unsigned char session_id[2][32]; - secp256k1_musig_secnonce secnonce[2]; - secp256k1_pubkey pk[2]; - const secp256k1_pubkey *pk_ptr[2]; - secp256k1_musig_partial_sig partial_sig[2]; - const secp256k1_musig_partial_sig *partial_sig_ptr[2]; - unsigned char final_sig[64]; - secp256k1_musig_session session; - int i; - - testrand256(msg); - for (i = 0; i < 2; i++) { - testrand256(session_id[i]); - testrand256(sk[i]); - pk_ptr[i] = &pk[i]; - pubnonce_ptr[i] = &pubnonce[i]; - partial_sig_ptr[i] = &partial_sig[i]; - - CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i])); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[i], &pubnonce[i], session_id[i], sk[i], &pk[i], NULL, NULL, NULL) == 1); - } - - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); - - for (i = 0; i < 2; i++) { - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[i], &secnonce[i], &keypair[i], &keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[i], &pubnonce[i], &pk[i], &keyagg_cache, &session) == 1); - } - - CHECK(secp256k1_musig_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 2) == 1); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); -} - -static void pubnonce_summing_to_inf(secp256k1_musig_pubnonce *pubnonce) { - secp256k1_ge ge[2]; - int i; - secp256k1_gej summed_nonces[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr[2]; - - ge[0] = secp256k1_ge_const_g; - ge[1] = secp256k1_ge_const_g; - - for (i = 0; i < 2; i++) { - secp256k1_musig_pubnonce_save(&pubnonce[i], ge); - pubnonce_ptr[i] = &pubnonce[i]; - secp256k1_ge_neg(&ge[0], &ge[0]); - secp256k1_ge_neg(&ge[1], &ge[1]); - } - - secp256k1_musig_sum_nonces(CTX, summed_nonces, pubnonce_ptr, 2); - CHECK(secp256k1_gej_is_infinity(&summed_nonces[0])); - CHECK(secp256k1_gej_is_infinity(&summed_nonces[1])); -} - -int memcmp_and_randomize(unsigned char *value, const unsigned char *expected, size_t len) { - int ret; - size_t i; - ret = secp256k1_memcmp_var(value, expected, len); - for (i = 0; i < len; i++) { - value[i] = testrand_bits(8); - } - return ret; -} - -static void musig_api_tests(secp256k1_scratch_space *scratch) { - secp256k1_scratch_space *scratch_small; - secp256k1_musig_partial_sig partial_sig[2]; - const secp256k1_musig_partial_sig *partial_sig_ptr[2]; - secp256k1_musig_partial_sig invalid_partial_sig; - const secp256k1_musig_partial_sig *invalid_partial_sig_ptr[2]; - unsigned char final_sig[64]; - unsigned char pre_sig[64]; - unsigned char buf[32]; - unsigned char sk[2][32]; - secp256k1_keypair keypair[2]; - secp256k1_keypair invalid_keypair; - unsigned char max64[64]; - unsigned char zeros132[132] = { 0 }; - unsigned char session_id[2][32]; - secp256k1_musig_secnonce secnonce[2]; - secp256k1_musig_secnonce secnonce_tmp; - secp256k1_musig_secnonce invalid_secnonce; - secp256k1_musig_pubnonce pubnonce[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr[2]; - unsigned char pubnonce_ser[66]; - secp256k1_musig_pubnonce inf_pubnonce[2]; - const secp256k1_musig_pubnonce *inf_pubnonce_ptr[2]; - secp256k1_musig_pubnonce invalid_pubnonce; - const secp256k1_musig_pubnonce *invalid_pubnonce_ptr[1]; - secp256k1_musig_aggnonce aggnonce; - unsigned char aggnonce_ser[66]; - unsigned char msg[32]; - secp256k1_xonly_pubkey agg_pk; - secp256k1_pubkey full_agg_pk; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_musig_keyagg_cache invalid_keyagg_cache; - secp256k1_musig_session session; - secp256k1_musig_session invalid_session; - secp256k1_pubkey pk[2]; - const secp256k1_pubkey *pk_ptr[2]; - secp256k1_pubkey invalid_pk; - const secp256k1_pubkey *invalid_pk_ptr2[2]; - const secp256k1_pubkey *invalid_pk_ptr3[3]; - unsigned char tweak[32]; - int nonce_parity; - unsigned char sec_adaptor[32]; - unsigned char sec_adaptor1[32]; - secp256k1_pubkey adaptor; - int i; - - /** setup **/ - memset(max64, 0xff, sizeof(max64)); - memset(&invalid_keypair, 0, sizeof(invalid_keypair)); - memset(&invalid_pk, 0, sizeof(invalid_pk)); - memset(&invalid_secnonce, 0, sizeof(invalid_secnonce)); - memset(&invalid_partial_sig, 0, sizeof(invalid_partial_sig)); - pubnonce_summing_to_inf(inf_pubnonce); - /* Simulate structs being uninitialized by setting it to 0s. We don't want - * to produce undefined behavior by actually providing uninitialized - * structs. */ - memset(&invalid_keyagg_cache, 0, sizeof(invalid_keyagg_cache)); - memset(&invalid_pk, 0, sizeof(invalid_pk)); - memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce)); - memset(&invalid_session, 0, sizeof(invalid_session)); - - testrand256(sec_adaptor); - testrand256(msg); - testrand256(tweak); - CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1); - for (i = 0; i < 2; i++) { - pk_ptr[i] = &pk[i]; - invalid_pk_ptr2[i] = &invalid_pk; - invalid_pk_ptr3[i] = &pk[i]; - pubnonce_ptr[i] = &pubnonce[i]; - inf_pubnonce_ptr[i] = &inf_pubnonce[i]; - partial_sig_ptr[i] = &partial_sig[i]; - invalid_partial_sig_ptr[i] = &partial_sig[i]; - testrand256(session_id[i]); - testrand256(sk[i]); - CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i])); - } - invalid_pubnonce_ptr[0] = &invalid_pubnonce; - invalid_partial_sig_ptr[0] = &invalid_partial_sig; - /* invalid_pk_ptr3 has two valid, one invalid pk, which is important to test - * musig_pubkey_agg */ - invalid_pk_ptr3[2] = &invalid_pk; - - /** main test body **/ - - /** Key aggregation **/ - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - /* pubkey_agg does not require a scratch space */ - CHECK(secp256k1_musig_pubkey_agg(CTX, NULL, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - /* A small scratch space works too, but will result in using an ineffecient algorithm */ - scratch_small = secp256k1_scratch_space_create(CTX, 1); - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch_small, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - secp256k1_scratch_space_destroy(CTX, scratch_small); - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, NULL, &keyagg_cache, pk_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, NULL, pk_ptr, 2) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, NULL, 2)); - CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, invalid_pk_ptr2, 2)); - CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, invalid_pk_ptr3, 3)); - CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, pk_ptr, 0)); - CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, NULL, 0)); - CHECK(memcmp_and_randomize(agg_pk.data, zeros132, sizeof(agg_pk.data)) == 0); - - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - - /* pubkey_get */ - CHECK(secp256k1_musig_pubkey_get(CTX, &full_agg_pk, &keyagg_cache) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_get(CTX, NULL, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubkey_get(CTX, &full_agg_pk, NULL)); - CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros132, sizeof(full_agg_pk)) == 0); - - /** Tweaking **/ - { - int (*tweak_func[2]) (const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32); - tweak_func[0] = secp256k1_musig_pubkey_ec_tweak_add; - tweak_func[1] = secp256k1_musig_pubkey_xonly_tweak_add; - for (i = 0; i < 2; i++) { - secp256k1_pubkey tmp_output_pk; - secp256k1_musig_keyagg_cache tmp_keyagg_cache = keyagg_cache; - CHECK((*tweak_func[i])(CTX, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - /* Reset keyagg_cache */ - tmp_keyagg_cache = keyagg_cache; - CHECK((*tweak_func[i])(CTX, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK((*tweak_func[i])(CTX, NULL, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK_ILLEGAL(CTX, (*tweak_func[i])(CTX, &tmp_output_pk, NULL, tweak)); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - CHECK_ILLEGAL(CTX, (*tweak_func[i])(CTX, &tmp_output_pk, &tmp_keyagg_cache, NULL)); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - CHECK((*tweak_func[i])(CTX, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - /* Uninitialized keyagg_cache */ - CHECK_ILLEGAL(CTX, (*tweak_func[i])(CTX, &tmp_output_pk, &invalid_keyagg_cache, tweak)); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros132, sizeof(tmp_output_pk.data)) == 0); - } - } - - /** Session creation **/ - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); - CHECK_ILLEGAL(STATIC_CTX, secp256k1_musig_nonce_gen(STATIC_CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_gen(CTX, NULL, &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_gen(CTX, &secnonce[0], NULL, session_id[0], sk[0], &pk[0], msg, &keyagg_cache, max64)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], NULL, sk[0], &pk[0], msg, &keyagg_cache, max64)); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); - /* no seckey and session_id is 0 */ - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros132, NULL, &pk[0], msg, &keyagg_cache, max64) == 0); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); - /* session_id 0 is fine when a seckey is provided */ - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], zeros132, sk[0], &pk[0], msg, &keyagg_cache, max64) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, &pk[0], msg, &keyagg_cache, max64) == 1); - /* invalid seckey */ - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], max64, &pk[0], msg, &keyagg_cache, max64) == 0); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], NULL, msg, &keyagg_cache, max64)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &invalid_pk, msg, &keyagg_cache, max64)); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], NULL, &keyagg_cache, max64) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, NULL, max64) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &invalid_keyagg_cache, max64)); - CHECK(memcmp_and_randomize(secnonce[0].data, zeros132, sizeof(secnonce[0].data)) == 0); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk[0], &pk[0], msg, &keyagg_cache, NULL) == 1); - - /* Every in-argument except session_id and pubkey can be NULL */ - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], NULL, &pk[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], sk[1], &pk[1], NULL, NULL, NULL) == 1); - - /** Serialize and parse public nonces **/ - CHECK_ILLEGAL(CTX, secp256k1_musig_pubnonce_serialize(CTX, NULL, &pubnonce[0])); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubnonce_serialize(CTX, pubnonce_ser, NULL)); - CHECK(memcmp_and_randomize(pubnonce_ser, zeros132, sizeof(pubnonce_ser)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubnonce_serialize(CTX, pubnonce_ser, &invalid_pubnonce)); - CHECK(memcmp_and_randomize(pubnonce_ser, zeros132, sizeof(pubnonce_ser)) == 0); - CHECK(secp256k1_musig_pubnonce_serialize(CTX, pubnonce_ser, &pubnonce[0]) == 1); - - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce[0], pubnonce_ser) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubnonce_parse(CTX, NULL, pubnonce_ser)); - CHECK_ILLEGAL(CTX, secp256k1_musig_pubnonce_parse(CTX, &pubnonce[0], NULL)); - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce[0], zeros132) == 0); - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce[0], pubnonce_ser) == 1); - - { - /* Check that serialize and parse results in the same value */ - secp256k1_musig_pubnonce tmp; - CHECK(secp256k1_musig_pubnonce_serialize(CTX, pubnonce_ser, &pubnonce[0]) == 1); - CHECK(secp256k1_musig_pubnonce_parse(CTX, &tmp, pubnonce_ser) == 1); - CHECK(secp256k1_memcmp_var(&tmp, &pubnonce[0], sizeof(tmp)) == 0); - } - - /** Receive nonces and aggregate **/ - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_agg(CTX, NULL, pubnonce_ptr, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_agg(CTX, &aggnonce, NULL, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 0)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_agg(CTX, &aggnonce, invalid_pubnonce_ptr, 1)); - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, inf_pubnonce_ptr, 2) == 1); - { - /* Check that the aggnonce encodes two points at infinity */ - secp256k1_ge aggnonce_pt[2]; - secp256k1_musig_aggnonce_load(CTX, aggnonce_pt, &aggnonce); - for (i = 0; i < 2; i++) { - secp256k1_ge_is_infinity(&aggnonce_pt[i]); - } - } - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - - /** Serialize and parse aggregate nonces **/ - CHECK(secp256k1_musig_aggnonce_serialize(CTX, aggnonce_ser, &aggnonce) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_aggnonce_serialize(CTX, NULL, &aggnonce)); - CHECK_ILLEGAL(CTX, secp256k1_musig_aggnonce_serialize(CTX, aggnonce_ser, NULL)); - CHECK(memcmp_and_randomize(aggnonce_ser, zeros132, sizeof(aggnonce_ser)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_aggnonce_serialize(CTX, aggnonce_ser, (secp256k1_musig_aggnonce*) &invalid_pubnonce)); - CHECK(memcmp_and_randomize(aggnonce_ser, zeros132, sizeof(aggnonce_ser)) == 0); - CHECK(secp256k1_musig_aggnonce_serialize(CTX, aggnonce_ser, &aggnonce) == 1); - - CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, aggnonce_ser) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_aggnonce_parse(CTX, NULL, aggnonce_ser)); - CHECK_ILLEGAL(CTX, secp256k1_musig_aggnonce_parse(CTX, &aggnonce, NULL)); - CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, zeros132) == 1); - CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, aggnonce_ser) == 1); - - { - /* Check that serialize and parse results in the same value */ - secp256k1_musig_aggnonce tmp; - CHECK(secp256k1_musig_aggnonce_serialize(CTX, aggnonce_ser, &aggnonce) == 1); - CHECK(secp256k1_musig_aggnonce_parse(CTX, &tmp, aggnonce_ser) == 1); - CHECK(secp256k1_memcmp_var(&tmp, &aggnonce, sizeof(tmp)) == 0); - } - - /** Process nonces **/ - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache, &adaptor)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache, &adaptor)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache, &adaptor)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache, &adaptor)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL, &adaptor)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache, &adaptor)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, (secp256k1_pubkey *)&invalid_pk)); - - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); - - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1); - /* The secnonce is set to 0 and subsequent signing attempts fail */ - CHECK(secp256k1_memcmp_var(&secnonce_tmp, zeros132, sizeof(secnonce_tmp)) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, NULL, &secnonce_tmp, &keypair[0], &keyagg_cache, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], NULL, &keypair[0], &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &invalid_secnonce, &keypair[0], &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, NULL, &keyagg_cache, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &invalid_keypair, &keyagg_cache, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - { - unsigned char sk_tmp[32]; - secp256k1_keypair keypair_tmp; - testrand256(sk_tmp); - CHECK(secp256k1_keypair_create(CTX, &keypair_tmp, sk_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair_tmp, &keyagg_cache, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - } - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], NULL, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &invalid_keyagg_cache, &session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, NULL)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &invalid_session)); - memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); - - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce[0], &keypair[0], &keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[1], &secnonce[1], &keypair[1], &keyagg_cache, &session) == 1); - - CHECK(secp256k1_musig_partial_sig_serialize(CTX, buf, &partial_sig[0]) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_serialize(CTX, NULL, &partial_sig[0])); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_serialize(CTX, buf, NULL)); - CHECK(secp256k1_musig_partial_sig_parse(CTX, &partial_sig[0], buf) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_parse(CTX, NULL, buf)); - CHECK(secp256k1_musig_partial_sig_parse(CTX, &partial_sig[0], max64) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_parse(CTX, &partial_sig[0], NULL)); - - { - /* Check that serialize and parse results in the same value */ - secp256k1_musig_partial_sig tmp; - CHECK(secp256k1_musig_partial_sig_serialize(CTX, buf, &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_sig_parse(CTX, &tmp, buf) == 1); - CHECK(secp256k1_memcmp_var(&tmp, &partial_sig[0], sizeof(tmp)) == 0); - } - - /** Partial signature verification */ - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, NULL, &pubnonce[0], &pk[0], &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &invalid_partial_sig, &pubnonce[0], &pk[0], &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], NULL, &pk[0], &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &invalid_pubnonce, &pk[0], &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], NULL, &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &invalid_pk, &keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], NULL, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], &invalid_keyagg_cache, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, NULL)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &invalid_session)); - - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], &keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[1], &pk[1], &keyagg_cache, &session) == 1); - - /** Signature aggregation and verification */ - CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 2) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, NULL, &session, partial_sig_ptr, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, NULL, partial_sig_ptr, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &invalid_session, partial_sig_ptr, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, NULL, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, invalid_partial_sig_ptr, 2)); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 0)); - CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 1) == 1); - CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 2) == 1); - - /** Adaptor signature verification */ - CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity, &session) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, NULL, &session)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, NULL)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, &invalid_session)); - - CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, NULL, pre_sig, sec_adaptor, 0)); - CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, NULL, sec_adaptor, 0)); - CHECK(secp256k1_musig_adapt(CTX, final_sig, max64, sec_adaptor, 0) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, NULL, 0)); - CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, max64, 0) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, 2)); - /* sig and pre_sig argument point to the same location */ - memcpy(final_sig, pre_sig, sizeof(final_sig)); - CHECK(secp256k1_musig_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); - - CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); - - /** Secret adaptor can be extracted from signature */ - CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1); - CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) == 0); - /* wrong nonce parity */ - CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1); - CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) != 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, NULL, final_sig, pre_sig, 0)); - CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, NULL, pre_sig, 0)); - CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, max64, pre_sig, 0) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, NULL, 0)); - CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, max64, 0) == 0); - CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, 2)); -} - -static void musig_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { - secp256k1_scalar k1[2], k2[2]; - - secp256k1_nonce_function_musig(k1, args[0], args[1], args[2], args[3], args[4], args[5]); - testrand_flip(args[n_flip], n_bytes); - secp256k1_nonce_function_musig(k2, args[0], args[1], args[2], args[3], args[4], args[5]); - CHECK(secp256k1_scalar_eq(&k1[0], &k2[0]) == 0); - CHECK(secp256k1_scalar_eq(&k1[1], &k2[1]) == 0); -} - -static void musig_nonce_test(void) { - unsigned char *args[6]; - unsigned char session_id[32]; - unsigned char sk[32]; - unsigned char pk[33]; - unsigned char msg[32]; - unsigned char agg_pk[32]; - unsigned char extra_input[32]; - int i, j; - secp256k1_scalar k[6][2]; - - testrand_bytes_test(session_id, sizeof(session_id)); - testrand_bytes_test(sk, sizeof(sk)); - testrand_bytes_test(pk, sizeof(pk)); - testrand_bytes_test(msg, sizeof(msg)); - testrand_bytes_test(agg_pk, sizeof(agg_pk)); - testrand_bytes_test(extra_input, sizeof(extra_input)); - - /* Check that a bitflip in an argument results in different nonces. */ - args[0] = session_id; - args[1] = msg; - args[2] = sk; - args[3] = pk; - args[4] = agg_pk; - args[5] = extra_input; - for (i = 0; i < COUNT; i++) { - musig_nonce_bitflip(args, 0, sizeof(session_id)); - musig_nonce_bitflip(args, 1, sizeof(msg)); - musig_nonce_bitflip(args, 2, sizeof(sk)); - musig_nonce_bitflip(args, 3, sizeof(pk)); - musig_nonce_bitflip(args, 4, sizeof(agg_pk)); - musig_nonce_bitflip(args, 5, sizeof(extra_input)); - } - /* Check that if any argument is NULL, a different nonce is produced than if - * any other argument is NULL. */ - memcpy(msg, session_id, sizeof(msg)); - memcpy(sk, session_id, sizeof(sk)); - memcpy(pk, session_id, sizeof(session_id)); - memcpy(agg_pk, session_id, sizeof(agg_pk)); - memcpy(extra_input, session_id, sizeof(extra_input)); - secp256k1_nonce_function_musig(k[0], args[0], args[1], args[2], args[3], args[4], args[5]); - secp256k1_nonce_function_musig(k[1], args[0], NULL, args[2], args[3], args[4], args[5]); - secp256k1_nonce_function_musig(k[2], args[0], args[1], NULL, args[3], args[4], args[5]); - secp256k1_nonce_function_musig(k[3], args[0], args[1], args[2], NULL, args[4], args[5]); - secp256k1_nonce_function_musig(k[4], args[0], args[1], args[2], args[3], NULL, args[5]); - secp256k1_nonce_function_musig(k[5], args[0], args[1], args[2], args[3], args[4], NULL); - for (i = 0; i < 6; i++) { - CHECK(!secp256k1_scalar_eq(&k[i][0], &k[i][1])); - for (j = i+1; j < 6; j++) { - CHECK(!secp256k1_scalar_eq(&k[i][0], &k[j][0])); - CHECK(!secp256k1_scalar_eq(&k[i][1], &k[j][1])); - } - } -} - -static void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { - /* Throughout this test "a" and "b" refer to two hypothetical blockchains, - * while the indices 0 and 1 refer to the two signers. Here signer 0 is - * sending a-coins to signer 1, while signer 1 is sending b-coins to signer - * 0. Signer 0 produces the adaptor signatures. */ - unsigned char pre_sig_a[64]; - unsigned char final_sig_a[64]; - unsigned char pre_sig_b[64]; - unsigned char final_sig_b[64]; - secp256k1_musig_partial_sig partial_sig_a[2]; - const secp256k1_musig_partial_sig *partial_sig_a_ptr[2]; - secp256k1_musig_partial_sig partial_sig_b[2]; - const secp256k1_musig_partial_sig *partial_sig_b_ptr[2]; - unsigned char sec_adaptor[32]; - unsigned char sec_adaptor_extracted[32]; - secp256k1_pubkey pub_adaptor; - unsigned char sk_a[2][32]; - unsigned char sk_b[2][32]; - secp256k1_keypair keypair_a[2]; - secp256k1_keypair keypair_b[2]; - secp256k1_pubkey pk_a[2]; - const secp256k1_pubkey *pk_a_ptr[2]; - secp256k1_pubkey pk_b[2]; - const secp256k1_pubkey *pk_b_ptr[2]; - secp256k1_musig_keyagg_cache keyagg_cache_a; - secp256k1_musig_keyagg_cache keyagg_cache_b; - secp256k1_xonly_pubkey agg_pk_a; - secp256k1_xonly_pubkey agg_pk_b; - secp256k1_musig_secnonce secnonce_a[2]; - secp256k1_musig_secnonce secnonce_b[2]; - secp256k1_musig_pubnonce pubnonce_a[2]; - secp256k1_musig_pubnonce pubnonce_b[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr_a[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr_b[2]; - secp256k1_musig_aggnonce aggnonce_a; - secp256k1_musig_aggnonce aggnonce_b; - secp256k1_musig_session session_a, session_b; - int nonce_parity_a; - int nonce_parity_b; - unsigned char seed_a[2][32] = { "a0", "a1" }; - unsigned char seed_b[2][32] = { "b0", "b1" }; - const unsigned char msg32_a[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'a'}; - const unsigned char msg32_b[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'b'}; - int i; - - /* Step 1: key setup */ - for (i = 0; i < 2; i++) { - pk_a_ptr[i] = &pk_a[i]; - pk_b_ptr[i] = &pk_b[i]; - pubnonce_ptr_a[i] = &pubnonce_a[i]; - pubnonce_ptr_b[i] = &pubnonce_b[i]; - partial_sig_a_ptr[i] = &partial_sig_a[i]; - partial_sig_b_ptr[i] = &partial_sig_b[i]; - - testrand256(sk_a[i]); - testrand256(sk_b[i]); - CHECK(create_keypair_and_pk(&keypair_a[i], &pk_a[i], sk_a[i]) == 1); - CHECK(create_keypair_and_pk(&keypair_b[i], &pk_b[i], sk_b[i]) == 1); - } - testrand256(sec_adaptor); - CHECK(secp256k1_ec_pubkey_create(CTX, &pub_adaptor, sec_adaptor) == 1); - - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk_a, &keyagg_cache_a, pk_a_ptr, 2) == 1); - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1); - - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], &pk_a[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_a[1], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], &pk_b[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], &pk_b[1], NULL, NULL, NULL) == 1); - - /* Step 2: Exchange nonces */ - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_a, pubnonce_ptr_a, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session_a, &aggnonce_a, msg32_a, &keyagg_cache_a, &pub_adaptor) == 1); - CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_a, &session_a) == 1); - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_b, pubnonce_ptr_b, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session_b, &aggnonce_b, msg32_b, &keyagg_cache_b, &pub_adaptor) == 1); - CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_b, &session_b) == 1); - - /* Step 3: Signer 0 produces partial signatures for both chains. */ - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[0], &secnonce_a[0], &keypair_a[0], &keyagg_cache_a, &session_a) == 1); - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[0], &secnonce_b[0], &keypair_b[0], &keyagg_cache_b, &session_b) == 1); - - /* Step 4: Signer 1 receives partial signatures, verifies them and creates a - * partial signature to send B-coins to signer 0. */ - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_a[0], &pubnonce_a[0], &pk_a[0], &keyagg_cache_a, &session_a) == 1); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_b[0], &pubnonce_b[0], &pk_b[0], &keyagg_cache_b, &session_b) == 1); - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[1], &secnonce_b[1], &keypair_b[1], &keyagg_cache_b, &session_b) == 1); - - /* Step 5: Signer 0 aggregates its own partial signature with the partial - * signature from signer 1 and adapts it. This results in a complete - * signature which is broadcasted by signer 0 to take B-coins. */ - CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_b, &session_b, partial_sig_b_ptr, 2) == 1); - CHECK(secp256k1_musig_adapt(CTX, final_sig_b, pre_sig_b, sec_adaptor, nonce_parity_b) == 1); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_b, msg32_b, sizeof(msg32_b), &agg_pk_b) == 1); - - /* Step 6: Signer 1 signs, extracts adaptor from the published signature, - * and adapts the signature to take A-coins. */ - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[1], &secnonce_a[1], &keypair_a[1], &keyagg_cache_a, &session_a) == 1); - CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_a, &session_a, partial_sig_a_ptr, 2) == 1); - CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor_extracted, final_sig_b, pre_sig_b, nonce_parity_b) == 1); - CHECK(secp256k1_memcmp_var(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ - CHECK(secp256k1_musig_adapt(CTX, final_sig_a, pre_sig_a, sec_adaptor_extracted, nonce_parity_a) == 1); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1); -} - -static void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) { - secp256k1_sha256 sha; - unsigned char buf[32]; - unsigned char buf2[32]; - size_t i; - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, tag, taglen); - secp256k1_sha256_finalize(&sha, buf); - /* buf = SHA256(tag) */ - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_write(&sha, buf, 32); - /* Is buffer fully consumed? */ - CHECK((sha.bytes & 0x3F) == 0); - - /* Compare with tagged SHA */ - for (i = 0; i < 8; i++) { - CHECK(sha_tagged->s[i] == sha.s[i]); - } - secp256k1_sha256_write(&sha, buf, 32); - secp256k1_sha256_write(sha_tagged, buf, 32); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_sha256_finalize(sha_tagged, buf2); - CHECK(secp256k1_memcmp_var(buf, buf2, 32) == 0); -} - -/* Checks that the initialized tagged hashes initialized have the expected - * state. */ -static void sha256_tag_test(void) { - secp256k1_sha256 sha_tagged; - { - char tag[] = {'K', 'e', 'y', 'A', 'g', 'g', ' ', 'l', 'i', 's', 't'}; - secp256k1_musig_keyagglist_sha256(&sha_tagged); - sha256_tag_test_internal(&sha_tagged, (unsigned char*)tag, sizeof(tag)); - } - { - char tag[] = {'K', 'e', 'y', 'A', 'g', 'g', ' ', 'c', 'o', 'e', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 't'}; - secp256k1_musig_keyaggcoef_sha256(&sha_tagged); - sha256_tag_test_internal(&sha_tagged, (unsigned char*)tag, sizeof(tag)); - } -} - -/* Attempts to create a signature for the aggregate public key using given secret - * keys and keyagg_cache. */ -static void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_keyagg_cache *keyagg_cache) { - secp256k1_pubkey pk[2]; - unsigned char session_id[2][32]; - unsigned char msg[32]; - secp256k1_musig_secnonce secnonce[2]; - secp256k1_musig_pubnonce pubnonce[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr[2]; - secp256k1_musig_aggnonce aggnonce; - secp256k1_keypair keypair[2]; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig[2]; - const secp256k1_musig_partial_sig *partial_sig_ptr[2]; - unsigned char final_sig[64]; - int i; - - for (i = 0; i < 2; i++) { - pubnonce_ptr[i] = &pubnonce[i]; - partial_sig_ptr[i] = &partial_sig[i]; - - testrand256(session_id[i]); - } - CHECK(create_keypair_and_pk(&keypair[0], &pk[0], sk0) == 1); - CHECK(create_keypair_and_pk(&keypair[1], &pk[1], sk1) == 1); - testrand256(msg); - - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[0], &pubnonce[0], session_id[0], sk0, &pk[0], NULL, NULL, NULL) == 1); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_id[1], sk1, &pk[1], NULL, NULL, NULL) == 1); - - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache, NULL) == 1); - - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce[0], &keypair[0], keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[1], &secnonce[1], &keypair[1], keyagg_cache, &session) == 1); - - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[0], &pubnonce[0], &pk[0], keyagg_cache, &session) == 1); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig[1], &pubnonce[1], &pk[1], keyagg_cache, &session) == 1); - - CHECK(secp256k1_musig_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, 2) == 1); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), agg_pk) == 1); -} - -/* Create aggregate public key P[0], tweak multiple times (using xonly and - * plain tweaking) and test signing. */ -static void musig_tweak_test(secp256k1_scratch_space *scratch) { - unsigned char sk[2][32]; - secp256k1_pubkey pk[2]; - const secp256k1_pubkey *pk_ptr[2]; - secp256k1_musig_keyagg_cache keyagg_cache; - enum { N_TWEAKS = 8 }; - secp256k1_pubkey P[N_TWEAKS + 1]; - secp256k1_xonly_pubkey P_xonly[N_TWEAKS + 1]; - int i; - - /* Key Setup */ - for (i = 0; i < 2; i++) { - pk_ptr[i] = &pk[i]; - testrand256(sk[i]); - CHECK(create_keypair_and_pk(NULL, &pk[i], sk[i]) == 1); - } - /* Compute P0 = keyagg(pk0, pk1) and test signing for it */ - CHECK(secp256k1_musig_pubkey_agg(CTX, scratch, &P_xonly[0], &keyagg_cache, pk_ptr, 2) == 1); - musig_tweak_test_helper(&P_xonly[0], sk[0], sk[1], &keyagg_cache); - CHECK(secp256k1_musig_pubkey_get(CTX, &P[0], &keyagg_cache)); - - /* Compute Pi = f(Pj) + tweaki*G where where j = i-1 and try signing for - * that key. If xonly is set to true, the function f is normalizes the input - * point to have an even X-coordinate ("xonly-tweaking"). - * Otherwise, the function f is the identity function. */ - for (i = 1; i <= N_TWEAKS; i++) { - unsigned char tweak[32]; - int P_parity; - int xonly = testrand_bits(1); - - testrand256(tweak); - if (xonly) { - CHECK(secp256k1_musig_pubkey_xonly_tweak_add(CTX, &P[i], &keyagg_cache, tweak) == 1); - } else { - CHECK(secp256k1_musig_pubkey_ec_tweak_add(CTX, &P[i], &keyagg_cache, tweak) == 1); - } - CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &P_xonly[i], &P_parity, &P[i])); - /* Check that musig_pubkey_tweak_add produces same result as - * xonly_pubkey_tweak_add or ec_pubkey_tweak_add. */ - if (xonly) { - unsigned char P_serialized[32]; - CHECK(secp256k1_xonly_pubkey_serialize(CTX, P_serialized, &P_xonly[i])); - CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1); - } else { - secp256k1_pubkey tmp_key = P[i-1]; - CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &tmp_key, tweak)); - CHECK(secp256k1_memcmp_var(&tmp_key, &P[i], sizeof(tmp_key)) == 0); - } - /* Test signing for P[i] */ - musig_tweak_test_helper(&P_xonly[i], sk[0], sk[1], &keyagg_cache); - } -} - -int musig_vectors_keyagg_and_tweak(enum MUSIG_ERROR *error, - secp256k1_musig_keyagg_cache *keyagg_cache, - unsigned char *agg_pk_ser, - const unsigned char pubkeys33[][33], - const unsigned char tweaks32[][32], - size_t key_indices_len, - const size_t *key_indices, - size_t tweak_indices_len, - const size_t *tweak_indices, - const int *is_xonly) { - secp256k1_pubkey pubkeys[MUSIG_VECTORS_MAX_PUBKEYS]; - const secp256k1_pubkey *pk_ptr[MUSIG_VECTORS_MAX_PUBKEYS]; - int i; - secp256k1_pubkey agg_pk; - secp256k1_xonly_pubkey agg_pk_xonly; - - for (i = 0; i < (int)key_indices_len; i++) { - if (!secp256k1_ec_pubkey_parse(CTX, &pubkeys[i], pubkeys33[key_indices[i]], 33)) { - *error = MUSIG_PUBKEY; - return 0; - } - pk_ptr[i] = &pubkeys[i]; - } - if (!secp256k1_musig_pubkey_agg(CTX, NULL, NULL, keyagg_cache, pk_ptr, key_indices_len)) { - *error = MUSIG_OTHER; - return 0; - } - - for (i = 0; i < (int)tweak_indices_len; i++) { - if (is_xonly[i]) { - if (!secp256k1_musig_pubkey_xonly_tweak_add(CTX, NULL, keyagg_cache, tweaks32[tweak_indices[i]])) { - *error = MUSIG_TWEAK; - return 0; - } - } else { - if (!secp256k1_musig_pubkey_ec_tweak_add(CTX, NULL, keyagg_cache, tweaks32[tweak_indices[i]])) { - *error = MUSIG_TWEAK; - return 0; - } - } - } - if (!secp256k1_musig_pubkey_get(CTX, &agg_pk, keyagg_cache)) { - *error = MUSIG_OTHER; - return 0; - } - - if (!secp256k1_xonly_pubkey_from_pubkey(CTX, &agg_pk_xonly, NULL, &agg_pk)) { - *error = MUSIG_OTHER; - return 0; - } - - if (agg_pk_ser != NULL) { - if (!secp256k1_xonly_pubkey_serialize(CTX, agg_pk_ser, &agg_pk_xonly)) { - *error = MUSIG_OTHER; - return 0; - } - } - - return 1; -} - -static void musig_test_vectors_keyagg(void) { - size_t i; - const struct musig_key_agg_vector *vector = &musig_key_agg_vector; - - for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { - const struct musig_key_agg_valid_test_case *c = &vector->valid_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - unsigned char agg_pk[32]; - - CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, 0, NULL, NULL)); - CHECK(secp256k1_memcmp_var(agg_pk, c->expected, sizeof(agg_pk)) == 0); - } - - for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { - const struct musig_key_agg_error_test_case *c = &vector->error_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - - CHECK(!musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); - CHECK(c->error == error); - } -} - -static void musig_test_vectors_noncegen(void) { - size_t i; - const struct musig_nonce_gen_vector *vector = &musig_nonce_gen_vector; - - for (i = 0; i < sizeof(vector->test_case)/sizeof(vector->test_case[0]); i++) { - const struct musig_nonce_gen_test_case *c = &vector->test_case[i]; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_musig_keyagg_cache *keyagg_cache_ptr = NULL; - secp256k1_musig_secnonce secnonce; - secp256k1_musig_pubnonce pubnonce; - const unsigned char *sk = NULL; - const unsigned char *msg = NULL; - const unsigned char *extra_in = NULL; - secp256k1_pubkey pk; - unsigned char pubnonce66[66]; - - if (c->has_sk) { - sk = c->sk; - } - if (c->has_aggpk) { - /* Create keyagg_cache from aggpk */ - secp256k1_keyagg_cache_internal cache_i; - secp256k1_xonly_pubkey aggpk; - memset(&cache_i, 0, sizeof(cache_i)); - CHECK(secp256k1_xonly_pubkey_parse(CTX, &aggpk, c->aggpk)); - CHECK(secp256k1_xonly_pubkey_load(CTX, &cache_i.pk, &aggpk)); - secp256k1_keyagg_cache_save(&keyagg_cache, &cache_i); - keyagg_cache_ptr = &keyagg_cache; - } - if (c->has_msg) { - msg = c->msg; - } - if (c->has_extra_in) { - extra_in = c->extra_in; - } - - CHECK(secp256k1_ec_pubkey_parse(CTX, &pk, c->pk, sizeof(c->pk))); - CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce, &pubnonce, c->rand_, sk, &pk, msg, keyagg_cache_ptr, extra_in) == 1); - CHECK(secp256k1_memcmp_var(&secnonce.data[4], c->expected_secnonce, 2*32) == 0); - CHECK(secp256k1_memcmp_var(&secnonce.data[4+2*32], &pk, sizeof(pk)) == 0); - - CHECK(secp256k1_musig_pubnonce_serialize(CTX, pubnonce66, &pubnonce) == 1); - CHECK(sizeof(c->expected_pubnonce) == sizeof(pubnonce66)); - CHECK(secp256k1_memcmp_var(pubnonce66, c->expected_pubnonce, sizeof(pubnonce66)) == 0); - } -} - - -static void musig_test_vectors_nonceagg(void) { - size_t i; - int j; - const struct musig_nonce_agg_vector *vector = &musig_nonce_agg_vector; - - for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { - const struct musig_nonce_agg_test_case *c = &vector->valid_case[i]; - secp256k1_musig_pubnonce pubnonce[2]; - const secp256k1_musig_pubnonce *pubnonce_ptr[2]; - secp256k1_musig_aggnonce aggnonce; - unsigned char aggnonce66[66]; - - for (j = 0; j < 2; j++) { - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce[j], vector->pnonces[c->pnonce_indices[j]]) == 1); - pubnonce_ptr[j] = &pubnonce[j]; - } - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2)); - CHECK(secp256k1_musig_aggnonce_serialize(CTX, aggnonce66, &aggnonce)); - CHECK(secp256k1_memcmp_var(aggnonce66, c->expected, 33) == 0); - } - for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { - const struct musig_nonce_agg_test_case *c = &vector->error_case[i]; - secp256k1_musig_pubnonce pubnonce[2]; - for (j = 0; j < 2; j++) { - int expected = c->invalid_nonce_idx != j; - CHECK(expected == secp256k1_musig_pubnonce_parse(CTX, &pubnonce[j], vector->pnonces[c->pnonce_indices[j]])); - } - } -} - -static void musig_test_set_secnonce(secp256k1_musig_secnonce *secnonce, const unsigned char *secnonce64, const secp256k1_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_pubkey_load(CTX, &pk, pubkey)); - secp256k1_musig_secnonce_save(secnonce, k, &pk); -} - -static void musig_test_vectors_signverify(void) { - size_t i; - const struct musig_sign_verify_vector *vector = &musig_sign_verify_vector; - - for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { - const struct musig_valid_case *c = &vector->valid_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_pubkey pubkey; - secp256k1_musig_pubnonce pubnonce; - secp256k1_musig_aggnonce aggnonce; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig; - secp256k1_musig_secnonce secnonce; - secp256k1_keypair keypair; - unsigned char partial_sig32[32]; - - CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); - CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); - - CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonces[c->aggnonce_index])); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); - - CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); - musig_test_set_secnonce(&secnonce, vector->secnonces[0], &pubkey); - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); - CHECK(secp256k1_musig_partial_sig_serialize(CTX, partial_sig32, &partial_sig)); - CHECK(secp256k1_memcmp_var(partial_sig32, c->expected, sizeof(partial_sig32)) == 0); - - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce, vector->pubnonces[0])); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig, &pubnonce, &pubkey, &keyagg_cache, &session)); - } - for (i = 0; i < sizeof(vector->sign_error_case)/sizeof(vector->sign_error_case[0]); i++) { - const struct musig_sign_error_case *c = &vector->sign_error_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_pubkey pubkey; - secp256k1_musig_aggnonce aggnonce; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig; - secp256k1_musig_secnonce secnonce; - secp256k1_keypair keypair; - int expected; - - if (i == 0) { - /* Skip this vector since the implementation does not error out when - * the signing key does not belong to any pubkey. */ - continue; - } - expected = c->error != MUSIG_PUBKEY; - CHECK(expected == musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); - CHECK(expected || c->error == error); - if (!expected) { - continue; - } - CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); - CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); - - expected = c->error != MUSIG_AGGNONCE; - CHECK(expected == secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonces[c->aggnonce_index])); - if (!expected) { - continue; - } - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); - - CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); - expected = c->error != MUSIG_SECNONCE; - CHECK(!expected); - musig_test_set_secnonce(&secnonce, vector->secnonces[c->secnonce_index], &pubkey); - CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); - } - for (i = 0; i < sizeof(vector->verify_fail_case)/sizeof(vector->verify_fail_case[0]); i++) { - const struct musig_verify_fail_error_case *c = &vector->verify_fail_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_musig_aggnonce aggnonce; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig; - enum { NUM_PUBNONCES = 3 }; - secp256k1_musig_pubnonce pubnonce[NUM_PUBNONCES]; - const secp256k1_musig_pubnonce *pubnonce_ptr[NUM_PUBNONCES]; - secp256k1_pubkey pubkey; - int expected; - size_t j; - - CHECK(NUM_PUBNONCES <= c->nonce_indices_len); - for (j = 0; j < c->nonce_indices_len; j++) { - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce[j], vector->pubnonces[c->nonce_indices[j]])); - pubnonce_ptr[j] = &pubnonce[j]; - } - - CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); - CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, c->nonce_indices_len) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); - - CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[c->signer_index], sizeof(vector->pubkeys[0]))); - - expected = c->error != MUSIG_SIG; - CHECK(expected == secp256k1_musig_partial_sig_parse(CTX, &partial_sig, c->sig)); - if (!expected) { - continue; - } - expected = c->error != MUSIG_SIG_VERIFY; - CHECK(expected == secp256k1_musig_partial_sig_verify(CTX, &partial_sig, pubnonce, &pubkey, &keyagg_cache, &session)); - } - for (i = 0; i < sizeof(vector->verify_error_case)/sizeof(vector->verify_error_case[0]); i++) { - const struct musig_verify_fail_error_case *c = &vector->verify_error_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_musig_pubnonce pubnonce; - int expected; - - expected = c->error != MUSIG_PUBKEY; - CHECK(expected == musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); - CHECK(expected || c->error == error); - if (!expected) { - continue; - } - expected = c->error != MUSIG_PUBNONCE; - CHECK(expected == secp256k1_musig_pubnonce_parse(CTX, &pubnonce, vector->pubnonces[c->nonce_indices[c->signer_index]])); - } -} - -static void musig_test_vectors_tweak(void) { - size_t i; - const struct musig_tweak_vector *vector = &musig_tweak_vector; - secp256k1_pubkey pubkey; - secp256k1_musig_aggnonce aggnonce; - secp256k1_musig_secnonce secnonce; - - CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonce)); - CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); - - for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { - const struct musig_tweak_case *c = &vector->valid_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - secp256k1_musig_pubnonce pubnonce; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig; - secp256k1_keypair keypair; - unsigned char partial_sig32[32]; - - musig_test_set_secnonce(&secnonce, vector->secnonce, &pubkey); - - CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); - CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); - - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); - - CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); - CHECK(secp256k1_musig_partial_sig_serialize(CTX, partial_sig32, &partial_sig)); - CHECK(secp256k1_memcmp_var(partial_sig32, c->expected, sizeof(partial_sig32)) == 0); - - CHECK(secp256k1_musig_pubnonce_parse(CTX, &pubnonce, vector->pubnonces[c->nonce_indices[c->signer_index]])); - CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig, &pubnonce, &pubkey, &keyagg_cache, &session)); - } - for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { - const struct musig_tweak_case *c = &vector->error_case[i]; - enum MUSIG_ERROR error; - secp256k1_musig_keyagg_cache keyagg_cache; - CHECK(!musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); - CHECK(error == MUSIG_TWEAK); - } -} - -static void musig_test_vectors_sigagg(void) { - size_t i, j; - const struct musig_sig_agg_vector *vector = &musig_sig_agg_vector; - - for (i = 0; i < sizeof(vector->valid_case)/sizeof(vector->valid_case[0]); i++) { - const struct musig_sig_agg_case *c = &vector->valid_case[i]; - enum MUSIG_ERROR error; - unsigned char final_sig[64]; - secp256k1_musig_keyagg_cache keyagg_cache; - unsigned char agg_pk32[32]; - secp256k1_xonly_pubkey agg_pk; - secp256k1_musig_aggnonce aggnonce; - secp256k1_musig_session session; - secp256k1_musig_partial_sig partial_sig[(sizeof(vector->psigs)/sizeof(vector->psigs[0]))]; - const secp256k1_musig_partial_sig *partial_sig_ptr[(sizeof(vector->psigs)/sizeof(vector->psigs[0]))]; - - CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk32, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); - CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, c->aggnonce)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); - for (j = 0; j < c->psig_indices_len; j++) { - CHECK(secp256k1_musig_partial_sig_parse(CTX, &partial_sig[j], vector->psigs[c->psig_indices[j]])); - partial_sig_ptr[j] = &partial_sig[j]; - } - - CHECK(secp256k1_musig_partial_sig_agg(CTX, final_sig, &session, partial_sig_ptr, c->psig_indices_len) == 1); - CHECK(secp256k1_memcmp_var(final_sig, c->expected, sizeof(final_sig)) == 0); - - CHECK(secp256k1_xonly_pubkey_parse(CTX, &agg_pk, agg_pk32)); - CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, vector->msg, sizeof(vector->msg), &agg_pk) == 1); - } - for (i = 0; i < sizeof(vector->error_case)/sizeof(vector->error_case[0]); i++) { - const struct musig_sig_agg_case *c = &vector->error_case[i]; - secp256k1_musig_partial_sig partial_sig[(sizeof(vector->psigs)/sizeof(vector->psigs[0]))]; - for (j = 0; j < c->psig_indices_len; j++) { - int expected = c->invalid_sig_idx != (int)j; - CHECK(expected == secp256k1_musig_partial_sig_parse(CTX, &partial_sig[j], vector->psigs[c->psig_indices[j]])); - } - } -} - -static void run_musig_tests(void) { - int i; - secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(CTX, 1024 * 1024); - - for (i = 0; i < COUNT; i++) { - musig_simple_test(scratch); - } - musig_api_tests(scratch); - musig_nonce_test(); - for (i = 0; i < COUNT; i++) { - /* Run multiple times to ensure that pk and nonce have different y - * parities */ - scriptless_atomic_swap(scratch); - musig_tweak_test(scratch); - } - sha256_tag_test(); - musig_test_vectors_keyagg(); - musig_test_vectors_noncegen(); - musig_test_vectors_nonceagg(); - musig_test_vectors_signverify(); - musig_test_vectors_tweak(); - musig_test_vectors_sigagg(); - - secp256k1_scratch_space_destroy(CTX, scratch); -} - -#endif diff --git a/src/modules/musig/vectors.h b/src/modules/musig/vectors.h deleted file mode 100644 index b959e0a2..00000000 --- a/src/modules/musig/vectors.h +++ /dev/null @@ -1,346 +0,0 @@ -/** - * Automatically generated by contrib/musig2-vectors.py. - * - * The test vectors for the KeySort function are included in this file. They can - * be found in src/modules/extrakeys/tests_impl.h. */ - -enum MUSIG_ERROR { - MUSIG_PUBKEY, - MUSIG_TWEAK, - MUSIG_PUBNONCE, - MUSIG_AGGNONCE, - MUSIG_SECNONCE, - MUSIG_SIG, - MUSIG_SIG_VERIFY, - MUSIG_OTHER -}; - -struct musig_key_agg_valid_test_case { - size_t key_indices_len; - size_t key_indices[4]; - unsigned char expected[32]; -}; - -struct musig_key_agg_error_test_case { - size_t key_indices_len; - size_t key_indices[4]; - size_t tweak_indices_len; - size_t tweak_indices[1]; - int is_xonly[1]; - enum MUSIG_ERROR error; -}; - -struct musig_key_agg_vector { - unsigned char pubkeys[7][33]; - unsigned char tweaks[2][32]; - struct musig_key_agg_valid_test_case valid_case[4]; - struct musig_key_agg_error_test_case error_case[5]; -}; - -static const struct musig_key_agg_vector musig_key_agg_vector = { - { - { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, - { 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }, - { 0x02, 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 }, - { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 }, - { 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 }, - { 0x04, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, - { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 } - }, - { - { 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 }, - { 0x25, 0x2E, 0x4B, 0xD6, 0x74, 0x10, 0xA7, 0x6C, 0xDF, 0x93, 0x3D, 0x30, 0xEA, 0xA1, 0x60, 0x82, 0x14, 0x03, 0x7F, 0x1B, 0x10, 0x5A, 0x01, 0x3E, 0xCC, 0xD3, 0xC5, 0xC1, 0x84, 0xA6, 0x11, 0x0B } - }, - { - { 3, { 0, 1, 2 }, { 0x90, 0x53, 0x9E, 0xED, 0xE5, 0x65, 0xF5, 0xD0, 0x54, 0xF3, 0x2C, 0xC0, 0xC2, 0x20, 0x12, 0x68, 0x89, 0xED, 0x1E, 0x5D, 0x19, 0x3B, 0xAF, 0x15, 0xAE, 0xF3, 0x44, 0xFE, 0x59, 0xD4, 0x61, 0x0C }}, - { 3, { 2, 1, 0 }, { 0x62, 0x04, 0xDE, 0x8B, 0x08, 0x34, 0x26, 0xDC, 0x6E, 0xAF, 0x95, 0x02, 0xD2, 0x70, 0x24, 0xD5, 0x3F, 0xC8, 0x26, 0xBF, 0x7D, 0x20, 0x12, 0x14, 0x8A, 0x05, 0x75, 0x43, 0x5D, 0xF5, 0x4B, 0x2B }}, - { 3, { 0, 0, 0 }, { 0xB4, 0x36, 0xE3, 0xBA, 0xD6, 0x2B, 0x8C, 0xD4, 0x09, 0x96, 0x9A, 0x22, 0x47, 0x31, 0xC1, 0x93, 0xD0, 0x51, 0x16, 0x2D, 0x8C, 0x5A, 0xE8, 0xB1, 0x09, 0x30, 0x61, 0x27, 0xDA, 0x3A, 0xA9, 0x35 }}, - { 4, { 0, 0, 1, 1 }, { 0x69, 0xBC, 0x22, 0xBF, 0xA5, 0xD1, 0x06, 0x30, 0x6E, 0x48, 0xA2, 0x06, 0x79, 0xDE, 0x1D, 0x73, 0x89, 0x38, 0x61, 0x24, 0xD0, 0x75, 0x71, 0xD0, 0xD8, 0x72, 0x68, 0x60, 0x28, 0xC2, 0x6A, 0x3E }}, - }, - { - { 2, { 0, 3 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY }, - { 2, { 0, 4 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY }, - { 2, { 5, 0 }, 0, { 0 }, { 0 }, MUSIG_PUBKEY }, - { 2, { 0, 1 }, 1, { 0 }, { 1 }, MUSIG_TWEAK }, - { 1, { 6 }, 1, { 1 }, { 0 }, MUSIG_TWEAK }, - }, -}; - -struct musig_nonce_gen_test_case { - unsigned char rand_[32]; - int has_sk; - unsigned char sk[32]; - unsigned char pk[33]; - int has_aggpk; - unsigned char aggpk[32]; - int has_msg; - unsigned char msg[32]; - int has_extra_in; - unsigned char extra_in[32]; - unsigned char expected_secnonce[97]; - unsigned char expected_pubnonce[66]; -}; - -struct musig_nonce_gen_vector { - struct musig_nonce_gen_test_case test_case[2]; -}; - -static const struct musig_nonce_gen_vector musig_nonce_gen_vector = { - { - { { 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 }, 1 , { 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 }, { 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 }, 1 , { 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 }, 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 }, 1 , { 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 }, { 0xB1, 0x14, 0xE5, 0x02, 0xBE, 0xAA, 0x4E, 0x30, 0x1D, 0xD0, 0x8A, 0x50, 0x26, 0x41, 0x72, 0xC8, 0x4E, 0x41, 0x65, 0x0E, 0x6C, 0xB7, 0x26, 0xB4, 0x10, 0xC0, 0x69, 0x4D, 0x59, 0xEF, 0xFB, 0x64, 0x95, 0xB5, 0xCA, 0xF2, 0x8D, 0x04, 0x5B, 0x97, 0x3D, 0x63, 0xE3, 0xC9, 0x9A, 0x44, 0xB8, 0x07, 0xBD, 0xE3, 0x75, 0xFD, 0x6C, 0xB3, 0x9E, 0x46, 0xDC, 0x4A, 0x51, 0x17, 0x08, 0xD0, 0xE9, 0xD2, 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, 0xF7, 0xBE, 0x70, 0x89, 0xE8, 0x37, 0x6E, 0xB3, 0x55, 0x27, 0x23, 0x68, 0x76, 0x6B, 0x17, 0xE8, 0x8E, 0x7D, 0xB7, 0x20, 0x47, 0xD0, 0x5E, 0x56, 0xAA, 0x88, 0x1E, 0xA5, 0x2B, 0x3B, 0x35, 0xDF, 0x02, 0xC2, 0x9C, 0x80, 0x46, 0xFD, 0xD0, 0xDE, 0xD4, 0xC7, 0xE5, 0x58, 0x69, 0x13, 0x72, 0x00, 0xFB, 0xDB, 0xFE, 0x2E, 0xB6, 0x54, 0x26, 0x7B, 0x6D, 0x70, 0x13, 0x60, 0x2C, 0xAE, 0xD3, 0x11, 0x5A } }, - { { 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 }, 0 , { 0 }, { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, 0 , { 0 }, 0 , { 0 }, 0 , { 0 }, { 0x89, 0xBD, 0xD7, 0x87, 0xD0, 0x28, 0x4E, 0x5E, 0x4D, 0x5F, 0xC5, 0x72, 0xE4, 0x9E, 0x31, 0x6B, 0xAB, 0x7E, 0x21, 0xE3, 0xB1, 0x83, 0x0D, 0xE3, 0x7D, 0xFE, 0x80, 0x15, 0x6F, 0xA4, 0x1A, 0x6D, 0x0B, 0x17, 0xAE, 0x8D, 0x02, 0x4C, 0x53, 0x67, 0x96, 0x99, 0xA6, 0xFD, 0x79, 0x44, 0xD9, 0xC4, 0xA3, 0x66, 0xB5, 0x14, 0xBA, 0xF4, 0x30, 0x88, 0xE0, 0x70, 0x8B, 0x10, 0x23, 0xDD, 0x28, 0x97, 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, { 0x02, 0xC9, 0x6E, 0x7C, 0xB1, 0xE8, 0xAA, 0x5D, 0xAC, 0x64, 0xD8, 0x72, 0x94, 0x79, 0x14, 0x19, 0x8F, 0x60, 0x7D, 0x90, 0xEC, 0xDE, 0x52, 0x00, 0xDE, 0x52, 0x97, 0x8A, 0xD5, 0xDE, 0xD6, 0x3C, 0x00, 0x02, 0x99, 0xEC, 0x51, 0x17, 0xC2, 0xD2, 0x9E, 0xDE, 0xE8, 0xA2, 0x09, 0x25, 0x87, 0xC3, 0x90, 0x9B, 0xE6, 0x94, 0xD5, 0xCF, 0xF0, 0x66, 0x7D, 0x6C, 0x02, 0xEA, 0x40, 0x59, 0xF7, 0xCD, 0x97, 0x86 } }, - }, -}; - -struct musig_nonce_agg_test_case { - size_t pnonce_indices[2]; - /* if valid case */ - unsigned char expected[66]; - /* if error case */ - int invalid_nonce_idx; -}; - -struct musig_nonce_agg_vector { - unsigned char pnonces[7][66]; - struct musig_nonce_agg_test_case valid_case[2]; - struct musig_nonce_agg_test_case error_case[3]; -}; - -static const struct musig_nonce_agg_vector musig_nonce_agg_vector = { - { - { 0x02, 0x01, 0x51, 0xC8, 0x0F, 0x43, 0x56, 0x48, 0xDF, 0x67, 0xA2, 0x2B, 0x74, 0x9C, 0xD7, 0x98, 0xCE, 0x54, 0xE0, 0x32, 0x1D, 0x03, 0x4B, 0x92, 0xB7, 0x09, 0xB5, 0x67, 0xD6, 0x0A, 0x42, 0xE6, 0x66, 0x03, 0xBA, 0x47, 0xFB, 0xC1, 0x83, 0x44, 0x37, 0xB3, 0x21, 0x2E, 0x89, 0xA8, 0x4D, 0x84, 0x25, 0xE7, 0xBF, 0x12, 0xE0, 0x24, 0x5D, 0x98, 0x26, 0x22, 0x68, 0xEB, 0xDC, 0xB3, 0x85, 0xD5, 0x06, 0x41 }, - { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x33 }, - { 0x02, 0x01, 0x51, 0xC8, 0x0F, 0x43, 0x56, 0x48, 0xDF, 0x67, 0xA2, 0x2B, 0x74, 0x9C, 0xD7, 0x98, 0xCE, 0x54, 0xE0, 0x32, 0x1D, 0x03, 0x4B, 0x92, 0xB7, 0x09, 0xB5, 0x67, 0xD6, 0x0A, 0x42, 0xE6, 0x66, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, - { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x03, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, - { 0x04, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x33 }, - { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0x48, 0xC2, 0x64, 0xCD, 0xD5, 0x7D, 0x3C, 0x24, 0xD7, 0x99, 0x90, 0xB0, 0xF8, 0x65, 0x67, 0x4E, 0xB6, 0x2A, 0x0F, 0x90, 0x18, 0x27, 0x7A, 0x95, 0x01, 0x1B, 0x41, 0xBF, 0xC1, 0x93, 0xB8, 0x31 }, - { 0x03, 0xFF, 0x40, 0x6F, 0xFD, 0x8A, 0xDB, 0x9C, 0xD2, 0x98, 0x77, 0xE4, 0x98, 0x50, 0x14, 0xF6, 0x6A, 0x59, 0xF6, 0xCD, 0x01, 0xC0, 0xE8, 0x8C, 0xAA, 0x8E, 0x5F, 0x31, 0x66, 0xB1, 0xF6, 0x76, 0xA6, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 } - }, - { - { { 0, 1 }, { 0x03, 0x5F, 0xE1, 0x87, 0x3B, 0x4F, 0x29, 0x67, 0xF5, 0x2F, 0xEA, 0x4A, 0x06, 0xAD, 0x5A, 0x8E, 0xCC, 0xBE, 0x9D, 0x0F, 0xD7, 0x30, 0x68, 0x01, 0x2C, 0x89, 0x4E, 0x2E, 0x87, 0xCC, 0xB5, 0x80, 0x4B, 0x02, 0x47, 0x25, 0x37, 0x73, 0x45, 0xBD, 0xE0, 0xE9, 0xC3, 0x3A, 0xF3, 0xC4, 0x3C, 0x0A, 0x29, 0xA9, 0x24, 0x9F, 0x2F, 0x29, 0x56, 0xFA, 0x8C, 0xFE, 0xB5, 0x5C, 0x85, 0x73, 0xD0, 0x26, 0x2D, 0xC8 }, 0 }, - { { 2, 3 }, { 0x03, 0x5F, 0xE1, 0x87, 0x3B, 0x4F, 0x29, 0x67, 0xF5, 0x2F, 0xEA, 0x4A, 0x06, 0xAD, 0x5A, 0x8E, 0xCC, 0xBE, 0x9D, 0x0F, 0xD7, 0x30, 0x68, 0x01, 0x2C, 0x89, 0x4E, 0x2E, 0x87, 0xCC, 0xB5, 0x80, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 }, - }, - { - { { 0, 4 }, { 0 }, 1 }, - { { 5, 1 }, { 0 }, 0 }, - { { 6, 1 }, { 0 }, 0 }, - }, -}; - -/* Omit pubnonces in the test vectors because our partial signature verification - * implementation is able to accept the aggnonce directly. */ -struct musig_valid_case { - size_t key_indices_len; - size_t key_indices[3]; - size_t aggnonce_index; - size_t msg_index; - size_t signer_index; - unsigned char expected[32]; -}; - -struct musig_sign_error_case { - size_t key_indices_len; - size_t key_indices[3]; - size_t aggnonce_index; - size_t msg_index; - size_t secnonce_index; - enum MUSIG_ERROR error; -}; - -struct musig_verify_fail_error_case { - unsigned char sig[32]; - size_t key_indices_len; - size_t key_indices[3]; - size_t nonce_indices_len; - size_t nonce_indices[3]; - size_t msg_index; - size_t signer_index; - enum MUSIG_ERROR error; -}; - -struct musig_sign_verify_vector { - unsigned char sk[32]; - unsigned char pubkeys[4][33]; - unsigned char secnonces[2][194]; - unsigned char pubnonces[5][194]; - unsigned char aggnonces[5][66]; - unsigned char msgs[1][32]; - struct musig_valid_case valid_case[4]; - struct musig_sign_error_case sign_error_case[6]; - struct musig_verify_fail_error_case verify_fail_case[3]; - struct musig_verify_fail_error_case verify_error_case[2]; -}; - -static const struct musig_sign_verify_vector musig_sign_verify_vector = { - { 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71 }, - { - { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, - { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, - { 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x61 }, - { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 } - }, - { - { 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 } - }, - { - { 0x03, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }, - { 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, - { 0x03, 0x2D, 0xE2, 0x66, 0x26, 0x28, 0xC9, 0x0B, 0x03, 0xF5, 0xE7, 0x20, 0x28, 0x4E, 0xB5, 0x2F, 0xF7, 0xD7, 0x1F, 0x42, 0x84, 0xF6, 0x27, 0xB6, 0x8A, 0x85, 0x3D, 0x78, 0xC7, 0x8E, 0x1F, 0xFE, 0x93, 0x03, 0xE4, 0xC5, 0x52, 0x4E, 0x83, 0xFF, 0xE1, 0x49, 0x3B, 0x90, 0x77, 0xCF, 0x1C, 0xA6, 0xBE, 0xB2, 0x09, 0x0C, 0x93, 0xD9, 0x30, 0x32, 0x10, 0x71, 0xAD, 0x40, 0xB2, 0xF4, 0x4E, 0x59, 0x90, 0x46 }, - { 0x02, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x03, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }, - { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 } - }, - { - { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x04, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 }, - { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 }, - { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 } - }, - { - { 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF } - }, - { - { 3, { 0, 1, 2 }, 0, 0, 0, { 0x01, 0x2A, 0xBB, 0xCB, 0x52, 0xB3, 0x01, 0x6A, 0xC0, 0x3A, 0xD8, 0x23, 0x95, 0xA1, 0xA4, 0x15, 0xC4, 0x8B, 0x93, 0xDE, 0xF7, 0x87, 0x18, 0xE6, 0x2A, 0x7A, 0x90, 0x05, 0x2F, 0xE2, 0x24, 0xFB }}, - { 3, { 1, 0, 2 }, 0, 0, 1, { 0x9F, 0xF2, 0xF7, 0xAA, 0xA8, 0x56, 0x15, 0x0C, 0xC8, 0x81, 0x92, 0x54, 0x21, 0x8D, 0x3A, 0xDE, 0xEB, 0x05, 0x35, 0x26, 0x90, 0x51, 0x89, 0x77, 0x24, 0xF9, 0xDB, 0x37, 0x89, 0x51, 0x3A, 0x52 }}, - { 3, { 1, 2, 0 }, 0, 0, 2, { 0xFA, 0x23, 0xC3, 0x59, 0xF6, 0xFA, 0xC4, 0xE7, 0x79, 0x6B, 0xB9, 0x3B, 0xC9, 0xF0, 0x53, 0x2A, 0x95, 0x46, 0x8C, 0x53, 0x9B, 0xA2, 0x0F, 0xF8, 0x6D, 0x7C, 0x76, 0xED, 0x92, 0x22, 0x79, 0x00 }}, - { 2, { 0, 1 }, 1, 0, 0, { 0xAE, 0x38, 0x60, 0x64, 0xB2, 0x61, 0x05, 0x40, 0x47, 0x98, 0xF7, 0x5D, 0xE2, 0xEB, 0x9A, 0xF5, 0xED, 0xA5, 0x38, 0x7B, 0x06, 0x4B, 0x83, 0xD0, 0x49, 0xCB, 0x7C, 0x5E, 0x08, 0x87, 0x95, 0x31 }}, - }, - { - { 2, { 1, 2 }, 0, 0, 0, MUSIG_PUBKEY }, - { 3, { 1, 0, 3 }, 0, 0, 0, MUSIG_PUBKEY }, - { 3, { 1, 2, 0 }, 2, 0, 0, MUSIG_AGGNONCE }, - { 3, { 1, 2, 0 }, 3, 0, 0, MUSIG_AGGNONCE }, - { 3, { 1, 2, 0 }, 4, 0, 0, MUSIG_AGGNONCE }, - { 3, { 0, 1, 2 }, 0, 0, 1, MUSIG_SECNONCE }, - }, - { - { { 0x97, 0xAC, 0x83, 0x3A, 0xDC, 0xB1, 0xAF, 0xA4, 0x2E, 0xBF, 0x9E, 0x07, 0x25, 0x61, 0x6F, 0x3C, 0x9A, 0x0D, 0x5B, 0x61, 0x4F, 0x6F, 0xE2, 0x83, 0xCE, 0xAA, 0xA3, 0x7A, 0x8F, 0xFA, 0xF4, 0x06 }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_SIG_VERIFY }, - { { 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 1, MUSIG_SIG_VERIFY }, - { { 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 }, 3, { 0, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_SIG }, - }, - { - { { 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B }, 3, { 0, 1, 2 }, 3, { 4, 1, 2 }, 0, 0, MUSIG_PUBNONCE }, - { { 0x68, 0x53, 0x7C, 0xC5, 0x23, 0x4E, 0x50, 0x5B, 0xD1, 0x40, 0x61, 0xF8, 0xDA, 0x9E, 0x90, 0xC2, 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B }, 3, { 3, 1, 2 }, 3, { 0, 1, 2 }, 0, 0, MUSIG_PUBKEY }, - }, -}; - -struct musig_tweak_case { - size_t key_indices_len; - size_t key_indices[3]; - size_t nonce_indices_len; - size_t nonce_indices[3]; - size_t tweak_indices_len; - size_t tweak_indices[4]; - int is_xonly[4]; - size_t signer_index; - unsigned char expected[32]; -}; - -struct musig_tweak_vector { - unsigned char sk[32]; - unsigned char secnonce[97]; - unsigned char aggnonce[66]; - unsigned char msg[32]; - unsigned char pubkeys[3][33]; - unsigned char pubnonces[3][194]; - unsigned char tweaks[5][32]; - struct musig_tweak_case valid_case[5]; - struct musig_tweak_case error_case[1]; -}; - -static const struct musig_tweak_vector musig_tweak_vector = { - { 0x7F, 0xB9, 0xE0, 0xE6, 0x87, 0xAD, 0xA1, 0xEE, 0xBF, 0x7E, 0xCF, 0xE2, 0xF2, 0x1E, 0x73, 0xEB, 0xDB, 0x51, 0xA7, 0xD4, 0x50, 0x94, 0x8D, 0xFE, 0x8D, 0x76, 0xD7, 0xF2, 0xD1, 0x00, 0x76, 0x71 }, - { 0x50, 0x8B, 0x81, 0xA6, 0x11, 0xF1, 0x00, 0xA6, 0xB2, 0xB6, 0xB2, 0x96, 0x56, 0x59, 0x08, 0x98, 0xAF, 0x48, 0x8B, 0xCF, 0x2E, 0x1F, 0x55, 0xCF, 0x22, 0xE5, 0xCF, 0xB8, 0x44, 0x21, 0xFE, 0x61, 0xFA, 0x27, 0xFD, 0x49, 0xB1, 0xD5, 0x00, 0x85, 0xB4, 0x81, 0x28, 0x5E, 0x1C, 0xA2, 0x05, 0xD5, 0x5C, 0x82, 0xCC, 0x1B, 0x31, 0xFF, 0x5C, 0xD5, 0x4A, 0x48, 0x98, 0x29, 0x35, 0x59, 0x01, 0xF7, 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, - { 0x02, 0x84, 0x65, 0xFC, 0xF0, 0xBB, 0xDB, 0xCF, 0x44, 0x3A, 0xAB, 0xCC, 0xE5, 0x33, 0xD4, 0x2B, 0x4B, 0x5A, 0x10, 0x96, 0x6A, 0xC0, 0x9A, 0x49, 0x65, 0x5E, 0x8C, 0x42, 0xDA, 0xAB, 0x8F, 0xCD, 0x61, 0x03, 0x74, 0x96, 0xA3, 0xCC, 0x86, 0x92, 0x6D, 0x45, 0x2C, 0xAF, 0xCF, 0xD5, 0x5D, 0x25, 0x97, 0x2C, 0xA1, 0x67, 0x5D, 0x54, 0x93, 0x10, 0xDE, 0x29, 0x6B, 0xFF, 0x42, 0xF7, 0x2E, 0xEE, 0xA8, 0xC9 }, - { 0xF9, 0x54, 0x66, 0xD0, 0x86, 0x77, 0x0E, 0x68, 0x99, 0x64, 0x66, 0x42, 0x19, 0x26, 0x6F, 0xE5, 0xED, 0x21, 0x5C, 0x92, 0xAE, 0x20, 0xBA, 0xB5, 0xC9, 0xD7, 0x9A, 0xDD, 0xDD, 0xF3, 0xC0, 0xCF }, - { - { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, - { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, - { 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 } - }, - { - { 0x03, 0x37, 0xC8, 0x78, 0x21, 0xAF, 0xD5, 0x0A, 0x86, 0x44, 0xD8, 0x20, 0xA8, 0xF3, 0xE0, 0x2E, 0x49, 0x9C, 0x93, 0x18, 0x65, 0xC2, 0x36, 0x0F, 0xB4, 0x3D, 0x0A, 0x0D, 0x20, 0xDA, 0xFE, 0x07, 0xEA, 0x02, 0x87, 0xBF, 0x89, 0x1D, 0x2A, 0x6D, 0xEA, 0xEB, 0xAD, 0xC9, 0x09, 0x35, 0x2A, 0xA9, 0x40, 0x5D, 0x14, 0x28, 0xC1, 0x5F, 0x4B, 0x75, 0xF0, 0x4D, 0xAE, 0x64, 0x2A, 0x95, 0xC2, 0x54, 0x84, 0x80 }, - { 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98 }, - { 0x03, 0x2D, 0xE2, 0x66, 0x26, 0x28, 0xC9, 0x0B, 0x03, 0xF5, 0xE7, 0x20, 0x28, 0x4E, 0xB5, 0x2F, 0xF7, 0xD7, 0x1F, 0x42, 0x84, 0xF6, 0x27, 0xB6, 0x8A, 0x85, 0x3D, 0x78, 0xC7, 0x8E, 0x1F, 0xFE, 0x93, 0x03, 0xE4, 0xC5, 0x52, 0x4E, 0x83, 0xFF, 0xE1, 0x49, 0x3B, 0x90, 0x77, 0xCF, 0x1C, 0xA6, 0xBE, 0xB2, 0x09, 0x0C, 0x93, 0xD9, 0x30, 0x32, 0x10, 0x71, 0xAD, 0x40, 0xB2, 0xF4, 0x4E, 0x59, 0x90, 0x46 } - }, - { - { 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB }, - { 0xAE, 0x2E, 0xA7, 0x97, 0xCC, 0x0F, 0xE7, 0x2A, 0xC5, 0xB9, 0x7B, 0x97, 0xF3, 0xC6, 0x95, 0x7D, 0x7E, 0x41, 0x99, 0xA1, 0x67, 0xA5, 0x8E, 0xB0, 0x8B, 0xCA, 0xFF, 0xDA, 0x70, 0xAC, 0x04, 0x55 }, - { 0xF5, 0x2E, 0xCB, 0xC5, 0x65, 0xB3, 0xD8, 0xBE, 0xA2, 0xDF, 0xD5, 0xB7, 0x5A, 0x4F, 0x45, 0x7E, 0x54, 0x36, 0x98, 0x09, 0x32, 0x2E, 0x41, 0x20, 0x83, 0x16, 0x26, 0xF2, 0x90, 0xFA, 0x87, 0xE0 }, - { 0x19, 0x69, 0xAD, 0x73, 0xCC, 0x17, 0x7F, 0xA0, 0xB4, 0xFC, 0xED, 0x6D, 0xF1, 0xF7, 0xBF, 0x99, 0x07, 0xE6, 0x65, 0xFD, 0xE9, 0xBA, 0x19, 0x6A, 0x74, 0xFE, 0xD0, 0xA3, 0xCF, 0x5A, 0xEF, 0x9D }, - { 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 } - }, - { - { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 0 }, { 1 }, 2, { 0xE2, 0x8A, 0x5C, 0x66, 0xE6, 0x1E, 0x17, 0x8C, 0x2B, 0xA1, 0x9D, 0xB7, 0x7B, 0x6C, 0xF9, 0xF7, 0xE2, 0xF0, 0xF5, 0x6C, 0x17, 0x91, 0x8C, 0xD1, 0x31, 0x35, 0xE6, 0x0C, 0xC8, 0x48, 0xFE, 0x91 }}, - { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 0 }, { 0 }, 2, { 0x38, 0xB0, 0x76, 0x77, 0x98, 0x25, 0x2F, 0x21, 0xBF, 0x57, 0x02, 0xC4, 0x80, 0x28, 0xB0, 0x95, 0x42, 0x83, 0x20, 0xF7, 0x3A, 0x4B, 0x14, 0xDB, 0x1E, 0x25, 0xDE, 0x58, 0x54, 0x3D, 0x2D, 0x2D }}, - { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 2, { 0, 1 }, { 0, 1 }, 2, { 0x40, 0x8A, 0x0A, 0x21, 0xC4, 0xA0, 0xF5, 0xDA, 0xCA, 0xF9, 0x64, 0x6A, 0xD6, 0xEB, 0x6F, 0xEC, 0xD7, 0xF7, 0xA1, 0x1F, 0x03, 0xED, 0x1F, 0x48, 0xDF, 0xFF, 0x21, 0x85, 0xBC, 0x2C, 0x24, 0x08 }}, - { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 4, { 0, 1, 2, 3 }, { 0, 0, 1, 1 }, 2, { 0x45, 0xAB, 0xD2, 0x06, 0xE6, 0x1E, 0x3D, 0xF2, 0xEC, 0x9E, 0x26, 0x4A, 0x6F, 0xEC, 0x82, 0x92, 0x14, 0x1A, 0x63, 0x3C, 0x28, 0x58, 0x63, 0x88, 0x23, 0x55, 0x41, 0xF9, 0xAD, 0xE7, 0x54, 0x35 }}, - { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 4, { 0, 1, 2, 3 }, { 1, 0, 1, 0 }, 2, { 0xB2, 0x55, 0xFD, 0xCA, 0xC2, 0x7B, 0x40, 0xC7, 0xCE, 0x78, 0x48, 0xE2, 0xD3, 0xB7, 0xBF, 0x5E, 0xA0, 0xED, 0x75, 0x6D, 0xA8, 0x15, 0x65, 0xAC, 0x80, 0x4C, 0xCC, 0xA3, 0xE1, 0xD5, 0xD2, 0x39 }}, - }, - { - { 3, { 1, 2, 0 }, 3, { 1, 2, 0 }, 1, { 4 }, { 0 }, 2, { 0 }}, - }, -}; - -/* Omit pubnonces in the test vectors because they're only needed for - * implementations that do not directly accept an aggnonce. */ -struct musig_sig_agg_case { - size_t key_indices_len; - size_t key_indices[2]; - size_t tweak_indices_len; - size_t tweak_indices[3]; - int is_xonly[3]; - unsigned char aggnonce[66]; - size_t psig_indices_len; - size_t psig_indices[2]; - /* if valid case */ - unsigned char expected[64]; - /* if error case */ - int invalid_sig_idx; -}; - -struct musig_sig_agg_vector { - unsigned char pubkeys[4][33]; - unsigned char tweaks[3][32]; - unsigned char psigs[9][32]; - unsigned char msg[32]; - struct musig_sig_agg_case valid_case[4]; - struct musig_sig_agg_case error_case[1]; -}; - -static const struct musig_sig_agg_vector musig_sig_agg_vector = { - { - { 0x03, 0x93, 0x5F, 0x97, 0x2D, 0xA0, 0x13, 0xF8, 0x0A, 0xE0, 0x11, 0x89, 0x0F, 0xA8, 0x9B, 0x67, 0xA2, 0x7B, 0x7B, 0xE6, 0xCC, 0xB2, 0x4D, 0x32, 0x74, 0xD1, 0x8B, 0x2D, 0x40, 0x67, 0xF2, 0x61, 0xA9 }, - { 0x02, 0xD2, 0xDC, 0x6F, 0x5D, 0xF7, 0xC5, 0x6A, 0xCF, 0x38, 0xC7, 0xFA, 0x0A, 0xE7, 0xA7, 0x59, 0xAE, 0x30, 0xE1, 0x9B, 0x37, 0x35, 0x9D, 0xFD, 0xE0, 0x15, 0x87, 0x23, 0x24, 0xC7, 0xEF, 0x6E, 0x05 }, - { 0x03, 0xC7, 0xFB, 0x10, 0x1D, 0x97, 0xFF, 0x93, 0x0A, 0xCD, 0x0C, 0x67, 0x60, 0x85, 0x2E, 0xF6, 0x4E, 0x69, 0x08, 0x3D, 0xE0, 0xB0, 0x6A, 0xC6, 0x33, 0x57, 0x24, 0x75, 0x4B, 0xB4, 0xB0, 0x52, 0x2C }, - { 0x02, 0x35, 0x24, 0x33, 0xB2, 0x1E, 0x7E, 0x05, 0xD3, 0xB4, 0x52, 0xB8, 0x1C, 0xAE, 0x56, 0x6E, 0x06, 0xD2, 0xE0, 0x03, 0xEC, 0xE1, 0x6D, 0x10, 0x74, 0xAA, 0xBA, 0x42, 0x89, 0xE0, 0xE3, 0xD5, 0x81 } - }, - { - { 0xB5, 0x11, 0xDA, 0x49, 0x21, 0x82, 0xA9, 0x1B, 0x0F, 0xFB, 0x9A, 0x98, 0x02, 0x0D, 0x55, 0xF2, 0x60, 0xAE, 0x86, 0xD7, 0xEC, 0xBD, 0x03, 0x99, 0xC7, 0x38, 0x3D, 0x59, 0xA5, 0xF2, 0xAF, 0x7C }, - { 0xA8, 0x15, 0xFE, 0x04, 0x9E, 0xE3, 0xC5, 0xAA, 0xB6, 0x63, 0x10, 0x47, 0x7F, 0xBC, 0x8B, 0xCC, 0xCA, 0xC2, 0xF3, 0x39, 0x5F, 0x59, 0xF9, 0x21, 0xC3, 0x64, 0xAC, 0xD7, 0x8A, 0x2F, 0x48, 0xDC }, - { 0x75, 0x44, 0x8A, 0x87, 0x27, 0x4B, 0x05, 0x64, 0x68, 0xB9, 0x77, 0xBE, 0x06, 0xEB, 0x1E, 0x9F, 0x65, 0x75, 0x77, 0xB7, 0x32, 0x0B, 0x0A, 0x33, 0x76, 0xEA, 0x51, 0xFD, 0x42, 0x0D, 0x18, 0xA8 } - }, - { - { 0xB1, 0x5D, 0x2C, 0xD3, 0xC3, 0xD2, 0x2B, 0x04, 0xDA, 0xE4, 0x38, 0xCE, 0x65, 0x3F, 0x6B, 0x4E, 0xCF, 0x04, 0x2F, 0x42, 0xCF, 0xDE, 0xD7, 0xC4, 0x1B, 0x64, 0xAA, 0xF9, 0xB4, 0xAF, 0x53, 0xFB }, - { 0x61, 0x93, 0xD6, 0xAC, 0x61, 0xB3, 0x54, 0xE9, 0x10, 0x5B, 0xBD, 0xC8, 0x93, 0x7A, 0x34, 0x54, 0xA6, 0xD7, 0x05, 0xB6, 0xD5, 0x73, 0x22, 0xA5, 0xA4, 0x72, 0xA0, 0x2C, 0xE9, 0x9F, 0xCB, 0x64 }, - { 0x9A, 0x87, 0xD3, 0xB7, 0x9E, 0xC6, 0x72, 0x28, 0xCB, 0x97, 0x87, 0x8B, 0x76, 0x04, 0x9B, 0x15, 0xDB, 0xD0, 0x5B, 0x81, 0x58, 0xD1, 0x7B, 0x5B, 0x91, 0x14, 0xD3, 0xC2, 0x26, 0x88, 0x75, 0x05 }, - { 0x66, 0xF8, 0x2E, 0xA9, 0x09, 0x23, 0x68, 0x9B, 0x85, 0x5D, 0x36, 0xC6, 0xB7, 0xE0, 0x32, 0xFB, 0x99, 0x70, 0x30, 0x14, 0x81, 0xB9, 0x9E, 0x01, 0xCD, 0xB4, 0xD6, 0xAC, 0x7C, 0x34, 0x7A, 0x15 }, - { 0x4F, 0x5A, 0xEE, 0x41, 0x51, 0x08, 0x48, 0xA6, 0x44, 0x7D, 0xCD, 0x1B, 0xBC, 0x78, 0x45, 0x7E, 0xF6, 0x90, 0x24, 0x94, 0x4C, 0x87, 0xF4, 0x02, 0x50, 0xD3, 0xEF, 0x2C, 0x25, 0xD3, 0x3E, 0xFE }, - { 0xDD, 0xEF, 0x42, 0x7B, 0xBB, 0x84, 0x7C, 0xC0, 0x27, 0xBE, 0xFF, 0x4E, 0xDB, 0x01, 0x03, 0x81, 0x48, 0x91, 0x78, 0x32, 0x25, 0x3E, 0xBC, 0x35, 0x5F, 0xC3, 0x3F, 0x4A, 0x8E, 0x2F, 0xCC, 0xE4 }, - { 0x97, 0xB8, 0x90, 0xA2, 0x6C, 0x98, 0x1D, 0xA8, 0x10, 0x2D, 0x3B, 0xC2, 0x94, 0x15, 0x9D, 0x17, 0x1D, 0x72, 0x81, 0x0F, 0xDF, 0x7C, 0x6A, 0x69, 0x1D, 0xEF, 0x02, 0xF0, 0xF7, 0xAF, 0x3F, 0xDC }, - { 0x53, 0xFA, 0x9E, 0x08, 0xBA, 0x52, 0x43, 0xCB, 0xCB, 0x0D, 0x79, 0x7C, 0x5E, 0xE8, 0x3B, 0xC6, 0x72, 0x8E, 0x53, 0x9E, 0xB7, 0x6C, 0x2D, 0x0B, 0xF0, 0xF9, 0x71, 0xEE, 0x4E, 0x90, 0x99, 0x71 }, - { 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 } - }, - { 0x59, 0x9C, 0x67, 0xEA, 0x41, 0x0D, 0x00, 0x5B, 0x9D, 0xA9, 0x08, 0x17, 0xCF, 0x03, 0xED, 0x3B, 0x1C, 0x86, 0x8E, 0x4D, 0xA4, 0xED, 0xF0, 0x0A, 0x58, 0x80, 0xB0, 0x08, 0x2C, 0x23, 0x78, 0x69 }, - { - { 2, { 0, 1 }, 0, { 0 }, { 0 }, { 0x03, 0x41, 0x43, 0x27, 0x22, 0xC5, 0xCD, 0x02, 0x68, 0xD8, 0x29, 0xC7, 0x02, 0xCF, 0x0D, 0x1C, 0xBC, 0xE5, 0x70, 0x33, 0xEE, 0xD2, 0x01, 0xFD, 0x33, 0x51, 0x91, 0x38, 0x52, 0x27, 0xC3, 0x21, 0x0C, 0x03, 0xD3, 0x77, 0xF2, 0xD2, 0x58, 0xB6, 0x4A, 0xAD, 0xC0, 0xE1, 0x6F, 0x26, 0x46, 0x23, 0x23, 0xD7, 0x01, 0xD2, 0x86, 0x04, 0x6A, 0x2E, 0xA9, 0x33, 0x65, 0x65, 0x6A, 0xFD, 0x98, 0x75, 0x98, 0x2B }, 2, { 0, 1 }, { 0x04, 0x1D, 0xA2, 0x22, 0x23, 0xCE, 0x65, 0xC9, 0x2C, 0x9A, 0x0D, 0x6C, 0x2C, 0xAC, 0x82, 0x8A, 0xAF, 0x1E, 0xEE, 0x56, 0x30, 0x4F, 0xEC, 0x37, 0x1D, 0xDF, 0x91, 0xEB, 0xB2, 0xB9, 0xEF, 0x09, 0x12, 0xF1, 0x03, 0x80, 0x25, 0x85, 0x7F, 0xED, 0xEB, 0x3F, 0xF6, 0x96, 0xF8, 0xB9, 0x9F, 0xA4, 0xBB, 0x2C, 0x58, 0x12, 0xF6, 0x09, 0x5A, 0x2E, 0x00, 0x04, 0xEC, 0x99, 0xCE, 0x18, 0xDE, 0x1E }, 0 }, - { 2, { 0, 2 }, 0, { 0 }, { 0 }, { 0x02, 0x24, 0xAF, 0xD3, 0x6C, 0x90, 0x20, 0x84, 0x05, 0x8B, 0x51, 0xB5, 0xD3, 0x66, 0x76, 0xBB, 0xA4, 0xDC, 0x97, 0xC7, 0x75, 0x87, 0x37, 0x68, 0xE5, 0x88, 0x22, 0xF8, 0x7F, 0xE4, 0x37, 0xD7, 0x92, 0x02, 0x8C, 0xB1, 0x59, 0x29, 0x09, 0x9E, 0xEE, 0x2F, 0x5D, 0xAE, 0x40, 0x4C, 0xD3, 0x93, 0x57, 0x59, 0x1B, 0xA3, 0x2E, 0x9A, 0xF4, 0xE1, 0x62, 0xB8, 0xD3, 0xE7, 0xCB, 0x5E, 0xFE, 0x31, 0xCB, 0x20 }, 2, { 2, 3 }, { 0x10, 0x69, 0xB6, 0x7E, 0xC3, 0xD2, 0xF3, 0xC7, 0xC0, 0x82, 0x91, 0xAC, 0xCB, 0x17, 0xA9, 0xC9, 0xB8, 0xF2, 0x81, 0x9A, 0x52, 0xEB, 0x5D, 0xF8, 0x72, 0x6E, 0x17, 0xE7, 0xD6, 0xB5, 0x2E, 0x9F, 0x01, 0x80, 0x02, 0x60, 0xA7, 0xE9, 0xDA, 0xC4, 0x50, 0xF4, 0xBE, 0x52, 0x2D, 0xE4, 0xCE, 0x12, 0xBA, 0x91, 0xAE, 0xAF, 0x2B, 0x42, 0x79, 0x21, 0x9E, 0xF7, 0x4B, 0xE1, 0xD2, 0x86, 0xAD, 0xD9 }, 0 }, - { 2, { 0, 2 }, 1, { 0 }, { 0 }, { 0x02, 0x08, 0xC5, 0xC4, 0x38, 0xC7, 0x10, 0xF4, 0xF9, 0x6A, 0x61, 0xE9, 0xFF, 0x3C, 0x37, 0x75, 0x88, 0x14, 0xB8, 0xC3, 0xAE, 0x12, 0xBF, 0xEA, 0x0E, 0xD2, 0xC8, 0x7F, 0xF6, 0x95, 0x4F, 0xF1, 0x86, 0x02, 0x0B, 0x18, 0x16, 0xEA, 0x10, 0x4B, 0x4F, 0xCA, 0x2D, 0x30, 0x4D, 0x73, 0x3E, 0x0E, 0x19, 0xCE, 0xAD, 0x51, 0x30, 0x3F, 0xF6, 0x42, 0x0B, 0xFD, 0x22, 0x23, 0x35, 0xCA, 0xA4, 0x02, 0x91, 0x6D }, 2, { 4, 5 }, { 0x5C, 0x55, 0x8E, 0x1D, 0xCA, 0xDE, 0x86, 0xDA, 0x0B, 0x2F, 0x02, 0x62, 0x6A, 0x51, 0x2E, 0x30, 0xA2, 0x2C, 0xF5, 0x25, 0x5C, 0xAE, 0xA7, 0xEE, 0x32, 0xC3, 0x8E, 0x9A, 0x71, 0xA0, 0xE9, 0x14, 0x8B, 0xA6, 0xC0, 0xE6, 0xEC, 0x76, 0x83, 0xB6, 0x42, 0x20, 0xF0, 0x29, 0x86, 0x96, 0xF1, 0xB8, 0x78, 0xCD, 0x47, 0xB1, 0x07, 0xB8, 0x1F, 0x71, 0x88, 0x81, 0x2D, 0x59, 0x39, 0x71, 0xE0, 0xCC }, 0 }, - { 2, { 0, 3 }, 3, { 0, 1, 2 }, { 1, 0, 1 }, { 0x02, 0xB5, 0xAD, 0x07, 0xAF, 0xCD, 0x99, 0xB6, 0xD9, 0x2C, 0xB4, 0x33, 0xFB, 0xD2, 0xA2, 0x8F, 0xDE, 0xB9, 0x8E, 0xAE, 0x2E, 0xB0, 0x9B, 0x60, 0x14, 0xEF, 0x0F, 0x81, 0x97, 0xCD, 0x58, 0x40, 0x33, 0x02, 0xE8, 0x61, 0x69, 0x10, 0xF9, 0x29, 0x3C, 0xF6, 0x92, 0xC4, 0x9F, 0x35, 0x1D, 0xB8, 0x6B, 0x25, 0xE3, 0x52, 0x90, 0x1F, 0x0E, 0x23, 0x7B, 0xAF, 0xDA, 0x11, 0xF1, 0xC1, 0xCE, 0xF2, 0x9F, 0xFD }, 2, { 6, 7 }, { 0x83, 0x9B, 0x08, 0x82, 0x0B, 0x68, 0x1D, 0xBA, 0x8D, 0xAF, 0x4C, 0xC7, 0xB1, 0x04, 0xE8, 0xF2, 0x63, 0x8F, 0x93, 0x88, 0xF8, 0xD7, 0xA5, 0x55, 0xDC, 0x17, 0xB6, 0xE6, 0x97, 0x1D, 0x74, 0x26, 0xCE, 0x07, 0xBF, 0x6A, 0xB0, 0x1F, 0x1D, 0xB5, 0x0E, 0x4E, 0x33, 0x71, 0x92, 0x95, 0xF4, 0x09, 0x45, 0x72, 0xB7, 0x98, 0x68, 0xE4, 0x40, 0xFB, 0x3D, 0xEF, 0xD3, 0xFA, 0xC1, 0xDB, 0x58, 0x9E }, 0 }, - }, - { - { 2, { 0, 3 }, 3, { 0, 1, 2 }, { 1, 0, 1 }, { 0x02, 0xB5, 0xAD, 0x07, 0xAF, 0xCD, 0x99, 0xB6, 0xD9, 0x2C, 0xB4, 0x33, 0xFB, 0xD2, 0xA2, 0x8F, 0xDE, 0xB9, 0x8E, 0xAE, 0x2E, 0xB0, 0x9B, 0x60, 0x14, 0xEF, 0x0F, 0x81, 0x97, 0xCD, 0x58, 0x40, 0x33, 0x02, 0xE8, 0x61, 0x69, 0x10, 0xF9, 0x29, 0x3C, 0xF6, 0x92, 0xC4, 0x9F, 0x35, 0x1D, 0xB8, 0x6B, 0x25, 0xE3, 0x52, 0x90, 0x1F, 0x0E, 0x23, 0x7B, 0xAF, 0xDA, 0x11, 0xF1, 0xC1, 0xCE, 0xF2, 0x9F, 0xFD }, 2, { 7, 8 }, { 0 }, 1 }, - }, -}; -enum { MUSIG_VECTORS_MAX_PUBKEYS = 7 }; diff --git a/src/secp256k1.c b/src/secp256k1.c index b9567015..cfd129cc 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -918,10 +918,6 @@ static int secp256k1_ge_parse_ext(secp256k1_ge* ge, const unsigned char *in33) { # include "modules/ecdsa_adaptor/main_impl.h" #endif -#ifdef ENABLE_MODULE_MUSIG -# include "modules/musig/main_impl.h" -#endif - #ifdef ENABLE_MODULE_GENERATOR # include "modules/generator/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index aef75618..7663527c 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7592,10 +7592,6 @@ static void run_ecdsa_wycheproof(void) { # include "modules/ecdh/tests_impl.h" #endif -#ifdef ENABLE_MODULE_MUSIG -# include "modules/musig/tests_impl.h" -#endif - #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/tests_impl.h" #endif @@ -7972,10 +7968,6 @@ int main(int argc, char **argv) { run_ecdh_tests(); #endif -#ifdef ENABLE_MODULE_MUSIG - run_musig_tests(); -#endif - /* ecdsa tests */ run_ec_illegal_argument_tests(); run_pubkey_comparison(); From 8d443b8030836fbf1561284866b41c42ff099ad7 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 13 Feb 2026 14:03:05 +0200 Subject: [PATCH 338/381] musig: Re-add adaptor signatures support --- doc/musig.md | 12 ++ examples/musig.c | 2 +- include/secp256k1_musig.h | 85 +++++++++++- src/ctime_tests.c | 24 +++- src/modules/musig/Makefile.am.include | 1 + src/modules/musig/adaptor_impl.h | 101 ++++++++++++++ src/modules/musig/main_impl.h | 1 + src/modules/musig/session_impl.h | 14 +- src/modules/musig/tests_impl.h | 182 +++++++++++++++++++++++--- 9 files changed, 401 insertions(+), 21 deletions(-) create mode 100644 src/modules/musig/adaptor_impl.h diff --git a/doc/musig.md b/doc/musig.md index ae21f9b1..ad09d983 100644 --- a/doc/musig.md +++ b/doc/musig.md @@ -18,6 +18,7 @@ Therefore, users of the musig module must take great care to make sure of the fo See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`. 3. Opaque data structures are never written to or read from directly. Instead, only the provided accessor functions are used. +4. If adaptor signatures are used, all partial signatures are verified. ## Key Aggregation and (Taproot) Tweaking @@ -52,3 +53,14 @@ Similarly, the API supports an alternative protocol flow where generating the ag ## Verification A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7. + +# Atomic Swaps + +The signing API supports the production of "adaptor signatures", modified partial signatures +which are offset by an auxiliary secret known to one party. That is, +1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`. +2. When calling `secp256k1_musig_nonce_process`, the public adaptor `T` is provided as the `adaptor` argument. +3. The party who is going to extract the secret adaptor `t` later must verify all partial signatures. +4. Due to step 2, the signature output of `secp256k1_musig_partial_sig_agg` is a pre-signature and not a valid Schnorr signature. All parties involved extract this session's `nonce_parity` with `secp256k1_musig_nonce_parity`. +5. The party who knows `t` must "adapt" the pre-signature with `t` (and the `nonce_parity` using `secp256k1_musig_adapt` to complete the signature. +6. Any party who sees both the final signature and the pre-signature (and has the `nonce_parity`) can extract `t` with `secp256k1_musig_extract_adaptor`. diff --git a/examples/musig.c b/examples/musig.c index 396dbb9f..3450cf82 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -139,7 +139,7 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr /* Every signer creates a partial signature */ for (i = 0; i < N_SIGNERS; i++) { /* Initialize the signing session by processing the aggregate nonce */ - if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache)) { + if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache, NULL)) { return 0; } /* partial_sign will clear the secnonce by setting it to 0. That's because diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 53501814..97243033 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -16,7 +16,7 @@ extern "C" { * v1.0.0. You can find an example demonstrating the musig module in * examples/musig.c. * - * The module also supports BIP 341 ("Taproot") public key tweaking. + * The module also supports BIP 341 ("Taproot") public key tweaking and adaptor signatures. * * It is recommended to read the documentation in this include file carefully. * Further notes on API usage can be found in doc/musig.md @@ -462,6 +462,11 @@ SECP256K1_API int secp256k1_musig_nonce_agg( /** Takes the aggregate nonce and creates a session that is required for signing * and verification of partial signatures. * + * If the adaptor argument is non-NULL, then the output of + * musig_partial_sig_agg will be a pre-signature which is not a valid Schnorr + * signature. In order to create a valid signature, the pre-signature and the + * secret adaptor must be provided to `musig_adapt`. + * * Returns: 0 if the arguments are invalid, 1 otherwise * Args: ctx: pointer to a context object * Out: session: pointer to a struct to store the session @@ -470,13 +475,17 @@ SECP256K1_API int secp256k1_musig_nonce_agg( * msg32: the 32-byte message to sign * keyagg_cache: pointer to the keyagg_cache that was used to create the * aggregate (and potentially tweaked) pubkey + * adaptor: optional pointer to an adaptor point encoded as a public + * key if this signing session is part of an adaptor + * signature protocol (can be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process( const secp256k1_context *ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, - const secp256k1_musig_keyagg_cache *keyagg_cache + const secp256k1_musig_keyagg_cache *keyagg_cache, + const secp256k1_pubkey *adaptor ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Produces a partial signature @@ -534,6 +543,7 @@ SECP256K1_API int secp256k1_musig_partial_sign( * before aggregating it with `musig_nonce_agg` and using the result to * create the `session` with `musig_nonce_process`. * + * This function is essential when using protocols with adaptor signatures. * It is not required to call this function in regular MuSig sessions, because * if any partial signature does not verify, the final signature will not * verify either, so the problem will be caught. However, this function @@ -581,6 +591,77 @@ SECP256K1_API int secp256k1_musig_partial_sig_agg( size_t n_sigs ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Extracts the nonce_parity bit from a session + * + * This is used for adaptor signatures. + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: nonce_parity: pointer to an integer that indicates the parity + * of the aggregate public nonce. Used for adaptor + * signatures. + * In: session: pointer to the session that was created with + * musig_nonce_process + */ +SECP256K1_API int secp256k1_musig_nonce_parity( + const secp256k1_context *ctx, + int *nonce_parity, + const secp256k1_musig_session *session +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Creates a signature from a pre-signature and an adaptor. + * + * If the sec_adaptor32 argument is incorrect, the output signature will be + * invalid. This function does not verify the signature. + * + * Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain + * invalid (overflowing) values. 1 otherwise (which does NOT mean the + * signature or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out: sig64: 64-byte signature. This pointer may point to the same + * memory area as `pre_sig`. + * In: pre_sig64: 64-byte pre-signature + * sec_adaptor32: 32-byte secret adaptor to add to the pre-signature + * nonce_parity: the output of `musig_nonce_parity` called with the + * session used for producing the pre-signature + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_adapt( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *pre_sig64, + const unsigned char *sec_adaptor32, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Extracts a secret adaptor from a MuSig pre-signature and corresponding + * signature + * + * This function will not fail unless given grossly invalid data; if it is + * merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier + * steps of any protocol that uses this function. In particular, this includes + * verifying all partial signatures that were aggregated into pre_sig64. + * + * Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain + * grossly invalid (overflowing) values. 1 otherwise (which does NOT + * mean the signatures or the adaptor are valid!) + * Args: ctx: pointer to a context object + * Out:sec_adaptor32: 32-byte secret adaptor + * In: sig64: complete, valid 64-byte signature + * pre_sig64: the pre-signature corresponding to sig64, i.e., the + * aggregate of partial signatures without the secret + * adaptor + * nonce_parity: the output of `musig_nonce_parity` called with the + * session used for producing sig64 + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_adaptor( + const secp256k1_context *ctx, + unsigned char *sec_adaptor32, + const unsigned char *sig64, + const unsigned char *pre_sig64, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/ctime_tests.c b/src/ctime_tests.c index 14d18e47..a4aef214 100644 --- a/src/ctime_tests.c +++ b/src/ctime_tests.c @@ -207,7 +207,12 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { secp256k1_musig_keyagg_cache cache; secp256k1_musig_session session; secp256k1_musig_partial_sig partial_sig; + const secp256k1_musig_partial_sig *partial_sig_ptr[1]; unsigned char extra_input[32]; + unsigned char sec_adaptor[32]; + secp256k1_pubkey adaptor; + unsigned char pre_sig[64]; + int nonce_parity; pk_ptr[0] = &pk; pubnonce_ptr[0] = &pubnonce; @@ -216,14 +221,19 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { session_secrand[0] = session_secrand[0] + 1; memcpy(extra_input, key, sizeof(extra_input)); extra_input[0] = extra_input[0] + 2; + memcpy(sec_adaptor, key, sizeof(sec_adaptor)); + sec_adaptor[0] = extra_input[0] + 3; + partial_sig_ptr[0] = &partial_sig; CHECK(secp256k1_keypair_create(ctx, &keypair, key)); CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair)); CHECK(secp256k1_musig_pubkey_agg(ctx, &agg_pk, &cache, pk_ptr, 1)); + CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor)); SECP256K1_CHECKMEM_UNDEFINE(key, 32); SECP256K1_CHECKMEM_UNDEFINE(session_secrand, sizeof(session_secrand)); SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input)); + SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor)); ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_secrand, key, &pk, msg, &cache, extra_input); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); @@ -234,7 +244,7 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1)); /* Make sure that previous tests don't undefine msg. It's not used as a secret here. */ SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg)); - CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache) == 1); + CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache, &adaptor) == 1); ret = secp256k1_keypair_create(ctx, &keypair, key); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); @@ -242,6 +252,18 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { ret = secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &cache, &session); SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); + + SECP256K1_CHECKMEM_DEFINE(&partial_sig, sizeof(partial_sig)); + CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1)); + SECP256K1_CHECKMEM_DEFINE(pre_sig, sizeof(pre_sig)); + + CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity, &session)); + ret = secp256k1_musig_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_musig_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); } #endif diff --git a/src/modules/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include index 796443c9..cd60e67f 100644 --- a/src/modules/musig/Makefile.am.include +++ b/src/modules/musig/Makefile.am.include @@ -4,5 +4,6 @@ noinst_HEADERS += src/modules/musig/keyagg.h noinst_HEADERS += src/modules/musig/keyagg_impl.h noinst_HEADERS += src/modules/musig/session.h noinst_HEADERS += src/modules/musig/session_impl.h +noinst_HEADERS += src/modules/musig/adaptor_impl.h noinst_HEADERS += src/modules/musig/tests_impl.h noinst_HEADERS += src/modules/musig/vectors.h diff --git a/src/modules/musig/adaptor_impl.h b/src/modules/musig/adaptor_impl.h new file mode 100644 index 00000000..3830e8a2 --- /dev/null +++ b/src/modules/musig/adaptor_impl.h @@ -0,0 +1,101 @@ +/*********************************************************************** + * Copyright (c) 2021 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H +#define SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H + +#include + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_musig.h" + +#include "session.h" +#include "../../scalar.h" + +int secp256k1_musig_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_musig_session *session) { + secp256k1_musig_session_internal session_i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(nonce_parity != NULL); + ARG_CHECK(session != NULL); + + if (!secp256k1_musig_session_load(ctx, &session_i, session)) { + return 0; + } + *nonce_parity = session_i.fin_nonce_parity; + return 1; +} + +int secp256k1_musig_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + ret &= !overflow; + + /* Determine if the secret adaptor should be negated. + * + * The musig_session stores the X-coordinate and the parity of the "final nonce" + * (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor. + * + * Since a BIP340 signature requires an x-only public nonce, in the case where + * (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce + * corresponding to the signature is actually (-r - t)*G. Thus adapting a + * pre-signature requires negating t in this case. + */ + if (nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(&sig64[32], &s); + memmove(sig64, pre_sig64, 32); + secp256k1_scalar_clear(&t); + return ret; +} + +int secp256k1_musig_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(pre_sig64 != NULL); + ARG_CHECK(nonce_parity == 0 || nonce_parity == 1); + + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); + ret &= !overflow; + secp256k1_scalar_negate(&t, &t); + + secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + + if (!nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return ret; +} + +#endif diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index a1311e41..044bd30e 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -8,5 +8,6 @@ #include "keyagg_impl.h" #include "session_impl.h" +#include "adaptor_impl.h" #endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 2715b09d..76f04c53 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -608,7 +608,7 @@ static void secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsign *fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y); } -int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache) { +int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey *adaptor) { secp256k1_keyagg_cache_internal cache_i; secp256k1_ge aggnonce_pts[2]; unsigned char fin_nonce[32]; @@ -630,6 +630,18 @@ int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_ return 0; } + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_ge adaptorp; + secp256k1_gej tmp; + if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) { + return 0; + } + secp256k1_gej_set_ge(&tmp, &aggnonce_pts[0]); + secp256k1_gej_add_ge_var(&tmp, &tmp, &adaptorp, NULL); + secp256k1_ge_set_gej(&aggnonce_pts[0], &tmp); + } + secp256k1_musig_nonce_process_internal(&session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_pts, agg_pk32, msg32); secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, agg_pk32); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ce6ae178..f4ae1585 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -34,7 +34,7 @@ static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_pubkey *p return ret; } -/* Just a simple (non-tweaked) 2-of-2 MuSig aggregate, sign, verify +/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig aggregate, sign, verify * test. */ static void musig_simple_test(void) { unsigned char sk[2][32]; @@ -74,7 +74,7 @@ static void musig_simple_test(void) { CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); for (i = 0; i < 2; i++) { CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[i], &secnonce[i], &keypair[i], &keyagg_cache, &session) == 1); @@ -123,6 +123,7 @@ static void musig_api_tests(void) { const secp256k1_musig_partial_sig *partial_sig_ptr[2]; secp256k1_musig_partial_sig invalid_partial_sig; const secp256k1_musig_partial_sig *invalid_partial_sig_ptr[2]; + unsigned char final_sig[64]; unsigned char pre_sig[64]; unsigned char buf[32]; unsigned char sk[2][32]; @@ -157,6 +158,10 @@ static void musig_api_tests(void) { const secp256k1_pubkey *invalid_pk_ptr2[2]; const secp256k1_pubkey *invalid_pk_ptr3[3]; unsigned char tweak[32]; + int nonce_parity; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor1[32]; + secp256k1_pubkey adaptor; int i; /** setup **/ @@ -174,8 +179,10 @@ static void musig_api_tests(void) { memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce)); memset(&invalid_session, 0, sizeof(invalid_session)); + testrand256(sec_adaptor); testrand256(msg); testrand256(tweak); + CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1); for (i = 0; i < 2; i++) { pk_ptr[i] = &pk[i]; invalid_pk_ptr2[i] = &invalid_pk; @@ -388,15 +395,17 @@ static void musig_api_tests(void) { } /** Process nonces **/ - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL)); - CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL, &adaptor)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache, &adaptor)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, (secp256k1_pubkey *)&invalid_pk)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1); memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp)); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1); @@ -481,6 +490,40 @@ static void musig_api_tests(void) { CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 0)); CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 1) == 1); CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 2) == 1); + + /** Adaptor signature verification */ + CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity, &session) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, NULL, &session)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, &invalid_session)); + + CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, NULL, pre_sig, sec_adaptor, 0)); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, NULL, sec_adaptor, 0)); + CHECK(secp256k1_musig_adapt(CTX, final_sig, max64, sec_adaptor, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, NULL, 0)); + CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, max64, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, 2)); + /* sig and pre_sig argument point to the same location */ + memcpy(final_sig, pre_sig, sizeof(final_sig)); + CHECK(secp256k1_musig_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); + + CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1); + + /** Secret adaptor can be extracted from signature */ + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1); + CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) == 0); + /* wrong nonce parity */ + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1); + CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) != 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, NULL, final_sig, pre_sig, 0)); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, NULL, pre_sig, 0)); + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, max64, pre_sig, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, NULL, 0)); + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, max64, 0) == 0); + CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, 2)); } static void musig_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { @@ -548,6 +591,111 @@ static void musig_nonce_test(void) { } } +static void scriptless_atomic_swap(void) { + /* Throughout this test "a" and "b" refer to two hypothetical blockchains, + * while the indices 0 and 1 refer to the two signers. Here signer 0 is + * sending a-coins to signer 1, while signer 1 is sending b-coins to signer + * 0. Signer 0 produces the adaptor signatures. */ + unsigned char pre_sig_a[64]; + unsigned char final_sig_a[64]; + unsigned char pre_sig_b[64]; + unsigned char final_sig_b[64]; + secp256k1_musig_partial_sig partial_sig_a[2]; + const secp256k1_musig_partial_sig *partial_sig_a_ptr[2]; + secp256k1_musig_partial_sig partial_sig_b[2]; + const secp256k1_musig_partial_sig *partial_sig_b_ptr[2]; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor_extracted[32]; + secp256k1_pubkey pub_adaptor; + unsigned char sk_a[2][32]; + unsigned char sk_b[2][32]; + secp256k1_keypair keypair_a[2]; + secp256k1_keypair keypair_b[2]; + secp256k1_pubkey pk_a[2]; + const secp256k1_pubkey *pk_a_ptr[2]; + secp256k1_pubkey pk_b[2]; + const secp256k1_pubkey *pk_b_ptr[2]; + secp256k1_musig_keyagg_cache keyagg_cache_a; + secp256k1_musig_keyagg_cache keyagg_cache_b; + secp256k1_xonly_pubkey agg_pk_a; + secp256k1_xonly_pubkey agg_pk_b; + secp256k1_musig_secnonce secnonce_a[2]; + secp256k1_musig_secnonce secnonce_b[2]; + secp256k1_musig_pubnonce pubnonce_a[2]; + secp256k1_musig_pubnonce pubnonce_b[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr_a[2]; + const secp256k1_musig_pubnonce *pubnonce_ptr_b[2]; + secp256k1_musig_aggnonce aggnonce_a; + secp256k1_musig_aggnonce aggnonce_b; + secp256k1_musig_session session_a, session_b; + int nonce_parity_a; + int nonce_parity_b; + unsigned char seed_a[2][32] = { "a0", "a1" }; + unsigned char seed_b[2][32] = { "b0", "b1" }; + const unsigned char msg32_a[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'a'}; + const unsigned char msg32_b[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'b'}; + int i; + + /* Step 1: key setup */ + for (i = 0; i < 2; i++) { + pk_a_ptr[i] = &pk_a[i]; + pk_b_ptr[i] = &pk_b[i]; + pubnonce_ptr_a[i] = &pubnonce_a[i]; + pubnonce_ptr_b[i] = &pubnonce_b[i]; + partial_sig_a_ptr[i] = &partial_sig_a[i]; + partial_sig_b_ptr[i] = &partial_sig_b[i]; + + testrand256(sk_a[i]); + testrand256(sk_b[i]); + CHECK(create_keypair_and_pk(&keypair_a[i], &pk_a[i], sk_a[i]) == 1); + CHECK(create_keypair_and_pk(&keypair_b[i], &pk_b[i], sk_b[i]) == 1); + } + testrand256(sec_adaptor); + CHECK(secp256k1_ec_pubkey_create(CTX, &pub_adaptor, sec_adaptor) == 1); + + CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk_a, &keyagg_cache_a, pk_a_ptr, 2) == 1); + CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1); + + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], &pk_a[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_a[1], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], &pk_b[0], NULL, NULL, NULL) == 1); + CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], &pk_b[1], NULL, NULL, NULL) == 1); + + /* Step 2: Exchange nonces */ + CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_a, pubnonce_ptr_a, 2) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session_a, &aggnonce_a, msg32_a, &keyagg_cache_a, &pub_adaptor) == 1); + CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_a, &session_a) == 1); + CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_b, pubnonce_ptr_b, 2) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session_b, &aggnonce_b, msg32_b, &keyagg_cache_b, &pub_adaptor) == 1); + CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_b, &session_b) == 1); + + /* Step 3: Signer 0 produces partial signatures for both chains. */ + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[0], &secnonce_a[0], &keypair_a[0], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[0], &secnonce_b[0], &keypair_b[0], &keyagg_cache_b, &session_b) == 1); + + /* Step 4: Signer 1 receives partial signatures, verifies them and creates a + * partial signature to send B-coins to signer 0. */ + CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_a[0], &pubnonce_a[0], &pk_a[0], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_b[0], &pubnonce_b[0], &pk_b[0], &keyagg_cache_b, &session_b) == 1); + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[1], &secnonce_b[1], &keypair_b[1], &keyagg_cache_b, &session_b) == 1); + + /* Step 5: Signer 0 aggregates its own partial signature with the partial + * signature from signer 1 and adapts it. This results in a complete + * signature which is broadcasted by signer 0 to take B-coins. */ + CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_b, &session_b, partial_sig_b_ptr, 2) == 1); + CHECK(secp256k1_musig_adapt(CTX, final_sig_b, pre_sig_b, sec_adaptor, nonce_parity_b) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_b, msg32_b, sizeof(msg32_b), &agg_pk_b) == 1); + + /* Step 6: Signer 1 signs, extracts adaptor from the published signature, + * and adapts the signature to take A-coins. */ + CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[1], &secnonce_a[1], &keypair_a[1], &keyagg_cache_a, &session_a) == 1); + CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_a, &session_a, partial_sig_a_ptr, 2) == 1); + CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor_extracted, final_sig_b, pre_sig_b, nonce_parity_b) == 1); + CHECK(secp256k1_memcmp_var(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ + CHECK(secp256k1_musig_adapt(CTX, final_sig_a, pre_sig_a, sec_adaptor_extracted, nonce_parity_a) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1); +} + static void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) { secp256k1_sha256 sha; secp256k1_sha256_initialize_tagged(&sha, tag, taglen); @@ -616,7 +764,7 @@ static void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_secrand[1], sk1, &pk[1], NULL, NULL, NULL) == 1); CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache) == 1); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache, NULL) == 1); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce[0], &keypair[0], keyagg_cache, &session) == 1); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[1], &secnonce[1], &keypair[1], keyagg_cache, &session) == 1); @@ -882,7 +1030,7 @@ static void musig_test_vectors_signverify(void) { CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonces[c->aggnonce_index])); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); musig_test_set_secnonce(&secnonce, vector->secnonces[0], &pubkey); @@ -922,8 +1070,9 @@ static void musig_test_vectors_signverify(void) { if (!expected) { continue; } - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); + CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0]))); musig_test_set_secnonce(&secnonce, vector->secnonces[c->secnonce_index], &pubkey); expected = c->error != MUSIG_SECNONCE; @@ -955,7 +1104,7 @@ static void musig_test_vectors_signverify(void) { CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL)); CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, c->nonce_indices_len) == 1); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL)); CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[c->signer_index], sizeof(vector->pubkeys[0]))); @@ -1010,7 +1159,7 @@ static void musig_test_vectors_tweak(void) { CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk)); CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session)); CHECK(secp256k1_musig_partial_sig_serialize(CTX, partial_sig32, &partial_sig)); @@ -1046,7 +1195,7 @@ static void musig_test_vectors_sigagg(void) { CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk32, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly)); CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, c->aggnonce)); - CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache)); + CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL)); for (j = 0; j < c->psig_indices_len; j++) { CHECK(secp256k1_musig_partial_sig_parse(CTX, &partial_sig[j], vector->psigs[c->psig_indices[j]])); partial_sig_ptr[j] = &partial_sig[j]; @@ -1127,6 +1276,7 @@ static void run_musig_tests(void) { for (i = 0; i < COUNT; i++) { /* Run multiple times to ensure that pk and nonce have different y * parities */ + scriptless_atomic_swap(); musig_tweak_test(); } sha256_tag_test(); From 8c7c24eb8a00c9d9122d6f9a6ec30b4f8fca20b4 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 16 Feb 2026 13:01:42 +0200 Subject: [PATCH 339/381] docs: simplify README description, fix musig docs --- README.md | 2 +- doc/musig.md | 2 +- include/secp256k1_musig.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 488044b4..675c2da2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ libsecp256k1-zkp ![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) -A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features such as Confidential Assets and Bulletproofs++ range proofs +A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support for advanced and experimental features Added features: * Experimental module for ECDSA adaptor signatures. diff --git a/doc/musig.md b/doc/musig.md index ad09d983..3732782f 100644 --- a/doc/musig.md +++ b/doc/musig.md @@ -54,7 +54,7 @@ Similarly, the API supports an alternative protocol flow where generating the ag A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7. -# Atomic Swaps +## Atomic Swaps The signing API supports the production of "adaptor signatures", modified partial signatures which are offset by an auxiliary secret known to one party. That is, diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 97243033..d49f2e09 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -544,7 +544,7 @@ SECP256K1_API int secp256k1_musig_partial_sign( * create the `session` with `musig_nonce_process`. * * This function is essential when using protocols with adaptor signatures. - * It is not required to call this function in regular MuSig sessions, because + * Without adaptor signatures, it is not required to call this function in regular MuSig sessions, because * if any partial signature does not verify, the final signature will not * verify either, so the problem will be caught. However, this function * provides the ability to identify which specific partial signature fails From a8e6a3cc347b80908227020ed315ab43b894942a Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 16 Feb 2026 16:12:41 +0200 Subject: [PATCH 340/381] Port bitcoin-core/secp256k1#1628 to zkp public API --- include/secp256k1_ecdsa_s2c.h | 2 +- include/secp256k1_generator.h | 4 ++-- include/secp256k1_surjectionproof.h | 4 ++-- include/secp256k1_whitelist.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h index ea4219fe..c931457d 100644 --- a/include/secp256k1_ecdsa_s2c.h +++ b/include/secp256k1_ecdsa_s2c.h @@ -25,7 +25,7 @@ extern "C" { * If you need to convert to a format suitable for storage, transmission, or * comparison, use secp256k1_ecdsa_s2c_opening_serialize and secp256k1_ecdsa_s2c_opening_parse. */ -typedef struct { +typedef struct secp256k1_ecdsa_s2c_opening { unsigned char data[64]; } secp256k1_ecdsa_s2c_opening; diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index 0a59c363..7bf32314 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -17,7 +17,7 @@ extern "C" { * If you need to convert to a format suitable for storage, transmission, or * comparison, use secp256k1_generator_serialize and secp256k1_generator_parse. */ -typedef struct { +typedef struct secp256k1_generator { unsigned char data[64]; } secp256k1_generator; @@ -100,7 +100,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blin * comparison, use secp256k1_pedersen_commitment_serialize and * secp256k1_pedersen_commitment_parse. */ -typedef struct { +typedef struct secp256k1_pedersen_commitment { unsigned char data[64]; } secp256k1_pedersen_commitment; diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index c9a4aaee..95c58384 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -39,7 +39,7 @@ extern "C" { * The representation is exposed to allow creation of these objects on the * stack; please *do not* use these internals directly. */ -typedef struct { +typedef struct secp256k1_surjectionproof { #ifdef VERIFY /** Mark whether this proof has gone through `secp256k1_surjectionproof_initialize` */ int initialized; @@ -100,7 +100,7 @@ SECP256K1_API int secp256k1_surjectionproof_serialize( * data the API user wants to use as an asset tag. Its contents have no * semantic meaning to libsecp whatsoever. */ -typedef struct { +typedef struct secp256k1_fixed_asset_tag { unsigned char data[32]; } secp256k1_fixed_asset_tag; diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index 9f9decce..20d495d3 100644 --- a/include/secp256k1_whitelist.h +++ b/include/secp256k1_whitelist.h @@ -31,7 +31,7 @@ extern "C" { * stack; please *do not* use these internals directly. To learn the number * of keys for a signature, use `secp256k1_whitelist_signature_n_keys`. */ -typedef struct { +typedef struct secp256k1_whitelist_signature { size_t n_keys; /* e0, scalars */ unsigned char data[32 * (1 + SECP256K1_WHITELIST_MAX_N_KEYS)]; From e3bddfa750064bd9ed6a90819790b149fff62aa8 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Wed, 18 Feb 2026 16:26:07 +0200 Subject: [PATCH 341/381] modules: Port bitcoin-core/secp256k1#1579 to zkp-specific code --- src/modules/bppp/bppp_transcript_impl.h | 1 + src/modules/ecdsa_adaptor/dleq_impl.h | 2 ++ src/modules/ecdsa_adaptor/main_impl.h | 2 ++ src/modules/ecdsa_s2c/main_impl.h | 4 +++- src/modules/generator/main_impl.h | 2 ++ src/modules/generator/pedersen_impl.h | 2 +- src/modules/rangeproof/borromean_impl.h | 5 ++++- src/modules/rangeproof/rangeproof_impl.h | 13 ++++++++----- src/modules/schnorrsig_halfagg/main_impl.h | 2 ++ src/modules/surjection/main_impl.h | 1 + src/modules/surjection/surjection_impl.h | 6 ++++-- src/modules/whitelist/main_impl.h | 4 ++-- src/modules/whitelist/whitelist_impl.h | 2 ++ 13 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/modules/bppp/bppp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h index 5fe9b96c..d53e9023 100644 --- a/src/modules/bppp/bppp_transcript_impl.h +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -34,6 +34,7 @@ static void secp256k1_bppp_challenge_scalar(secp256k1_scalar* ch, const secp256k secp256k1_bppp_le64(buf, idx); secp256k1_sha256_write(&sha, buf, 8); secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_clear(&sha); secp256k1_scalar_set_b32(ch, buf, NULL); } diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index ff946c2c..53b3003e 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -46,6 +46,7 @@ static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, secp256k1_sha256_write(&sha, p1_33, size); secp256k1_sha256_write(&sha, p2_33, size); secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_clear(&sha); if (!noncefp(nonce, buf, sk32, gen2_33, dleq_algo, sizeof(dleq_algo), ndata)) { return 0; @@ -71,6 +72,7 @@ static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *gen2, se secp256k1_dleq_hash_point(&sha, r1); secp256k1_dleq_hash_point(&sha, r2); secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_clear(&sha); secp256k1_scalar_set_b32(e, buf, NULL); } diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index d75764f4..5d16e011 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -114,6 +114,7 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha); secp256k1_sha256_write(&sha, data, 32); secp256k1_sha256_finalize(&sha, masked_key); + secp256k1_sha256_clear(&sha); for (i = 0; i < 32; i++) { masked_key[i] ^= key32[i]; } @@ -141,6 +142,7 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c secp256k1_sha256_write(&sha, pk33, 33); secp256k1_sha256_write(&sha, msg32, 32); secp256k1_sha256_finalize(&sha, nonce32); + secp256k1_sha256_clear(&sha); return 1; } diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 95cb088d..471ae8fb 100644 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -86,6 +86,7 @@ int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signa secp256k1_s2c_ecdsa_data_sha256_tagged(&s2c_sha); secp256k1_sha256_write(&s2c_sha, s2c_data32, 32); secp256k1_sha256_finalize(&s2c_sha, ndata); + secp256k1_sha256_clear(&s2c_sha); secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha); ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, &s2c_sha, s2c_opening, s2c_data32, msg32, seckey, NULL, ndata); @@ -147,6 +148,7 @@ int secp256k1_ecdsa_anti_exfil_host_commit(const secp256k1_context* ctx, unsigne secp256k1_s2c_ecdsa_data_sha256_tagged(&sha); secp256k1_sha256_write(&sha, rand32, 32); secp256k1_sha256_finalize(&sha, rand_commitment32); + secp256k1_sha256_clear(&sha); return 1; } @@ -180,7 +182,7 @@ int secp256k1_ecdsa_anti_exfil_signer_commit(const secp256k1_context* ctx, secp2 secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); secp256k1_ge_set_gej(&r, &rj); secp256k1_ecdsa_s2c_opening_save(opening, &r); - memset(nonce32, 0, 32); + secp256k1_memclear(nonce32, 32); secp256k1_scalar_clear(&k); return 1; } diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index d3dce9fa..c3f1c027 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -222,6 +222,7 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s secp256k1_sha256_write(&sha256, prefix1, 16); secp256k1_sha256_write(&sha256, key32, 32); secp256k1_sha256_finalize(&sha256, b32); + secp256k1_sha256_clear(&sha256); ret &= secp256k1_fe_set_b32_limit(&t, b32); shallue_van_de_woestijne(&add, &t); if (blind32) { @@ -234,6 +235,7 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s secp256k1_sha256_write(&sha256, prefix2, 16); secp256k1_sha256_write(&sha256, key32, 32); secp256k1_sha256_finalize(&sha256, b32); + secp256k1_sha256_clear(&sha256); ret &= secp256k1_fe_set_b32_limit(&t, b32); shallue_van_de_woestijne(&add, &t); secp256k1_gej_add_ge(&accum, &accum, &add); diff --git a/src/modules/generator/pedersen_impl.h b/src/modules/generator/pedersen_impl.h index 6b859fc5..f5526eb6 100644 --- a/src/modules/generator/pedersen_impl.h +++ b/src/modules/generator/pedersen_impl.h @@ -28,7 +28,7 @@ static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t va value <<= 8; } secp256k1_scalar_set_b32(sec, data, NULL); - memset(data, 0, 32); + secp256k1_memclear(data, 32); } static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) { diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 3a3b74e2..3ca86108 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -33,6 +33,7 @@ SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const secp256k1_sha256_write(&sha256_en, ring, 4); secp256k1_sha256_write(&sha256_en, epos, 4); secp256k1_sha256_finalize(&sha256_en, hash); + secp256k1_sha256_clear(&sha256_en); } /** "Borromean" ring signature. @@ -99,6 +100,7 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e } secp256k1_sha256_write(&sha256_e0, m, mlen); secp256k1_sha256_finalize(&sha256_e0, tmp); + secp256k1_sha256_clear(&sha256_e0); return secp256k1_memcmp_var(e0, tmp, 32) == 0; } @@ -157,6 +159,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, } secp256k1_sha256_write(&sha256_e0, m, mlen); secp256k1_sha256_finalize(&sha256_e0, e0); + secp256k1_sha256_clear(&sha256_e0); count = 0; for (i = 0; i < nrings; i++) { VERIFY_CHECK(INT_MAX - count > rsizes[i]); @@ -189,7 +192,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar_clear(&ens); secp256k1_ge_clear(&rge); secp256k1_gej_clear(&rgej); - memset(tmp, 0, 33); + secp256k1_memclear(tmp, 33); return 1; } diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 5fa55372..fc12ad4f 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -76,7 +76,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_rangeproof_serialize_point(rngseed + 32 + 33, genp); memcpy(rngseed + 33 + 33 + 32, proof, len); secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + 33 + len); - secp256k1_scalar_clear(&acc); + secp256k1_scalar_set_int(&acc, 0); npub = 0; ret = 1; for (i = 0; i < rings; i++) { @@ -105,8 +105,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, } } secp256k1_rfc6979_hmac_sha256_finalize(&rng); + secp256k1_rfc6979_hmac_sha256_clear(&rng); secp256k1_scalar_clear(&acc); - memset(tmp, 0, 32); + secp256k1_memclear(tmp, 32); return ret; } @@ -269,7 +270,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len, genp)) { return 0; } - memset(prep, 0, 4096); + secp256k1_memclear(prep, 4096); for (i = 0; i < rings; i++) { /* Sign will overwrite the non-forged signature, move that random value into the nonce. */ k[i] = s[i * 4 + secidx[i]]; @@ -320,6 +321,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); } secp256k1_sha256_finalize(&sha256_m, tmp); + secp256k1_sha256_clear(&sha256_m); if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { return 0; } @@ -330,7 +332,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } VERIFY_CHECK(len <= *plen); *plen = len; - memset(prep, 0, 4096); + secp256k1_memclear(prep, 4096); return 1; } @@ -471,7 +473,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } } *mlen = offset; - memset(prep, 0, 4096); + secp256k1_memclear(prep, 4096); for (i = 0; i < 128; i++) { secp256k1_scalar_clear(&s_orig[i]); } @@ -646,6 +648,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); } secp256k1_sha256_finalize(&sha256_m, m); + secp256k1_sha256_clear(&sha256_m); ret = secp256k1_borromean_verify(nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); if (ret && nonce) { /* Given the nonce, try rewinding the witness to recover its initial state. */ diff --git a/src/modules/schnorrsig_halfagg/main_impl.h b/src/modules/schnorrsig_halfagg/main_impl.h index 7eac1079..0d3662ba 100644 --- a/src/modules/schnorrsig_halfagg/main_impl.h +++ b/src/modules/schnorrsig_halfagg/main_impl.h @@ -85,6 +85,7 @@ int secp256k1_schnorrsig_inc_aggregate(const secp256k1_context *ctx, unsigned ch hashcopy = hash; /* 1.c) Finalize the copy to get zi*/ secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_sha256_clear(&hashcopy); /* Note: No need to check overflow, comes from hash */ secp256k1_scalar_set_b32(&zi, hashoutput, NULL); @@ -162,6 +163,7 @@ int secp256k1_schnorrsig_aggverify(const secp256k1_context *ctx, const secp256k1 hashcopy = hash; /* 1.c) Finalize the copy to get zi*/ secp256k1_sha256_finalize(&hashcopy, hashoutput); + secp256k1_sha256_clear(&hashcopy); secp256k1_scalar_set_b32(&zi, hashoutput, NULL); /* Step 2: T_i = R_i+e_i*P_i */ diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index f1d7d42f..1d35219a 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -153,6 +153,7 @@ static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_cs secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, csprng->state, 32); secp256k1_sha256_finalize(&sha, csprng->state); + secp256k1_sha256_clear(&sha); csprng->state_i = 0; } val = csprng->state[csprng->state_i]; diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index e125cbc0..cc0ad300 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -32,6 +32,7 @@ SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg3 memcpy(&pk_ser[1], &ephemeral_output_tag->data[0], 32); secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); secp256k1_sha256_finalize(&sha256_en, msg32); + secp256k1_sha256_clear(&sha256_en); } SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key) { @@ -51,13 +52,14 @@ SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, si secp256k1_sha256_initialize(&sha256_en); secp256k1_sha256_write(&sha256_en, sec_input, 36); secp256k1_sha256_finalize(&sha256_en, sec_input); + secp256k1_sha256_clear(&sha256_en); secp256k1_scalar_set_b32(&s[i], sec_input, &overflow); if (overflow == 1) { - memset(sec_input, 0, 32); + secp256k1_memclear(sec_input, 32); return 0; } } - memset(sec_input, 0, 32); + secp256k1_memclear(sec_input, 32); return 1; } diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index da631522..ce94d23a 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -54,7 +54,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s break; } secp256k1_scalar_set_b32(&non, nonce32, &overflow); - memset(nonce32, 0, 32); + secp256k1_memclear(nonce32, 32); if (overflow || secp256k1_scalar_is_zero(&non)) { count++; continue; @@ -80,7 +80,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s break; } } - memset(seckey32, 0, 32); + secp256k1_memclear(seckey32, 32); } /* Actually sign */ if (ret) { diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h index 8d691127..48f8bcca 100644 --- a/src/modules/whitelist/whitelist_impl.h +++ b/src/modules/whitelist/whitelist_impl.h @@ -23,6 +23,7 @@ static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_g } secp256k1_sha256_write(&sha, c, size); secp256k1_sha256_finalize(&sha, h); + secp256k1_sha256_clear(&sha); secp256k1_scalar_set_b32(output, h, &overflow); if (overflow || secp256k1_scalar_is_zero(output)) { @@ -122,6 +123,7 @@ static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* secp256k1_gej_add_ge_var(&keys[i], &tweaked_gej, &online_ge, NULL); } secp256k1_sha256_finalize(&sha, msg32); + secp256k1_sha256_clear(&sha); return 1; } From ec343f0b2fa9ad0b5746fd60636c17be9a8186d7 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 20 Feb 2026 18:41:10 +0200 Subject: [PATCH 342/381] Port bitcoin-core/secp256k1#1642 to zkp-specific code --- src/modules/ecdsa_adaptor/main_impl.h | 2 +- src/modules/whitelist/whitelist_impl.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 5d16e011..25232b48 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -341,7 +341,7 @@ int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char /* We declassify non-secret enckey_expected_ge to allow using it as a * branch point. */ secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge)); - if (!secp256k1_eckey_pubkey_serialize(&enckey_expected_ge, enckey_expected33, &size, SECP256K1_EC_COMPRESSED)) { + if (!secp256k1_eckey_pubkey_serialize(&enckey_expected_ge, enckey_expected33, &size, 1)) { /* Unreachable from tests (and other VERIFY builds) and therefore this * branch should be ignored in test coverage analysis. * diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h index 48f8bcca..bb244907 100644 --- a/src/modules/whitelist/whitelist_impl.h +++ b/src/modules/whitelist/whitelist_impl.h @@ -18,7 +18,7 @@ static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_g secp256k1_ge_set_gej(&ge, pubkey); secp256k1_sha256_initialize(&sha); - if (!secp256k1_eckey_pubkey_serialize(&ge, c, &size, SECP256K1_EC_COMPRESSED)) { + if (!secp256k1_eckey_pubkey_serialize(&ge, c, &size, 1)) { return 0; } secp256k1_sha256_write(&sha, c, size); @@ -95,7 +95,7 @@ static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* secp256k1_pubkey_load(ctx, &subkey_ge, sub_pubkey); /* commit to sub-key */ - if (!secp256k1_eckey_pubkey_serialize(&subkey_ge, c, &size, SECP256K1_EC_COMPRESSED)) { + if (!secp256k1_eckey_pubkey_serialize(&subkey_ge, c, &size, 1)) { return 0; } secp256k1_sha256_write(&sha, c, size); @@ -106,12 +106,12 @@ static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* /* commit to fixed keys */ secp256k1_pubkey_load(ctx, &offline_ge, &offline_pubkeys[i]); - if (!secp256k1_eckey_pubkey_serialize(&offline_ge, c, &size, SECP256K1_EC_COMPRESSED)) { + if (!secp256k1_eckey_pubkey_serialize(&offline_ge, c, &size, 1)) { return 0; } secp256k1_sha256_write(&sha, c, size); secp256k1_pubkey_load(ctx, &online_ge, &online_pubkeys[i]); - if (!secp256k1_eckey_pubkey_serialize(&online_ge, c, &size, SECP256K1_EC_COMPRESSED)) { + if (!secp256k1_eckey_pubkey_serialize(&online_ge, c, &size, 1)) { return 0; } secp256k1_sha256_write(&sha, c, size); From 17ad19601850ee3c0e2dc107a44c196fecede60e Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 20 Feb 2026 18:50:53 +0200 Subject: [PATCH 343/381] schnorrsig_halfagg: Fix symbol visibility for internal function --- src/modules/schnorrsig_halfagg/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/schnorrsig_halfagg/main_impl.h b/src/modules/schnorrsig_halfagg/main_impl.h index 0d3662ba..af612195 100644 --- a/src/modules/schnorrsig_halfagg/main_impl.h +++ b/src/modules/schnorrsig_halfagg/main_impl.h @@ -8,7 +8,7 @@ /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("HalfAgg/randomizer")||SHA256("HalfAgg/randomizer"). */ -void secp256k1_schnorrsig_sha256_tagged_aggregation(secp256k1_sha256 *sha) { +static void secp256k1_schnorrsig_sha256_tagged_aggregation(secp256k1_sha256 *sha) { secp256k1_sha256_initialize(sha); sha->s[0] = 0xd11f5532ul; sha->s[1] = 0xfa57f70ful; From 2f057a145fbb304342d517c3fe0b6a40fef0e15b Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 21 Jul 2025 16:35:16 +0200 Subject: [PATCH 344/381] ci: Don't hardcode ABI version --- .github/workflows/ci.yml | 3 ++- ci/ci.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a930e997..9e25a909 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -704,10 +704,11 @@ jobs: - name: Symbol check if: ${{ matrix.configuration.symbol_check }} + shell: bash run: | py -3 --version py -3 -m pip install lief - py -3 .\tools\symbol-check.py build\bin\RelWithDebInfo\libsecp256k1-5.dll + py -3 ./tools/symbol-check.py build/bin/RelWithDebInfo/libsecp256k1-*.dll - name: Check run: | diff --git a/ci/ci.sh b/ci/ci.sh index 170f04df..c0252493 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -118,7 +118,7 @@ then case "$HOST" in *mingw*) ls -l .libs - python3 ./tools/symbol-check.py .libs/libsecp256k1-5.dll + python3 ./tools/symbol-check.py .libs/libsecp256k1-*.dll ;; *) python3 ./tools/symbol-check.py .libs/libsecp256k1.so From 795f19af1f683642ede445d644a652298757e115 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:31:21 +0100 Subject: [PATCH 345/381] ci: Switch to macOS 15 Sequoia Intel-based image The `macos-13` image has been deprecated and will be unavailable soon. See: https://github.com/actions/runner-images/issues/13045. --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e25a909..e255fe6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -557,9 +557,8 @@ jobs: if: ${{ !cancelled() }} x86_64-macos-native: - name: "x86_64: macOS Ventura, Valgrind" - # See: https://github.com/actions/runner-images#available-images. - runs-on: macos-13 + name: "x86_64: macOS Sequoia, Valgrind" + runs-on: macos-15-intel env: CC: 'clang' From 4dda31229e047066b9a8b3fab6f4a8320b9e30dd Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 23 Feb 2026 15:03:08 +0200 Subject: [PATCH 346/381] ci: Use Python virtual environment in x86_64-macos-native job --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e255fe6e..b3922355 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -598,8 +598,12 @@ jobs: run: ./ci/ci.sh - name: Symbol check + env: + VIRTUAL_ENV: '${{ github.workspace }}/venv' run: | python3 --version + python3 -m venv $VIRTUAL_ENV + export PATH="$VIRTUAL_ENV/bin:$PATH" python3 -m pip install lief python3 ./tools/symbol-check.py .libs/libsecp256k1.dylib From 01b1b916ebe101c619c68026196bac5f1e41e4c8 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Wed, 25 Feb 2026 13:11:18 +0200 Subject: [PATCH 347/381] modules: Port bitcoin-core/secp256k1#1725 to zkp-specific code --- src/modules/ecdsa_adaptor/tests_impl.h | 27 +++++---------------- src/modules/schnorrsig_halfagg/tests_impl.h | 6 ++--- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index fcdee325..a3360223 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -718,24 +718,12 @@ static void nonce_function_ecdsa_adaptor_bitflip(unsigned char **args, size_t n_ CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); } -/* Tests for the equality of two sha256 structs. This function only produces a - * correct result if an integer multiple of 64 many bytes have been written - * into the hash functions. */ -static void ecdsa_adaptor_test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { - /* Is buffer fully consumed? */ - CHECK((sha1->bytes & 0x3F) == 0); - - CHECK(sha1->bytes == sha2->bytes); - CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); -} - static void run_nonce_function_ecdsa_adaptor_tests(void) { - unsigned char tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; - unsigned char aux_tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'a', 'u', 'x'}; + static const unsigned char tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; + static const unsigned char aux_tag[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'a', 'u', 'x'}; unsigned char algo[] = {'E', 'C', 'D', 'S', 'A', 'a', 'd', 'a', 'p', 't', 'o', 'r', '/', 'n', 'o', 'n'}; size_t algolen = sizeof(algo); - unsigned char dleq_tag[] = {'D', 'L', 'E', 'Q'}; - secp256k1_sha256 sha; + static const unsigned char dleq_tag[] = {'D', 'L', 'E', 'Q'}; secp256k1_sha256 sha_optimized; unsigned char nonce[32]; unsigned char msg[32]; @@ -748,23 +736,20 @@ static void run_nonce_function_ecdsa_adaptor_tests(void) { /* Check that hash initialized by * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha_optimized); - ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, tag, sizeof(tag)); /* Check that hash initialized by * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha_optimized); - ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, aux_tag, sizeof(aux_tag)); /* Check that hash initialized by * secp256k1_nonce_function_dleq_sha256_tagged_aux has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, dleq_tag, sizeof(dleq_tag)); secp256k1_nonce_function_dleq_sha256_tagged(&sha_optimized); - ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, dleq_tag, sizeof(dleq_tag)); testrand_bytes_test(msg, sizeof(msg)); testrand_bytes_test(key, sizeof(key)); diff --git a/src/modules/schnorrsig_halfagg/tests_impl.h b/src/modules/schnorrsig_halfagg/tests_impl.h index 49ab51e4..b93999b6 100644 --- a/src/modules/schnorrsig_halfagg/tests_impl.h +++ b/src/modules/schnorrsig_halfagg/tests_impl.h @@ -8,13 +8,11 @@ /* We test that the hash initialized by secp256k1_schnorrsig_sha256_tagged_aggregate * has the expected state. */ void test_schnorrsig_sha256_tagged_aggregate(void) { - unsigned char tag[] = {'H', 'a', 'l', 'f', 'A', 'g', 'g', '/', 'r', 'a', 'n', 'd', 'o', 'm', 'i', 'z', 'e', 'r'}; - secp256k1_sha256 sha; + static const unsigned char tag[] = {'H', 'a', 'l', 'f', 'A', 'g', 'g', '/', 'r', 'a', 'n', 'd', 'o', 'm', 'i', 'z', 'e', 'r'}; secp256k1_sha256 sha_optimized; - secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); secp256k1_schnorrsig_sha256_tagged_aggregation(&sha_optimized); - test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, tag, sizeof(tag)); } /* Create n many x-only pubkeys and sigs for random messages */ From 7699fe9aa6dbf9f7b3273ae919c0ca6eafba953e Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Thu, 26 Feb 2026 14:35:11 +0200 Subject: [PATCH 348/381] modules: Port bitcoin-core/secp256k1#1735 to zkp-specific code --- src/modules/ecdsa_s2c/main_impl.h | 2 +- src/modules/generator/pedersen_impl.h | 2 +- src/modules/rangeproof/borromean_impl.h | 2 +- src/modules/rangeproof/rangeproof_impl.h | 8 ++++---- src/modules/surjection/surjection_impl.h | 4 ++-- src/modules/whitelist/main_impl.h | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 471ae8fb..59a4cdfa 100644 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -182,7 +182,7 @@ int secp256k1_ecdsa_anti_exfil_signer_commit(const secp256k1_context* ctx, secp2 secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); secp256k1_ge_set_gej(&r, &rj); secp256k1_ecdsa_s2c_opening_save(opening, &r); - secp256k1_memclear(nonce32, 32); + secp256k1_memclear_explicit(nonce32, 32); secp256k1_scalar_clear(&k); return 1; } diff --git a/src/modules/generator/pedersen_impl.h b/src/modules/generator/pedersen_impl.h index f5526eb6..7d2a8a27 100644 --- a/src/modules/generator/pedersen_impl.h +++ b/src/modules/generator/pedersen_impl.h @@ -28,7 +28,7 @@ static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t va value <<= 8; } secp256k1_scalar_set_b32(sec, data, NULL); - secp256k1_memclear(data, 32); + secp256k1_memclear_explicit(data, 32); } static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) { diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 3ca86108..4906ce85 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -192,7 +192,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar_clear(&ens); secp256k1_ge_clear(&rge); secp256k1_gej_clear(&rgej); - secp256k1_memclear(tmp, 33); + secp256k1_memclear_explicit(tmp, 33); return 1; } diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index fc12ad4f..476da5eb 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -107,7 +107,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_rfc6979_hmac_sha256_finalize(&rng); secp256k1_rfc6979_hmac_sha256_clear(&rng); secp256k1_scalar_clear(&acc); - secp256k1_memclear(tmp, 32); + secp256k1_memclear_explicit(tmp, 32); return ret; } @@ -270,7 +270,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len, genp)) { return 0; } - secp256k1_memclear(prep, 4096); + secp256k1_memclear_explicit(prep, 4096); for (i = 0; i < rings; i++) { /* Sign will overwrite the non-forged signature, move that random value into the nonce. */ k[i] = s[i * 4 + secidx[i]]; @@ -332,7 +332,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } VERIFY_CHECK(len <= *plen); *plen = len; - secp256k1_memclear(prep, 4096); + secp256k1_memclear_explicit(prep, 4096); return 1; } @@ -473,7 +473,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } } *mlen = offset; - secp256k1_memclear(prep, 4096); + secp256k1_memclear_explicit(prep, 4096); for (i = 0; i < 128; i++) { secp256k1_scalar_clear(&s_orig[i]); } diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index cc0ad300..0776e4c8 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -55,11 +55,11 @@ SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, si secp256k1_sha256_clear(&sha256_en); secp256k1_scalar_set_b32(&s[i], sec_input, &overflow); if (overflow == 1) { - secp256k1_memclear(sec_input, 32); + secp256k1_memclear_explicit(sec_input, 32); return 0; } } - secp256k1_memclear(sec_input, 32); + secp256k1_memclear_explicit(sec_input, 32); return 1; } diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index ce94d23a..301d2476 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -54,7 +54,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s break; } secp256k1_scalar_set_b32(&non, nonce32, &overflow); - secp256k1_memclear(nonce32, 32); + secp256k1_memclear_explicit(nonce32, 32); if (overflow || secp256k1_scalar_is_zero(&non)) { count++; continue; @@ -80,7 +80,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s break; } } - secp256k1_memclear(seckey32, 32); + secp256k1_memclear_explicit(seckey32, 32); } /* Actually sign */ if (ret) { From 7111d365fbe210fb346f31abf68121b9911c7f0f Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Thu, 26 Feb 2026 15:28:51 +0200 Subject: [PATCH 349/381] modules, tests: Port bitcoin-core/secp256k1#1734 to zkp-specific code --- src/modules/bppp/tests_impl.h | 28 +++++++++------- src/modules/ecdsa_adaptor/tests_impl.h | 34 +++++++++---------- src/modules/ecdsa_s2c/tests_impl.h | 21 ++++++------ src/modules/generator/tests_impl.h | 27 ++++++++-------- src/modules/rangeproof/tests_impl.h | 36 +++++++++++---------- src/modules/schnorrsig_halfagg/tests_impl.h | 34 ++++++++++--------- src/modules/surjection/tests_impl.h | 32 +++++++++++------- src/modules/whitelist/tests_impl.h | 25 ++++++++------ src/tests.c | 25 ++++++++++++++ 9 files changed, 153 insertions(+), 109 deletions(-) diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 6764694c..85d1c1ca 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -15,6 +15,7 @@ #include "bppp_transcript_impl.h" #include "test_vectors/verify.h" #include "test_vectors/prove.h" +#include "../../unit_test.h" static void test_bppp_generators_api(void) { secp256k1_bppp_generators *gens; @@ -649,15 +650,7 @@ static void norm_arg_prove_vectors(void) { #undef IDX_TO_TEST -static void run_bppp_tests(void) { - test_log_exp(); - test_norm_util_helpers(); - test_serialize_two_points(); - test_bppp_generators_api(); - test_bppp_generators_fixed(); - test_bppp_tagged_hash(); - - norm_arg_verify_zero_len(); +static void norm_arg_test_all(void) { norm_arg_test(1, 1); norm_arg_test(1, 64); norm_arg_test(64, 1); @@ -665,9 +658,20 @@ static void run_bppp_tests(void) { norm_arg_test(32, 64); norm_arg_test(64, 32); norm_arg_test(64, 64); - - norm_arg_verify_vectors(); - norm_arg_prove_vectors(); } +/* --- Test registry --- */ +static const struct tf_test_entry tests_bppp[] = { + CASE1(test_log_exp), + CASE1(test_norm_util_helpers), + CASE1(test_serialize_two_points), + CASE1(test_bppp_generators_api), + CASE1(test_bppp_generators_fixed), + CASE1(test_bppp_tagged_hash), + CASE1(norm_arg_verify_zero_len), + CASE1(norm_arg_test_all), + CASE1(norm_arg_verify_vectors), + CASE1(norm_arg_prove_vectors), +}; + #endif diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index a3360223..e2293adc 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -2,6 +2,7 @@ #define SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H #include "../../../include/secp256k1_ecdsa_adaptor.h" +#include "../../unit_test.h" static void rand_scalar(secp256k1_scalar *scalar) { unsigned char buf32[32]; @@ -27,7 +28,7 @@ static void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_byt CHECK(secp256k1_scalar_eq(&k1, &k2) == 0); } -static void dleq_tests(void) { +static void dleq_tests_internal(void) { secp256k1_scalar s, e, sk, k; secp256k1_ge gen2, p1, p2; unsigned char *args[5]; @@ -850,7 +851,7 @@ static void test_ecdsa_adaptor_api(void) { CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, asig, &zero_pk)); } -static void adaptor_tests(void) { +static void adaptor_tests_internal(void) { unsigned char seckey[32]; secp256k1_pubkey pubkey; unsigned char msg[32]; @@ -1050,7 +1051,7 @@ static void adaptor_tests(void) { } } -static void multi_hop_lock_tests(void) { +static void multi_hop_lock_tests_internal(void) { unsigned char seckey_a[32]; unsigned char seckey_b[32]; unsigned char pop[32]; @@ -1124,21 +1125,18 @@ static void multi_hop_lock_tests(void) { CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0); } -static void run_ecdsa_adaptor_tests(void) { - int i; - run_nonce_function_ecdsa_adaptor_tests(); +/* --- Test registry --- */ +REPEAT_TEST(dleq_tests) +REPEAT_TEST(adaptor_tests) +REPEAT_TEST(multi_hop_lock_tests) - test_ecdsa_adaptor_api(); - test_ecdsa_adaptor_spec_vectors(); - for (i = 0; i < COUNT; i++) { - dleq_tests(); - } - for (i = 0; i < COUNT; i++) { - adaptor_tests(); - } - for (i = 0; i < COUNT; i++) { - multi_hop_lock_tests(); - } -} +static const struct tf_test_entry tests_ecdsa_adaptor[] = { + CASE1(run_nonce_function_ecdsa_adaptor_tests), + CASE1(test_ecdsa_adaptor_api), + CASE1(test_ecdsa_adaptor_spec_vectors), + CASE1(dleq_tests), + CASE1(adaptor_tests), + CASE1(multi_hop_lock_tests), +}; #endif /* SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index f84443f3..d13e5164 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_ECDSA_S2C_TESTS_H #include "../../../include/secp256k1_ecdsa_s2c.h" +#include "../../unit_test.h" static void test_ecdsa_s2c_tagged_hash(void) { unsigned char tag_data[] = {'s', '2', 'c', '/', 'e', 'c', 'd', 's', 'a', '/', 'd', 'a', 't', 'a'}; @@ -323,15 +324,15 @@ static void test_ecdsa_anti_exfil(void) { } } -static void run_ecdsa_s2c_tests(void) { - run_s2c_opening_test(); - test_ecdsa_s2c_tagged_hash(); - test_ecdsa_s2c_api(); - test_ecdsa_s2c_fixed_vectors(); - test_ecdsa_s2c_sign_verify(); - - test_ecdsa_anti_exfil_signer_commit(); - test_ecdsa_anti_exfil(); -} +/* --- Test registry --- */ +static const struct tf_test_entry tests_ecdsa_s2c[] = { + CASE1(run_s2c_opening_test), + CASE1(test_ecdsa_s2c_tagged_hash), + CASE1(test_ecdsa_s2c_api), + CASE1(test_ecdsa_s2c_fixed_vectors), + CASE1(test_ecdsa_s2c_sign_verify), + CASE1(test_ecdsa_anti_exfil_signer_commit), + CASE1(test_ecdsa_anti_exfil) +}; #endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index bd8a0d92..c9f60c0f 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -14,6 +14,7 @@ #include "../../scalar.h" #include "../../testrand.h" #include "../../util.h" +#include "../../unit_test.h" #include "../../../include/secp256k1_generator.h" @@ -228,7 +229,7 @@ static void test_pedersen_api(void) { CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, NULL, 1, 0)); } -static void test_pedersen(void) { +static void test_pedersen_internal(void) { secp256k1_pedersen_commitment commits[19]; const secp256k1_pedersen_commitment *cptr[19]; unsigned char blinds[32*19]; @@ -310,19 +311,17 @@ static void test_pedersen_commitment_fixed_vector(void) { CHECK(!secp256k1_pedersen_commitment_parse(CTX, &parse, result)); } +/* --- Test registry --- */ +REPEAT_TEST(test_pedersen) -static void run_generator_tests(void) { - int i; - - test_shallue_van_de_woestijne(); - test_generator_fixed_vector(); - test_generator_api(); - test_generator_generate(); - test_pedersen_api(); - test_pedersen_commitment_fixed_vector(); - for (i = 0; i < COUNT / 2 + 1; i++) { - test_pedersen(); - } -} +static const struct tf_test_entry tests_generator[] = { + CASE1(test_shallue_van_de_woestijne), + CASE1(test_generator_fixed_vector), + CASE1(test_generator_api), + CASE1(test_generator_generate), + CASE1(test_pedersen), + CASE1(test_pedersen_api), + CASE1(test_pedersen_commitment_fixed_vector), +}; #endif diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index e0686f90..74d9f3fd 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -13,10 +13,11 @@ #include "../../scalar.h" #include "../../testrand.h" #include "../../util.h" +#include "../../unit_test.h" #include "../../../include/secp256k1_rangeproof.h" -static void test_rangeproof_api(void) { +static void test_rangeproof_api_internal(void) { unsigned char proof[5134]; unsigned char blind[32]; secp256k1_pedersen_commitment commit; @@ -121,7 +122,7 @@ static void test_rangeproof_api(void) { CHECK(secp256k1_rangeproof_max_size(CTX, UINT64_MAX, 0) == 5134); } -static void test_borromean(void) { +static void test_borromean_internal(void) { unsigned char e0[32]; secp256k1_scalar s[64]; secp256k1_gej pubs[64]; @@ -1346,24 +1347,25 @@ static void test_rangeproof_fixed_vectors_reproducible(void) { } } -static void run_rangeproof_tests(void) { - int i; - for (i = 0; i < COUNT; i++) { - test_rangeproof_api(); - } - +static void test_single_value_proof_all(void) { test_single_value_proof(0); test_single_value_proof(12345678); test_single_value_proof(UINT64_MAX); - - test_rangeproof_fixed_vectors(); - test_rangeproof_fixed_vectors_reproducible(); - for (i = 0; i < COUNT / 2 + 1; i++) { - test_borromean(); - } - test_rangeproof(); - test_rangeproof_null_blinder(); - test_multiple_generators(); } +/* --- Test registry --- */ +REPEAT_TEST(test_rangeproof_api) +REPEAT_TEST(test_borromean) + +static const struct tf_test_entry tests_rangeproof[] = { + CASE1(test_rangeproof_api), + CASE1(test_single_value_proof_all), + CASE1(test_rangeproof_fixed_vectors), + CASE1(test_rangeproof_fixed_vectors_reproducible), + CASE1(test_borromean), + CASE1(test_rangeproof), + CASE1(test_rangeproof_null_blinder), + CASE1(test_multiple_generators), +}; + #endif diff --git a/src/modules/schnorrsig_halfagg/tests_impl.h b/src/modules/schnorrsig_halfagg/tests_impl.h index b93999b6..29d39b2c 100644 --- a/src/modules/schnorrsig_halfagg/tests_impl.h +++ b/src/modules/schnorrsig_halfagg/tests_impl.h @@ -2,6 +2,7 @@ #define SECP256K1_MODULE_SCHNORRSIG_HALFAGG_TESTS_H #include "../../../include/secp256k1_schnorrsig_halfagg.h" +#include "../../unit_test.h" #define N_MAX 50 @@ -34,7 +35,7 @@ void test_schnorrsig_aggregate_input_helper(secp256k1_xonly_pubkey *pubkeys, uns * aggregate some of them in one shot, and then * aggregate the others incrementally to the already aggregated ones. * The aggregate signature should verify after both steps. */ -void test_schnorrsig_aggregate(void) { +void test_schnorrsig_aggregate_internal(void) { secp256k1_xonly_pubkey pubkeys[N_MAX]; unsigned char msgs32[N_MAX*32]; unsigned char sigs64[N_MAX*64]; @@ -165,7 +166,7 @@ void test_schnorrsig_aggverify_spec_vectors(void) { } } -static void test_schnorrsig_aggregate_api(void) { +static void test_schnorrsig_aggregate_api_internal(void) { size_t n = testrand_int(N_MAX + 1); size_t n_initial = testrand_int(n + 1); size_t n_new = n - n_initial; @@ -241,7 +242,7 @@ static void test_schnorrsig_aggregate_api(void) { /* In this test, we make sure that trivial attempts to break * the security of verification do not work. */ -static void test_schnorrsig_aggregate_unforge(void) { +static void test_schnorrsig_aggregate_unforge_internal(void) { secp256k1_xonly_pubkey pubkeys[N_MAX]; unsigned char msgs32[N_MAX*32]; unsigned char sigs64[N_MAX*64]; @@ -297,7 +298,7 @@ static void test_schnorrsig_aggregate_unforge(void) { /* In this test, we make sure that the algorithms properly reject * for overflowing and non parseable values. */ -static void test_schnorrsig_aggregate_overflow(void) { +static void test_schnorrsig_aggregate_overflow_internal(void) { secp256k1_xonly_pubkey pubkeys[N_MAX]; unsigned char msgs32[N_MAX*32]; unsigned char sigs64[N_MAX*64]; @@ -317,19 +318,20 @@ static void test_schnorrsig_aggregate_overflow(void) { } } -static void run_schnorrsig_halfagg_tests(void) { - int i; +/* --- Test registry --- */ +REPEAT_TEST(test_schnorrsig_aggregate) +REPEAT_TEST(test_schnorrsig_aggregate_api) +REPEAT_TEST(test_schnorrsig_aggregate_unforge) +REPEAT_TEST(test_schnorrsig_aggregate_overflow) - test_schnorrsig_sha256_tagged_aggregate(); - test_schnorrsig_aggverify_spec_vectors(); - - for (i = 0; i < COUNT; i++) { - test_schnorrsig_aggregate(); - test_schnorrsig_aggregate_api(); - test_schnorrsig_aggregate_unforge(); - test_schnorrsig_aggregate_overflow(); - } -} +static const struct tf_test_entry tests_schnorrsig_halfagg[] = { + CASE1(test_schnorrsig_sha256_tagged_aggregate), + CASE1(test_schnorrsig_aggverify_spec_vectors), + CASE1(test_schnorrsig_aggregate), + CASE1(test_schnorrsig_aggregate_api), + CASE1(test_schnorrsig_aggregate_unforge), + CASE1(test_schnorrsig_aggregate_overflow), +}; #undef N_MAX diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index e609d170..731e2fd4 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -9,6 +9,7 @@ #include "../../testrand.h" #include "../../group.h" +#include "../../unit_test.h" #include "../../../include/secp256k1_generator.h" #include "../../../include/secp256k1_rangeproof.h" #include "../../../include/secp256k1_surjectionproof.h" @@ -627,22 +628,29 @@ static void test_fixed_vectors(void) { CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used3_len)); } -static void run_surjection_tests(void) { - test_surjectionproof_api(); - test_input_eq_output(); - test_fixed_vectors(); - +static void test_input_selection_all(void) { test_input_selection(0); test_input_selection(1); test_input_selection(5); test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); - - test_input_selection_distribution(); - test_gen_verify(10, 3); - test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); - test_no_used_inputs_verify(); - test_bad_serialize(); - test_bad_parse(); } +static void test_gen_verify_all(void) { + test_gen_verify(10, 3); + test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); +} + +/* --- Test registry --- */ +static const struct tf_test_entry tests_surjection[] = { + CASE1(test_surjectionproof_api), + CASE1(test_input_eq_output), + CASE1(test_fixed_vectors), + CASE1(test_input_selection_all), + CASE1(test_input_selection_distribution), + CASE1(test_gen_verify_all), + CASE1(test_no_used_inputs_verify), + CASE1(test_bad_serialize), + CASE1(test_bad_parse), +}; + #endif diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 38f91e18..9cbb8287 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_WHITELIST_TESTS_H #include "../../../include/secp256k1_whitelist.h" +#include "../../unit_test.h" static void test_whitelist_end_to_end_internal(const unsigned char *summed_seckey, const unsigned char *online_seckey, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey, const size_t signer_i, const size_t n_keys) { unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; @@ -148,16 +149,20 @@ static void test_whitelist_bad_serialize(void) { CHECK(secp256k1_whitelist_signature_serialize(CTX, serialized, &serialized_len, &sig) == 0); } -static void run_whitelist_tests(void) { - int i; - test_whitelist_bad_parse(); - test_whitelist_bad_serialize(); - for (i = 0; i < COUNT; i++) { - test_whitelist_end_to_end(1, 1); - test_whitelist_end_to_end(10, 1); - test_whitelist_end_to_end(50, 1); - test_whitelist_end_to_end(SECP256K1_WHITELIST_MAX_N_KEYS, 0); - } +static void test_whitelist_end_to_end_all_internal(void) { + test_whitelist_end_to_end(1, 1); + test_whitelist_end_to_end(10, 1); + test_whitelist_end_to_end(50, 1); + test_whitelist_end_to_end(SECP256K1_WHITELIST_MAX_N_KEYS, 0); } +/* --- Test registry --- */ +REPEAT_TEST(test_whitelist_end_to_end_all) + +static const struct tf_test_entry tests_whitelist[] = { + CASE1(test_whitelist_bad_parse), + CASE1(test_whitelist_bad_serialize), + CASE1(test_whitelist_end_to_end_all), +}; + #endif diff --git a/src/tests.c b/src/tests.c index f7ef46fe..7c1a03f8 100644 --- a/src/tests.c +++ b/src/tests.c @@ -7982,6 +7982,31 @@ static const struct tf_test_module registry_modules[] = { #endif #ifdef ENABLE_MODULE_ELLSWIFT MAKE_TEST_MODULE(ellswift), +#endif + /* --- ZKP-SPECIFIC MODULES --- */ +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG + MAKE_TEST_MODULE(schnorrsig_halfagg), +#endif +#ifdef ENABLE_MODULE_BPPP + MAKE_TEST_MODULE(bppp), +#endif +#ifdef ENABLE_MODULE_GENERATOR + MAKE_TEST_MODULE(generator), +#endif +#ifdef ENABLE_MODULE_RANGEPROOF + MAKE_TEST_MODULE(rangeproof), +#endif +#ifdef ENABLE_MODULE_WHITELIST + MAKE_TEST_MODULE(whitelist), +#endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF + MAKE_TEST_MODULE(surjection), +#endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + MAKE_TEST_MODULE(ecdsa_adaptor), +#endif +#ifdef ENABLE_MODULE_ECDSA_S2C + MAKE_TEST_MODULE(ecdsa_s2c), #endif MAKE_TEST_MODULE(utils), }; From d380549e388709efeeaf29e116f41d27bb6f5ddf Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Wed, 25 Feb 2026 15:08:56 +0200 Subject: [PATCH 350/381] ecdsa_adaptor: optimize encrypt with batch affine conversion --- src/modules/ecdsa_adaptor/main_impl.h | 37 +++++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 25232b48..ee0b09a7 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -150,8 +150,8 @@ const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_e int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char *adaptor_sig162, unsigned char *seckey32, const secp256k1_pubkey *enckey, const unsigned char *msg32, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { secp256k1_scalar k; - secp256k1_gej rj, rpj; - secp256k1_ge r, rp; + secp256k1_ge nonce_pts[2]; + secp256k1_gej nonce_ptj[2]; secp256k1_ge enckey_ge; secp256k1_scalar dleq_proof_s; secp256k1_scalar dleq_proof_e; @@ -179,32 +179,35 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char noncefp = secp256k1_nonce_function_ecdsa_adaptor; } - ret &= secp256k1_pubkey_load(ctx, &enckey_ge, enckey); - ret &= secp256k1_eckey_pubkey_serialize(&enckey_ge, buf33, &size, 1); + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { + return 0; + } + + secp256k1_eckey_pubkey_serialize(&enckey_ge, buf33, &size, 1); ret &= !!noncefp(nonce32, msg32, seckey32, buf33, ecdsa_adaptor_algo, sizeof(ecdsa_adaptor_algo), ndata); secp256k1_scalar_set_b32(&k, nonce32, NULL); ret &= !secp256k1_scalar_is_zero(&k); secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); /* R' := k*G */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rpj, &k); - secp256k1_ge_set_gej(&rp, &rpj); - /* R = k*Y; */ - secp256k1_ecmult_const(&rj, &enckey_ge, &k); - secp256k1_ge_set_gej(&r, &rj); - /* We declassify the non-secret values rp and r to allow using them - * as branch points. */ - secp256k1_declassify(ctx, &rp, sizeof(rp)); - secp256k1_declassify(ctx, &r, sizeof(r)); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj[0], &k); + /* R := k*Y */ + secp256k1_ecmult_const(&nonce_ptj[1], &enckey_ge, &k); + + secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2); + + /* We declassify the non-secret nonce values to allow using them as branch points. */ + secp256k1_declassify(ctx, &nonce_pts[0], sizeof(nonce_pts[0])); + secp256k1_declassify(ctx, &nonce_pts[1], sizeof(nonce_pts[1])); /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ - ret &= secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &rp, &r, noncefp, ndata); + ret &= secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &nonce_pts[0], &nonce_pts[1], noncefp, ndata); ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey32); secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret); secp256k1_scalar_set_b32(&msg, msg32, NULL); - secp256k1_fe_normalize(&r.x); - secp256k1_fe_get_b32(buf33, &r.x); + secp256k1_fe_normalize(&nonce_pts[1].x); + secp256k1_fe_get_b32(buf33, &nonce_pts[1].x); secp256k1_scalar_set_b32(&sigr, buf33, NULL); ret &= !secp256k1_scalar_is_zero(&sigr); /* s' = k⁻¹(m + R.x * x) */ @@ -215,7 +218,7 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char ret &= !secp256k1_scalar_is_zero(&sp); /* return (R, R', s', dleq_proof) */ - ret &= secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); + ret &= secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &nonce_pts[1], &nonce_pts[0], &sp, &dleq_proof_e, &dleq_proof_s); secp256k1_memczero(adaptor_sig162, 162, !ret); secp256k1_scalar_clear(&n); From 3f7a9429adccf16e192cd792c1f92b42ad83aaea Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Wed, 25 Feb 2026 16:14:32 +0200 Subject: [PATCH 351/381] ecdsa_adaptor: batch affine conversion in dleq_pair and dleq_prove --- src/modules/ecdsa_adaptor/dleq_impl.h | 62 ++++++++++++++------------ src/modules/ecdsa_adaptor/main_impl.h | 9 +++- src/modules/ecdsa_adaptor/tests_impl.h | 5 ++- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index 53b3003e..11d2d667 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -77,29 +77,27 @@ static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *gen2, se secp256k1_scalar_set_b32(e, buf, NULL); } -/* P1 = x*G, P2 = x*Y */ -static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *p1, secp256k1_ge *p2, const secp256k1_scalar *sk, const secp256k1_ge *gen2) { - secp256k1_gej p1j, p2j; +/* p[0] = x*G, p[1] = x*Y */ +static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *p, const secp256k1_scalar *sk, const secp256k1_ge *gen2) { + secp256k1_gej pj[2]; - secp256k1_ecmult_gen(ecmult_gen_ctx, &p1j, sk); - secp256k1_ge_set_gej(p1, &p1j); - secp256k1_ecmult_const(&p2j, gen2, sk); - secp256k1_ge_set_gej(p2, &p2j); + secp256k1_ecmult_gen(ecmult_gen_ctx, &pj[0], sk); + secp256k1_ecmult_const(&pj[1], gen2, sk); + secp256k1_ge_set_all_gej(p, pj, 2); } /* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the * same as the discrete logarithm of P2 to the base Y */ static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *gen2, secp256k1_ge *p1, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { - secp256k1_ge r1, r2; + secp256k1_ge r[2]; secp256k1_scalar k = { 0 }; unsigned char sk32[32]; unsigned char gen2_33[33]; unsigned char p1_33[33]; unsigned char p2_33[33]; - int ret = 1; size_t pubkey_size = 33; + int ret; - secp256k1_scalar_get_b32(sk32, sk); if (!secp256k1_eckey_pubkey_serialize(gen2, gen2_33, &pubkey_size, 1)) { return 0; } @@ -110,22 +108,30 @@ static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar * return 0; } - ret &= secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, noncefp, ndata); - /* R1 = k*G, R2 = k*Y */ - secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &r1, &r2, &k, gen2); - /* 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)); + secp256k1_scalar_get_b32(sk32, sk); - /* e = tagged hash(p1, gen2, p2, r1, r2) */ + ret = secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, noncefp, ndata); + secp256k1_declassify(ctx, &ret, sizeof(ret)); + if (!ret) { + secp256k1_memclear_explicit(sk32, sizeof(sk32)); + return 0; + } + /* R1 = k*G, R2 = k*Y */ + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, r, &k, gen2); + /* We declassify the non-secret values r[0] and r[1] to allow using them as + * branch points. */ + secp256k1_declassify(ctx, &r[0], sizeof(r[0])); + secp256k1_declassify(ctx, &r[1], sizeof(r[1])); + + /* e = tagged hash(p1, gen2, p2, r[0], r[1]) */ /* s = k + e * sk */ - secp256k1_dleq_challenge(e, gen2, &r1, &r2, p1, p2); + secp256k1_dleq_challenge(e, gen2, &r[0], &r[1], p1, p2); secp256k1_scalar_mul(s, e, sk); secp256k1_scalar_add(s, s, &k); secp256k1_scalar_clear(&k); - return ret; + secp256k1_memclear_explicit(sk32, sizeof(sk32)); + return 1; } static int secp256k1_dleq_verify(const secp256k1_scalar *s, const secp256k1_scalar *e, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2) { @@ -133,8 +139,8 @@ static int secp256k1_dleq_verify(const secp256k1_scalar *s, const secp256k1_scal secp256k1_scalar e_expected; secp256k1_gej gen2j; secp256k1_gej p1j, p2j; - secp256k1_gej r1j, r2j; - secp256k1_ge r1, r2; + secp256k1_gej rj[2]; + secp256k1_ge r[2]; secp256k1_gej tmpj; secp256k1_gej_set_ge(&p1j, p1); @@ -142,16 +148,16 @@ static int secp256k1_dleq_verify(const secp256k1_scalar *s, const secp256k1_scal secp256k1_scalar_negate(&e_neg, e); /* R1 = s*G - e*P1 */ - secp256k1_ecmult(&r1j, &p1j, &e_neg, s); + secp256k1_ecmult(&rj[0], &p1j, &e_neg, s); /* R2 = s*gen2 - e*P2 */ secp256k1_ecmult(&tmpj, &p2j, &e_neg, &secp256k1_scalar_zero); secp256k1_gej_set_ge(&gen2j, gen2); - secp256k1_ecmult(&r2j, &gen2j, s, &secp256k1_scalar_zero); - secp256k1_gej_add_var(&r2j, &r2j, &tmpj, NULL); + secp256k1_ecmult(&rj[1], &gen2j, s, &secp256k1_scalar_zero); + secp256k1_gej_add_var(&rj[1], &rj[1], &tmpj, NULL); - secp256k1_ge_set_gej(&r1, &r1j); - secp256k1_ge_set_gej(&r2, &r2j); - secp256k1_dleq_challenge(&e_expected, gen2, &r1, &r2, p1, p2); + secp256k1_ge_set_all_gej_var(r, rj, 2); + + secp256k1_dleq_challenge(&e_expected, gen2, &r[0], &r[1], p1, p2); secp256k1_scalar_add(&e_expected, &e_expected, &e_neg); return secp256k1_scalar_is_zero(&e_expected); diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index ee0b09a7..e97bb5a0 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -201,8 +201,12 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char secp256k1_declassify(ctx, &nonce_pts[1], sizeof(nonce_pts[1])); /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ - ret &= secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &nonce_pts[0], &nonce_pts[1], noncefp, ndata); - + if (!secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &nonce_pts[0], &nonce_pts[1], noncefp, ndata)) { + memset(adaptor_sig162, 0, 162); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); + secp256k1_scalar_clear(&k); + return 0; + } ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey32); secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret); secp256k1_scalar_set_b32(&msg, msg32, NULL); @@ -221,6 +225,7 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char ret &= secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &nonce_pts[1], &nonce_pts[0], &sp, &dleq_proof_e, &dleq_proof_s); secp256k1_memczero(adaptor_sig162, 162, !ret); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); secp256k1_scalar_clear(&n); secp256k1_scalar_clear(&k); secp256k1_scalar_clear(&sk); diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index e2293adc..5e41a9c0 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -31,6 +31,7 @@ static void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_byt static void dleq_tests_internal(void) { secp256k1_scalar s, e, sk, k; secp256k1_ge gen2, p1, p2; + secp256k1_ge p[2]; unsigned char *args[5]; unsigned char sk32[32]; unsigned char gen2_33[33]; @@ -42,7 +43,9 @@ static void dleq_tests_internal(void) { rand_point(&gen2); rand_scalar(&sk); - secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, &p1, &p2, &sk, &gen2); + secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, p, &sk, &gen2); + p1 = p[0]; + p2 = p[1]; CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &gen2, &p1, &p2, NULL, NULL) == 1); CHECK(secp256k1_dleq_verify(&s, &e, &p1, &gen2, &p2) == 1); From 6f7c112cc8b99cb1d8113bbbb5096bfafc58eb52 Mon Sep 17 00:00:00 2001 From: Mykyta Date: Wed, 25 Feb 2026 18:34:44 +0200 Subject: [PATCH 352/381] include: add description of range proofs focusing on the differences between the implementation and the CA paper --- include/secp256k1_rangeproof.h | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 03315277..8816bdcf 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -10,6 +10,41 @@ extern "C" { #include +/** This module implements a variant of Back-Maxwell range proofs as described + * in the Confidential Assets paper (https://blockstream.com/bitcoin17-final41.pdf). + * The construction is based on Borromean ring signatures. + * (https://nt4tn.net/papers/borromean_draft_0.01_34241bb.pdf) + * + * This implementation differs from the variant in the paper mainly in that it + * omits an optimization that saves one scalar per ring. This optimization complicates + * the protocol and security analysis, as it requires differentiating cases where + * the i-th bit v_i = 0 versus otherwise, and makes calculating response points R_i less + * straightforward. The implemented version uses Borromean ring signatures in + * an unmodified way. + * + * Another difference is that the implementation omits the last ring's commitment + * from the proof and recovered by the verifier by subtracting all other digit + * commitments from the total, reducing proof size by one group element. + * + * Furthermore, in the implementation every hash calculation includes a message + * m=SHA256(C||H||header||C_0||...||C_(n-2)||extra_commit), binding the commitment C, + * generator H, proof header, the n-1 explicit digit commitments, and any extra data. + * This prevents an attack that would compromise non-malleability. In the paper's + * version of the protocol, a prover could pick distinct indices i, j and a scalar y, + * and modify digit commitments in the original proof by setting C'_i = C_i + yG and + * C'_j = C_j - yG, obtaining a different valid proof for the same commitment and + * witness. + * + * In the current implementation, up to 3968 bytes of message data can be + * embedded and recovered within maximally-sized proofs. The implemented embedding + * method using the forged parts of ring signatures could also be applied to the + * construction in the paper, but is not mentioned there. Message embedding is used + * in Confidential Assets to transmit values and blinding factors of the corresponding + * commitments. This is possible because randomness is generated by seeding HMAC-DRBG + * with the shared ECDH key, allowing the receiver to rewind the proof using the same + * random values the sender used. + */ + /** Length of a message that can be embedded into a maximally-sized rangeproof * * It is not be possible to fit a message of this size into a non-maximally-sized From 2542b4345196fba49dec0d794c6b94f80f3f8275 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 2 Mar 2026 16:40:12 +0200 Subject: [PATCH 353/381] modules: Port bitcoin-core/secp256k1#1774 to zkp-specific code --- src/modules/bppp/bppp_util.h | 7 +++- src/modules/ecdsa_adaptor/dleq_impl.h | 20 +++------- src/modules/ecdsa_adaptor/main_impl.h | 49 ++++++++++--------------- src/modules/ecdsa_adaptor/tests_impl.h | 24 +++--------- src/modules/rangeproof/borromean_impl.h | 14 +++---- src/modules/whitelist/whitelist_impl.h | 15 +++----- src/secp256k1.c | 10 +---- 7 files changed, 46 insertions(+), 93 deletions(-) diff --git a/src/modules/bppp/bppp_util.h b/src/modules/bppp/bppp_util.h index 1debfb92..ec979c69 100644 --- a/src/modules/bppp/bppp_util.h +++ b/src/modules/bppp/bppp_util.h @@ -47,8 +47,11 @@ static int secp256k1_bppp_parse_one_of_points(secp256k1_ge *pt, const unsigned c /* Outputs a serialized point in compressed form. Returns 0 at point at infinity. */ static int secp256k1_bppp_serialize_pt(unsigned char *output, secp256k1_ge *lpt) { - size_t size; - return secp256k1_eckey_pubkey_serialize(lpt, output, &size, 1 /*compressed*/); + if (secp256k1_ge_is_infinity(lpt)) { + return 0; + } + secp256k1_eckey_pubkey_serialize33(lpt, output); + return 1; } /* little-endian encodes a uint64 */ diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index 11d2d667..60e63a34 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -20,16 +20,13 @@ static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { /* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ static const unsigned char dleq_algo[] = {'D','L','E','Q'}; -static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { +static void secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { unsigned char buf[33]; size_t size = 33; - if (!secp256k1_eckey_pubkey_serialize(p, buf, &size, 1)) { - return 0; - } + secp256k1_eckey_pubkey_serialize33(p, buf); secp256k1_sha256_write(sha, buf, size); - return 1; } static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, const unsigned char *gen2_33, const unsigned char *p1_33, const unsigned char *p2_33, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { @@ -95,18 +92,11 @@ static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar * unsigned char gen2_33[33]; unsigned char p1_33[33]; unsigned char p2_33[33]; - size_t pubkey_size = 33; int ret; - if (!secp256k1_eckey_pubkey_serialize(gen2, gen2_33, &pubkey_size, 1)) { - return 0; - } - if (!secp256k1_eckey_pubkey_serialize(p1, p1_33, &pubkey_size, 1)) { - return 0; - } - if (!secp256k1_eckey_pubkey_serialize(p2, p2_33, &pubkey_size, 1)) { - return 0; - } + secp256k1_eckey_pubkey_serialize33(gen2, gen2_33); + secp256k1_eckey_pubkey_serialize33(p1, p1_33); + secp256k1_eckey_pubkey_serialize33(p2, p2_33); secp256k1_scalar_get_b32(sk32, sk); diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index e97bb5a0..e43a4222 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -11,20 +11,12 @@ #include "dleq_impl.h" /* (R, R', s', dleq_proof) */ -static int secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { - size_t size = 33; - - if (!secp256k1_eckey_pubkey_serialize(r, adaptor_sig162, &size, 1)) { - return 0; - } - if (!secp256k1_eckey_pubkey_serialize(rp, &adaptor_sig162[33], &size, 1)) { - return 0; - } +static void secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { + secp256k1_eckey_pubkey_serialize33(r, adaptor_sig162); + secp256k1_eckey_pubkey_serialize33(rp, &adaptor_sig162[33]); secp256k1_scalar_get_b32(&adaptor_sig162[66], sp); secp256k1_scalar_get_b32(&adaptor_sig162[98], dleq_proof_e); secp256k1_scalar_get_b32(&adaptor_sig162[130], dleq_proof_s); - - return 1; } static int secp256k1_ecdsa_adaptor_sig_deserialize(secp256k1_ge *r, secp256k1_scalar *sigr, secp256k1_ge *rp, secp256k1_scalar *sp, secp256k1_scalar *dleq_proof_e, secp256k1_scalar *dleq_proof_s, const unsigned char *adaptor_sig162) { @@ -162,7 +154,6 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char secp256k1_scalar n; unsigned char nonce32[32] = { 0 }; unsigned char buf33[33]; - size_t size = 33; int ret = 1; VERIFY_CHECK(ctx != NULL); @@ -183,7 +174,7 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char return 0; } - secp256k1_eckey_pubkey_serialize(&enckey_ge, buf33, &size, 1); + secp256k1_eckey_pubkey_serialize33(&enckey_ge, buf33); ret &= !!noncefp(nonce32, msg32, seckey32, buf33, ecdsa_adaptor_algo, sizeof(ecdsa_adaptor_algo), ndata); secp256k1_scalar_set_b32(&k, nonce32, NULL); ret &= !secp256k1_scalar_is_zero(&k); @@ -222,7 +213,7 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char ret &= !secp256k1_scalar_is_zero(&sp); /* return (R, R', s', dleq_proof) */ - ret &= secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &nonce_pts[1], &nonce_pts[0], &sp, &dleq_proof_e, &dleq_proof_s); + secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &nonce_pts[1], &nonce_pts[0], &sp, &dleq_proof_e, &dleq_proof_s); secp256k1_memczero(adaptor_sig162, 162, !ret); secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); @@ -319,10 +310,10 @@ int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char secp256k1_scalar s, r; secp256k1_scalar deckey; secp256k1_ge enckey_expected_ge; + secp256k1_ge enckey_ge; secp256k1_gej enckey_expected_gej; unsigned char enckey33[33]; unsigned char enckey_expected33[33]; - size_t size = 33; int ret = 1; VERIFY_CHECK(ctx != NULL); @@ -349,23 +340,21 @@ int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char /* We declassify non-secret enckey_expected_ge to allow using it as a * branch point. */ secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge)); - if (!secp256k1_eckey_pubkey_serialize(&enckey_expected_ge, enckey_expected33, &size, 1)) { - /* Unreachable from tests (and other VERIFY builds) and therefore this - * branch should be ignored in test coverage analysis. - * - * Proof: - * eckey_pubkey_serialize fails <=> deckey = 0 - * deckey = 0 <=> s^-1 = 0 or sp = 0 - * case 1: s^-1 = 0 impossible by the definition of multiplicative - * inverse and because the scalar_inverse implementation - * VERIFY_CHECKs that the inputs are valid scalars. - * case 2: sp = 0 impossible because ecdsa_adaptor_sig_deserialize would have already failed - */ - return 0; - } - if (!secp256k1_ec_pubkey_serialize(ctx, enckey33, &size, enckey, SECP256K1_EC_COMPRESSED)) { + /* enckey_expected_ge cannot be infinity: + * + * Proof: + * enckey_expected_ge is infinity <=> deckey = 0 + * deckey = 0 <=> s^-1 = 0 or sp = 0 + * case 1: s^-1 = 0 impossible by the definition of multiplicative + * inverse and because the scalar_inverse implementation + * VERIFY_CHECKs that the inputs are valid scalars. + * case 2: sp = 0 impossible because ecdsa_adaptor_sig_deserialize would have already failed + */ + secp256k1_eckey_pubkey_serialize33(&enckey_expected_ge, enckey_expected33); + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { return 0; } + secp256k1_eckey_pubkey_serialize33(&enckey_ge, enckey33); if (secp256k1_memcmp_var(&enckey_expected33[1], &enckey33[1], 32) != 0) { return 0; } diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index 5e41a9c0..3abc8127 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -39,7 +39,6 @@ static void dleq_tests_internal(void) { unsigned char p2_33[33]; unsigned char aux_rand[32]; int i; - size_t pubkey_size = 33; rand_point(&gen2); rand_scalar(&sk); @@ -62,19 +61,12 @@ static void dleq_tests_internal(void) { CHECK(secp256k1_dleq_verify(&s, &e, &p1, &p_tmp, &p2) == 0); CHECK(secp256k1_dleq_verify(&s, &e, &p1, &gen2, &p_tmp) == 0); } - { - secp256k1_ge p_inf; - secp256k1_ge_set_infinity(&p_inf); - CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &p_inf, &p1, &p2, NULL, NULL) == 0); - CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &gen2, &p_inf, &p2, NULL, NULL) == 0); - CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &gen2, &p1, &p_inf, NULL, NULL) == 0); - } /* Nonce tests */ secp256k1_scalar_get_b32(sk32, &sk); - CHECK(secp256k1_eckey_pubkey_serialize(&gen2, gen2_33, &pubkey_size, 1)); - CHECK(secp256k1_eckey_pubkey_serialize(&p1, p1_33, &pubkey_size, 1)); - CHECK(secp256k1_eckey_pubkey_serialize(&p2, p2_33, &pubkey_size, 1)); + secp256k1_eckey_pubkey_serialize33(&gen2, gen2_33); + secp256k1_eckey_pubkey_serialize33(&p1, p1_33); + secp256k1_eckey_pubkey_serialize33(&p2, p2_33); CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); testrand_bytes_test(sk32, sizeof(sk32)); @@ -165,7 +157,7 @@ static void test_ecdsa_adaptor_spec_vectors_check_serialization(const unsigned c CHECK(expected == secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)); if (expected == 1) { - CHECK(secp256k1_ecdsa_adaptor_sig_serialize(buf, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 1); + secp256k1_ecdsa_adaptor_sig_serialize(buf, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); CHECK(secp256k1_memcmp_var(buf, adaptor_sig162, 162) == 0); } } @@ -896,18 +888,12 @@ static void adaptor_tests_internal(void) { secp256k1_scalar sigr; secp256k1_scalar sp; secp256k1_scalar dleq_proof_s, dleq_proof_e; - secp256k1_ge p_inf; unsigned char adaptor_sig_tmp[162]; CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); - CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 1); + secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)) == 0); - - /* Test adaptor_sig_serialize points at infinity */ - secp256k1_ge_set_infinity(&p_inf); - CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &p_inf, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 0); - CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &p_inf, &sp, &dleq_proof_e, &dleq_proof_s) == 0); } { /* Test adaptor_sig_deserialize */ diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 4906ce85..2fff3c28 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -60,7 +60,6 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e size_t i; size_t j; size_t count; - size_t size; int overflow; VERIFY_CHECK(e0 != NULL); VERIFY_CHECK(s != NULL); @@ -88,12 +87,12 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e } /* OPT: loop can be hoisted and split to use batch inversion across all the rings; this would make it much faster. */ secp256k1_ge_set_gej_var(&rge, &rgej); - secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); if (j != rsizes[i] - 1) { secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); secp256k1_scalar_set_b32(&ens, tmp, &overflow); } else { - secp256k1_sha256_write(&sha256_e0, tmp, size); + secp256k1_sha256_write(&sha256_e0, tmp, 33); } count++; } @@ -115,7 +114,6 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, size_t i; size_t j; size_t count; - size_t size; int overflow; VERIFY_CHECK(ecmult_gen_ctx != NULL); VERIFY_CHECK(e0 != NULL); @@ -136,7 +134,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, if (secp256k1_gej_is_infinity(&rgej)) { return 0; } - secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); for (j = secidx[i] + 1; j < rsizes[i]; j++) { secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j); secp256k1_scalar_set_b32(&ens, tmp, &overflow); @@ -152,9 +150,9 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, return 0; } secp256k1_ge_set_gej_var(&rge, &rgej); - secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); } - secp256k1_sha256_write(&sha256_e0, tmp, size); + secp256k1_sha256_write(&sha256_e0, tmp, 33); count += rsizes[i]; } secp256k1_sha256_write(&sha256_e0, m, mlen); @@ -174,7 +172,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, return 0; } secp256k1_ge_set_gej_var(&rge, &rgej); - secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + secp256k1_eckey_pubkey_serialize33(&rge, tmp); secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); secp256k1_scalar_set_b32(&ens, tmp, &overflow); if (overflow || secp256k1_scalar_is_zero(&ens)) { diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h index bb244907..edb90b4f 100644 --- a/src/modules/whitelist/whitelist_impl.h +++ b/src/modules/whitelist/whitelist_impl.h @@ -18,9 +18,10 @@ static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_g secp256k1_ge_set_gej(&ge, pubkey); secp256k1_sha256_initialize(&sha); - if (!secp256k1_eckey_pubkey_serialize(&ge, c, &size, 1)) { + if (secp256k1_ge_is_infinity(&ge)) { return 0; } + secp256k1_eckey_pubkey_serialize33(&ge, c); secp256k1_sha256_write(&sha, c, size); secp256k1_sha256_finalize(&sha, h); secp256k1_sha256_clear(&sha); @@ -95,9 +96,7 @@ static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* secp256k1_pubkey_load(ctx, &subkey_ge, sub_pubkey); /* commit to sub-key */ - if (!secp256k1_eckey_pubkey_serialize(&subkey_ge, c, &size, 1)) { - return 0; - } + secp256k1_eckey_pubkey_serialize33(&subkey_ge, c); secp256k1_sha256_write(&sha, c, size); for (i = 0; i < n_keys; i++) { secp256k1_ge offline_ge; @@ -106,14 +105,10 @@ static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* /* commit to fixed keys */ secp256k1_pubkey_load(ctx, &offline_ge, &offline_pubkeys[i]); - if (!secp256k1_eckey_pubkey_serialize(&offline_ge, c, &size, 1)) { - return 0; - } + secp256k1_eckey_pubkey_serialize33(&offline_ge, c); secp256k1_sha256_write(&sha, c, size); secp256k1_pubkey_load(ctx, &online_ge, &online_pubkeys[i]); - if (!secp256k1_eckey_pubkey_serialize(&online_ge, c, &size, 1)) { - return 0; - } + secp256k1_eckey_pubkey_serialize33(&online_ge, c); secp256k1_sha256_write(&sha, c, size); /* compute tweaked keys */ diff --git a/src/secp256k1.c b/src/secp256k1.c index 9245c126..69bc9d94 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -858,15 +858,7 @@ static void secp256k1_ge_serialize_ext(unsigned char *out33, secp256k1_ge* ge) { if (secp256k1_ge_is_infinity(ge)) { memset(out33, 0, 33); } else { - int ret; - size_t size = 33; - ret = secp256k1_eckey_pubkey_serialize(ge, out33, &size, 1); -#ifdef VERIFY - /* Serialize must succeed because the point is not at infinity */ - VERIFY_CHECK(ret && size == 33); -#else - (void) ret; -#endif + secp256k1_eckey_pubkey_serialize33(ge, out33); } } From d8e87e45f33c151949d7decfb9e8a0d02c1029a6 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 2 Mar 2026 16:42:19 +0200 Subject: [PATCH 354/381] unit_test: bump MAX_ARGS from 150 to 200 --- src/unit_test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unit_test.h b/src/unit_test.h index bf301e53..d3b0f153 100644 --- a/src/unit_test.h +++ b/src/unit_test.h @@ -13,7 +13,7 @@ /* Maximum number of command-line arguments. * Must be at least as large as the total number of tests * to allow specifying all tests individually. */ -#define MAX_ARGS 150 +#define MAX_ARGS 200 /* Maximum number of parallel jobs */ #define MAX_SUBPROCESSES 16 From d111d31293b479832c767946145702151897785d Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 2 Mar 2026 16:53:18 +0200 Subject: [PATCH 355/381] generator: Port bitcoin-core/secp256k1#1779 to zkp-specific code --- src/modules/generator/main_impl.h | 13 +++++++++++++ src/modules/generator/tests_impl.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index c3f1c027..9ce2defb 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -343,6 +343,9 @@ int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *bl VERIFY_CHECK(ctx != NULL); ARG_CHECK(blind_out != NULL); ARG_CHECK(blinds != NULL); + for (i = 0; i < n; i++) { + ARG_CHECK(blinds[i] != NULL); + } ARG_CHECK(npositive <= n); (void) ctx; secp256k1_scalar_set_int(&acc, 0); @@ -370,6 +373,12 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k VERIFY_CHECK(ctx != NULL); ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); + for (i = 0; i < pcnt; i++) { + ARG_CHECK(commits[i] != NULL); + } + for (i = 0; i < ncnt; i++) { + ARG_CHECK(ncommits[i] != NULL); + } (void) ctx; secp256k1_gej_set_infinity(&accj); for (i = 0; i < ncnt; i++) { @@ -394,6 +403,10 @@ int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, c ARG_CHECK(n_total == 0 || generator_blind != NULL); ARG_CHECK(n_total == 0 || blinding_factor != NULL); ARG_CHECK(n_total > n_inputs); + for (i = 0; i < n_total; i++) { + ARG_CHECK(generator_blind[i] != NULL); + ARG_CHECK(blinding_factor[i] != NULL); + } (void) ctx; if (n_total == 0) { diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index c9f60c0f..14ec95dc 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -227,6 +227,13 @@ static void test_pedersen_api(void) { CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, NULL, &blind_ptr, &blind_out_ptr, 1, 0)); CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, NULL, &blind_out_ptr, 1, 0)); CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, NULL, 1, 0)); + /* check that NULL in array of generator_blind pointers is not allowed */ + blind_ptr = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 1, 0)); + blind_ptr = blind; + /* check that NULL in array of blinding_factor pointers is not allowed */ + blind_out_ptr = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, &blind_out_ptr, 1, 0)); } static void test_pedersen_internal(void) { @@ -264,6 +271,14 @@ static void test_pedersen_internal(void) { secp256k1_scalar_get_b32(&blinds[i * 32], &s); } CHECK(secp256k1_pedersen_blind_sum(CTX, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); + /* check that NULL in array of blind pointers is not allowed */ + for (i = 0; i < total - 1; i++) { + unsigned char blind_out[32]; + const unsigned char *original_ptr = bptr[i]; + bptr[i] = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_sum(CTX, blind_out, bptr, total - 1, inputs)); + bptr[i] = original_ptr; + } for (i = 0; i < total; i++) { unsigned char result[33]; secp256k1_pedersen_commitment parse; @@ -275,6 +290,20 @@ static void test_pedersen_internal(void) { } CHECK(secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); CHECK(secp256k1_pedersen_verify_tally(CTX, &cptr[inputs], outputs, cptr, inputs)); + /* check that NULL in array of commits pointers is not allowed */ + for (i = 0; i < inputs; i++) { + const secp256k1_pedersen_commitment *original_ptr = cptr[i]; + cptr[i] = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); + cptr[i] = original_ptr; + } + /* check that NULL in array of ncommits pointers is not allowed */ + for (i = 0; i < outputs; i++) { + const secp256k1_pedersen_commitment *original_ptr = cptr[inputs + i]; + cptr[inputs + i] = NULL; + CHECK_ILLEGAL(CTX, secp256k1_pedersen_verify_tally(CTX, cptr, inputs, &cptr[inputs], outputs)); + cptr[inputs + i] = original_ptr; + } if (inputs > 0 && values[0] > 0) { CHECK(!secp256k1_pedersen_verify_tally(CTX, cptr, inputs - 1, &cptr[inputs], outputs)); } From fe48cc9fa5d99a7d046e86a96c5dd270e1b7061c Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 2 Mar 2026 16:58:33 +0200 Subject: [PATCH 356/381] generator: Port bitcoin-core/secp256k1#1764 to zkp-specific code --- src/modules/generator/main_impl.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 9ce2defb..c58d66fe 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -38,12 +38,13 @@ const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_intern static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { + secp256k1_fe x, y; int succeed; - succeed = secp256k1_fe_set_b32_limit(&ge->x, &gen->data[0]); + succeed = secp256k1_fe_set_b32_limit(&x, &gen->data[0]); VERIFY_CHECK(succeed != 0); - succeed = secp256k1_fe_set_b32_limit(&ge->y, &gen->data[32]); + succeed = secp256k1_fe_set_b32_limit(&y, &gen->data[32]); VERIFY_CHECK(succeed != 0); - ge->infinity = 0; + secp256k1_ge_set_xy(ge, &x, &y); (void) succeed; } From dc0bda5731c756a66786eccec96d9dc419b62d0f Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 2 Mar 2026 17:15:31 +0200 Subject: [PATCH 357/381] bench: Port bitcoin-core/secp256k1#1796 to zkp-specific code --- src/bench_bppp.c | 3 +++ src/bench_generator.c | 3 +++ src/bench_rangeproof.c | 3 +++ src/bench_whitelist.c | 3 +++ 4 files changed, 12 insertions(+) diff --git a/src/bench_bppp.c b/src/bench_bppp.c index b74aec6c..63ad8cea 100644 --- a/src/bench_bppp.c +++ b/src/bench_bppp.c @@ -29,6 +29,9 @@ static void bench_bppp(void* arg, int iters) { int main(void) { bench_bppp_data data; int iters = get_iters(32); + if (iters == 0) { + return EXIT_FAILURE; + } data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); diff --git a/src/bench_generator.c b/src/bench_generator.c index ecccecb7..93af6129 100644 --- a/src/bench_generator.c +++ b/src/bench_generator.c @@ -50,6 +50,9 @@ static void bench_generator_generate_blinded(void* arg, int iters) { int main(void) { bench_generator_t data; int iters = get_iters(20000); + if (iters == 0) { + return EXIT_FAILURE; + } data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 76893456..add1e1e1 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -58,6 +58,9 @@ int main(void) { data.min_bits = 32; iters = data.min_bits*get_iters(32); + if (iters == 0) { + return EXIT_FAILURE; + } run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, iters); diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c index 6cbe6fd6..f02f1e86 100644 --- a/src/bench_whitelist.c +++ b/src/bench_whitelist.c @@ -69,6 +69,9 @@ int main(void) { size_t n_keys = 30; secp256k1_scalar ssub; int iters = get_iters(5); + if (iters == 0) { + return EXIT_FAILURE; + } data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); From 126501f58b41f2374d6a75a183f01d14a2d548dc Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Tue, 3 Mar 2026 15:16:04 +0200 Subject: [PATCH 358/381] modules: Port bitcoin-core/secp256k1#1815 to zkp-specific code --- src/modules/bppp/main_impl.h | 8 ++++---- src/modules/bppp/tests_impl.h | 4 ++-- src/modules/rangeproof/tests_impl.h | 4 ++-- src/modules/surjection/main_impl.h | 2 +- src/modules/whitelist/tests_impl.h | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/modules/bppp/main_impl.h b/src/modules/bppp/main_impl.h index 49a09447..e8e9e30a 100644 --- a/src/modules/bppp/main_impl.h +++ b/src/modules/bppp/main_impl.h @@ -23,11 +23,11 @@ secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_cont VERIFY_CHECK(ctx != NULL); - ret = (secp256k1_bppp_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + ret = checked_malloc(&ctx->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; } - ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + ret->gens = checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); if (ret->gens == NULL) { free(ret); return NULL; @@ -60,12 +60,12 @@ secp256k1_bppp_generators* secp256k1_bppp_generators_parse(const secp256k1_conte return NULL; } - ret = (secp256k1_bppp_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + ret = checked_malloc(&ctx->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; } ret->n = n; - ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + ret->gens = checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); if (ret->gens == NULL) { free(ret); return NULL; diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 85d1c1ca..dda9a8c0 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -514,12 +514,12 @@ secp256k1_bppp_generators* bppp_generators_parse_regular(const unsigned char* da return NULL; } - ret = (secp256k1_bppp_generators *)checked_malloc(&CTX->error_callback, sizeof(*ret)); + ret = checked_malloc(&CTX->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; } ret->n = n; - ret->gens = (secp256k1_ge*)checked_malloc(&CTX->error_callback, n * sizeof(*ret->gens)); + ret->gens = checked_malloc(&CTX->error_callback, n * sizeof(*ret->gens)); if (ret->gens == NULL) { free(ret); return NULL; diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 74d9f3fd..7538558e 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -544,8 +544,8 @@ static void test_multiple_generators(void) { secp256k1_scalar_get_b32(generator_seed, &s); /* Create all the needed generators */ for (i = 0; i < n_generators; i++) { - generator_blind[i] = (unsigned char*) malloc(32); - pedersen_blind[i] = (unsigned char*) malloc(32); + generator_blind[i] = malloc(32); + pedersen_blind[i] = malloc(32); testutil_random_scalar_order(&s); secp256k1_scalar_get_b32(generator_blind[i], &s); diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 1d35219a..2bd2c2cd 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -182,7 +182,7 @@ int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, ARG_CHECK(proof_out_p != NULL); *proof_out_p = 0; - proof = (secp256k1_surjectionproof*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_surjectionproof)); + proof = checked_malloc(&ctx->error_callback, sizeof(secp256k1_surjectionproof)); if (proof != NULL) { ret = secp256k1_surjectionproof_initialize(ctx, proof, input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, fixed_output_tag, n_max_iterations, random_seed32); if (ret) { diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 9cbb8287..dad8a479 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -43,10 +43,10 @@ static void test_whitelist_end_to_end_internal(const unsigned char *summed_secke } static void test_whitelist_end_to_end(const size_t n_keys, int test_all_keys) { - unsigned char **online_seckey = (unsigned char **) malloc(n_keys * sizeof(*online_seckey)); - unsigned char **summed_seckey = (unsigned char **) malloc(n_keys * sizeof(*summed_seckey)); - secp256k1_pubkey *online_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*online_pubkeys)); - secp256k1_pubkey *offline_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*offline_pubkeys)); + unsigned char **online_seckey = malloc(n_keys * sizeof(*online_seckey)); + unsigned char **summed_seckey = malloc(n_keys * sizeof(*summed_seckey)); + secp256k1_pubkey *online_pubkeys = malloc(n_keys * sizeof(*online_pubkeys)); + secp256k1_pubkey *offline_pubkeys = malloc(n_keys * sizeof(*offline_pubkeys)); secp256k1_scalar ssub; unsigned char csub[32]; @@ -63,8 +63,8 @@ static void test_whitelist_end_to_end(const size_t n_keys, int test_all_keys) { for (i = 0; i < n_keys; i++) { secp256k1_scalar son, soff; - online_seckey[i] = (unsigned char *) malloc(32); - summed_seckey[i] = (unsigned char *) malloc(32); + online_seckey[i] = malloc(32); + summed_seckey[i] = malloc(32); /* Create two keys */ testutil_random_scalar_order_test(&son); From 48cbd78dfc624afd45febf5cd0f15a7485655716 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Tue, 3 Mar 2026 15:42:49 +0200 Subject: [PATCH 359/381] modules: Port bitcoin-core/secp256k1#1825 to zkp-specific code --- src/modules/bppp/bppp_transcript_impl.h | 16 ++++------- src/modules/ecdsa_adaptor/dleq_impl.h | 16 ++++------- src/modules/ecdsa_adaptor/main_impl.h | 32 +++++++--------------- src/modules/ecdsa_s2c/main_impl.h | 32 +++++++--------------- src/modules/schnorrsig_halfagg/main_impl.h | 16 ++++------- 5 files changed, 35 insertions(+), 77 deletions(-) diff --git a/src/modules/bppp/bppp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h index d53e9023..5e212231 100644 --- a/src/modules/bppp/bppp_transcript_impl.h +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -14,17 +14,11 @@ * SHA256 to SHA256("Bulletproofs_pp/v0/commitment")||SHA256("Bulletproofs_pp/v0/commitment"). */ static void secp256k1_bppp_sha256_tagged_commitment_init(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0x52fc8185ul; - sha->s[1] = 0x0e7debf0ul; - sha->s[2] = 0xb0967270ul; - sha->s[3] = 0x6f5abfe1ul; - sha->s[4] = 0x822bdec0ul; - sha->s[5] = 0x36db8beful; - sha->s[6] = 0x03d9e1f1ul; - sha->s[7] = 0x8a5cef6ful; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0x52fc8185ul, 0x0e7debf0ul, 0xb0967270ul, 0x6f5abfe1ul, + 0x822bdec0ul, 0x36db8beful, 0x03d9e1f1ul, 0x8a5cef6ful + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } /* Obtain a challenge scalar from the current transcript.*/ diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index 60e63a34..63642dc8 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -4,17 +4,11 @@ /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("DLEQ")||SHA256("DLEQ"). */ static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0x8cc4beacul; - sha->s[1] = 0x2e011f3ful; - sha->s[2] = 0x355c75fbul; - sha->s[3] = 0x3ba6a2c5ul; - sha->s[4] = 0xe96f3aeful; - sha->s[5] = 0x180530fdul; - sha->s[6] = 0x94582499ul; - sha->s[7] = 0x577fd564ul; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0x8cc4beacul, 0x2e011f3ful, 0x355c75fbul, 0x3ba6a2c5ul, + 0xe96f3aeful, 0x180530fdul, 0x94582499ul, 0x577fd564ul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } /* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index e43a4222..ea10207f 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -60,33 +60,21 @@ static int secp256k1_ecdsa_adaptor_sig_deserialize(secp256k1_ge *r, secp256k1_sc /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0x791dae43ul; - sha->s[1] = 0xe52d3b44ul; - sha->s[2] = 0x37f9edeaul; - sha->s[3] = 0x9bfd2ab1ul; - sha->s[4] = 0xcfb0f44dul; - sha->s[5] = 0xccf1d880ul; - sha->s[6] = 0xd18f2c13ul; - sha->s[7] = 0xa37b9024ul; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0x791dae43ul, 0xe52d3b44ul, 0x37f9edeaul, 0x9bfd2ab1ul, + 0xcfb0f44dul, 0xccf1d880ul, 0xd18f2c13ul, 0xa37b9024ul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("ECDSAadaptor/aux")||SHA256("ECDSAadaptor/aux"). */ static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0xd14c7bd9ul; - sha->s[1] = 0x095d35e6ul; - sha->s[2] = 0xb8490a88ul; - sha->s[3] = 0xfb00ef74ul; - sha->s[4] = 0x0baa488ful; - sha->s[5] = 0x69366693ul; - sha->s[6] = 0x1c81c5baul; - sha->s[7] = 0xc33b296aul; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0xd14c7bd9ul, 0x095d35e6ul, 0xb8490a88ul, 0xfb00ef74ul, + 0x0baa488ful, 0x69366693ul, 0x1c81c5baul, 0xc33b296aul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } /* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 59a4cdfa..cdc54737 100644 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -36,33 +36,21 @@ int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("s2c/ecdsa/point")||SHA256("s2c/ecdsa/point"). */ static void secp256k1_s2c_ecdsa_point_sha256_tagged(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0xa9b21c7bul; - sha->s[1] = 0x358c3e3eul; - sha->s[2] = 0x0b6863d1ul; - sha->s[3] = 0xc62b2035ul; - sha->s[4] = 0xb44b40ceul; - sha->s[5] = 0x254a8912ul; - sha->s[6] = 0x0f85d0d4ul; - sha->s[7] = 0x8a5bf91cul; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0xa9b21c7bul, 0x358c3e3eul, 0x0b6863d1ul, 0xc62b2035ul, + 0xb44b40ceul, 0x254a8912ul, 0x0f85d0d4ul, 0x8a5bf91cul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("s2c/ecdsa/data")||SHA256("s2c/ecdsa/data"). */ static void secp256k1_s2c_ecdsa_data_sha256_tagged(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0xfeefd675ul; - sha->s[1] = 0x73166c99ul; - sha->s[2] = 0xe2309cb8ul; - sha->s[3] = 0x6d458113ul; - sha->s[4] = 0x01d3a512ul; - sha->s[5] = 0x00e18112ul; - sha->s[6] = 0x37ee0874ul; - sha->s[7] = 0x421fc55ful; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0xfeefd675ul, 0x73166c99ul, 0xe2309cb8ul, 0x6d458113ul, + 0x01d3a512ul, 0x00e18112ul, 0x37ee0874ul, 0x421fc55ful + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* signature, secp256k1_ecdsa_s2c_opening* s2c_opening, const unsigned char diff --git a/src/modules/schnorrsig_halfagg/main_impl.h b/src/modules/schnorrsig_halfagg/main_impl.h index af612195..5d424a38 100644 --- a/src/modules/schnorrsig_halfagg/main_impl.h +++ b/src/modules/schnorrsig_halfagg/main_impl.h @@ -9,17 +9,11 @@ /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("HalfAgg/randomizer")||SHA256("HalfAgg/randomizer"). */ static void secp256k1_schnorrsig_sha256_tagged_aggregation(secp256k1_sha256 *sha) { - secp256k1_sha256_initialize(sha); - sha->s[0] = 0xd11f5532ul; - sha->s[1] = 0xfa57f70ful; - sha->s[2] = 0x5db0d728ul; - sha->s[3] = 0xf806ffe1ul; - sha->s[4] = 0x1d4db069ul; - sha->s[5] = 0xb4d587e1ul; - sha->s[6] = 0x50451c2aul; - sha->s[7] = 0x10fb63e9ul; - - sha->bytes = 64; + static const uint32_t midstate[8] = { + 0xd11f5532ul, 0xfa57f70ful, 0x5db0d728ul, 0xf806ffe1ul, + 0x1d4db069ul, 0xb4d587e1ul, 0x50451c2aul, 0x10fb63e9ul + }; + secp256k1_sha256_initialize_midstate(sha, 64, midstate); } int secp256k1_schnorrsig_inc_aggregate(const secp256k1_context *ctx, unsigned char *aggsig, size_t *aggsig_len, const secp256k1_xonly_pubkey *all_pubkeys, const unsigned char *all_msgs32, const unsigned char *new_sigs64, size_t n_before, size_t n_new) { From 799a27c81361221c996d442551191a4aff940f5e Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Tue, 3 Mar 2026 17:33:57 +0200 Subject: [PATCH 360/381] ecdsa_adaptor: Check for infinity in secp256k1_dleq_verify --- src/modules/ecdsa_adaptor/dleq_impl.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index 60e63a34..1d1548c4 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -145,6 +145,10 @@ static int secp256k1_dleq_verify(const secp256k1_scalar *s, const secp256k1_scal secp256k1_ecmult(&rj[1], &gen2j, s, &secp256k1_scalar_zero); secp256k1_gej_add_var(&rj[1], &rj[1], &tmpj, NULL); + if (secp256k1_gej_is_infinity(&rj[0]) || secp256k1_gej_is_infinity(&rj[1])) { + return 0; + } + secp256k1_ge_set_all_gej_var(r, rj, 2); secp256k1_dleq_challenge(&e_expected, gen2, &r[0], &r[1], p1, p2); From a4af91d5b9b31b802683ee8432859afd0b52827e Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 09:16:36 +0100 Subject: [PATCH 361/381] ecdsa_adaptor: Add test case for R1==infinity in DLEQ proof --- src/modules/ecdsa_adaptor/tests_impl.h | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index 3abc8127..c9bf0dac 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -1114,6 +1114,62 @@ static void multi_hop_lock_tests_internal(void) { CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0); } +static void adaptor_test_issue335(void) { + /* Inputs that will trigger R1==infinity in secp256k1_dleq_verify. */ + unsigned char adaptor_sig[162] = { + 0x03, 0x63, 0x3D, 0x56, 0xAB, 0xEE, 0x6F, 0x36, 0xE6, 0x07, 0xC6, 0x04, + 0x2C, 0x68, 0xB4, 0x09, 0xBE, 0x4F, 0x3D, 0x56, 0x3A, 0x51, 0x7B, 0xCA, + 0x95, 0xE6, 0xD9, 0x48, 0x1E, 0x95, 0xD0, 0xD6, 0xC6, 0x03, 0x91, 0x66, + 0xC2, 0x89, 0xB9, 0xF9, 0x05, 0xE5, 0x5F, 0x9E, 0x3D, 0xF9, 0xF6, 0x9D, + 0x7F, 0x35, 0x6B, 0x4A, 0x22, 0x09, 0x5F, 0x89, 0x4F, 0x47, 0x15, 0x71, + 0x4A, 0xA4, 0xB5, 0x66, 0x06, 0xAF, 0x84, 0x40, 0xB2, 0x83, 0x34, 0xF6, + 0x74, 0x18, 0xD8, 0x3D, 0x5C, 0xDC, 0x14, 0x0A, 0xAB, 0x22, 0x2B, 0x19, + 0x15, 0x13, 0xC3, 0x5D, 0x9C, 0xBC, 0x6D, 0x89, 0x1C, 0xB5, 0x38, 0x74, + 0xB0, 0xCE, 0x5F, 0x34, 0xD7, 0xA0, 0xA9, 0x89, 0x7A, 0x19, 0x45, 0x77, + 0xBD, 0x5F, 0x0F, 0x31, 0xD8, 0x3B, 0x50, 0xC6, 0x2A, 0x4D, 0xCF, 0x4D, + 0xCB, 0x91, 0x71, 0x8C, 0x66, 0xAE, 0xB8, 0xE2, 0x1A, 0x01, 0x65, 0x05, + 0x2D, 0x93, 0x73, 0x97, 0xB7, 0x66, 0xC4, 0xEB, 0x23, 0x8D, 0x3B, 0x55, + 0xA2, 0x3D, 0xF8, 0x8E, 0x56, 0x84, 0x87, 0x10, 0x76, 0x18, 0xC2, 0xE8, + 0x35, 0xF9, 0x4E, 0x2A, 0x29, 0xB2 + }; + unsigned char msg[32] = { + 0x38, 0x9C, 0x43, 0x7B, 0x37, 0xBB, 0x6F, 0x74, 0x09, 0x3D, 0x69, + 0x3E, 0x3D, 0x9B, 0x4F, 0xC7, 0x9D, 0xDF, 0xA9, 0x33, 0x39, 0x8C, + 0x90, 0x03, 0x95, 0x2D, 0x67, 0xCD, 0xD9, 0x99, 0xDC, 0x55 + }; + unsigned char deckey[32] = { + 0x4A, 0x0B, 0x45, 0xA7, 0x4F, 0xBF, 0x49, 0xC3, 0x4B, 0x7C, 0xE0, + 0x8E, 0x34, 0x89, 0xFB, 0xEA, 0xD5, 0x41, 0xA1, 0x2E, 0xBE, 0x13, + 0x3F, 0xD6, 0x8E, 0x24, 0x86, 0x60, 0x1B, 0x19, 0xC1, 0xB5 + }; + unsigned char seckey[32] = { + 0x12, 0xDB, 0x27, 0x33, 0x51, 0x3D, 0xD9, 0xDF, 0x6A, 0x3C, 0x5A, + 0xEC, 0x3C, 0xA9, 0xF5, 0xDA, 0xA7, 0x3E, 0xB4, 0x61, 0xC8, 0xBB, + 0x12, 0xB7, 0xD4, 0xAA, 0xF5, 0x9A, 0xE9, 0xE5, 0x8B, 0xB7 + }; + secp256k1_pubkey pubkey; + secp256k1_pubkey enckey; + + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, seckey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &enckey, deckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig, &pubkey, msg, &enckey) == 0); + + /* This explains how the inputs were obtained. */ + { + unsigned char adaptor_sig_tmp[sizeof(adaptor_sig)]; + /* Since the same nonce function with different algo arguments is used + * both for the adaptor sig secret nonce and the dleq secret nonce, + * but ecdsa_adaptor_nonce_function_overflowing ignores the algo arg + * (in violation of the documented API contract), the resulting secret + * nonces will be the same. */ + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig_tmp, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_overflowing, NULL) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(CTX, adaptor_sig_tmp, &pubkey, msg, &enckey) == 1); + /* Increment the last least significant bit of e. */ + adaptor_sig_tmp[129] = 0x01; + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig)) == 0); + } +} + /* --- Test registry --- */ REPEAT_TEST(dleq_tests) REPEAT_TEST(adaptor_tests) @@ -1126,6 +1182,7 @@ static const struct tf_test_entry tests_ecdsa_adaptor[] = { CASE1(dleq_tests), CASE1(adaptor_tests), CASE1(multi_hop_lock_tests), + CASE1(adaptor_test_issue335), }; #endif /* SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H */ From dd8db2ea2b37b4b20e2249b1e47dea271146f0c8 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 09:34:07 +0100 Subject: [PATCH 362/381] ecdsa_adaptor: Run tests with default and overflowing nonce function --- src/modules/ecdsa_adaptor/tests_impl.h | 39 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index c9bf0dac..6176faec 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -803,6 +803,7 @@ static void test_ecdsa_adaptor_api(void) { unsigned char msg[32]; unsigned char asig[162]; unsigned char deckey[32]; + unsigned char zeros162[162] = { 0 }; /** setup **/ testrand256(sk); @@ -821,6 +822,14 @@ static void test_ecdsa_adaptor_api(void) { CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, NULL, msg, NULL, NULL)); CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &zero_pk, msg, NULL, NULL)); + /* Test bad nonce functions */ + memset(asig, 1, sizeof(asig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, ecdsa_adaptor_nonce_function_failing, NULL) == 0); + CHECK(secp256k1_memcmp_var(asig, zeros162, sizeof(asig)) == 0); + memset(asig, 1, sizeof(asig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, ecdsa_adaptor_nonce_function_0, NULL) == 0); + CHECK(secp256k1_memcmp_var(asig, zeros162, sizeof(asig)) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, asig, sk, &enckey, msg, NULL, NULL) == 1); CHECK(secp256k1_ecdsa_adaptor_verify(CTX, asig, &pubkey, msg, &enckey) == 1); CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_verify(CTX, NULL, &pubkey, msg, &enckey)); @@ -846,7 +855,7 @@ static void test_ecdsa_adaptor_api(void) { CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, asig, &zero_pk)); } -static void adaptor_tests_internal(void) { +static void adaptor_tests_internal_impl(secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void* ndata) { unsigned char seckey[32]; secp256k1_pubkey pubkey; unsigned char msg[32]; @@ -864,23 +873,15 @@ static void adaptor_tests_internal(void) { CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, seckey) == 1); CHECK(secp256k1_ec_pubkey_create(CTX, &enckey, deckey) == 1); - CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, seckey, &enckey, msg, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, seckey, &enckey, msg, noncefp, ndata) == 1); { + unsigned char adaptor_sig_tmp[162] = { 0 }; + /* Test overflowing seckey */ memset(big, 0xFF, 32); - CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, big, &enckey, msg, NULL, NULL) == 0); - CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); - - /* Test different nonce functions */ - memset(adaptor_sig, 1, sizeof(adaptor_sig)); - CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_failing, NULL) == 0); - CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); - memset(&adaptor_sig, 1, sizeof(adaptor_sig)); - CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_0, NULL) == 0); - CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); - CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_overflowing, NULL) == 1); - CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) != 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(CTX, adaptor_sig_tmp, big, &enckey, msg, NULL, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, zeros162, sizeof(adaptor_sig)) == 0); } { /* Test adaptor_sig_serialize roundtrip */ @@ -1040,6 +1041,16 @@ static void adaptor_tests_internal(void) { } } +static void adaptor_tests_internal(void) { + adaptor_tests_internal_impl(NULL, NULL); + /* Since the same nonce function with different algo arguments is used + * both for the adaptor sig secret nonce and the dleq secret nonce, + * but ecdsa_adaptor_nonce_function_overflowing ignores the algo arg + * (in violation of the documented API contract), the resulting secret + * nonces will be the same. */ + adaptor_tests_internal_impl(ecdsa_adaptor_nonce_function_overflowing, NULL); +} + static void multi_hop_lock_tests_internal(void) { unsigned char seckey_a[32]; unsigned char seckey_b[32]; From 7f1c5390c2313ca103a6be0dba0e058298dd6022 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 10:09:45 +0100 Subject: [PATCH 363/381] ecdsa_adaptor: Make files more self-contained --- src/modules/ecdsa_adaptor/dleq_impl.h | 10 ++++++++++ src/modules/ecdsa_adaptor/main_impl.h | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index f0f5ea21..a2f1b5ea 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -1,6 +1,16 @@ #ifndef SECP256K1_DLEQ_IMPL_H #define SECP256K1_DLEQ_IMPL_H +#include + +#include "../../../include/secp256k1_ecdsa_adaptor.h" + +#include "../../../src/eckey.h" +#include "../../../src/ecmult_const.h" +#include "../../../src/group.h" +#include "../../../src/hash.h" +#include "../../../src/scalar.h" + /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("DLEQ")||SHA256("DLEQ"). */ static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index ea10207f..2fdd301c 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -7,9 +7,18 @@ #ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H #define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H +#include + #include "../../../include/secp256k1_ecdsa_adaptor.h" #include "dleq_impl.h" +#include "../../../src/eckey.h" +#include "../../../src/ecmult.h" +#include "../../../src/ecmult_const.h" +#include "../../../src/group.h" +#include "../../../src/hash.h" +#include "../../../src/scalar.h" + /* (R, R', s', dleq_proof) */ static void secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { secp256k1_eckey_pubkey_serialize33(r, adaptor_sig162); From c0a26a9c1bd90ae46ededb223bad62d43614efc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:00:11 +0000 Subject: [PATCH 364/381] ci: enable surjectionproof module in CI configs Co-authored-by: real-or-random <1071625+real-or-random@users.noreply.github.com> --- .github/workflows/ci.yml | 43 +++++++++++++++++++++++++--------------- ci/ci.sh | 4 ++-- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c745ea8..d6ff8142 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ env: ECDSA_S2C: 'no' GENERATOR: 'no' RANGEPROOF: 'no' + SURJECTIONPROOF: 'no' WHITELIST: 'no' ECDSAADAPTOR: 'no' BPPP: 'no' @@ -103,14 +104,14 @@ jobs: matrix: configuration: - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } - - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} - env_vars: { WIDEMUL: 'int128' } - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' } - - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } - - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} - - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes'} + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } @@ -170,6 +171,7 @@ jobs: EXPERIMENTAL: 'yes' ECDSA_S2C: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' GENERATOR: 'yes' ECDSAADAPTOR: 'yes' @@ -206,6 +208,7 @@ jobs: EXPERIMENTAL: 'yes' ECDSA_S2C: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' GENERATOR: 'yes' ECDSAADAPTOR: 'yes' @@ -245,6 +248,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -274,6 +278,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -322,6 +327,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -375,6 +381,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -412,6 +419,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -466,6 +474,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -499,6 +508,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' @@ -535,15 +545,15 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } - BUILD: 'distcheck' steps: @@ -592,13 +602,13 @@ jobs: fail-fast: false matrix: env_vars: - - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } - - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - { WIDEMUL: 'int128', RECOVERY: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } - - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', EXPERIMENTAL: 'yes', ECDSA_S2C: 'yes', RANGEPROOF: 'yes', SURJECTIONPROOF: 'yes', WHITELIST: 'yes', GENERATOR: 'yes', ECDSAADAPTOR: 'yes', BPPP: 'yes', SCHNORRSIG_HALFAGG: 'yes', CPPFLAGS: '-DVERIFY' } - BUILD: 'distcheck' steps: @@ -715,6 +725,7 @@ jobs: ECDSA_S2C: 'yes' GENERATOR: 'yes' RANGEPROOF: 'yes' + SURJECTIONPROOF: 'yes' WHITELIST: 'yes' ECDSAADAPTOR: 'yes' BPPP: 'yes' diff --git a/ci/ci.sh b/ci/ci.sh index 15916b79..2185e789 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -14,7 +14,7 @@ print_environment() { for var in WERROR_CFLAGS MAKEFLAGS BUILD \ ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG MUSIG SCHNORRSIG_HALFAGG ELLSWIFT \ - ECDSA_S2C GENERATOR RANGEPROOF WHITELIST ECDSAADAPTOR BPPP \ + ECDSA_S2C GENERATOR RANGEPROOF SURJECTIONPROOF WHITELIST ECDSAADAPTOR BPPP \ SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS SYMBOL_CHECK \ EXAMPLES \ HOST WRAPPER_CMD \ @@ -65,7 +65,7 @@ fi --enable-module-extrakeys="$EXTRAKEYS" \ --enable-module-ecdsa-s2c="$ECDSA_S2C" \ --enable-module-bppp="$BPPP" \ - --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ + --enable-module-rangeproof="$RANGEPROOF" --enable-module-surjectionproof="$SURJECTIONPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-musig="$MUSIG" \ --enable-module-schnorrsig-halfagg="$SCHNORRSIG_HALFAGG" \ From 41a8a2a65b5f11835148e904652eacb014fd27e1 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 10:10:19 +0100 Subject: [PATCH 365/381] ecdsa_adaptor: Clarify identifiers --- src/modules/ecdsa_adaptor/dleq_impl.h | 2 ++ src/modules/ecdsa_adaptor/main_impl.h | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index a2f1b5ea..f183e50a 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -90,6 +90,8 @@ static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_c /* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the * same as the discrete logarithm of P2 to the base Y */ static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *gen2, secp256k1_ge *p1, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + /* Note: r[2] and k are local to the DLEQ proof, and they differ from the + * values with the same identifiers in main_impl.h. */ secp256k1_ge r[2]; secp256k1_scalar k = { 0 }; unsigned char sk32[32]; diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 2fdd301c..b18b86cc 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -139,9 +139,9 @@ const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_e int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char *adaptor_sig162, unsigned char *seckey32, const secp256k1_pubkey *enckey, const unsigned char *msg32, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { secp256k1_scalar k; - secp256k1_ge nonce_pts[2]; - secp256k1_gej nonce_ptj[2]; - secp256k1_ge enckey_ge; + secp256k1_ge r[2]; /* R, R' */ + secp256k1_gej rj[2]; /* R, R' */ + secp256k1_ge enckey_ge; /* Y */ secp256k1_scalar dleq_proof_s; secp256k1_scalar dleq_proof_e; secp256k1_scalar sk; @@ -177,19 +177,19 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char ret &= !secp256k1_scalar_is_zero(&k); secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); - /* R' := k*G */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj[0], &k); /* R := k*Y */ - secp256k1_ecmult_const(&nonce_ptj[1], &enckey_ge, &k); + secp256k1_ecmult_const(&rj[0], &enckey_ge, &k); + /* R' := k*G */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj[1], &k); - secp256k1_ge_set_all_gej(nonce_pts, nonce_ptj, 2); + secp256k1_ge_set_all_gej(r, rj, 2); /* We declassify the non-secret nonce values to allow using them as branch points. */ - secp256k1_declassify(ctx, &nonce_pts[0], sizeof(nonce_pts[0])); - secp256k1_declassify(ctx, &nonce_pts[1], sizeof(nonce_pts[1])); + secp256k1_declassify(ctx, &r[0], sizeof(r[0])); + secp256k1_declassify(ctx, &r[1], sizeof(r[1])); /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ - if (!secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &nonce_pts[0], &nonce_pts[1], noncefp, ndata)) { + if (!secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &r[1], &r[0], noncefp, ndata)) { memset(adaptor_sig162, 0, 162); secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); secp256k1_scalar_clear(&k); @@ -198,8 +198,8 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey32); secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret); secp256k1_scalar_set_b32(&msg, msg32, NULL); - secp256k1_fe_normalize(&nonce_pts[1].x); - secp256k1_fe_get_b32(buf33, &nonce_pts[1].x); + secp256k1_fe_normalize(&r[0].x); + secp256k1_fe_get_b32(buf33, &r[0].x); secp256k1_scalar_set_b32(&sigr, buf33, NULL); ret &= !secp256k1_scalar_is_zero(&sigr); /* s' = k⁻¹(m + R.x * x) */ @@ -210,7 +210,7 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char ret &= !secp256k1_scalar_is_zero(&sp); /* return (R, R', s', dleq_proof) */ - secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &nonce_pts[1], &nonce_pts[0], &sp, &dleq_proof_e, &dleq_proof_s); + secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &r[0], &r[1], &sp, &dleq_proof_e, &dleq_proof_s); secp256k1_memczero(adaptor_sig162, 162, !ret); secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); From a7d0f246d7ce529508b725421dd0178fdb354bd5 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 10:14:02 +0100 Subject: [PATCH 366/381] ecdsa_adaptor: Simplify code --- src/modules/ecdsa_adaptor/dleq_impl.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index f183e50a..cb097958 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -26,26 +26,23 @@ static const unsigned char dleq_algo[] = {'D','L','E','Q'}; static void secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { unsigned char buf[33]; - size_t size = 33; secp256k1_eckey_pubkey_serialize33(p, buf); - - secp256k1_sha256_write(sha, buf, size); + secp256k1_sha256_write(sha, buf, 33); } static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, const unsigned char *gen2_33, const unsigned char *p1_33, const unsigned char *p2_33, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { secp256k1_sha256 sha; unsigned char buf[32]; unsigned char nonce[32]; - size_t size = 33; if (noncefp == NULL) { noncefp = secp256k1_nonce_function_ecdsa_adaptor; } secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, p1_33, size); - secp256k1_sha256_write(&sha, p2_33, size); + secp256k1_sha256_write(&sha, p1_33, 33); + secp256k1_sha256_write(&sha, p2_33, 33); secp256k1_sha256_finalize(&sha, buf); secp256k1_sha256_clear(&sha); From ed985641f47caaf3836657b5d45e9ca1a7158b7c Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 13:28:23 +0100 Subject: [PATCH 367/381] ecdsa_adaptor: Make arg order in dleq_{prove,verify} consistent --- src/modules/ecdsa_adaptor/dleq_impl.h | 2 +- src/modules/ecdsa_adaptor/main_impl.h | 2 +- src/modules/ecdsa_adaptor/tests_impl.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h index cb097958..a117d69d 100644 --- a/src/modules/ecdsa_adaptor/dleq_impl.h +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -86,7 +86,7 @@ static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_c /* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the * same as the discrete logarithm of P2 to the base Y */ -static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *gen2, secp256k1_ge *p1, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { +static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { /* Note: r[2] and k are local to the DLEQ proof, and they differ from the * values with the same identifiers in main_impl.h. */ secp256k1_ge r[2]; diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index b18b86cc..0d590907 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -189,7 +189,7 @@ int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char secp256k1_declassify(ctx, &r[1], sizeof(r[1])); /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ - if (!secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &r[1], &r[0], noncefp, ndata)) { + if (!secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &r[1], &enckey_ge, &r[0], noncefp, ndata)) { memset(adaptor_sig162, 0, 162); secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); secp256k1_scalar_clear(&k); diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index 6176faec..09058997 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -45,7 +45,7 @@ static void dleq_tests_internal(void) { secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, p, &sk, &gen2); p1 = p[0]; p2 = p[1]; - CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &gen2, &p1, &p2, NULL, NULL) == 1); + CHECK(secp256k1_dleq_prove(CTX, &s, &e, &sk, &p1, &gen2, &p2, NULL, NULL) == 1); CHECK(secp256k1_dleq_verify(&s, &e, &p1, &gen2, &p2) == 1); { From 349a94b169357bd79102119d796ea557040b49e8 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 11:02:59 +0100 Subject: [PATCH 368/381] sync-upstream: Remove "select" mode and simplify I believe it was introduced to cherry-pick upstream PRs, but that simply doesn't work. Assume upstream is two PRs A and B ahead, and A has been merged before B. Then trying to cherry-picking B by merging the state of upstream's master after the merge-B commit won't do what we expect. In particular, the merge result will *include A's changes* because A had already been merged in upstream's master when B was merged. (One could think that merging the PR branch of B instead works, but this will yield the same result if B was rebased on master before it was merged.) The proper way to cherry-pick B is to create a PR that cherry-picks all commits that had been included in B. This could be done automatically, but the need to cherry-pick a PR is rare enough that we don't need tool support for it. In fact, because we want to keep cherry-picking at a minimum, there's a good chance that we'd anyway want to pick only a subset of the commits in a upstream PR, and that would need manual work anyway. --- contrib/sync-upstream.sh | 45 ++++++++++++---------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index c1ad8e0a..a0495f60 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -6,14 +6,11 @@ help() { echo "Sync merge commits from bitcoin-core/secp256k1 into secp256k1-zkp." echo echo "Usage:" - echo " $0 [-b ] range [end]" + echo " $0 [-b ] [end]" echo " Merges every merge commit present in upstream/master and missing in " echo " (default: master). If the optional [end] commit is provided, only merges" echo " up to and including [end]." echo - echo " $0 [-b ] select ... " - echo " Merges every selected merge commit into (default: master)." - echo echo "This tool creates a temporary branch and attempts to merge the upstream commits." echo "If there are merge conflicts, resolve them and run tests, then use the generated" echo "script contrib/gh-pr-create.sh to create the PR (requires the gh tool)." @@ -26,7 +23,7 @@ help() { echo "Listing upstream merge commits:" echo " To list merge commits in upstream/master that are missing from (oldest first):" echo " git log --oneline --merges \$(git merge-base upstream/master )..upstream/master | tac" - echo " Use these for [end] in 'range' or as arguments to 'select'." + echo " These are candidates for [end]." exit 1 } @@ -67,14 +64,18 @@ range() { esac } -# Process -b argument -while getopts "b:" opt; do +# Process -b and -h arguments +while getopts "b:h" opt; do case $opt in b) LOCAL_BRANCH=$OPTARG ;; - \?) - echo "Invalid option: -$OPTARG" >&2 + h) + help + ;; + *) + echo + help ;; esac done @@ -82,31 +83,11 @@ done # Shift off the processed options shift $((OPTIND -1)) -if [ "$#" -lt 1 ]; then - help -fi - -case $1 in - range) - shift - setup - range "$@" - REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH range $RANGEEND_COMMIT" - ;; - select) - shift - setup - COMMITS=$* - REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH select $@" - ;; - help) - help - ;; - *) - help -esac +setup +range "$@" TITLE="Upstream PRs" +REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH $RANGEEND_COMMIT" BODY="" for COMMIT in $COMMITS do From 656c7cc70449a2116de4e3475bce21809ee4a3f1 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 12:01:09 +0100 Subject: [PATCH 369/381] sync-upstream: Clarify that we merge a *single* upstream ref Roughly speaking, this changes (assuming 3 upstream PRs) git merge into git merge This is more intuitive. We're merging a single upstream revision, namely . The other two commits are simply parents of that one, i.e., they're included anyway, and git merge ignores them. (In fact, passing multiple refs looks like we're doing an octopus merge. It's just that git recognizes the fact that everything is included in the last ref anyway, and behaves as if only the last one had been passed.) This commit also makes some further clean ups and improvements. --- contrib/sync-upstream.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index a0495f60..1cb285bb 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -22,7 +22,7 @@ help() { echo echo "Listing upstream merge commits:" echo " To list merge commits in upstream/master that are missing from (oldest first):" - echo " git log --oneline --merges \$(git merge-base upstream/master )..upstream/master | tac" + echo " git log --oneline --topo-order --reverse --merges \$(git merge-base upstream/master )..upstream/master" echo " These are candidates for [end]." exit 1 } @@ -53,15 +53,7 @@ range() { if [ "$#" = 1 ]; then RANGEEND_COMMIT=$1 fi - - COMMITS=$(git --no-pager log --oneline --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") - COMMITS=$(echo "$COMMITS" | tac | awk '{ print $1 }' ORS=' ') - echo "Merging $COMMITS. Continue with y" - read -r yn - case $yn in - [Yy]* ) ;; - * ) exit 1;; - esac + COMMITS=$(git --no-pager log --pretty=format:%H --topo-order --reverse --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") } # Process -b and -h arguments @@ -94,6 +86,7 @@ do PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\).*'/'\2'/) TITLE="$TITLE $PRNUM," BODY=$(printf "%s\n%s" "$BODY" "$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\)'/'[bitcoin-core\/secp256k1#\2]'/)") + LAST_COMMIT="$COMMIT" done # Remove trailing "," TITLE=${TITLE%?} @@ -109,6 +102,13 @@ Tips: EOF ) +echo "Merging $TITLE. Continue with y" +read -r yn +case $yn in + [Yy]* ) ;; + * ) exit 1;; +esac + echo "-----------------------------------" echo "$TITLE" echo "-----------------------------------" @@ -140,4 +140,4 @@ EOT chmod +x "$FNAME" echo Run "$FNAME" after solving the merge conflicts -git merge --no-edit -m "Merge commits '$COMMITS' into temp-merge-$PRNUM" $COMMITS +git merge --no-edit -m "Merge upstream '${LAST_COMMIT:0:7}' into temp-merge-$PRNUM" "$LAST_COMMIT" From 229e1f127aeb3bf61d5301b1afc54bdb08858cdc Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 10:24:14 +0100 Subject: [PATCH 370/381] surjection: Fix read of uninitialized value in tests --- src/modules/surjection/tests_impl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 731e2fd4..6083deb8 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -438,6 +438,9 @@ static void test_bad_serialize(void) { size_t serialized_len; proof.n_inputs = 0; + memset(proof.used_inputs, 0, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8); + memset(proof.data, 0, 32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)); + serialized_len = 2 + 31; /* e0 is one byte too short */ CHECK(secp256k1_surjectionproof_serialize(CTX, serialized_proof, &serialized_len, &proof) == 0); From 78999f3a9ae732c7e80a15264160bf1addcdf708 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 21:12:04 +0100 Subject: [PATCH 371/381] surjection: Fix leading whitespace --- src/modules/surjection/main_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 2bd2c2cd..248efe14 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -25,7 +25,7 @@ static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) size_t i; for (i = 0; i < count; i++) { #ifdef HAVE_BUILTIN_POPCOUNT - ret += __builtin_popcount(data[i]); + ret += __builtin_popcount(data[i]); #else ret += !!(data[i] & 0x1); ret += !!(data[i] & 0x2); From 4359f050cc64c6e22280b5d8d22abda45f5d7de6 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 5 Mar 2026 21:26:12 +0100 Subject: [PATCH 372/381] surjection: Remove test that reads out of bounds --- src/modules/surjection/tests_impl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 6083deb8..d81ac624 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -89,7 +89,6 @@ static void test_surjectionproof_api(void) { CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key)); CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &proof, NULL, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key)); - CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); CHECK(secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, 0, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0); CHECK_ILLEGAL(CTX, secp256k1_surjectionproof_generate(CTX, &proof, ephemeral_input_tags, n_inputs, NULL, 0, input_blinding_key[0], output_blinding_key)); From 92e61ba95fecac82fd585f05c146788126bc1209 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 6 Mar 2026 12:39:49 +0200 Subject: [PATCH 373/381] build: Add missing schnorrsig_halfagg module configuration --- CMakeLists.txt | 2 ++ configure.ac | 3 +++ src/CMakeLists.txt | 9 +++++++++ 3 files changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce6a706e..c55cf10f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ option(SECP256K1_ENABLE_MODULE_WHITELIST "Enable key whitelist module." ON) option(SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR "Enable ecdsa adaptor signatures module." ON) option(SECP256K1_ENABLE_MODULE_ECDSA_S2C "Enable ECDSA sign-to-contract module." ON) option(SECP256K1_ENABLE_MODULE_BPPP "Enable Bulletproofs++ module." ON) +option(SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG "Enable schnorrsig half-aggregation module." ON) option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) @@ -302,6 +303,7 @@ message(" whitelist ........................... ${SECP256K1_ENABLE_MODULE_WHITE message(" ecdsa-s2c ........................... ${SECP256K1_ENABLE_MODULE_ECDSA_S2C}") message(" ecdsa-adaptor ....................... ${SECP256K1_ENABLE_MODULE_ECDSA_ADAPTOR}") message(" bppp ................................ ${SECP256K1_ENABLE_MODULE_BPPP}") +message(" schnorrsig-halfagg .................. ${SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG}") message("Parameters:") message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") message(" ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB") diff --git a/configure.ac b/configure.ac index 1d961b09..05a8c586 100644 --- a/configure.ac +++ b/configure.ac @@ -455,6 +455,9 @@ SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" # Processing must be done in a reverse topological sorting of the dependency graph # (dependent module first). if test x"$enable_module_schnorrsig_halfagg" = x"yes"; then + if test x"$enable_module_schnorrsig" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the schnorrsig_halfagg module.]) + fi SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG_HALFAGG=1" enable_module_schnorrsig=yes fi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 938d8040..ddd5d314 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,15 @@ set_property(TARGET secp256k1 PROPERTY PUBLIC_HEADER # Processing must be done in a topological sorting of the dependency graph # (dependent module first). +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG_HALFAGG) + if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG) + message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the schnorrsig_halfagg module.") + endif() + set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON) + add_compile_definitions(ENABLE_MODULE_SCHNORRSIG_HALFAGG=1) + set_property(TARGET secp256k1 APPEND PROPERTY PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig_halfagg.h) +endif() + if(SECP256K1_ENABLE_MODULE_BPPP) if(DEFINED SECP256K1_ENABLE_MODULE_GENERATOR AND NOT SECP256K1_ENABLE_MODULE_GENERATOR) message(FATAL_ERROR "Module dependency error: You have disabled the generator module explicitly, but it is required by the bppp module.") From 4681be065b0cd431c22881a964136c366736d3a1 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Thu, 12 Mar 2026 14:12:17 +0200 Subject: [PATCH 374/381] docs: Fix README module descriptions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f786bbe5..af62b832 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ A fork of [libsecp256k1](https://github.com/bitcoin-core/secp256k1) with support Added features: * Experimental module for ECDSA adaptor signatures. * Experimental module for ECDSA sign-to-contract. -* Experimental module for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)). -* Experimental module for Bulletproofs++ range proofs. +* Experimental modules for Confidential Assets (Pedersen commitments, range proofs, and [surjection proofs](src/modules/surjection/surjection.md)). * Experimental module for [address whitelisting](src/modules/whitelist/whitelist.md). +* Experimental module for Schnorr signature half-aggregation. Experimental features are made available for testing and review by the community. The APIs of these features should not be considered stable. From bb736825c1d25384a58401e03d975b616e2b25e7 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Mon, 9 Mar 2026 18:36:11 +0200 Subject: [PATCH 375/381] sync-upstream: Add automatic GitHub Actions sync script --- .github/workflows/sync.yml | 98 ++++++++++++++++++++++++++++++++++++++ contrib/sync-upstream.sh | 75 +++++++++-------------------- 2 files changed, 121 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/sync.yml diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 00000000..ea16d068 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,98 @@ +name: Upstream Sync + +on: + schedule: + - cron: '0 0 1 * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + sync-upstream: + runs-on: ubuntu-latest + env: + UPSTREAM: "https://github.com/bitcoin-core/secp256k1.git" + UPSTREAM_BRANCH: "master" + ORIGIN_BRANCH: "master" + MIN_UPSTREAM_MERGES: 1 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Fetch upstream & generate branch name & check for new commits + id: check_commits + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh repo set-default ${{ github.repository }} # Set the default repo to the origin repository + git remote add upstream ${{ env.UPSTREAM }} + git fetch upstream + + MERGES=$(git rev-list --count --merges HEAD..upstream/${{ env.UPSTREAM_BRANCH }}) + echo "Found $MERGES new merge commits in upstream." + + if [ "$MERGES" -lt ${{ env.MIN_UPSTREAM_MERGES }} ]; then + echo "Exiting." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + # Generate a sync branch name "sync-UPSTREAM_HEAD", where UPSTREAM_HEAD is a commit ID. + UPSTREAM_HEAD=$(git rev-parse --short upstream/${{ env.UPSTREAM_BRANCH }}) + SYNC_BRANCH="sync-$UPSTREAM_HEAD" + echo "Sync branch name: $SYNC_BRANCH" + echo "SYNC_BRANCH=$SYNC_BRANCH" >> "$GITHUB_ENV" + + # Check if the sync branch already exists in the origin repository + if git ls-remote --heads origin "$SYNC_BRANCH" | grep -q "$SYNC_BRANCH"; then + echo "Branch $SYNC_BRANCH already exists. Skipping the sync." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Generate PR metadata + id: branch_pr_metadata + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Call the sync script to generate PR title and body + ./contrib/sync-upstream.sh -b ${{ env.ORIGIN_BRANCH }} "$SYNC_BRANCH" + + - name: Push a sync branch + id: push_branch + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Creating sync branch: $SYNC_BRANCH" + git checkout upstream/${{ env.UPSTREAM_BRANCH }} + git checkout -b "$SYNC_BRANCH" + git push -u origin "$SYNC_BRANCH" + + - name: Create pull request + id: create_pr + if: steps.check_commits.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Execute the generated PR creation script + ./contrib/gh-pr-create.sh + + - name: Cleanup sync branch on failure + if: steps.push_branch.outcome == 'success' && steps.create_pr.outcome == 'failure' + run: | + echo "PR creation failed but branch was pushed. Deleting: $SYNC_BRANCH" + git push origin --delete "$SYNC_BRANCH" \ No newline at end of file diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index 1cb285bb..b3274040 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -6,53 +6,35 @@ help() { echo "Sync merge commits from bitcoin-core/secp256k1 into secp256k1-zkp." echo echo "Usage:" - echo " $0 [-b ] [end]" - echo " Merges every merge commit present in upstream/master and missing in " - echo " (default: master). If the optional [end] commit is provided, only merges" - echo " up to and including [end]." + echo " $0 [-b ] " + echo " Find every merge commit present in upstream/master and missing in (default: master)." echo - echo "This tool creates a temporary branch and attempts to merge the upstream commits." - echo "If there are merge conflicts, resolve them and run tests, then use the generated" - echo "script contrib/gh-pr-create.sh to create the PR (requires the gh tool)." + echo "This tool prepares the title and body for a sync PR" + echo "and generates a helper script contrib/gh-pr-create.sh." echo echo "Setup:" echo " Requires a remote named 'upstream' pointing to bitcoin-core/secp256k1." - echo " The script will fetch it automatically, and offer to create it if missing." - echo " To add manually: git remote add upstream git@github.com:bitcoin-core/secp256k1.git" echo echo "Listing upstream merge commits:" echo " To list merge commits in upstream/master that are missing from (oldest first):" echo " git log --oneline --topo-order --reverse --merges \$(git merge-base upstream/master )..upstream/master" - echo " These are candidates for [end]." exit 1 } REMOTE=upstream REMOTE_BRANCH="$REMOTE/master" LOCAL_BRANCH="master" -# Makes sure you have a remote "upstream" that is up-to-date -setup() { - ret=0 - git fetch "$REMOTE" &> /dev/null || ret="$?" - if [ ${ret} == 0 ]; then - return - fi - echo "Adding remote \"$REMOTE\" with URL git@github.com:bitcoin-core/secp256k1.git. Continue with y" - read -r yn - case $yn in - [Yy]* ) ;; - * ) exit 1;; - esac - git remote add "$REMOTE" git@github.com:bitcoin-core/secp256k1.git &> /dev/null - git fetch "$REMOTE" &> /dev/null -} + +if ! git remote get-url "$REMOTE" &> /dev/null; then +echo "Error: Remote '$REMOTE' not found." +echo "Add it with: git remote add upstream git@github.com:bitcoin-core/secp256k1.git" +echo "Then run: git fetch upstream" +exit 1 +fi range() { RANGESTART_COMMIT=$(git merge-base "$REMOTE_BRANCH" "$LOCAL_BRANCH") RANGEEND_COMMIT=$(git rev-parse "$REMOTE_BRANCH") - if [ "$#" = 1 ]; then - RANGEEND_COMMIT=$1 - fi COMMITS=$(git --no-pager log --pretty=format:%H --topo-order --reverse --merges "$RANGESTART_COMMIT".."$RANGEEND_COMMIT") } @@ -74,12 +56,19 @@ done # Shift off the processed options shift $((OPTIND -1)) +if [ "$#" -lt 1 ]; then + echo "Error: argument is required." >&2 + echo + help + exit 1 +fi -setup -range "$@" +# Extract the PR branch argument +PR_BRANCH=$1 + +range TITLE="Upstream PRs" -REPRODUCE_COMMAND="$0 -b $LOCAL_BRANCH $RANGEEND_COMMIT" BODY="" for COMMIT in $COMMITS do @@ -93,8 +82,6 @@ TITLE=${TITLE%?} BODY+=$(cat <\` to show the conflict resolution in the merge commit. * Use \`git read-tree --reset -u \` to replay these resolutions during the conflict resolution stage when recreating the PR branch locally. @@ -102,22 +89,11 @@ Tips: EOF ) -echo "Merging $TITLE. Continue with y" -read -r yn -case $yn in - [Yy]* ) ;; - * ) exit 1;; -esac - echo "-----------------------------------" echo "$TITLE" echo "-----------------------------------" echo "$BODY" echo "-----------------------------------" -# Create branch from PR commit and create PR -git checkout "$LOCAL_BRANCH" -git pull --autostash -git checkout -b temp-merge-"$PRNUM" # Escape single quote # ' -> '\'' @@ -132,12 +108,7 @@ BASEDIR=$(dirname "$0") FNAME="$BASEDIR/gh-pr-create.sh" cat < "$FNAME" #!/bin/sh -gh pr create -t '$TITLE' -b '$BODY' --web -# Remove temporary branch -git checkout "$LOCAL_BRANCH" -git branch -D temp-merge-"$PRNUM" +gh pr create -t '$TITLE' -b '$BODY' --base '$LOCAL_BRANCH' --head '$PR_BRANCH' EOT chmod +x "$FNAME" -echo Run "$FNAME" after solving the merge conflicts - -git merge --no-edit -m "Merge upstream '${LAST_COMMIT:0:7}' into temp-merge-$PRNUM" "$LAST_COMMIT" +echo "Generated $FNAME for creating a pull request with the above title and body." \ No newline at end of file From 3a18afd5ffe284e919fee74db9e694bc23f93665 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Tue, 17 Mar 2026 13:46:40 +0200 Subject: [PATCH 376/381] sync-upstream: Allow PAT for sync workflow --- .github/workflows/sync.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index ea16d068..04d60172 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -32,7 +32,7 @@ jobs: - name: Fetch upstream & generate branch name & check for new commits id: check_commits env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | gh repo set-default ${{ github.repository }} # Set the default repo to the origin repository git remote add upstream ${{ env.UPSTREAM }} @@ -66,7 +66,7 @@ jobs: id: branch_pr_metadata if: steps.check_commits.outputs.skip == 'false' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | # Call the sync script to generate PR title and body ./contrib/sync-upstream.sh -b ${{ env.ORIGIN_BRANCH }} "$SYNC_BRANCH" @@ -75,7 +75,7 @@ jobs: id: push_branch if: steps.check_commits.outputs.skip == 'false' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | echo "Creating sync branch: $SYNC_BRANCH" git checkout upstream/${{ env.UPSTREAM_BRANCH }} @@ -86,7 +86,7 @@ jobs: id: create_pr if: steps.check_commits.outputs.skip == 'false' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | # Execute the generated PR creation script ./contrib/gh-pr-create.sh From be075fe86cdc2d5d70f0791633d2225da2079ae6 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Tue, 17 Mar 2026 14:12:24 +0200 Subject: [PATCH 377/381] bench: Fix bench_whitelist hang --- src/bench_whitelist.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c index f02f1e86..46de7d5a 100644 --- a/src/bench_whitelist.c +++ b/src/bench_whitelist.c @@ -14,7 +14,6 @@ #include "hash_impl.h" #include "int128_impl.h" #include "scalar_impl.h" -#include "testrand_impl.h" #define MAX_N_KEYS 30 @@ -50,17 +49,19 @@ static void run_test(bench_data* data, int iters) { run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, iters); } -static void random_scalar_order(secp256k1_scalar *num) { - do { - unsigned char b32[32]; - int overflow = 0; - testrand256(b32); - secp256k1_scalar_set_b32(num, b32, &overflow); - if (overflow || secp256k1_scalar_is_zero(num)) { - continue; - } - break; - } while(1); +static void generate_scalar(secp256k1_scalar *scalar, unsigned char *seckey, uint32_t num) { + secp256k1_sha256 sha256; + unsigned char c[13] = {'w','h','i','t','e','l','i','s','t', 0, 0, 0, 0}; + int is_valid; + c[9] = num; + c[10] = num >> 8; + c[11] = num >> 16; + c[12] = num >> 24; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, c, sizeof(c)); + secp256k1_sha256_finalize(&sha256, seckey); + is_valid = secp256k1_scalar_set_b32_seckey(scalar, seckey); + CHECK(is_valid); } int main(void) { @@ -76,22 +77,19 @@ int main(void) { data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); /* Start with subkey */ - random_scalar_order(&ssub); - secp256k1_scalar_get_b32(data.csub, &ssub); + generate_scalar(&ssub, data.csub, 0); CHECK(secp256k1_ec_seckey_verify(data.ctx, data.csub) == 1); CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.sub_pubkey, data.csub) == 1); /* Then offline and online whitelist keys */ for (i = 0; i < n_keys; i++) { secp256k1_scalar son, soff; - /* Create two keys */ - random_scalar_order(&son); - secp256k1_scalar_get_b32(data.online_seckey[i], &son); + /* Create two keys using different counter values to ensure different keys */ + generate_scalar(&son, data.online_seckey[i], i + 1); CHECK(secp256k1_ec_seckey_verify(data.ctx, data.online_seckey[i]) == 1); CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.online_pubkeys[i], data.online_seckey[i]) == 1); - random_scalar_order(&soff); - secp256k1_scalar_get_b32(data.summed_seckey[i], &soff); + generate_scalar(&soff, data.summed_seckey[i], i + 1 + n_keys); CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.offline_pubkeys[i], data.summed_seckey[i]) == 1); From c9a623c36265da7a0576ec17a3bddfde99316e60 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Thu, 19 Mar 2026 14:31:26 +0200 Subject: [PATCH 378/381] sync-upstream: Pass token via checkout for git push operations --- .github/workflows/sync.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 04d60172..0bfc76df 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -23,6 +23,7 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + token: ${{ secrets.SYNC_PAT }} - name: Configure Git run: | @@ -65,8 +66,6 @@ jobs: - name: Generate PR metadata id: branch_pr_metadata if: steps.check_commits.outputs.skip == 'false' - env: - GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | # Call the sync script to generate PR title and body ./contrib/sync-upstream.sh -b ${{ env.ORIGIN_BRANCH }} "$SYNC_BRANCH" @@ -74,8 +73,6 @@ jobs: - name: Push a sync branch id: push_branch if: steps.check_commits.outputs.skip == 'false' - env: - GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | echo "Creating sync branch: $SYNC_BRANCH" git checkout upstream/${{ env.UPSTREAM_BRANCH }} From efa4e11b65599870022fa97c8c50ed97255f171b Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Fri, 20 Mar 2026 17:09:45 +0200 Subject: [PATCH 379/381] sync-upstream: Use GITHUB_TOKEN for gh commands --- .github/workflows/sync.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 0bfc76df..48184d49 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -6,7 +6,6 @@ on: workflow_dispatch: permissions: - contents: write pull-requests: write jobs: @@ -33,7 +32,7 @@ jobs: - name: Fetch upstream & generate branch name & check for new commits id: check_commits env: - GH_TOKEN: ${{ secrets.SYNC_PAT }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh repo set-default ${{ github.repository }} # Set the default repo to the origin repository git remote add upstream ${{ env.UPSTREAM }} @@ -83,7 +82,7 @@ jobs: id: create_pr if: steps.check_commits.outputs.skip == 'false' env: - GH_TOKEN: ${{ secrets.SYNC_PAT }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Execute the generated PR creation script ./contrib/gh-pr-create.sh From b96b655a82cd5b79188438de98f3c05792f2b6c3 Mon Sep 17 00:00:00 2001 From: Mykyta Redko Date: Tue, 24 Mar 2026 09:06:33 +0200 Subject: [PATCH 380/381] include: fix a minor grammar mistake in the rangeproof description --- include/secp256k1_rangeproof.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 8816bdcf..0224972f 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -23,7 +23,7 @@ extern "C" { * an unmodified way. * * Another difference is that the implementation omits the last ring's commitment - * from the proof and recovered by the verifier by subtracting all other digit + * from the proof, which is recovered by the verifier by subtracting all other digit * commitments from the total, reducing proof size by one group element. * * Furthermore, in the implementation every hash calculation includes a message From e371ab5df0df9c945b8c3dd10294e15a214ebf26 Mon Sep 17 00:00:00 2001 From: DarkWindman Date: Mon, 23 Mar 2026 17:23:03 +0200 Subject: [PATCH 381/381] sync-upstream: Restore SYNC_PAT for gh commands --- .github/workflows/sync.yml | 7 ++----- contrib/sync-upstream.sh | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 48184d49..cd534821 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -5,9 +5,6 @@ on: - cron: '0 0 1 * *' workflow_dispatch: -permissions: - pull-requests: write - jobs: sync-upstream: runs-on: ubuntu-latest @@ -32,7 +29,7 @@ jobs: - name: Fetch upstream & generate branch name & check for new commits id: check_commits env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | gh repo set-default ${{ github.repository }} # Set the default repo to the origin repository git remote add upstream ${{ env.UPSTREAM }} @@ -82,7 +79,7 @@ jobs: id: create_pr if: steps.check_commits.outputs.skip == 'false' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SYNC_PAT }} run: | # Execute the generated PR creation script ./contrib/gh-pr-create.sh diff --git a/contrib/sync-upstream.sh b/contrib/sync-upstream.sh index b3274040..aaf8cb10 100755 --- a/contrib/sync-upstream.sh +++ b/contrib/sync-upstream.sh @@ -69,7 +69,7 @@ PR_BRANCH=$1 range TITLE="Upstream PRs" -BODY="" +BODY="${GITHUB_ACTIONS+This PR has been created by a GitHub Actions workflow without human involvement.}"$'\n' for COMMIT in $COMMITS do PRNUM=$(git log -1 "$COMMIT" --pretty=format:%s | sed s/'Merge \(bitcoin-core\/secp256k1\)\?#\([0-9]*\).*'/'\2'/)