From efc61dc0cad5fa7fcb8c6af65c266a1fdec7427f Mon Sep 17 00:00:00 2001 From: Greg Maxwell Date: Wed, 5 Aug 2015 16:17:50 +0200 Subject: [PATCH 01/58] 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 68c45a56f..7838f48cf 100644 --- a/configure.ac +++ b/configure.ac @@ -195,6 +195,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 f1f9be077..82599593c 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 30a91e529..0db523d24 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 61bb7fce8..bf3b58052 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.* **********************************************************************/ @@ -137,6 +137,52 @@ void random_scalar_order(secp256k1_scalar *num) { } while(1); } +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(void) { secp256k1_pubkey pubkey; secp256k1_pubkey zero_pubkey; @@ -5021,6 +5067,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 e0147500f..aca79d7be 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.* **********************************************************************/ @@ -84,6 +84,32 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void 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 ae1e576f67fdd47515e7c25b54c0284d097fbe89 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Wed, 5 Aug 2015 19:04:14 +0200 Subject: [PATCH 02/58] 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. --- Makefile.am | 4 + configure.ac | 14 + 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 | 176 +++++ src/modules/rangeproof/pedersen.h | 34 + src/modules/rangeproof/pedersen_impl.h | 139 ++++ src/modules/rangeproof/rangeproof.h | 31 + src/modules/rangeproof/rangeproof_impl.h | 736 +++++++++++++++++++++ src/modules/rangeproof/tests_impl.h | 281 ++++++++ src/secp256k1.c | 25 + src/tests.c | 8 + 15 files changed, 1939 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 01fd0cd6d..c7f7298ae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,3 +181,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 7838f48cf..e0c5aab05 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,11 @@ 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(jni, AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]), [use_jni=$enableval], @@ -441,6 +446,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 @@ -464,6 +473,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 @@ -472,6 +482,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]) @@ -488,6 +501,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_JNI], [test x"$use_jni" == 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 000000000..54b454ef6 --- /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 000000000..54776b72f --- /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 i; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + for (i = 0; i < 1000; 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; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_pedersen_context_initialize(data.ctx); + secp256k1_rangeproof_context_initialize(data.ctx); + + data.min_bits = 32; + + run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, 1000 * data.min_bits); + + 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 000000000..ff8b8d380 --- /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 000000000..11fd6c5b0 --- /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 000000000..831451602 --- /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 000000000..d6104fd2f --- /dev/null +++ b/src/modules/rangeproof/main_impl.h @@ -0,0 +1,176 @@ +/********************************************************************** + * 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" + +void secp256k1_pedersen_context_initialize(secp256k1_context* ctx) { + secp256k1_pedersen_context_build(&ctx->pedersen_ctx, &ctx->error_callback); +} + +/* 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(secp256k1_pedersen_context_is_built(&ctx->pedersen_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, &ctx->pedersen_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)); + ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); + 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(&ctx->pedersen_ctx, &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); +} + +void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx) { + secp256k1_rangeproof_context_build(&ctx->rangeproof_ctx, &ctx->error_callback); +} + +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)); + ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); + ARG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &ctx->pedersen_ctx, &ctx->rangeproof_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)); + ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); + ARG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, &ctx->pedersen_ctx, &ctx->rangeproof_ctx, + 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)); + ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); + ARG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); + return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &ctx->pedersen_ctx, &ctx->rangeproof_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 000000000..f167d4dfb --- /dev/null +++ b/src/modules/rangeproof/pedersen.h @@ -0,0 +1,34 @@ +/********************************************************************** + * 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 + +typedef struct { + secp256k1_ge_storage (*prec)[16][16]; /* prec[j][i] = 16^j * i * G + U_i */ +} secp256k1_pedersen_context; + +static void secp256k1_pedersen_context_init(secp256k1_pedersen_context* ctx); +static void secp256k1_pedersen_context_build(secp256k1_pedersen_context* ctx, const secp256k1_callback* cb); +static void secp256k1_pedersen_context_clone(secp256k1_pedersen_context *dst, + const secp256k1_pedersen_context* src, const secp256k1_callback* cb); +static void secp256k1_pedersen_context_clear(secp256k1_pedersen_context* ctx); + +static int secp256k1_pedersen_context_is_built(const secp256k1_pedersen_context* ctx); + +/** Multiply a small number with the generator: r = gn*G2 */ +static void secp256k1_pedersen_ecmult_small(const secp256k1_pedersen_context *ctx, secp256k1_gej *r, uint64_t gn); + +/* sec * G + value * G2. */ +static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, + const secp256k1_pedersen_context *pedersen_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 000000000..c2a169afd --- /dev/null +++ b/src/modules/rangeproof/pedersen_impl.h @@ -0,0 +1,139 @@ +/*********************************************************************** + * 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_context_init(secp256k1_pedersen_context *ctx) { + ctx->prec = NULL; +} + +static void secp256k1_pedersen_context_build(secp256k1_pedersen_context *ctx, const secp256k1_callback *cb) { + secp256k1_ge prec[256]; + secp256k1_gej gj; + secp256k1_gej nums_gej; + int i, j; + + if (ctx->prec != NULL) { + return; + } + + ctx->prec = (secp256k1_ge_storage (*)[16][16])checked_malloc(cb, sizeof(*ctx->prec)); + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g2); + + /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ + { + static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; + secp256k1_fe nums_x; + secp256k1_ge nums_ge; + VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32)); + VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0)); + secp256k1_gej_set_ge(&nums_gej, &nums_ge); + /* Add G to make the bits in x uniformly distributed. */ + secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g2, NULL); + } + + /* compute prec. */ + { + secp256k1_gej precj[256]; /* Jacobian versions of prec. */ + secp256k1_gej gbase; + secp256k1_gej numsbase; + gbase = gj; /* 16^j * G */ + numsbase = nums_gej; /* 2^j * nums. */ + for (j = 0; j < 16; j++) { + /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ + precj[j*16] = numsbase; + for (i = 1; i < 16; i++) { + secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); + } + /* Multiply gbase by 16. */ + for (i = 0; i < 4; i++) { + secp256k1_gej_double_var(&gbase, &gbase, NULL); + } + /* Multiply numbase by 2. */ + secp256k1_gej_double_var(&numsbase, &numsbase, NULL); + if (j == 14) { + /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ + secp256k1_gej_neg(&numsbase, &numsbase); + secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); + } + } + secp256k1_ge_set_all_gej_var(prec, precj, 256, cb); + } + for (j = 0; j < 16; j++) { + for (i = 0; i < 16; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); + } + } +} + +static int secp256k1_pedersen_context_is_built(const secp256k1_pedersen_context* ctx) { + return ctx->prec != NULL; +} + +static void secp256k1_pedersen_context_clone(secp256k1_pedersen_context *dst, + const secp256k1_pedersen_context *src, const secp256k1_callback *cb) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { + dst->prec = (secp256k1_ge_storage (*)[16][16])checked_malloc(cb, sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + } +} + +static void secp256k1_pedersen_context_clear(secp256k1_pedersen_context *ctx) { + free(ctx->prec); + ctx->prec = NULL; +} + +/* Version of secp256k1_ecmult_gen using the second generator and working only on numbers in the range [0 .. 2^64). */ +static void secp256k1_pedersen_ecmult_small(const secp256k1_pedersen_context *ctx, secp256k1_gej *r, uint64_t gn) { + secp256k1_ge add; + secp256k1_ge_storage adds; + int bits; + int i, j; + memset(&adds, 0, sizeof(adds)); + secp256k1_gej_set_infinity(r); + add.infinity = 0; + for (j = 0; j < 16; j++) { + bits = (gn >> (j * 4)) & 15; + for (i = 0; i < 16; i++) { + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + } + secp256k1_ge_from_storage(&add, &adds); + secp256k1_gej_add_ge(r, r, &add); + } + bits = 0; + secp256k1_ge_clear(&add); +} + +/* sec * G + value * G2. */ +SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, + const secp256k1_pedersen_context *pedersen_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(pedersen_ctx, &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 000000000..e6d99717f --- /dev/null +++ b/src/modules/rangeproof/rangeproof.h @@ -0,0 +1,31 @@ +/********************************************************************** + * 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" + +typedef struct { + secp256k1_ge_storage (*prec)[1005]; +} secp256k1_rangeproof_context; + + +static void secp256k1_rangeproof_context_init(secp256k1_rangeproof_context* ctx); +static void secp256k1_rangeproof_context_build(secp256k1_rangeproof_context* ctx, const secp256k1_callback* cb); +static void secp256k1_rangeproof_context_clone(secp256k1_rangeproof_context *dst, + const secp256k1_rangeproof_context* src, const secp256k1_callback* cb); +static void secp256k1_rangeproof_context_clear(secp256k1_rangeproof_context* ctx); +static int secp256k1_rangeproof_context_is_built(const secp256k1_rangeproof_context* ctx); + +static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, + const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + const secp256k1_pedersen_context* pedersen_ctx, const secp256k1_rangeproof_context* rangeproof_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 000000000..e64eeb27d --- /dev/null +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -0,0 +1,736 @@ +/********************************************************************** + * 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" + +static const int secp256k1_rangeproof_offsets[20] = { + 0, 96, 189, 276, 360, 438, 510, 579, 642, + 699, 753, 801, 843, 882, 915, 942, 966, 984, + 996, 1005, +}; + +static void secp256k1_rangeproof_context_init(secp256k1_rangeproof_context *ctx) { + ctx->prec = NULL; +} + +static void secp256k1_rangeproof_context_build(secp256k1_rangeproof_context *ctx, const secp256k1_callback* cb) { + secp256k1_ge *prec; + secp256k1_gej *precj; + secp256k1_gej gj; + secp256k1_gej one; + int i, pos; + + if (ctx->prec != NULL) { + return; + } + + precj = (secp256k1_gej (*))checked_malloc(cb, sizeof(*precj) * 1005); + if (precj == NULL) { + return; + } + prec = (secp256k1_ge (*))checked_malloc(cb, sizeof(*prec) * 1005); + if (prec == NULL) { + free(precj); + return; + } + + /* get the generator */ + secp256k1_gej_set_ge(&one, &secp256k1_ge_const_g2); + secp256k1_gej_neg(&one, &one); + + /* compute prec. */ + pos = 0; + for (i = 0; i < 19; i++) { + int pmax; + pmax = secp256k1_rangeproof_offsets[i + 1]; + gj = one; + while (pos < pmax) { + precj[pos] = gj; + pos++; + secp256k1_gej_double_var(&precj[pos], &gj, NULL); + pos++; + secp256k1_gej_add_var(&precj[pos], &precj[pos - 1], &gj, NULL); + pos++; + if (pos < pmax - 1) { + secp256k1_gej_double_var(&gj, &precj[pos - 2], NULL); + } + } + if (i < 18) { + secp256k1_gej_double_var(&gj, &one, NULL); + one = gj; + secp256k1_gej_double_var(&gj, &gj, NULL); + secp256k1_gej_double_var(&gj, &gj, NULL); + secp256k1_gej_add_var(&one, &one, &gj, NULL); + } + } + VERIFY_CHECK(pos == 1005); + secp256k1_ge_set_all_gej_var(prec, precj, 1005, cb); + + free(precj); + + ctx->prec = (secp256k1_ge_storage (*)[1005])checked_malloc(cb, sizeof(*ctx->prec)); + if (ctx->prec == NULL) { + free(prec); + return; + } + + for (i = 0; i < 1005; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[i], &prec[i]); + } + free(prec); +} + + +static int secp256k1_rangeproof_context_is_built(const secp256k1_rangeproof_context* ctx) { + return ctx->prec != NULL; +} + +static void secp256k1_rangeproof_context_clone(secp256k1_rangeproof_context *dst, + const secp256k1_rangeproof_context *src, const secp256k1_callback* cb) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { + dst->prec = (secp256k1_ge_storage (*)[1005])checked_malloc(cb, sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + } +} + +static void secp256k1_rangeproof_context_clear(secp256k1_rangeproof_context *ctx) { + free(ctx->prec); + ctx->prec = NULL; +} + +SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(const secp256k1_rangeproof_context *ctx, secp256k1_gej *pubs, + int exp, int *rsizes, int rings) { + secp256k1_ge ge; + secp256k1_ge_storage *basis; + int i; + int j; + int npub; + VERIFY_CHECK(exp < 19); + if (exp < 0) { + exp = 0; + } + basis = &(*ctx->prec)[secp256k1_rangeproof_offsets[exp]]; + npub = 0; + for (i = 0; i < rings; i++) { + for (j = 1; j < rsizes[i]; j++) { + secp256k1_ge_from_storage(&ge, &basis[i * 3 + j - 1]); + secp256k1_gej_add_ge_var(&pubs[npub + j], &pubs[npub], &ge, 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, const secp256k1_pedersen_context* pedersen_ctx, + const secp256k1_rangeproof_context* rangeproof_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, pedersen_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(rangeproof_ctx, 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, + const secp256k1_pedersen_context* pedersen_ctx, const secp256k1_rangeproof_context* rangeproof_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(pedersen_ctx, &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(rangeproof_ctx, 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, pedersen_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 000000000..e764e69ff --- /dev/null +++ b/src/modules/rangeproof/tests_impl.h @@ -0,0 +1,281 @@ +/********************************************************************** + * 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; + secp256k1_pedersen_context_initialize(ctx); + secp256k1_rangeproof_context_initialize(ctx); + 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 a1e390807..f96b7b687 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -19,6 +19,11 @@ #include "hash_impl.h" #include "scratch_impl.h" +#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); \ @@ -52,6 +57,10 @@ static const secp256k1_callback default_error_callback = { struct secp256k1_context_struct { secp256k1_ecmult_context ecmult_ctx; secp256k1_ecmult_gen_context ecmult_gen_ctx; +#ifdef ENABLE_MODULE_RANGEPROOF + secp256k1_pedersen_context pedersen_ctx; + secp256k1_rangeproof_context rangeproof_ctx; +#endif secp256k1_callback illegal_callback; secp256k1_callback error_callback; }; @@ -78,6 +87,10 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) { secp256k1_ecmult_context_init(&ret->ecmult_ctx); secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); +#ifdef ENABLE_MODULE_RANGEPROOF + secp256k1_pedersen_context_init(&ret->pedersen_ctx); + secp256k1_rangeproof_context_init(&ret->rangeproof_ctx); +#endif if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); @@ -95,6 +108,10 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { ret->error_callback = ctx->error_callback; secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); +#ifdef ENABLE_MODULE_RANGEPROOF + secp256k1_pedersen_context_clone(&ret->pedersen_ctx, &ctx->pedersen_ctx, &ctx->error_callback); + secp256k1_rangeproof_context_clone(&ret->rangeproof_ctx, &ctx->rangeproof_ctx, &ctx->error_callback); +#endif return ret; } @@ -103,6 +120,10 @@ void secp256k1_context_destroy(secp256k1_context* ctx) { if (ctx != NULL) { secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); +#ifdef ENABLE_MODULE_RANGEPROOF + secp256k1_pedersen_context_clear(&ctx->pedersen_ctx); + secp256k1_rangeproof_context_clear(&ctx->rangeproof_ctx); +#endif free(ctx); } @@ -607,3 +628,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 bf3b58052..19cf141a5 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5012,6 +5012,10 @@ void run_ecdsa_openssl(void) { # include "modules/recovery/tests_impl.h" #endif +#ifdef ENABLE_MODULE_RANGEPROOF +# include "modules/rangeproof/tests_impl.h" +#endif + int main(int argc, char **argv) { unsigned char seed16[16] = {0}; unsigned char run32[32] = {0}; @@ -5135,6 +5139,10 @@ int main(int argc, char **argv) { run_recovery_tests(); #endif +#ifdef ENABLE_MODULE_RANGEPROOF + run_rangeproof_tests(); +#endif + secp256k1_rand256(run32); printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); From 6d28767c7942d444a779a0ae6b8d6451ea1ae129 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 13 Mar 2018 16:33:31 +0000 Subject: [PATCH 03/58] Get rid of precomputed H tables (Pieter Wuille) --- src/bench_rangeproof.c | 2 - src/modules/rangeproof/main_impl.h | 26 +---- src/modules/rangeproof/pedersen.h | 17 +-- src/modules/rangeproof/pedersen_impl.h | 119 +++----------------- src/modules/rangeproof/rangeproof.h | 13 --- src/modules/rangeproof/rangeproof_impl.h | 135 ++++------------------- src/modules/rangeproof/tests_impl.h | 2 - src/secp256k1.c | 16 --- 8 files changed, 48 insertions(+), 282 deletions(-) diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 54776b72f..f3faafb59 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -53,8 +53,6 @@ int main(void) { bench_rangeproof_t data; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - secp256k1_pedersen_context_initialize(data.ctx); - secp256k1_rangeproof_context_initialize(data.ctx); data.min_bits = 32; diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index d6104fd2f..20f05a053 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -11,10 +11,6 @@ #include "modules/rangeproof/borromean_impl.h" #include "modules/rangeproof/rangeproof_impl.h" -void secp256k1_pedersen_context_initialize(secp256k1_context* ctx) { - secp256k1_pedersen_context_build(&ctx->pedersen_ctx, &ctx->error_callback); -} - /* 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; @@ -25,12 +21,11 @@ int secp256k1_pedersen_commit(const secp256k1_context* ctx, unsigned char *commi int ret = 0; ARG_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_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, &ctx->pedersen_ctx, &rj, &sec, value); + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value); if (!secp256k1_gej_is_infinity(&rj)) { secp256k1_ge_set_gej(&r, &rj); sz = 33; @@ -80,14 +75,13 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const unsigned ARG_CHECK(ctx != NULL); ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); - ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); 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(&ctx->pedersen_ctx, &accj, ex); + secp256k1_pedersen_ecmult_small(&accj, ex); if (neg) { secp256k1_gej_neg(&accj, &accj); } @@ -108,10 +102,6 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const unsigned return secp256k1_gej_is_infinity(&accj); } -void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx) { - secp256k1_rangeproof_context_build(&ctx->rangeproof_ctx, &ctx->error_callback); -} - 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; @@ -137,9 +127,7 @@ 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)); - ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); - ARG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); - return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &ctx->pedersen_ctx, &ctx->rangeproof_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); } @@ -151,9 +139,7 @@ int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_valu ARG_CHECK(min_value != NULL); ARG_CHECK(max_value != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); - ARG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); - return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, &ctx->pedersen_ctx, &ctx->rangeproof_ctx, + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, NULL, NULL, NULL, NULL, NULL, min_value, max_value, commit, proof, plen); } @@ -167,9 +153,7 @@ 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)); - ARG_CHECK(secp256k1_pedersen_context_is_built(&ctx->pedersen_ctx)); - ARG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); - return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &ctx->pedersen_ctx, &ctx->rangeproof_ctx, + return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, proof, plen, min_value, commit, blind, nonce, exp, min_bits, value); } diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h index f167d4dfb..cdfe2f8e0 100644 --- a/src/modules/rangeproof/pedersen.h +++ b/src/modules/rangeproof/pedersen.h @@ -12,23 +12,10 @@ #include -typedef struct { - secp256k1_ge_storage (*prec)[16][16]; /* prec[j][i] = 16^j * i * G + U_i */ -} secp256k1_pedersen_context; - -static void secp256k1_pedersen_context_init(secp256k1_pedersen_context* ctx); -static void secp256k1_pedersen_context_build(secp256k1_pedersen_context* ctx, const secp256k1_callback* cb); -static void secp256k1_pedersen_context_clone(secp256k1_pedersen_context *dst, - const secp256k1_pedersen_context* src, const secp256k1_callback* cb); -static void secp256k1_pedersen_context_clear(secp256k1_pedersen_context* ctx); - -static int secp256k1_pedersen_context_is_built(const secp256k1_pedersen_context* ctx); - /** Multiply a small number with the generator: r = gn*G2 */ -static void secp256k1_pedersen_ecmult_small(const secp256k1_pedersen_context *ctx, secp256k1_gej *r, uint64_t gn); +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, - const secp256k1_pedersen_context *pedersen_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); #endif diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/rangeproof/pedersen_impl.h index c2a169afd..3ce2767cf 100644 --- a/src/modules/rangeproof/pedersen_impl.h +++ b/src/modules/rangeproof/pedersen_impl.h @@ -20,117 +20,32 @@ static const secp256k1_ge secp256k1_ge_const_g2 = SECP256K1_GE_CONST( 0x36dac28aUL, 0xf1766968UL, 0xc30c2313UL, 0xf3a38904UL ); -static void secp256k1_pedersen_context_init(secp256k1_pedersen_context *ctx) { - ctx->prec = NULL; -} - -static void secp256k1_pedersen_context_build(secp256k1_pedersen_context *ctx, const secp256k1_callback *cb) { - secp256k1_ge prec[256]; - secp256k1_gej gj; - secp256k1_gej nums_gej; - int i, j; - - if (ctx->prec != NULL) { - return; - } - - ctx->prec = (secp256k1_ge_storage (*)[16][16])checked_malloc(cb, sizeof(*ctx->prec)); - - /* get the generator */ - secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g2); - - /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ - { - static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; - secp256k1_fe nums_x; - secp256k1_ge nums_ge; - VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32)); - VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0)); - secp256k1_gej_set_ge(&nums_gej, &nums_ge); - /* Add G to make the bits in x uniformly distributed. */ - secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g2, NULL); - } - - /* compute prec. */ - { - secp256k1_gej precj[256]; /* Jacobian versions of prec. */ - secp256k1_gej gbase; - secp256k1_gej numsbase; - gbase = gj; /* 16^j * G */ - numsbase = nums_gej; /* 2^j * nums. */ - for (j = 0; j < 16; j++) { - /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ - precj[j*16] = numsbase; - for (i = 1; i < 16; i++) { - secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); - } - /* Multiply gbase by 16. */ - for (i = 0; i < 4; i++) { - secp256k1_gej_double_var(&gbase, &gbase, NULL); - } - /* Multiply numbase by 2. */ - secp256k1_gej_double_var(&numsbase, &numsbase, NULL); - if (j == 14) { - /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ - secp256k1_gej_neg(&numsbase, &numsbase); - secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); - } - } - secp256k1_ge_set_all_gej_var(prec, precj, 256, cb); +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 (j = 0; j < 16; j++) { - for (i = 0; i < 16; i++) { - secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); - } + for (; i < 32; i++) { + data[i] = value >> 56; + value <<= 8; } + secp256k1_scalar_set_b32(sec, data, NULL); + memset(data, 0, 32); } -static int secp256k1_pedersen_context_is_built(const secp256k1_pedersen_context* ctx) { - return ctx->prec != NULL; -} - -static void secp256k1_pedersen_context_clone(secp256k1_pedersen_context *dst, - const secp256k1_pedersen_context *src, const secp256k1_callback *cb) { - if (src->prec == NULL) { - dst->prec = NULL; - } else { - dst->prec = (secp256k1_ge_storage (*)[16][16])checked_malloc(cb, sizeof(*dst->prec)); - memcpy(dst->prec, src->prec, sizeof(*dst->prec)); - } -} - -static void secp256k1_pedersen_context_clear(secp256k1_pedersen_context *ctx) { - free(ctx->prec); - ctx->prec = NULL; -} - -/* Version of secp256k1_ecmult_gen using the second generator and working only on numbers in the range [0 .. 2^64). */ -static void secp256k1_pedersen_ecmult_small(const secp256k1_pedersen_context *ctx, secp256k1_gej *r, uint64_t gn) { - secp256k1_ge add; - secp256k1_ge_storage adds; - int bits; - int i, j; - memset(&adds, 0, sizeof(adds)); - secp256k1_gej_set_infinity(r); - add.infinity = 0; - for (j = 0; j < 16; j++) { - bits = (gn >> (j * 4)) & 15; - for (i = 0; i < 16; i++) { - secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); - } - secp256k1_ge_from_storage(&add, &adds); - secp256k1_gej_add_ge(r, r, &add); - } - bits = 0; - secp256k1_ge_clear(&add); +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, - const secp256k1_pedersen_context *pedersen_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) { secp256k1_gej vj; secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec); - secp256k1_pedersen_ecmult_small(pedersen_ctx, &vj, value); + secp256k1_pedersen_ecmult_small(&vj, value); /* 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 e6d99717f..b0f536968 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -10,21 +10,8 @@ #include "scalar.h" #include "group.h" -typedef struct { - secp256k1_ge_storage (*prec)[1005]; -} secp256k1_rangeproof_context; - - -static void secp256k1_rangeproof_context_init(secp256k1_rangeproof_context* ctx); -static void secp256k1_rangeproof_context_build(secp256k1_rangeproof_context* ctx, const secp256k1_callback* cb); -static void secp256k1_rangeproof_context_clone(secp256k1_rangeproof_context *dst, - const secp256k1_rangeproof_context* src, const secp256k1_callback* cb); -static void secp256k1_rangeproof_context_clear(secp256k1_rangeproof_context* ctx); -static int secp256k1_rangeproof_context_is_built(const secp256k1_rangeproof_context* ctx); - static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, - const secp256k1_pedersen_context* pedersen_ctx, const secp256k1_rangeproof_context* rangeproof_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); diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index e64eeb27d..f76e1b028 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -15,107 +15,9 @@ #include "modules/rangeproof/pedersen.h" #include "modules/rangeproof/borromean.h" -static const int secp256k1_rangeproof_offsets[20] = { - 0, 96, 189, 276, 360, 438, 510, 579, 642, - 699, 753, 801, 843, 882, 915, 942, 966, 984, - 996, 1005, -}; - -static void secp256k1_rangeproof_context_init(secp256k1_rangeproof_context *ctx) { - ctx->prec = NULL; -} - -static void secp256k1_rangeproof_context_build(secp256k1_rangeproof_context *ctx, const secp256k1_callback* cb) { - secp256k1_ge *prec; - secp256k1_gej *precj; - secp256k1_gej gj; - secp256k1_gej one; - int i, pos; - - if (ctx->prec != NULL) { - return; - } - - precj = (secp256k1_gej (*))checked_malloc(cb, sizeof(*precj) * 1005); - if (precj == NULL) { - return; - } - prec = (secp256k1_ge (*))checked_malloc(cb, sizeof(*prec) * 1005); - if (prec == NULL) { - free(precj); - return; - } - - /* get the generator */ - secp256k1_gej_set_ge(&one, &secp256k1_ge_const_g2); - secp256k1_gej_neg(&one, &one); - - /* compute prec. */ - pos = 0; - for (i = 0; i < 19; i++) { - int pmax; - pmax = secp256k1_rangeproof_offsets[i + 1]; - gj = one; - while (pos < pmax) { - precj[pos] = gj; - pos++; - secp256k1_gej_double_var(&precj[pos], &gj, NULL); - pos++; - secp256k1_gej_add_var(&precj[pos], &precj[pos - 1], &gj, NULL); - pos++; - if (pos < pmax - 1) { - secp256k1_gej_double_var(&gj, &precj[pos - 2], NULL); - } - } - if (i < 18) { - secp256k1_gej_double_var(&gj, &one, NULL); - one = gj; - secp256k1_gej_double_var(&gj, &gj, NULL); - secp256k1_gej_double_var(&gj, &gj, NULL); - secp256k1_gej_add_var(&one, &one, &gj, NULL); - } - } - VERIFY_CHECK(pos == 1005); - secp256k1_ge_set_all_gej_var(prec, precj, 1005, cb); - - free(precj); - - ctx->prec = (secp256k1_ge_storage (*)[1005])checked_malloc(cb, sizeof(*ctx->prec)); - if (ctx->prec == NULL) { - free(prec); - return; - } - - for (i = 0; i < 1005; i++) { - secp256k1_ge_to_storage(&(*ctx->prec)[i], &prec[i]); - } - free(prec); -} - - -static int secp256k1_rangeproof_context_is_built(const secp256k1_rangeproof_context* ctx) { - return ctx->prec != NULL; -} - -static void secp256k1_rangeproof_context_clone(secp256k1_rangeproof_context *dst, - const secp256k1_rangeproof_context *src, const secp256k1_callback* cb) { - if (src->prec == NULL) { - dst->prec = NULL; - } else { - dst->prec = (secp256k1_ge_storage (*)[1005])checked_malloc(cb, sizeof(*dst->prec)); - memcpy(dst->prec, src->prec, sizeof(*dst->prec)); - } -} - -static void secp256k1_rangeproof_context_clear(secp256k1_rangeproof_context *ctx) { - free(ctx->prec); - ctx->prec = NULL; -} - -SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(const secp256k1_rangeproof_context *ctx, secp256k1_gej *pubs, +SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, int *rsizes, int rings) { - secp256k1_ge ge; - secp256k1_ge_storage *basis; + secp256k1_gej base; int i; int j; int npub; @@ -123,12 +25,24 @@ SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(const secp256k1_ran if (exp < 0) { exp = 0; } - basis = &(*ctx->prec)[secp256k1_rangeproof_offsets[exp]]; + 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_ge_from_storage(&ge, &basis[i * 3 + j - 1]); - secp256k1_gej_add_ge_var(&pubs[npub + j], &pubs[npub], &ge, NULL); + 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]; } @@ -264,8 +178,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, const secp256k1_pedersen_context* pedersen_ctx, - const secp256k1_rangeproof_context* rangeproof_ctx, unsigned char *proof, int *plen, uint64_t min_value, + 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. */ @@ -357,7 +271,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, pedersen_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)); if (secp256k1_gej_is_infinity(&pubs[npub])) { return 0; } @@ -376,7 +290,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } npub += rsizes[i]; } - secp256k1_rangeproof_pub_expand(rangeproof_ctx, pubs, exp, rsizes, rings); + 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; @@ -596,7 +510,6 @@ SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(int *offset, int /* 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, - const secp256k1_pedersen_context* pedersen_ctx, const secp256k1_rangeproof_context* rangeproof_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; @@ -660,7 +573,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(pedersen_ctx, &accj, *min_value); + secp256k1_pedersen_ecmult_small(&accj, *min_value); } for(i = 0; i < rings - 1; i++) { memcpy(&m[1], &proof[offset], 32); @@ -682,7 +595,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(rangeproof_ctx, pubs, exp, rsizes, rings); + secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings); npub += rsizes[rings - 1]; e0 = &proof[offset]; offset += 32; @@ -713,7 +626,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm /* 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, pedersen_ctx, &accj, &blind, vv); + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv); 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 e764e69ff..4e6016974 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -267,8 +267,6 @@ void test_rangeproof(void) { void run_rangeproof_tests(void) { int i; - secp256k1_pedersen_context_initialize(ctx); - secp256k1_rangeproof_context_initialize(ctx); for (i = 0; i < 10*count; i++) { test_pedersen(); } diff --git a/src/secp256k1.c b/src/secp256k1.c index f96b7b687..545d0b40f 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -57,10 +57,6 @@ static const secp256k1_callback default_error_callback = { struct secp256k1_context_struct { secp256k1_ecmult_context ecmult_ctx; secp256k1_ecmult_gen_context ecmult_gen_ctx; -#ifdef ENABLE_MODULE_RANGEPROOF - secp256k1_pedersen_context pedersen_ctx; - secp256k1_rangeproof_context rangeproof_ctx; -#endif secp256k1_callback illegal_callback; secp256k1_callback error_callback; }; @@ -87,10 +83,6 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) { secp256k1_ecmult_context_init(&ret->ecmult_ctx); secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); -#ifdef ENABLE_MODULE_RANGEPROOF - secp256k1_pedersen_context_init(&ret->pedersen_ctx); - secp256k1_rangeproof_context_init(&ret->rangeproof_ctx); -#endif if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); @@ -108,10 +100,6 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { ret->error_callback = ctx->error_callback; secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); -#ifdef ENABLE_MODULE_RANGEPROOF - secp256k1_pedersen_context_clone(&ret->pedersen_ctx, &ctx->pedersen_ctx, &ctx->error_callback); - secp256k1_rangeproof_context_clone(&ret->rangeproof_ctx, &ctx->rangeproof_ctx, &ctx->error_callback); -#endif return ret; } @@ -120,10 +108,6 @@ void secp256k1_context_destroy(secp256k1_context* ctx) { if (ctx != NULL) { secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); -#ifdef ENABLE_MODULE_RANGEPROOF - secp256k1_pedersen_context_clear(&ctx->pedersen_ctx); - secp256k1_rangeproof_context_clear(&ctx->rangeproof_ctx); -#endif free(ctx); } From cf40b1bed218c6b34f37e0d383f5d030e8e1efb3 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 4 Jul 2016 13:04:57 +0000 Subject: [PATCH 04/58] [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 | 148 ++++++++++++----------- src/modules/rangeproof/tests_impl.h | 81 +++++++------ src/secp256k1.c | 1 + 11 files changed, 286 insertions(+), 175 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 54b454ef6..94cfaee88 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 f3faafb59..76466574f 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) { @@ -42,7 +42,7 @@ static void bench_rangeproof(void* arg) { 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 11fd6c5b0..8f8cfedd0 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 831451602..3a82f096a 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 20f05a053..a1ad67157 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 cdfe2f8e0..84dd20b4d 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 3ce2767cf..991c60b36 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 b0f536968..85f94bc42 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 f76e1b028..3f2c3a467 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_sha256_write(&sha256_m, m, 33); + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &proof[offset]); + secp256k1_ge_set_xquad(&c, &fe); + if (signs[i]) { + secp256k1_ge_neg(&c, &c); + } + /* Not using secp256k1_rangeproof_serialize_point as we almost have it + * serialized form already. */ + secp256k1_sha256_write(&sha256_m, &signs[i], 1); + secp256k1_sha256_write(&sha256_m, &proof[offset], 32); secp256k1_gej_set_ge(&pubs[npub], &c); secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL); offset += 32; npub += rsizes[i]; } secp256k1_gej_neg(&accj, &accj); - 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 4e6016974..7cd27969b 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 545d0b40f..8bce2be54 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -20,6 +20,7 @@ #include "scratch_impl.h" #ifdef ENABLE_MODULE_RANGEPROOF +# include "include/secp256k1_rangeproof.h" # include "modules/rangeproof/pedersen.h" # include "modules/rangeproof/rangeproof.h" #endif From d46fc3c191c764a040f8a58fcad298cf482743e2 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 5 Jul 2016 15:46:07 +0000 Subject: [PATCH 05/58] 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 94cfaee88..7803e7a59 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 76466574f..66fb28696 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 a1ad67157..c743b6d73 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 3f2c3a467..efd43e120 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 7cd27969b..c8815afb8 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 f4620de040d3549b01f54001ab86c2a35d2245e3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 7 Jul 2016 00:47:41 +0200 Subject: [PATCH 06/58] Constant-time generator module --- Makefile.am | 4 + configure.ac | 19 ++ include/secp256k1_generator.h | 96 ++++++++++ include/secp256k1_rangeproof.h | 2 +- sage/shallue_van_de_woestijne.sage | 51 ++++++ src/bench_generator.c | 59 +++++++ 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 c7f7298ae..d5c9f81e8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,6 +182,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 e0c5aab05..66b0eb5df 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,11 @@ 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)]), [enable_module_rangeproof=$enableval], @@ -446,6 +451,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 @@ -473,8 +482,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.]) @@ -482,6 +497,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 @@ -501,6 +519,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_JNI], [test x"$use_jni" == x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h new file mode 100644 index 000000000..7743b06e4 --- /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 7803e7a59..0afeed32d 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 000000000..1cc97b655 --- /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 000000000..7d7bb4de0 --- /dev/null +++ b/src/bench_generator.c @@ -0,0 +1,59 @@ +/********************************************************************** + * 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 i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < 20000; 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 i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < 20000; 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; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, 20000); + run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, 20000); + + 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 000000000..bc3c514f2 --- /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 000000000..07ad32e71 --- /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 000000000..eee51fac2 --- /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 8bce2be54..37ba046e8 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -19,6 +19,10 @@ #include "hash_impl.h" #include "scratch_impl.h" +#ifdef ENABLE_MODULE_GENERATOR +# include "include/secp256k1_generator.h" +#endif + #ifdef ENABLE_MODULE_RANGEPROOF # include "include/secp256k1_rangeproof.h" # include "modules/rangeproof/pedersen.h" @@ -614,6 +618,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 19cf141a5..8b9ef34b3 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5012,6 +5012,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 @@ -5139,6 +5143,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 21bfb3c91a7b04d2caadf4e3f727ec13fa2df5bd Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 6 Jul 2016 13:46:23 +0200 Subject: [PATCH 07/58] 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 0afeed32d..b8fca4725 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 66fb28696..721792d8a 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) { @@ -42,7 +42,7 @@ static void bench_rangeproof(void* arg) { 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 c743b6d73..342798871 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 84dd20b4d..14d9920eb 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 991c60b36..69f22e382 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 85f94bc42..5aa3a68d4 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 efd43e120..1f824020e 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 c8815afb8..82cf426d4 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 a2bc6604f9f6637c2e8d3750eb87ff14ea290114 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 6 Jul 2016 15:44:09 +0000 Subject: [PATCH 08/58] 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 b8fca4725..528b6662f 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 721792d8a..9724f5b46 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) { @@ -42,7 +42,7 @@ static void bench_rangeproof(void* arg) { 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 07ad32e71..94cdc4487 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 342798871..4427667a6 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 5aa3a68d4..840a09ae6 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 1f824020e..8d4dc6547 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 82cf426d4..29b0a659e 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, 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, 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(!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 59304cb66..1fc3a73ff 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -38,6 +38,9 @@ static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b /** 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 db1ebf94b..97401968b 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 4f9ed61fe..ad4d050df 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 c80e70c5a..37af136e4 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 c174f0c60907d7cd994351d296de179c5f496b6c Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 21 Apr 2016 22:22:39 +0000 Subject: [PATCH 09/58] 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 d5c9f81e8..98c0091a7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -189,3 +189,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 66b0eb5df..0e06a7103 100644 --- a/configure.ac +++ b/configure.ac @@ -144,6 +144,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(jni, AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]), [use_jni=$enableval], @@ -459,6 +464,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 @@ -484,12 +493,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.]) @@ -503,6 +520,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]) @@ -521,6 +541,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_JNI], [test x"$use_jni" == 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 000000000..c3175ce0a --- /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 000000000..e926ffce1 --- /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 000000000..0de178fc1 --- /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 000000000..e307de16b --- /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 000000000..15ab998cc --- /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 000000000..ff8d87f4a --- /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 37ba046e8..f64e81fea 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -625,3 +625,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 8b9ef34b3..4c86f7526 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5020,6 +5020,10 @@ void run_ecdsa_openssl(void) { # include "modules/rangeproof/tests_impl.h" #endif +#ifdef ENABLE_MODULE_WHITELIST +# include "modules/whitelist/tests_impl.h" +#endif + int main(int argc, char **argv) { unsigned char seed16[16] = {0}; unsigned char run32[32] = {0}; @@ -5151,6 +5155,11 @@ int main(int argc, char **argv) { run_rangeproof_tests(); #endif +#ifdef ENABLE_MODULE_WHITELIST + /* Key whitelisting tests */ + run_whitelist_tests(); +#endif + secp256k1_rand256(run32); printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); From 0c17f7972a907f6d71322ddb54fa99a1e095e5cd Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 1 Jul 2016 15:51:07 +0000 Subject: [PATCH 10/58] 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 98c0091a7..ec3c64ccf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -193,3 +193,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 0e06a7103..c4442eb25 100644 --- a/configure.ac +++ b/configure.ac @@ -154,6 +154,11 @@ AC_ARG_ENABLE(jni, [use_jni=$enableval], [use_jni=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], [Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) @@ -182,6 +187,12 @@ else CFLAGS="$CFLAGS -O3" 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 save_cross_compiling=$cross_compiling cross_compiling=no @@ -468,6 +479,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 @@ -494,6 +509,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 @@ -502,10 +518,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 @@ -523,6 +542,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]) @@ -545,6 +567,7 @@ AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x" AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == 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 000000000..57f2afb64 --- /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 000000000..51ece21fc --- /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 000000000..2a70c50ce --- /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 000000000..20ac49316 --- /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 000000000..e7bd4db15 --- /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 000000000..f58026dec --- /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 000000000..13397830a --- /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 f64e81fea..9aedec5b2 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -629,3 +629,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 4c86f7526..5c8dc8b17 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5024,6 +5024,10 @@ void run_ecdsa_openssl(void) { # include "modules/whitelist/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF +# include "modules/surjection/tests_impl.h" +#endif + int main(int argc, char **argv) { unsigned char seed16[16] = {0}; unsigned char run32[32] = {0}; @@ -5160,6 +5164,10 @@ int main(int argc, char **argv) { run_whitelist_tests(); #endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF + run_surjection_tests(); +#endif + secp256k1_rand256(run32); printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); From e609591b668594ab1dbd8c0d69e18afc635472f9 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 22 Apr 2017 18:31:28 +0000 Subject: [PATCH 11/58] 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 29b0a659e..ea3c12e9a 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 7878a298b21637b0f2effbc5cd84f58e25f6beae Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 May 2017 13:50:58 +0000 Subject: [PATCH 12/58] 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 13397830a..25afaa0c1 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 1e2d5c1a269c243c57c6866b7323cc717ac5f784 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 May 2017 16:54:14 +0000 Subject: [PATCH 13/58] 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 2a70c50ce..f57ddba1d 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 25afaa0c1..b24f0c86f 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 417bb0643ff64aeb3035e491c8bc2bbfdab97bb4 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 3 May 2017 17:06:39 +0000 Subject: [PATCH 14/58] surjectionproof: rename unit test functions to be more consistent with other modules --- src/modules/surjection/tests_impl.h | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index b24f0c86f..08742e149 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); - - 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(0); + test_input_selection(1); + test_input_selection(5); + test_input_selection(100); + test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + + 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 0d817020d96048224a06af698f789cbf641a4ce7 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 3 May 2017 18:08:31 +0000 Subject: [PATCH 15/58] 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 528b6662f..a41d2be5b 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 4427667a6..f16a0abf3 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 ea3c12e9a..f604aa607 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 7f1751560906ba9b96d2079eefedfabee0b81e02 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 9 May 2017 01:46:55 +0200 Subject: [PATCH 16/58] 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 a41d2be5b..c71f432e5 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 9b8a9d91eb6e06172ce131c857aa40d231de476d Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 26 Jun 2017 17:08:47 +0000 Subject: [PATCH 17/58] 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 c3175ce0a..194128834 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 0de178fc1..7445d6e39 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 e307de16b..dcaf3baab 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 37c57de083a05e251f6d701cb705c6c9af926407 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 27 Jun 2017 12:14:29 +0200 Subject: [PATCH 18/58] 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 | 51 ++++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h index 194128834..e1e170221 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 7445d6e39..8ac93dedc 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 dcaf3baab..647e237be 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; + + 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)) == 0); + 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 b51886e722c90545691de9f38cb4809542a198ba Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 10 Jul 2017 18:51:16 +0200 Subject: [PATCH 19/58] 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 e1e170221..c536c11a7 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 8ac93dedc..0b2d6c9c9 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 647e237be..7cf1fb096 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 526c65499f44ef95d7139e804067140654025bc0 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 10 Jul 2017 18:56:00 +0200 Subject: [PATCH 20/58] 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 c71f432e5..866fdca5c 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 b0e9aa828fb9bd3d5288103d6f3e6258f27465b9 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 15 Aug 2017 22:39:26 -0400 Subject: [PATCH 21/58] 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 bc3c514f2..69933e999 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 ec1ef040f54c548d62c4f124e865042b6ed33a7c Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 30 Aug 2017 17:59:26 +0000 Subject: [PATCH 22/58] 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 94cdc4487..46032f442 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 a707865bc57e48ef96dda48f2e6bb75c2ac09523 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 30 Aug 2017 18:08:40 +0000 Subject: [PATCH 23/58] 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 7743b06e4..57f6ca079 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 eee51fac2..8b1a5acb5 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 52a9f8f8f3473dbe013856f9ad3cc948a7064c8e Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 13 Feb 2018 16:28:30 -0500 Subject: [PATCH 24/58] 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 e926ffce1..8d0bea633 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 b1f31bc4b609dc19f78ecd4eda10c76b27afe9a5 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 16 Mar 2018 13:55:55 +0000 Subject: [PATCH 25/58] Add whitelisting benchmark --- src/bench_whitelist.c | 104 ++++++++++++++++++++++ src/modules/whitelist/Makefile.am.include | 6 ++ 2 files changed, 110 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 000000000..d011e9214 --- /dev/null +++ b/src/bench_whitelist.c @@ -0,0 +1,104 @@ +/********************************************************************** + * 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 "bench.h" +#include "util.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) { + bench_data* data = (bench_data*)arg; + 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) { + char str[32]; + sprintf(str, "whitelist_%i", (int)data->n_keys); + run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, 1); +} + +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; + + 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); + } + + secp256k1_context_destroy(data.ctx); + return(0); +} diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include index 8d0bea633..0dc5a64db 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 0c77ae9a755e4c4ba8d41a95c0ae122c96ced184 Mon Sep 17 00:00:00 2001 From: datavetaren Date: Wed, 16 May 2018 05:02:21 +0200 Subject: [PATCH 26/58] 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 46032f442..8646cf9ce 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 a3a1800ba65ef69fa36cb3aa2cebe59f8839ba74 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 May 2018 14:56:14 +0200 Subject: [PATCH 27/58] 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 f57ddba1d..c67d4c0d3 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 9b2cf1708d11464a86db29c84ed60829f871e2de Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 May 2018 15:59:01 +0200 Subject: [PATCH 28/58] 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 08742e149..a0856e229 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 fb75faa147d7046fe92490c580e5539db4bafe0d Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 24 May 2018 13:23:08 +0200 Subject: [PATCH 29/58] 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 f604aa607..429ed5b55 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 fb1ba329aa3f8bc95475ecaa4af91b385c12bd82 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 20 Jun 2018 11:43:18 -0400 Subject: [PATCH 30/58] 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 57f6ca079..14a68754f 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 e065d7df9f0c3e49e5bf88552f105d67102f4454 Mon Sep 17 00:00:00 2001 From: "Frank V. Castellucci" Date: Wed, 25 Jul 2018 13:30:11 -0400 Subject: [PATCH 31/58] 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 866fdca5c..bdd2bcc11 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 44fe43d75799a20524c74fc2ec0a68e46888a72f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 17:58:39 +0000 Subject: [PATCH 32/58] 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 429ed5b55..ef7cf2221 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 ae14e8a9d8c294f83e155ca26672a0d12ba91538 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 16:23:08 +0000 Subject: [PATCH 33/58] 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 8d4dc6547..8056f0a78 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 32d7526cd5682a8c8430ffbe691c7298d42cb317 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 16:23:35 +0000 Subject: [PATCH 34/58] 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 14a68754f..c2743a6e0 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 8646cf9ce..12447591d 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 f16a0abf3..021a5bf9f 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 60c173b6408e57e6ff4a2fb85be855f0ad7f2c74 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 2 Oct 2018 18:03:05 +0000 Subject: [PATCH 35/58] 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 bdd2bcc11..22cc53eb0 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 021a5bf9f..3fe1693f4 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 2ccf885419eeacc4f071960f4ecb64acea2f5a40 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Oct 2018 05:17:26 +0000 Subject: [PATCH 36/58] 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 3fe1693f4..d3f1dd337 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 85fd42fb7eb9d15e4906385e10e74b06d2db8ea0 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Oct 2018 05:15:34 +0000 Subject: [PATCH 37/58] 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 8b1a5acb5..20acf2e7d 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 ef7cf2221..e8cf1f177 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 71c5fe0f6e0f5cedf7c078b98efc7c9ef273deb4 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 17 Apr 2018 22:34:01 +0000 Subject: [PATCH 38/58] 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 57f2afb64..38f67990f 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 53ad841cafa3bcb94b65409aec91fd7043533cf7 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 19 Nov 2018 15:43:08 +0000 Subject: [PATCH 39/58] 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 15ab998cc..89d19caff 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 ed59fbe8b7d60fed0fdcc62a2aa94249ab0e9d21 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 20 Dec 2018 20:48:19 +0000 Subject: [PATCH 40/58] Add trivial ecmult_multi algorithm. It is selected when no scratch space is given and just multiplies and adds the points. --- src/ecmult.h | 3 ++- src/ecmult_impl.h | 29 +++++++++++++++++++++++++++++ src/tests.c | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/ecmult.h b/src/ecmult.h index ea1cd8a21..3d75a960f 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -37,7 +37,8 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge * Chooses the right algorithm for a given number of points and scratch space * size. Resets and overwrites the given scratch space. If the points do not * fit in the scratch space the algorithm is repeatedly run with batches of - * points. + * points. If no scratch space is given then a simple algorithm is used that + * simply multiplies the points with the corresponding scalars and adds them up. * Returns: 1 on success (including when inp_g_sc is NULL and n is 0) * 0 if there is not enough scratch space for a single point or * callback returns 0 diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index c00578bed..1f113b520 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -1083,6 +1083,32 @@ static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) { return res; } +/* Computes ecmult_multi by simply multiplying and adding each point. Does not + * require a scratch space */ +static int secp256k1_ecmult_multi_var_simple(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) { + size_t point_idx; + secp256k1_scalar szero; + secp256k1_gej tmpj; + + secp256k1_scalar_set_int(&szero, 0); + /* r = inp_g_sc*G */ + secp256k1_gej_set_infinity(r); + secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc); + for (point_idx = 0; point_idx < n_points; point_idx++) { + secp256k1_ge point; + secp256k1_gej pointj; + secp256k1_scalar scalar; + if (!cb(&scalar, &point, point_idx, cbdata)) { + return 0; + } + /* r += scalar*point */ + secp256k1_gej_set_ge(&pointj, &point); + secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL); + secp256k1_gej_add_var(r, r, &tmpj, NULL); + } + return 1; +} + typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t); static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { size_t i; @@ -1101,6 +1127,9 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2 secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc); return 1; } + if (scratch == NULL) { + return secp256k1_ecmult_multi_var_simple(ctx, r, inp_g_sc, cb, cbdata, n); + } max_points = secp256k1_pippenger_max_points(scratch); if (max_points == 0) { diff --git a/src/tests.c b/src/tests.c index 5c8dc8b17..492c7ca3d 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2968,6 +2968,7 @@ void run_ecmult_multi_tests(void) { test_ecmult_multi_pippenger_max_points(); scratch = secp256k1_scratch_create(&ctx->error_callback, 819200); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); + test_ecmult_multi(NULL, secp256k1_ecmult_multi_var); test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single); test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single); secp256k1_scratch_destroy(scratch); From 8c444eef6a9a7033382ff9203cdebb34ca015c52 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 3 Jan 2019 14:18:39 -0500 Subject: [PATCH 41/58] 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 e8cf1f177..d2ce3d5c6 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 12b0e5dda790dba049a6426e1dca0664d7c2217e Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 3 Jan 2019 13:45:36 -0500 Subject: [PATCH 42/58] Enable more builds with rest of experimental flags --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c4154e9a8..dbb9d8409 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,11 @@ cache: - src/java/guava/ env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no GENERATOR=no RANGEPROOF=no WHITELIST=no - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar 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 @@ -65,5 +67,5 @@ before_script: ./autogen.sh script: - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-module-rangeproof=$RANGEPROOF --enable-module-whitelist=$WHITELIST --enable-module-generator=$GENERATOR --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD os: linux From 2b2429dfaa694aec7a68e328bed8f5b32533ab58 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 3 Jan 2019 19:17:05 +0000 Subject: [PATCH 43/58] 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 e8cf1f177..99c86d6e9 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 826080049148998af26ed0d227f9ee4c845ccc87 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 3 Apr 2018 22:06:07 +0000 Subject: [PATCH 44/58] 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 1fc3a73ff..57389da3a 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -106,4 +106,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); +/** 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 97401968b..38390c495 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) @@ -955,4 +956,94 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } +#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 ad4d050df..11fac6c23 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) @@ -729,4 +731,102 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } +#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 37af136e4..d6fdeadcc 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -112,4 +112,9 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } +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 492c7ca3d..55dd7dcdf 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1007,12 +1007,122 @@ void scalar_test(void) { } +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++) { scalar_test(); } + scalar_chacha_tests(); + { /* (-1)+1 should be zero. */ secp256k1_scalar s, o; From f0e4bb9283156b657097f32be33ef9416b6e4350 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 9 May 2018 15:37:35 +0000 Subject: [PATCH 45/58] 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 +- Makefile.am | 4 + configure.ac | 14 + include/secp256k1_schnorrsig.h | 118 ++++ src/bench_schnorrsig.c | 128 ++++ src/modules/schnorrsig/Makefile.am.include | 8 + src/modules/schnorrsig/main_impl.h | 338 ++++++++++ src/modules/schnorrsig/tests_impl.h | 726 +++++++++++++++++++++ src/secp256k1.c | 25 + src/tests.c | 9 + 10 files changed, 1371 insertions(+), 1 deletion(-) 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 55d325aee..905be9873 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/Makefile.am b/Makefile.am index ec3c64ccf..caf30e25e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,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 c4442eb25..44da65707 100644 --- a/configure.ac +++ b/configure.ac @@ -129,6 +129,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 is no)]), [enable_module_recovery=$enableval], @@ -463,6 +468,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 @@ -510,6 +519,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 @@ -530,6 +540,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 @@ -560,6 +573,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"]) diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h new file mode 100644 index 000000000..e507c63f4 --- /dev/null +++ b/include/secp256k1_schnorrsig.h @@ -0,0 +1,118 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +/** 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); +#endif diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c new file mode 100644 index 000000000..d24510921 --- /dev/null +++ b/src/bench_schnorrsig.c @@ -0,0 +1,128 @@ +/********************************************************************** + * 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" + +#define MAX_SIGS (32768) + +typedef struct { + secp256k1_context *ctx; + secp256k1_scratch_space *scratch; + size_t n; + const unsigned char **pk; + const secp256k1_schnorrsig **sigs; + const unsigned char **msgs; +} bench_schnorrsig_data; + +void bench_schnorrsig_sign(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i; + unsigned char sk[32] = "benchmarkexample secrettemplate"; + unsigned char msg[32] = "benchmarkexamplemessagetemplate"; + secp256k1_schnorrsig sig; + + for (i = 0; i < 1000; 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) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i; + + for (i = 0; i < 1000; 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) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i, j; + const secp256k1_pubkey **pk = (const secp256k1_pubkey **)malloc(data->n * sizeof(*pk)); + + CHECK(pk != NULL); + for (j = 0; j < MAX_SIGS/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) { + size_t i; + bench_schnorrsig_data data; + + 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(MAX_SIGS * sizeof(unsigned char *)); + data.msgs = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *)); + data.sigs = (const secp256k1_schnorrsig **)malloc(MAX_SIGS * sizeof(secp256k1_schnorrsig *)); + + for (i = 0; i < MAX_SIGS; 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, 1000); + run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, 1000); + for (i = 1; i <= MAX_SIGS; i *= 2) { + char name[64]; + sprintf(name, "schnorrsig_batch_verify_%d", (int) i); + + data.n = i; + run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, MAX_SIGS); + } + + for (i = 0; i < MAX_SIGS; 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.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 000000000..a82bafe43 --- /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 000000000..bebb49fe9 --- /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) + */ +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, 32); + } + 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) + */ +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 < (size_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->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 000000000..e067058aa --- /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, 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(scratch); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 9aedec5b2..95ad5a86f 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -343,6 +343,27 @@ 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 secp256k1_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) data; + (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); + } + 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; @@ -614,6 +635,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 55dd7dcdf..60c451967 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5119,6 +5119,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 @@ -5247,6 +5251,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 77d5b4ac7dc2eaea96fdd75636bd2dc621e4104b Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Dec 2018 22:12:35 +0000 Subject: [PATCH 46/58] 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 caf30e25e..86729cfc1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,6 +182,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 44da65707..204d11d1b 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,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 is no)]), [enable_module_recovery=$enableval], @@ -472,6 +477,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 @@ -520,8 +529,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.]) @@ -543,6 +560,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 @@ -574,6 +594,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 000000000..035adfe0e --- /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 000000000..6099ab727 --- /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 000000000..d6d9e17d1 --- /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->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 000000000..79688af24 --- /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); + /* If a scratch space is given it shouldn't be too small */ + scratch_small = secp256k1_scratch_space_create(ctx, 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, pk_hash, pk, 2) == 0); + secp256k1_scratch_space_destroy(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(scratch); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 95ad5a86f..a3f6726be 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -639,6 +639,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 60c451967..b7714e532 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5123,6 +5123,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 @@ -5256,6 +5260,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 2fc700a943a52ecff7e6488b37de1a4f42da5f56 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 22 Dec 2018 22:15:19 +0000 Subject: [PATCH 47/58] 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 035adfe0e..ead762d2c 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 6099ab727..0cd254d8a 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 000000000..5aebfa205 --- /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 892ab773f329f2993fdebb19469c251c12759da9 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 27 Mar 2018 21:26:43 +0000 Subject: [PATCH 48/58] generator: add constant G and H generators --- include/secp256k1_generator.h | 6 ++++++ src/modules/generator/main_impl.h | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index c2743a6e0..9789250a2 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -21,6 +21,12 @@ typedef struct { unsigned char data[64]; } secp256k1_generator; +/** Standard secp256k1 generator G */ +SECP256K1_API extern const secp256k1_generator secp256k1_generator_const_g; + +/** Alternate secp256k1 generator from Elements Alpha */ +SECP256K1_API extern const secp256k1_generator secp256k1_generator_const_h; + /** Parse a 33-byte generator byte sequence into a generator object. * * Returns: 1 if input contains a valid generator. diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 12447591d..8edaa7a72 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -14,6 +14,28 @@ #include "hash.h" #include "scalar.h" +/** Standard secp256k1 generator */ +const secp256k1_generator secp256k1_generator_const_g = {{ + 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, + 0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11, 0x08, 0xa8, + 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8 +}}; + +/** Alternate secp256k1 generator, used in Elements Alpha. + * Computed as the hash of the above G, DER-encoded with 0x04 (uncompressed pubkey) as its flag byte. + * import hashlib + * C = EllipticCurve ([F (0), F (7)]) + * G_bytes = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex') + * H = C.lift_x(int(hashlib.sha256(G_bytes).hexdigest(),16)) + */ +const secp256k1_generator secp256k1_generator_const_h = {{ + 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 +}}; + static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { int succeed; succeed = secp256k1_fe_set_b32(&ge->x, &gen->data[0]); From db394478b179b50a96b03e5823169f8eaa5f31de Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 28 Mar 2018 14:59:43 +0000 Subject: [PATCH 49/58] split Pedersen commitments out into their own module --- .gitignore | 2 + Makefile.am | 4 + configure.ac | 26 +- include/secp256k1_commitment.h | 168 +++++++++ include/secp256k1_rangeproof.h | 155 +-------- src/bench_rangeproof.c | 9 +- src/modules/commitment/Makefile.am.include | 4 + src/modules/commitment/main_impl.h | 215 ++++++++++++ .../pedersen_impl.h | 4 +- src/modules/commitment/tests_impl.h | 228 +++++++++++++ src/modules/rangeproof/Makefile.am.include | 2 - src/modules/rangeproof/main_impl.h | 204 +---------- src/modules/rangeproof/pedersen.h | 22 -- src/modules/rangeproof/rangeproof_impl.h | 3 +- src/modules/rangeproof/tests_impl.h | 318 ++++-------------- src/secp256k1.c | 10 +- src/tests.c | 4 + 17 files changed, 734 insertions(+), 644 deletions(-) create mode 100644 include/secp256k1_commitment.h create mode 100644 src/modules/commitment/Makefile.am.include create mode 100644 src/modules/commitment/main_impl.h rename src/modules/{rangeproof => commitment}/pedersen_impl.h (94%) create mode 100644 src/modules/commitment/tests_impl.h delete mode 100644 src/modules/rangeproof/pedersen.h diff --git a/.gitignore b/.gitignore index 905be9873..55480b1fd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ bench_sign bench_verify bench_recover bench_internal +bench_generator +bench_rangeproof tests exhaustive_tests gen_context diff --git a/Makefile.am b/Makefile.am index 86729cfc1..8fc761187 100644 --- a/Makefile.am +++ b/Makefile.am @@ -194,6 +194,10 @@ if ENABLE_MODULE_GENERATOR include src/modules/generator/Makefile.am.include endif +if ENABLE_MODULE_COMMITMENT +include src/modules/commitment/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 204d11d1b..c6a5ecf64 100644 --- a/configure.ac +++ b/configure.ac @@ -149,8 +149,13 @@ AC_ARG_ENABLE(module_generator, [enable_module_generator=$enableval], [enable_module_generator=no]) +AC_ARG_ENABLE(module_commitment, + AS_HELP_STRING([--enable-module-commitment],[enable Pedersen commitments module (default is no)]), + [enable_module_commitment=$enableval], + [enable_module_commitment=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 zero-knowledge range proofs module (default is no)]), [enable_module_rangeproof=$enableval], [enable_module_rangeproof=no]) @@ -489,8 +494,12 @@ 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_commitment" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_COMMITMENT, 1, [Define this symbol to enable the Pedersen commitment 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]) + AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the zero knowledge range proof module]) fi if test x"$enable_module_whitelist" = x"yes"; then @@ -525,6 +534,7 @@ 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([Building ECDH module: $enable_module_ecdh]) AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator]) + AC_MSG_NOTICE([Building Pedersen commitment module: $enable_module_commitment]) 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]) @@ -540,8 +550,14 @@ if test x"$enable_experimental" = x"yes"; then fi if test x"$enable_module_generator" != x"yes"; then + if test x"$enable_module_commitment" = x"yes"; then + AC_MSG_ERROR([Commitment module requires the generator module. Use --enable-module-generator to allow.]) + fi + fi + + if test x"$enable_module_commitment" != 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.]) + AC_MSG_ERROR([Rangeproof module requires the commitment module. Use --enable-module-commitment to allow.]) fi fi @@ -569,6 +585,9 @@ else 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_commitment" = x"yes"; then + AC_MSG_ERROR([Pedersen commitment 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 @@ -597,6 +616,7 @@ AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = 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_COMMITMENT], [test x"$enable_module_commitment" = 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_JNI], [test x"$use_jni" == x"yes"]) diff --git a/include/secp256k1_commitment.h b/include/secp256k1_commitment.h new file mode 100644 index 000000000..20a5756ae --- /dev/null +++ b/include/secp256k1_commitment.h @@ -0,0 +1,168 @@ +#ifndef _SECP256K1_COMMITMENT_ +# define _SECP256K1_COMMITMENT_ + +# include "secp256k1.h" +# include "secp256k1_generator.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#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 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); + +/** 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 + * (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 + +#endif diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 22cc53eb0..be8d0df73 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -3,6 +3,7 @@ # include "secp256k1.h" # include "secp256k1_generator.h" +# include "secp256k1_commitment.h" # ifdef __cplusplus extern "C" { @@ -10,160 +11,6 @@ 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 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); - -/** 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 - * (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 -); - -/** 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. diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 9724f5b46..3bd0cf1df 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -6,6 +6,7 @@ #include +#include "include/secp256k1_commitment.h" #include "include/secp256k1_rangeproof.h" #include "util.h" #include "bench.h" @@ -28,10 +29,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, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v, &secp256k1_generator_const_h)); data->len = 5134; - CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, NULL, 0, secp256k1_generator_h)); - CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h)); + 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_const_h)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, &secp256k1_generator_const_h)); } static void bench_rangeproof(void* arg) { @@ -42,7 +43,7 @@ static void bench_rangeproof(void* arg) { int j; uint64_t minv; uint64_t maxv; - j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h); + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, &secp256k1_generator_const_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/commitment/Makefile.am.include b/src/modules/commitment/Makefile.am.include new file mode 100644 index 000000000..132d6fe66 --- /dev/null +++ b/src/modules/commitment/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_commitment.h +noinst_HEADERS += src/modules/commitment/main_impl.h +noinst_HEADERS += src/modules/commitment/pedersen_impl.h +noinst_HEADERS += src/modules/commitment/tests_impl.h diff --git a/src/modules/commitment/main_impl.h b/src/modules/commitment/main_impl.h new file mode 100644 index 000000000..fe75e0660 --- /dev/null +++ b/src/modules/commitment/main_impl.h @@ -0,0 +1,215 @@ +/********************************************************************** + * 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_COMMITMENT_MAIN +#define SECP256K1_MODULE_COMMITMENT_MAIN + +#include "group.h" + +#include "modules/commitment/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(F(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16))) + * sage: '%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, + 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); + 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; +} + +#endif diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/commitment/pedersen_impl.h similarity index 94% rename from src/modules/rangeproof/pedersen_impl.h rename to src/modules/commitment/pedersen_impl.h index 69f22e382..b5b600370 100644 --- a/src/modules/rangeproof/pedersen_impl.h +++ b/src/modules/commitment/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_MODULE_COMMITMENT_PEDERSEN +#define SECP256K1_MODULE_COMMITMENT_PEDERSEN #include diff --git a/src/modules/commitment/tests_impl.h b/src/modules/commitment/tests_impl.h new file mode 100644 index 000000000..99756ac77 --- /dev/null +++ b/src/modules/commitment/tests_impl.h @@ -0,0 +1,228 @@ +/********************************************************************** + * 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_COMMITMENT_TESTS +#define SECP256K1_MODULE_COMMITMENT_TESTS + +#include + +#include "group.h" +#include "scalar.h" +#include "testrand.h" +#include "util.h" + +#include "include/secp256k1_commitment.h" + +static void test_commitment_api(void) { + secp256k1_pedersen_commitment commit; + const secp256k1_pedersen_commitment *commit_ptr = &commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + const unsigned char *blind_ptr = blind; + unsigned char *blind_out_ptr = blind_out; + uint64_t val = secp256k1_rand32(); + + 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; + + 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(blind); + CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, &secp256k1_generator_const_h) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, &secp256k1_generator_const_h) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, &secp256k1_generator_const_h) != 0); + CHECK(ecount == 2); + + CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, &secp256k1_generator_const_h) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, &secp256k1_generator_const_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_const_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); + + 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]; + 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]; + bptr[i] = &blinds[i * 32]; + } + totalv = 0; + for (i = 0; i < inputs; i++) { + values[i] = secp256k1_rands64(0, INT64_MAX - totalv); + totalv += values[i]; + } + 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); + } + 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_const_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_const_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)); +} + +#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)); + + /* Cleanup */ + for (i = 0; i < n_generators; i++) { + free(generator_blind[i]); + free(pedersen_blind[i]); + } +} +#undef MAX_N_GENS + +void run_commitment_tests(void) { + int i; + test_commitment_api(); + for (i = 0; i < 10*count; i++) { + test_pedersen(); + } + test_multiple_generators(); +} + +#endif diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include index ff8b8d380..5272f2293 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 d3f1dd337..9eebe38ba 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -9,211 +9,11 @@ #include "group.h" -#include "modules/rangeproof/pedersen_impl.h" +#include "modules/commitment/main_impl.h" + #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(F(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16))) - * sage: '%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, - 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); - 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; diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h deleted file mode 100644 index 14d9920eb..000000000 --- a/src/modules/rangeproof/pedersen.h +++ /dev/null @@ -1,22 +0,0 @@ -/********************************************************************** - * Copyright (c) 2014, 2015 Gregory Maxwell * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_PEDERSEN_H_ -#define _SECP256K1_PEDERSEN_H_ - -#include "ecmult_gen.h" -#include "group.h" -#include "scalar.h" - -#include - -/** Multiply a small number with the generator: r = gn*G2 */ -static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp); - -/* sec * G + value * G2. */ -static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp); - -#endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 8056f0a78..39768a7ac 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -12,10 +12,9 @@ #include "group.h" #include "rangeproof.h" #include "hash_impl.h" -#include "pedersen_impl.h" #include "util.h" -#include "modules/rangeproof/pedersen.h" +#include "modules/commitment/pedersen_impl.h" #include "modules/rangeproof/borromean.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index c4cc666fa..38e259e79 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -14,68 +14,9 @@ #include "testrand.h" #include "util.h" +#include "include/secp256k1_commitment.h" #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]; @@ -90,41 +31,41 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c size_t ext_commit_len = sizeof(ext_commit); secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(none, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(sign, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(vrfy, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, NULL, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, NULL, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, NULL, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, NULL, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, NULL, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, vmin - 1, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, &secp256k1_generator_const_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); + CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_h) != 0); { int exp; int mantissa; @@ -153,26 +94,26 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c { 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(secp256k1_rangeproof_verify(none, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(sign, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, &secp256k1_generator_const_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(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, &secp256k1_generator_const_h) == 0); CHECK(*ecount == 23); - CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h) == 0); CHECK(*ecount == 23); CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0); CHECK(*ecount == 24); @@ -185,13 +126,13 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c 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(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_const_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(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_const_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(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_const_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(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_const_h) != 0); CHECK(*ecount == 27); CHECK(min_value == vmin); @@ -200,29 +141,29 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c 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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(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_const_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(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, &secp256k1_generator_const_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); @@ -247,8 +188,6 @@ static void test_api(void) { 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); } @@ -259,63 +198,6 @@ static void test_api(void) { secp256k1_context_destroy(both); } -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_rand32() & 7) + 1; - outputs = (secp256k1_rand32() & 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_rands64(0, INT64_MAX - totalv); - totalv += values[i]; - } - 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); - } - 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]; @@ -411,7 +293,7 @@ static void test_rangeproof(void) { secp256k1_rand256(blind); for (i = 0; i < 11; i++) { v = testvs[i]; - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { const unsigned char *input_message = NULL; size_t input_message_len = 0; @@ -427,10 +309,10 @@ static 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, NULL, 0, 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_const_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h)); if (input_message != NULL) { CHECK(memcmp(message, input_message, input_message_len) == 0); } @@ -443,9 +325,9 @@ static 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, 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_const_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, NULL, 0, &secp256k1_generator_const_h)); CHECK(memcmp(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); @@ -453,11 +335,11 @@ static void test_rangeproof(void) { /* 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(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), &secp256k1_generator_const_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(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h)); + CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), &secp256k1_generator_const_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), &secp256k1_generator_const_h)); CHECK(memcmp(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); @@ -466,34 +348,34 @@ static void test_rangeproof(void) { } secp256k1_rand256(blind); v = INT64_MAX - 1; - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); for (i = 0; i < 19; i++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h)); - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, &secp256k1_generator_const_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_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)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), &secp256k1_generator_const_h)); } secp256k1_rand256(blind); { /*Malleability test.*/ v = secp256k1_rands64(0, 255); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, &secp256k1_generator_const_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)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len + 1, NULL, 0, &secp256k1_generator_const_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)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h)); proof[i >> 3] ^= 1 << (i & 7); } - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h)); CHECK(minv <= v); CHECK(maxv >= v); } @@ -507,7 +389,7 @@ static void test_rangeproof(void) { vmin = secp256k1_rands64(0, v); } secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); len = 5134; exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); if (exp < 0) { @@ -517,10 +399,10 @@ static 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, 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_const_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h)); for (j = 0; j < mlen; j++) { CHECK(message[j] == 0); } @@ -529,7 +411,7 @@ static void test_rangeproof(void) { 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)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, &secp256k1_generator_const_h)); memcpy(&commit2, &commit, sizeof(commit)); } for (j = 0; j < 5; j++) { @@ -538,72 +420,10 @@ static void test_rangeproof(void) { } for (k = 0; k < 128; k++) { len = k; - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, &secp256k1_generator_const_h)); } len = secp256k1_rands64(0, 3072); - 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)); - - /* Cleanup */ - for (i = 0; i < n_generators; i++) { - free(generator_blind[i]); - free(pedersen_blind[i]); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, &secp256k1_generator_const_h)); } } @@ -696,14 +516,10 @@ void run_rangeproof_tests(void) { test_api(); test_rangeproof_fixed_vectors(); test_pedersen_commitment_fixed_vector(); - for (i = 0; i < 10*count; i++) { - test_pedersen(); - } for (i = 0; i < 10*count; i++) { test_borromean(); } test_rangeproof(); - test_multiple_generators(); } #endif diff --git a/src/secp256k1.c b/src/secp256k1.c index a3f6726be..468826124 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -23,10 +23,12 @@ # include "include/secp256k1_generator.h" #endif +#ifdef ENABLE_MODULE_COMMITMENT +# include "include/secp256k1_commitment.h" +#endif + #ifdef ENABLE_MODULE_RANGEPROOF # include "include/secp256k1_rangeproof.h" -# include "modules/rangeproof/pedersen.h" -# include "modules/rangeproof/rangeproof.h" #endif #define ARG_CHECK(cond) do { \ @@ -651,6 +653,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/generator/main_impl.h" #endif +#ifdef ENABLE_MODULE_COMMITMENT +# include "modules/commitment/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 b7714e532..9eb803c6b 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5135,6 +5135,10 @@ void run_ecdsa_openssl(void) { # include "modules/generator/tests_impl.h" #endif +#ifdef ENABLE_MODULE_COMMITMENT +# include "modules/commitment/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RANGEPROOF # include "modules/rangeproof/tests_impl.h" #endif From 0ac92f4790481d7c166baa0e2317159cd3dc2723 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 28 Mar 2018 15:24:09 +0000 Subject: [PATCH 50/58] commitment: allow setting the blinding factor generator for Pedersen commitments We now use ecmult_const rather than ecmult_gen, which will slow down the generation of Pedersen commitments. However as far as I'm aware, this is never the bottleneck in proof generation. --- include/secp256k1_commitment.h | 10 +++-- src/bench_rangeproof.c | 2 +- src/modules/commitment/main_impl.h | 14 ++++--- src/modules/commitment/pedersen_impl.h | 49 +++++++++-------------- src/modules/commitment/tests_impl.h | 50 ++++++++++++------------ src/modules/rangeproof/rangeproof_impl.h | 9 +++-- src/modules/rangeproof/tests_impl.h | 10 ++--- 7 files changed, 70 insertions(+), 74 deletions(-) diff --git a/include/secp256k1_commitment.h b/include/secp256k1_commitment.h index 20a5756ae..dd54e9a75 100644 --- a/include/secp256k1_commitment.h +++ b/include/secp256k1_commitment.h @@ -63,10 +63,11 @@ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); * 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) + * In: ctx: pointer to a context object (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' + * value_gen: value generator 'h' + * blind_gen: blinding factor generator 'g' * 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. @@ -76,8 +77,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( 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); + const secp256k1_generator *value_gen, + const secp256k1_generator *blind_gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); /** Computes the sum of multiple positive and negative blinding factors. * Returns 1: Sum successfully computed. diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c index 3bd0cf1df..67a987c1a 100644 --- a/src/bench_rangeproof.c +++ b/src/bench_rangeproof.c @@ -29,7 +29,7 @@ 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, &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->v, &secp256k1_generator_const_h, &secp256k1_generator_const_g)); data->len = 5134; CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, NULL, 0, &secp256k1_generator_const_h)); CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, &secp256k1_generator_const_h)); diff --git a/src/modules/commitment/main_impl.h b/src/modules/commitment/main_impl.h index fe75e0660..4f3a444d7 100644 --- a/src/modules/commitment/main_impl.h +++ b/src/modules/commitment/main_impl.h @@ -78,22 +78,24 @@ 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, const secp256k1_generator* gen) { - secp256k1_ge genp; +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* value_gen, const secp256k1_generator* blind_gen) { + secp256k1_ge value_genp; + secp256k1_ge blind_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); + ARG_CHECK(value_gen != NULL); + ARG_CHECK(blind_gen != NULL); + secp256k1_generator_load(&value_genp, value_gen); + secp256k1_generator_load(&blind_genp, blind_gen); secp256k1_scalar_set_b32(&sec, blind, &overflow); if (!overflow) { - secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); + secp256k1_pedersen_ecmult(&rj, &sec, value, &value_genp, &blind_genp); if (!secp256k1_gej_is_infinity(&rj)) { secp256k1_ge_set_gej(&r, &rj); secp256k1_pedersen_commitment_save(commit, &r); diff --git a/src/modules/commitment/pedersen_impl.h b/src/modules/commitment/pedersen_impl.h index b5b600370..a6d9dfb1c 100644 --- a/src/modules/commitment/pedersen_impl.h +++ b/src/modules/commitment/pedersen_impl.h @@ -9,43 +9,30 @@ #include -#include "eckey.h" #include "ecmult_const.h" -#include "ecmult_gen.h" #include "group.h" -#include "field.h" #include "scalar.h" -#include "util.h" -static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t value) { - unsigned char data[32]; - int i; - for (i = 0; i < 24; i++) { - data[i] = 0; - } - for (; i < 32; i++) { - data[i] = value >> 56; - value <<= 8; - } - secp256k1_scalar_set_b32(sec, data, NULL); - memset(data, 0, 32); -} +/* sec * G + value * G2. */ +SECP256K1_INLINE static void secp256k1_pedersen_ecmult(secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* value_gen, const secp256k1_ge* blind_gen) { + secp256k1_scalar vs; + secp256k1_gej bj; + secp256k1_ge bp; -static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) { - secp256k1_scalar s; - secp256k1_pedersen_scalar_set_u64(&s, gn); - secp256k1_ecmult_const(r, genp, &s, 64); - secp256k1_scalar_clear(&s); -} + secp256k1_scalar_set_u64(&vs, value); + secp256k1_ecmult_const(rj, value_gen, &vs, 64); + secp256k1_ecmult_const(&bj, blind_gen, sec, 256); -/* sec * G + value * G2. */ -SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp) { - secp256k1_gej vj; - secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec); - secp256k1_pedersen_ecmult_small(&vj, value, genp); - /* FIXME: constant time. */ - secp256k1_gej_add_var(rj, rj, &vj, NULL); - secp256k1_gej_clear(&vj); + /* zero blinding factor indicates that we are not trying to be zero-knowledge, + * so not being constant-time in this case is OK. */ + if (!secp256k1_gej_is_infinity(&bj)) { + secp256k1_ge_set_gej(&bp, &bj); + secp256k1_gej_add_ge(rj, rj, &bp); + } + + secp256k1_gej_clear(&bj); + secp256k1_ge_clear(&bp); + secp256k1_scalar_clear(&vs); } #endif diff --git a/src/modules/commitment/tests_impl.h b/src/modules/commitment/tests_impl.h index 99756ac77..fe4f925f3 100644 --- a/src/modules/commitment/tests_impl.h +++ b/src/modules/commitment/tests_impl.h @@ -41,54 +41,56 @@ static void test_commitment_api(void) { secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, &secp256k1_generator_const_h) == 0); + CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g) == 0); CHECK(ecount == 1); - CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, &secp256k1_generator_const_h) == 0); + CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g) == 0); CHECK(ecount == 2); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, &secp256k1_generator_const_h) != 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g) != 0); CHECK(ecount == 2); - CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, &secp256k1_generator_const_h) == 0); + CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g) == 0); CHECK(ecount == 3); - CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, &secp256k1_generator_const_h) == 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g) == 0); CHECK(ecount == 4); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL, &secp256k1_generator_const_g) == 0); CHECK(ecount == 5); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, &secp256k1_generator_const_h, NULL) == 0); + CHECK(ecount == 6); 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(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); CHECK(ecount == 7); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); CHECK(ecount == 8); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); + CHECK(ecount == 9); CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); - CHECK(ecount == 8); + CHECK(ecount == 9); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, &secp256k1_generator_const_h) != 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g) != 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(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); CHECK(ecount == 10); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); + CHECK(ecount == 11); 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(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); CHECK(ecount == 12); - 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 == 13); - 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 == 14); - 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 == 15); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); + CHECK(ecount == 16); secp256k1_context_destroy(none); secp256k1_context_destroy(sign); @@ -132,7 +134,7 @@ 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++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], &secp256k1_generator_const_h, &secp256k1_generator_const_g)); } CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); @@ -147,7 +149,7 @@ static 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], &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], &secp256k1_generator_const_h, &secp256k1_generator_const_g)); } CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); @@ -202,7 +204,7 @@ void test_multiple_generators(void) { /* 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])); + CHECK(secp256k1_pedersen_commit(ctx, &commit[i], pedersen_blind[i], value[i], &generator[i], &secp256k1_generator_const_h)); } /* Verify */ diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 39768a7ac..77ae79c50 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -296,7 +296,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), genp); + secp256k1_pedersen_ecmult(&pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2), genp, &secp256k1_ge_const_g); if (secp256k1_gej_is_infinity(&pubs[npub])) { return 0; } @@ -604,7 +604,10 @@ 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, genp); + secp256k1_scalar mvs; + secp256k1_scalar_set_u64(&mvs, *min_value); + secp256k1_ecmult_const(&accj, genp, &mvs, 64); + secp256k1_scalar_clear(&mvs); } for(i = 0; i < rings - 1; i++) { secp256k1_fe fe; @@ -662,7 +665,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm /* Unwind apparently successful, see if the commitment can be reconstructed. */ /* FIXME: should check vv is in the mantissa's range. */ vv = (vv * scale) + *min_value; - secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv, genp); + secp256k1_pedersen_ecmult(&accj, &blind, vv, genp, &secp256k1_ge_const_g); 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 38e259e79..9d5e1c8be 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -31,7 +31,7 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c size_t ext_commit_len = sizeof(ext_commit); secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, &secp256k1_generator_const_h, &secp256k1_generator_const_g)); CHECK(secp256k1_rangeproof_sign(none, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, &secp256k1_generator_const_h) == 0); CHECK(*ecount == 1); @@ -293,7 +293,7 @@ static void test_rangeproof(void) { secp256k1_rand256(blind); for (i = 0; i < 11; i++) { v = testvs[i]; - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h, &secp256k1_generator_const_g)); for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { const unsigned char *input_message = NULL; size_t input_message_len = 0; @@ -348,7 +348,7 @@ static void test_rangeproof(void) { } secp256k1_rand256(blind); v = INT64_MAX - 1; - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h, &secp256k1_generator_const_g)); 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_const_h)); @@ -363,7 +363,7 @@ static void test_rangeproof(void) { { /*Malleability test.*/ v = secp256k1_rands64(0, 255); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h, &secp256k1_generator_const_g)); len = 5134; CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, &secp256k1_generator_const_h)); CHECK(len <= 5134); @@ -389,7 +389,7 @@ static void test_rangeproof(void) { vmin = secp256k1_rands64(0, v); } secp256k1_rand256(blind); - CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h)); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, &secp256k1_generator_const_h, &secp256k1_generator_const_g)); len = 5134; exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); if (exp < 0) { From 72ffc029a320f729fbfcffd0692abe00e2ccd438 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 25 Mar 2018 15:27:35 +0000 Subject: [PATCH 51/58] bulletproofs: add module, full support for rangeproofs --- .gitignore | 1 + Makefile.am | 4 + configure.ac | 34 + include/secp256k1_bulletproofs.h | 146 ++++ src/modules/bulletproofs/Makefile.am.include | 6 + src/modules/bulletproofs/inner_product_impl.h | 785 ++++++++++++++++++ src/modules/bulletproofs/main_impl.h | 211 +++++ src/modules/bulletproofs/rangeproof_impl.h | 650 +++++++++++++++ src/modules/bulletproofs/tests_impl.h | 565 +++++++++++++ src/modules/bulletproofs/util.h | 150 ++++ src/secp256k1.c | 8 + src/tests.c | 8 + 12 files changed, 2568 insertions(+) create mode 100644 include/secp256k1_bulletproofs.h create mode 100644 src/modules/bulletproofs/Makefile.am.include create mode 100644 src/modules/bulletproofs/inner_product_impl.h create mode 100644 src/modules/bulletproofs/main_impl.h create mode 100644 src/modules/bulletproofs/rangeproof_impl.h create mode 100644 src/modules/bulletproofs/tests_impl.h create mode 100644 src/modules/bulletproofs/util.h diff --git a/.gitignore b/.gitignore index 55480b1fd..901b1fd5e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ bench_recover bench_internal bench_generator bench_rangeproof +bench_bulletproof tests exhaustive_tests gen_context diff --git a/Makefile.am b/Makefile.am index 8fc761187..9b9034e44 100644 --- a/Makefile.am +++ b/Makefile.am @@ -202,6 +202,10 @@ if ENABLE_MODULE_RANGEPROOF include src/modules/rangeproof/Makefile.am.include endif +if ENABLE_MODULE_BULLETPROOF +include src/modules/bulletproofs/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 c6a5ecf64..4e4b42a77 100644 --- a/configure.ac +++ b/configure.ac @@ -159,6 +159,12 @@ AC_ARG_ENABLE(module_rangeproof, [enable_module_rangeproof=$enableval], [enable_module_rangeproof=no]) +AC_ARG_ENABLE(module_bulletproof, + AS_HELP_STRING([--enable-module-bulletproof],[enable Pedersen / zero-knowledge bulletproofs module (default is no)]), + [enable_module_bulletproof=$enableval], + [enable_module_bulletproof=no]) + + AC_ARG_ENABLE(module_whitelist, AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module (default is no)]), [enable_module_whitelist=$enableval], @@ -208,6 +214,19 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])], [ AC_MSG_RESULT([no]) ]) +AC_MSG_CHECKING([for __builtin_popcountl]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcountl(0);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_POPCOUNTL,1,[Define this symbol if __builtin_popcountl is available]) ], + [ AC_MSG_RESULT([no]) + ]) + +AC_MSG_CHECKING([for __builtin_ctzl]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_ctzl(0);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_CTZL,1,[Define this symbol if __builtin_ctzl is available]) ], + [ AC_MSG_RESULT([no]) + ]) + + if test x"$use_ecmult_static_precomputation" != x"no"; then save_cross_compiling=$cross_compiling cross_compiling=no @@ -502,6 +521,10 @@ if test x"$enable_module_rangeproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the zero knowledge range proof module]) fi +if test x"$enable_module_bulletproof" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_BULLETPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge bulletproof 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 @@ -536,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 Pedersen commitment module: $enable_module_commitment]) AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) + AC_MSG_NOTICE([Building bulletproof module: $enable_module_bulletproof]) 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]) @@ -553,12 +577,18 @@ if test x"$enable_experimental" = x"yes"; then if test x"$enable_module_commitment" = x"yes"; then AC_MSG_ERROR([Commitment module requires the generator module. Use --enable-module-generator to allow.]) fi + if test x"$enable_module_bulletproof" = x"yes"; then + AC_MSG_ERROR([Bulletproof module requires the generator module. Use --enable-module-generator to allow.]) + fi fi if test x"$enable_module_commitment" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then AC_MSG_ERROR([Rangeproof module requires the commitment module. Use --enable-module-commitment to allow.]) fi + if test x"$enable_module_bulletproof" = x"yes"; then + AC_MSG_ERROR([Bulletproof module requires the commitment module. Use --enable-module-commitment to allow.]) + fi fi if test x"$enable_module_rangeproof" != x"yes"; then @@ -591,6 +621,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_bulletproof" = x"yes"; then + AC_MSG_ERROR([Bulletproof 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 @@ -618,6 +651,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"ye AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_COMMITMENT], [test x"$enable_module_commitment" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_BULLETPROOF], [test x"$enable_module_bulletproof" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"]) AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h new file mode 100644 index 000000000..8d29e4e03 --- /dev/null +++ b/include/secp256k1_bulletproofs.h @@ -0,0 +1,146 @@ +#ifndef _SECP256K1_BULLETPROOF_ +# define _SECP256K1_BULLETPROOF_ + +# include "secp256k1.h" +# include "secp256k1_generator.h" +# include "secp256k1_rangeproof.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Opaque structure representing a large number of NUMS generators */ +typedef struct secp256k1_bulletproof_generators secp256k1_bulletproof_generators; + +/* Maximum depth of 31 lets us validate an aggregate of 2^25 64-bit proofs */ +#define SECP256K1_BULLETPROOF_MAX_DEPTH 31 + +/* Size of a hypothetical 31-depth rangeproof, in bytes */ +#define SECP256K1_BULLETPROOF_MAX_PROOF (160 + 66*32 + 7) + +/** Allocates and initializes a list of NUMS generators, along with precomputation data + * Returns a list of generators, or NULL if allocation failed. + * Args: ctx: pointer to a context object (cannot be NULL) + * In: blinding_gen: generator that blinding factors will be multiplied by (cannot be NULL) + * n: number of NUMS generators to produce + */ +SECP256K1_API secp256k1_bulletproof_generators *secp256k1_bulletproof_generators_create( + const secp256k1_context* ctx, + const secp256k1_generator *blinding_gen, + size_t n +) SECP256K1_ARG_NONNULL(1); + +/** Destroys a list of NUMS generators, freeing allocated memory + * Args: ctx: pointer to a context object (cannot be NULL) + * gen: pointer to the generator set to be destroyed + */ +SECP256K1_API void secp256k1_bulletproof_generators_destroy( + const secp256k1_context* ctx, + secp256k1_bulletproof_generators *gen +) SECP256K1_ARG_NONNULL(1); + +/** Verifies a single bulletproof (aggregate) rangeproof + * Returns: 1: rangeproof was valid + * 0: rangeproof was invalid, or out of memory + * Args: ctx: pointer to a context object initialized for verification (cannot be NULL) + * scratch: scratch space with enough memory for verification (cannot be NULL) + * gens: generator set with at least 2*nbits*n_commits many generators (cannot be NULL) + * In: proof: byte-serialized rangeproof (cannot be NULL) + * plen: length of the proof + * min_value: array of minimum values to prove ranges above, or NULL for all-zeroes + * commit: array of pedersen commitment that this rangeproof is over (cannot be NULL) + * n_commits: number of commitments in the above array (cannot be 0) + * nbits: number of bits proven for each range + * value_gen: generator multiplied by value in pedersen commitments (cannot be NULL) + * extra_commit: additonal data committed to by the rangeproof (may be NULL if `extra_commit_len` is 0) + * extra_commit_len: length of additional data + */ +SECP256K1_API int secp256k1_bulletproof_rangeproof_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const secp256k1_bulletproof_generators *gens, + const unsigned char* proof, + size_t plen, + const uint64_t* min_value, + const secp256k1_pedersen_commitment* commit, + size_t n_commits, + size_t nbits, + const secp256k1_generator* value_gen, + const unsigned char* extra_commit, + size_t extra_commit_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(10); + +/** Batch-verifies multiple bulletproof (aggregate) rangeproofs of the same size using same generator + * Returns: 1: all rangeproofs were valid + * 0: some rangeproof was invalid, or out of memory + * Args: ctx: pointer to a context object initialized for verification (cannot be NULL) + * scratch: scratch space with enough memory for verification (cannot be NULL) + * gens: generator set with at least 2*nbits*n_commits many generators (cannot be NULL) + * In: proof: array of byte-serialized rangeproofs (cannot be NULL) + * n_proofs: number of proofs in the above array, and number of arrays in the `commit` array + * plen: length of every individual proof + * min_value: array of arrays of minimum values to prove ranges above, or NULL for all-zeroes + * commit: array of arrays of pedersen commitment that the rangeproofs is over (cannot be NULL) + * n_commits: number of commitments in each element of the above array (cannot be 0) + * nbits: number of bits in each proof + * value_gen: generator multiplied by value in pedersen commitments (cannot be NULL) + * extra_commit: additonal data committed to by the rangeproof (may be NULL if `extra_commit_len` is 0) + * extra_commit_len: array of lengths of additional data + */ +SECP256K1_API int secp256k1_bulletproof_rangeproof_verify_multi( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const secp256k1_bulletproof_generators *gens, + const unsigned char* const* proof, + size_t n_proofs, + size_t plen, + const uint64_t* const* min_value, + const secp256k1_pedersen_commitment* const* commit, + size_t n_commits, + size_t nbits, + const secp256k1_generator* value_gen, + const unsigned char* const* extra_commit, + size_t *extra_commit_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(8); + + +/** Produces an aggregate Bulletproof rangeproof for a set of Pedersen commitments + * Returns: 1: rangeproof was successfully created + * 0: rangeproof could not be created, or out of memory + * Args: ctx: pointer to a context object initialized for signing and verification (cannot be NULL) + * scratch: scratch space with enough memory for verification (cannot be NULL) + * gens: generator set with at least 2*nbits*n_commits many generators (cannot be NULL) + * Out: proof: byte-serialized rangeproof (cannot be NULL) + * In/out: plen: pointer to size of `proof`, to be replaced with actual length of proof (cannot be NULL) + * In: value: array of values committed by the Pedersen commitments (cannot be NULL) + * min_value: array of minimum values to prove ranges above, or NULL for all-zeroes + * blind: array of blinding factors of the Pedersen commitments (cannot be NULL) + * n_commits: number of entries in the `value` and `blind` arrays + * value_gen: generator multiplied by value in pedersen commitments (cannot be NULL) + * nbits: number of bits proven for each range + * nonce: random 32-byte seed used to derive blinding factors (cannot be NULL) + * extra_commit: additonal data committed to by the rangeproof + * extra_commit_len: length of additional data + */ +SECP256K1_API int secp256k1_bulletproof_rangeproof_prove( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const secp256k1_bulletproof_generators *gens, + unsigned char* proof, + size_t* plen, + const uint64_t *value, + const uint64_t *min_value, + const unsigned char* const* blind, + size_t n_commits, + const secp256k1_generator* value_gen, + size_t nbits, + const unsigned char* nonce, + const unsigned char* extra_commit, + size_t extra_commit_len +) 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(8) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(12); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include new file mode 100644 index 000000000..2fa121b78 --- /dev/null +++ b/src/modules/bulletproofs/Makefile.am.include @@ -0,0 +1,6 @@ +include_HEADERS += include/secp256k1_bulletproofs.h +noinst_HEADERS += src/modules/bulletproofs/inner_product_impl.h +noinst_HEADERS += src/modules/bulletproofs/rangeproof_impl.h +noinst_HEADERS += src/modules/bulletproofs/main_impl.h +noinst_HEADERS += src/modules/bulletproofs/tests_impl.h +noinst_HEADERS += src/modules/bulletproofs/util.h diff --git a/src/modules/bulletproofs/inner_product_impl.h b/src/modules/bulletproofs/inner_product_impl.h new file mode 100644 index 000000000..30cb8ba64 --- /dev/null +++ b/src/modules/bulletproofs/inner_product_impl.h @@ -0,0 +1,785 @@ +/********************************************************************** + * 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_BULLETPROOF_INNER_PRODUCT_IMPL +#define SECP256K1_MODULE_BULLETPROOF_INNER_PRODUCT_IMPL + +#include "group.h" +#include "scalar.h" + +#include "modules/bulletproofs/main_impl.h" +#include "modules/bulletproofs/util.h" + +#define IP_AB_SCALARS 4 + +typedef int (secp256k1_bulletproof_vfy_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, secp256k1_scalar *randomizer, size_t idx, void *data); + +/* used by callers to wrap a proof with surrounding context */ +typedef struct { + const unsigned char *proof; + secp256k1_scalar p_offs; + secp256k1_scalar yinv; + unsigned char commit[32]; + secp256k1_bulletproof_vfy_callback *rangeproof_cb; + void *rangeproof_cb_data; + size_t n_extra_rangeproof_points; +} secp256k1_bulletproof_innerproduct_context; + +/* used internally */ +typedef struct { + const secp256k1_bulletproof_innerproduct_context *proof; + secp256k1_scalar abinv[IP_AB_SCALARS]; + secp256k1_scalar xsq[SECP256K1_BULLETPROOF_MAX_DEPTH + 1]; + secp256k1_scalar xsqinv[SECP256K1_BULLETPROOF_MAX_DEPTH + 1]; + secp256k1_scalar xsqinvy[SECP256K1_BULLETPROOF_MAX_DEPTH + 1]; + secp256k1_scalar xcache[SECP256K1_BULLETPROOF_MAX_DEPTH + 1]; + secp256k1_scalar xsqinv_mask; + const unsigned char *serialized_lr; +} secp256k1_bulletproof_innerproduct_vfy_data; + +/* used by callers to modify the multiexp */ +typedef struct { + size_t n_proofs; + secp256k1_scalar p_offs; + const secp256k1_ge *g; + const secp256k1_ge *geng; + const secp256k1_ge *genh; + size_t vec_len; + size_t lg_vec_len; + int shared_g; + secp256k1_scalar *randomizer; + secp256k1_bulletproof_innerproduct_vfy_data *proof; +} secp256k1_bulletproof_innerproduct_vfy_ecmult_context; + +size_t secp256k1_bulletproof_innerproduct_proof_length(size_t n) { + if (n < IP_AB_SCALARS / 2) { + return 32 * (1 + 2 * n); + } else { + size_t bit_count = secp256k1_popcountl(n); + size_t log = secp256k1_floor_lg(2 * n / IP_AB_SCALARS); + return 32 * (1 + 2 * (bit_count - 1 + log) + IP_AB_SCALARS) + (2*log + 7) / 8; + } +} + +/* Bulletproof rangeproof verification comes down to a single multiexponentiation of the form + * + * P + (c-a*b)*x*G - sum_{i=1}^n [a*s'_i*G_i + b*s_i*H_i] + sum_{i=1}^log2(n) [x_i^-2 L_i + x_i^2 R_i + * + * which will equal infinity if the rangeproof is correct. Here + * - `G_i` and `H_i` are standard NUMS generators. `G` is the standard secp256k1 generator. + * - `P` and `c` are inputs to the proof, which claims that there exist `a_i` and `b_i`, `i` ranging + * from 0 to `n-1`, such that `P = sum_i [a_i G_i + b_i H_i]` and that `<{a_i}, {b_i}> = c`. + * - `a`, `b`, `L_i` and `R_i`are auxillary components of the proof, where `i` ranges from 0 to `log2(n)-1`. + * - `x_i = H(x_{i-1} || L_i || R_i)`, where `x_{-1}` is passed through the `commit` variable and + * must be a commitment to `P` and `c`. + * - `x` is a hash of `commit` and is used to rerandomize `c`. See Protocol 2 vs Protocol 1 in the paper. + * - `s_i` and `s'_i` are computed as follows. + * + * For each `i` between 0 and `n-1` inclusive, let `b_{ij}` be -1 (1) if the `j`th bit of `i` is zero (one). + * Here `j` ranges from 0 to `log2(n)-1`. Then for each such `i` we define + * - `s_i = prod_j x_j^{b_{ij}}` + * - `s'_i = 1/s_i` + * + * Alternately we can define `s_i` and `s'_i` recursively as follows: + * - `s_0 = s`_{n - 1} = 1 / prod_j x_j` + * - `s_i = s'_{n - 1 - i} = s_{i - 2^j} * x_j^2` where `j = i & (i - 1)` is `i` with its least significant 1 set to 0. + * + * Our ecmult_multi function takes `(c - a*b)*x` directly and multiplies this by `G`. For every other + * (scalar, point) pair it calls the following callback function, which takes an index and outputs a + * pair. The function therefore has three regimes: + * + * For the first `2n` invocations, it alternately returns `(s'_{n - i}, G_{n - i})` and `(s_i, H_i)`, + * where `i` is `floor(idx / 2)`. The reason for the funny indexing is that we use the above recursive + * definition of `s_i` and `s'_i` which produces each element with only a single scalar multiplication, + * but in this mixed order. (We start with an array of `x_j^2` for each `x_j`.) + * + * As a side-effect, whenever `n - i = 2^j` for some `j`, `s_i = x_j^{-1} * prod_{j' != j} x_{j'}`, + * so `x_j^{-2} = s_i*s_0`. Therefore we compute an array of inverse squares during this computation, + * using only one multiplication per. We will need it in the following step. + * + * For the next `2*log2(n)` invocations it alternately returns `(x_i^-2, L_i)` and `(x_i^2, R_i)` + * where `i` is `idx - 2*n`. + * + * For the remaining invocations it passes through to another callback, `rangeproof_cb_data` which + * computes `P`. The reason for this is that in practice `P` is usually defined by another multiexp + * rather than being a known point, and it is more efficient to compute one exponentiation. + * + */ + +/* For the G and H generators, we choose the ith generator with a scalar computed from the + * L/R hashes as follows: prod_{j=1}^m x_j^{e_j}, where each exponent e_j is either -1 or 1. + * The choice directly maps to the bits of i: for the G generators, a 0 bit means e_j is 1 + * and a 1 bit means e_j is -1. For the H generators it is the opposite. Finally, each of the + * G scalars is further multiplied by -a, while each of the H scalars is further multiplied + * by -b. + * + * These scalars are computed starting from I, the inverse of the product of every x_j, which + * is then selectively multiplied by x_j^2 for whichever j's are needed. As it turns out, by + * caching logarithmically many scalars, this can always be done by multiplying one of the + * cached values by a single x_j, rather than starting from I and doing multiple multiplications. + */ + +static int secp256k1_bulletproof_innerproduct_vfy_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_innerproduct_vfy_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_vfy_ecmult_context *) data; + + /* First 2N points use the standard Gi, Hi generators, and the scalars can be aggregated across proofs */ + if (idx < 2 * ctx->vec_len) { + const size_t grouping = ctx->vec_len < IP_AB_SCALARS / 2 ? ctx->vec_len : IP_AB_SCALARS / 2; + const size_t lg_grouping = secp256k1_floor_lg(grouping); + size_t i; + /* TODO zero this point when appropriate for non-2^n numbers of pairs */ + if (idx < ctx->vec_len) { + *pt = ctx->geng[idx]; + } else { + *pt = ctx->genh[idx - ctx->vec_len]; + } + + secp256k1_scalar_clear(sc); + for (i = 0; i < ctx->n_proofs; i++) { + const size_t cache_idx = secp256k1_popcountl(idx); + secp256k1_scalar term; + VERIFY_CHECK(cache_idx < SECP256K1_BULLETPROOF_MAX_DEPTH); + /* Compute the normal inner-product scalar... */ + if (cache_idx > 0) { + if (idx % (ctx->vec_len / grouping) == 0) { + const size_t abinv_idx = idx / (ctx->vec_len / grouping) - 1; + size_t prev_cache_idx; + if (idx == ctx->vec_len) { + /* Transition from G to H, a's to b's */ + secp256k1_scalar yinvn = ctx->proof[i].proof->yinv; + size_t j; + prev_cache_idx = secp256k1_popcountl(idx - 1); + for (j = 0; j < (size_t) secp256k1_ctzl(idx) - lg_grouping; j++) { + secp256k1_scalar_mul(&ctx->proof[i].xsqinvy[j], &ctx->proof[i].xsqinv[j], &yinvn); + secp256k1_scalar_sqr(&yinvn, &yinvn); + } + for (j = 0; j < lg_grouping; j++) { + /* TODO this only does the right thing for lg_grouping = 0 or 1 */ + secp256k1_scalar_mul(&ctx->proof[i].abinv[2], &ctx->proof[i].abinv[2], &yinvn); + secp256k1_scalar_sqr(&yinvn, &yinvn); + } + } else { + prev_cache_idx = cache_idx - 1; + } + secp256k1_scalar_mul( + &ctx->proof[i].xcache[cache_idx], + &ctx->proof[i].xcache[prev_cache_idx], + &ctx->proof[i].abinv[abinv_idx] + ); + } else if (idx < ctx->vec_len) { + const size_t xsq_idx = secp256k1_ctzl(idx); + secp256k1_scalar_mul(&ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xcache[cache_idx - 1], &ctx->proof[i].xsq[xsq_idx]); + } else { + const size_t xsqinv_idx = secp256k1_ctzl(idx); + secp256k1_scalar_mul(&ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xcache[cache_idx - 1], &ctx->proof[i].xsqinvy[xsqinv_idx]); + } + } + term = ctx->proof[i].xcache[cache_idx]; + + /* When going through the G generators, compute the x-inverses as side effects */ + if (idx < ctx->vec_len / grouping && secp256k1_popcountl(idx) == ctx->lg_vec_len - 1) { /* if the scalar has only one 0, i.e. only one inverse... */ + const size_t xsqinv_idx = secp256k1_ctzl(~idx); + /* ...multiply it by the total inverse, to get x_j^-2 */ + secp256k1_scalar_mul(&ctx->proof[i].xsqinv[xsqinv_idx], &ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xsqinv_mask); + } + + /* ...add whatever offset the rangeproof wants... */ + if (ctx->proof[i].proof->rangeproof_cb != NULL) { + secp256k1_scalar rangeproof_offset; + if ((ctx->proof[i].proof->rangeproof_cb)(&rangeproof_offset, NULL, &ctx->randomizer[i], idx, ctx->proof[i].proof->rangeproof_cb_data) != 1) { + return 0; + } + secp256k1_scalar_add(&term, &term, &rangeproof_offset); + } + + secp256k1_scalar_add(sc, sc, &term); + } + /* Next 2lgN points are the L and R vectors */ + } else if (idx < 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs)) { + size_t real_idx = idx - 2 * ctx->vec_len; + const size_t proof_idx = real_idx / (2 * ctx->lg_vec_len); + real_idx = real_idx % (2 * ctx->lg_vec_len); + if (!secp256k1_bulletproof_deserialize_point( + pt, + ctx->proof[proof_idx].serialized_lr, + real_idx, + 2 * ctx->lg_vec_len + )) { + return 0; + } + if (idx % 2 == 0) { + *sc = ctx->proof[proof_idx].xsq[real_idx / 2]; + } else { + *sc = ctx->proof[proof_idx].xsqinv[real_idx / 2]; + } + secp256k1_scalar_mul(sc, sc, &ctx->randomizer[proof_idx]); + /* After the G's, H's, L's and R's, do the blinding_gen */ + } else if (idx == 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs)) { + *sc = ctx->p_offs; + *pt = *ctx->g; + /* Remaining points are whatever the rangeproof wants */ + } else if (ctx->shared_g && idx == 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs) + 1) { + /* Special case: the first extra point is independent of the proof, for both rangeproof and circuit */ + size_t i; + secp256k1_scalar_clear(sc); + for (i = 0; i < ctx->n_proofs; i++) { + secp256k1_scalar term; + if ((ctx->proof[i].proof->rangeproof_cb)(&term, pt, &ctx->randomizer[i], 2 * (ctx->vec_len + ctx->lg_vec_len), ctx->proof[i].proof->rangeproof_cb_data) != 1) { + return 0; + } + secp256k1_scalar_add(sc, sc, &term); + } + } else { + size_t proof_idx = 0; + size_t real_idx = idx - 2 * (ctx->vec_len + ctx->lg_vec_len * ctx->n_proofs) - 1 - !!ctx->shared_g; + while (real_idx >= ctx->proof[proof_idx].proof->n_extra_rangeproof_points - !!ctx->shared_g) { + real_idx -= ctx->proof[proof_idx].proof->n_extra_rangeproof_points - !!ctx->shared_g; + proof_idx++; + VERIFY_CHECK(proof_idx < ctx->n_proofs); + } + if ((ctx->proof[proof_idx].proof->rangeproof_cb)(sc, pt, &ctx->randomizer[proof_idx], 2 * (ctx->vec_len + ctx->lg_vec_len), ctx->proof[proof_idx].proof->rangeproof_cb_data) != 1) { + return 0; + } + } + + return 1; +} + +/* nb For security it is essential that `commit_inp` already commit to all data + * needed to compute `P`. We do not hash it in during verification since `P` + * may be specified indirectly as a bunch of scalar offsets. + */ +static int secp256k1_bulletproof_inner_product_verify_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, const secp256k1_bulletproof_generators *gens, size_t vec_len, const secp256k1_bulletproof_innerproduct_context *proof, size_t n_proofs, size_t plen, int shared_g) { + secp256k1_sha256 sha256; + secp256k1_bulletproof_innerproduct_vfy_ecmult_context ecmult_data; + unsigned char commit[32]; + size_t total_n_points = 2 * vec_len + !!shared_g + 1; /* +1 for shared G (value_gen), +1 for H (blinding_gen) */ + secp256k1_gej r; + secp256k1_scalar zero; + size_t i; + + if (plen != secp256k1_bulletproof_innerproduct_proof_length(vec_len)) { + return 0; + } + + if (n_proofs == 0) { + return 1; + } + + if (!secp256k1_scratch_allocate_frame(scratch, n_proofs * (sizeof(*ecmult_data.randomizer) + sizeof(*ecmult_data.proof)), 2)) { + return 0; + } + + secp256k1_scalar_clear(&zero); + ecmult_data.n_proofs = n_proofs; + ecmult_data.g = gens->blinding_gen; + ecmult_data.geng = gens->gens; + ecmult_data.genh = gens->gens + gens->n / 2; + ecmult_data.vec_len = vec_len; + ecmult_data.lg_vec_len = secp256k1_floor_lg(2 * vec_len / IP_AB_SCALARS); + ecmult_data.shared_g = shared_g; + ecmult_data.randomizer = (secp256k1_scalar *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*ecmult_data.randomizer)); + ecmult_data.proof = (secp256k1_bulletproof_innerproduct_vfy_data *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*ecmult_data.proof)); + /* Seed RNG for per-proof randomizers */ + secp256k1_sha256_initialize(&sha256); + for (i = 0; i < n_proofs; i++) { + secp256k1_sha256_write(&sha256, proof[i].proof, plen); + secp256k1_sha256_write(&sha256, proof[i].commit, 32); + secp256k1_scalar_get_b32(commit, &proof[i].p_offs); + secp256k1_sha256_write(&sha256, commit, 32); + } + secp256k1_sha256_finalize(&sha256, commit); + + secp256k1_scalar_clear(&ecmult_data.p_offs); + for (i = 0; i < n_proofs; i++) { + const unsigned char *serproof = proof[i].proof; + unsigned char proof_commit[32]; + secp256k1_scalar dot; + secp256k1_scalar ab[IP_AB_SCALARS]; + secp256k1_scalar negprod; + secp256k1_scalar x; + int overflow; + size_t j; + const size_t n_ab = 2 * vec_len < IP_AB_SCALARS ? 2 * vec_len : IP_AB_SCALARS; + + total_n_points += 2 * ecmult_data.lg_vec_len + proof[i].n_extra_rangeproof_points - !!shared_g; /* -1 for shared G */ + + /* Extract dot product, will always be the first 32 bytes */ + secp256k1_scalar_set_b32(&dot, serproof, &overflow); + if (overflow) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + /* Commit to dot product */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, proof[i].commit, 32); + secp256k1_sha256_write(&sha256, serproof, 32); + secp256k1_sha256_finalize(&sha256, proof_commit); + serproof += 32; + + /* Extract a, b */ + for (j = 0; j < n_ab; j++) { + secp256k1_scalar_set_b32(&ab[j], serproof, &overflow); + if (overflow) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + /* TODO our verifier currently bombs out with zeros because it uses + * scalar inverses gratuitously. Fix that. */ + if (secp256k1_scalar_is_zero(&ab[j])) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + serproof += 32; + } + secp256k1_scalar_dot_product(&negprod, &ab[0], &ab[n_ab / 2], n_ab / 2); + + ecmult_data.proof[i].proof = &proof[i]; + /* set per-proof randomizer */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_finalize(&sha256, commit); + secp256k1_scalar_set_b32(&ecmult_data.randomizer[i], commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ecmult_data.randomizer[i])) { + /* cryptographically unreachable */ + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + /* Compute x*(dot - a*b) for each proof; add it and p_offs to the p_offs accumulator */ + secp256k1_scalar_set_b32(&x, proof_commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_scalar_negate(&negprod, &negprod); + secp256k1_scalar_add(&negprod, &negprod, &dot); + secp256k1_scalar_mul(&x, &x, &negprod); + secp256k1_scalar_add(&x, &x, &proof[i].p_offs); + + secp256k1_scalar_mul(&x, &x, &ecmult_data.randomizer[i]); + secp256k1_scalar_add(&ecmult_data.p_offs, &ecmult_data.p_offs, &x); + + /* Special-case: trivial proofs are valid iff the explicitly revealed scalars + * dot to the explicitly revealed dot product. */ + if (2 * vec_len <= IP_AB_SCALARS) { + if (!secp256k1_scalar_is_zero(&negprod)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + /* remaining data does not (and cannot) be computed for proofs with no a's or b's. */ + if (vec_len == 0) { + continue; + } + } + + /* Compute the inverse product and the array of squares; the rest will be filled + * in by the callback during the multiexp. */ + ecmult_data.proof[i].serialized_lr = serproof; /* bookmark L/R location in proof */ + negprod = ab[n_ab - 1]; + ab[n_ab - 1] = ecmult_data.randomizer[i]; /* build r * x1 * x2 * ... * xn in last slot of `ab` array */ + for (j = 0; j < ecmult_data.lg_vec_len; j++) { + secp256k1_scalar xi; + const size_t lidx = 2 * j; + const size_t ridx = 2 * j + 1; + const size_t bitveclen = (2 * ecmult_data.lg_vec_len + 7) / 8; + const unsigned char lrparity = 2 * !!(serproof[lidx / 8] & (1 << (lidx % 8))) + !!(serproof[ridx / 8] & (1 << (ridx % 8))); + /* Map commit -> H(commit || LR parity || Lx || Rx), compute xi from it */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, proof_commit, 32); + secp256k1_sha256_write(&sha256, &lrparity, 1); + secp256k1_sha256_write(&sha256, &serproof[32 * lidx + bitveclen], 32); + secp256k1_sha256_write(&sha256, &serproof[32 * ridx + bitveclen], 32); + secp256k1_sha256_finalize(&sha256, proof_commit); + + secp256k1_scalar_set_b32(&xi, proof_commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&xi)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_scalar_mul(&ab[n_ab - 1], &ab[n_ab - 1], &xi); + secp256k1_scalar_sqr(&ecmult_data.proof[i].xsq[j], &xi); + } + /* Compute inverse of all a's and b's, except the last b whose inverse is not needed. + * Also compute the inverse of (-r * x1 * ... * xn) which will be needed */ + secp256k1_scalar_inverse_all_var(ecmult_data.proof[i].abinv, ab, n_ab); + ab[n_ab - 1] = negprod; + + /* Compute (-a0 * r * x1 * ... * xn)^-1 which will be used to mask out individual x_i^-2's */ + secp256k1_scalar_negate(&ecmult_data.proof[i].xsqinv_mask, &ecmult_data.proof[i].abinv[0]); + secp256k1_scalar_mul(&ecmult_data.proof[i].xsqinv_mask, &ecmult_data.proof[i].xsqinv_mask, &ecmult_data.proof[i].abinv[n_ab - 1]); + + /* Compute each scalar times the previous' inverse, which is used to switch between a's and b's */ + for (j = n_ab - 1; j > 0; j--) { + size_t prev_idx; + if (j == n_ab / 2) { + prev_idx = j - 1; /* we go from a_n to b_0 */ + } else { + prev_idx = j & (j - 1); /* but from a_i' to a_i, where i' is i with its lowest set bit unset */ + } + secp256k1_scalar_mul( + &ecmult_data.proof[i].abinv[j - 1], + &ecmult_data.proof[i].abinv[prev_idx], + &ab[j] + ); + } + + /* Extract -a0 * r * (x1 * ... * xn)^-1 which is our first coefficient. Use negprod as a dummy */ + secp256k1_scalar_mul(&negprod, &ecmult_data.randomizer[i], &ab[0]); /* r*a */ + secp256k1_scalar_sqr(&negprod, &negprod); /* (r*a)^2 */ + secp256k1_scalar_mul(&ecmult_data.proof[i].xcache[0], &ecmult_data.proof[i].xsqinv_mask, &negprod); /* -a * r * (x1 * x2 * ... * xn)^-1 */ + } + + /* Do the multiexp */ + if (secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &r, NULL, secp256k1_bulletproof_innerproduct_vfy_ecmult_callback, (void *) &ecmult_data, total_n_points) != 1) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_scratch_deallocate_frame(scratch); + return secp256k1_gej_is_infinity(&r); +} + +typedef struct { + secp256k1_scalar x[SECP256K1_BULLETPROOF_MAX_DEPTH]; + secp256k1_scalar xinv[SECP256K1_BULLETPROOF_MAX_DEPTH]; + secp256k1_scalar yinv; + secp256k1_scalar yinvn; + const secp256k1_ge *geng; + const secp256k1_ge *genh; + const secp256k1_ge *g; + const secp256k1_scalar *a; + const secp256k1_scalar *b; + secp256k1_scalar g_sc; + size_t grouping; + size_t n; +} secp256k1_bulletproof_innerproduct_pf_ecmult_context; + +/* At each level i of recursion (i from 0 upto lg(vector size) - 1) + * L = a_even . G_odd + b_odd . H_even (18) + * which, by expanding the generators into the original G's and H's + * and setting n = (1 << i), can be computed as follows: + * + * For j from 1 to [vector size], + * 1. Use H[j] or G[j] as generator, starting with H and switching + * every n. + * 2. Start with b1 with H and a0 with G, and increment by 2 each switch. + * 3. For k = 1, 2, 4, ..., n/2, use the same algorithm to choose + * between a and b to choose between x and x^-1, except using + * k in place of n. With H's choose x then x^-1, with G's choose + * x^-1 then x. + * + * For R everything is the same except swap G/H and a/b and x/x^-1. + */ +static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_l(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data; + const size_t ab_idx = (idx / ctx->grouping) ^ 1; + size_t i; + + /* Special-case the primary generator */ + if (idx == ctx->n) { + *pt = *ctx->g; + *sc = ctx->g_sc; + return 1; + } + + /* steps 1/2 */ + if ((idx / ctx->grouping) % 2 == 0) { + *pt = ctx->genh[idx]; + *sc = ctx->b[ab_idx]; + /* Map h -> h' (eqn 59) */ + secp256k1_scalar_mul(sc, sc, &ctx->yinvn); + } else { + *pt = ctx->geng[idx]; + *sc = ctx->a[ab_idx]; + } + + /* step 3 */ + for (i = 0; (1u << i) < ctx->grouping; i++) { + size_t grouping = (1u << i); + if ((((idx / grouping) % 2) ^ ((idx / ctx->grouping) % 2)) == 0) { + secp256k1_scalar_mul(sc, sc, &ctx->x[i]); + } else { + secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]); + } + } + + secp256k1_scalar_mul(&ctx->yinvn, &ctx->yinvn, &ctx->yinv); + return 1; +} + +/* Identical code except `== 0` changed to `== 1` twice, and the + * `+ 1` from Step 1/2 was moved to the other if branch. */ +static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_r(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data; + const size_t ab_idx = (idx / ctx->grouping) ^ 1; + size_t i; + + /* Special-case the primary generator */ + if (idx == ctx->n) { + *pt = *ctx->g; + *sc = ctx->g_sc; + return 1; + } + + /* steps 1/2 */ + if ((idx / ctx->grouping) % 2 == 1) { + *pt = ctx->genh[idx]; + *sc = ctx->b[ab_idx]; + /* Map h -> h' (eqn 59) */ + secp256k1_scalar_mul(sc, sc, &ctx->yinvn); + } else { + *pt = ctx->geng[idx]; + *sc = ctx->a[ab_idx]; + } + + /* step 3 */ + for (i = 0; (1u << i) < ctx->grouping; i++) { + size_t grouping = (1u << i); + if ((((idx / grouping) % 2) ^ ((idx / ctx->grouping) % 2)) == 1) { + secp256k1_scalar_mul(sc, sc, &ctx->x[i]); + } else { + secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]); + } + } + + secp256k1_scalar_mul(&ctx->yinvn, &ctx->yinvn, &ctx->yinv); + return 1; +} + +static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_g(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data; + size_t i; + + *pt = ctx->geng[idx]; + secp256k1_scalar_set_int(sc, 1); + for (i = 0; (1u << i) <= ctx->grouping; i++) { + if (idx & (1u << i)) { + secp256k1_scalar_mul(sc, sc, &ctx->x[i]); + } else { + secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]); + } + } + return 1; +} + +static int secp256k1_bulletproof_innerproduct_pf_ecmult_callback_h(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_innerproduct_pf_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_pf_ecmult_context *) data; + size_t i; + + *pt = ctx->genh[idx]; + secp256k1_scalar_set_int(sc, 1); + for (i = 0; (1u << i) <= ctx->grouping; i++) { + if (idx & (1u << i)) { + secp256k1_scalar_mul(sc, sc, &ctx->xinv[i]); + } else { + secp256k1_scalar_mul(sc, sc, &ctx->x[i]); + } + } + secp256k1_scalar_mul(sc, sc, &ctx->yinvn); + secp256k1_scalar_mul(&ctx->yinvn, &ctx->yinvn, &ctx->yinv); + return 1; +} + +/* These proofs are not zero-knowledge. There is no need to worry about constant timeness. + * `commit_inp` must contain 256 bits of randomness, it is used immediately as a randomizer. + */ +static int secp256k1_bulletproof_inner_product_real_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, secp256k1_ge *out_pt, size_t *pt_idx, const secp256k1_ge *g, secp256k1_ge *geng, secp256k1_ge *genh, secp256k1_scalar *a_arr, secp256k1_scalar *b_arr, const secp256k1_scalar *yinv, const secp256k1_scalar *ux, const size_t n, unsigned char *commit) { + size_t i; + size_t halfwidth; + + secp256k1_bulletproof_innerproduct_pf_ecmult_context pfdata; + pfdata.yinv = *yinv; + pfdata.g = g; + pfdata.geng = geng; + pfdata.genh = genh; + pfdata.a = a_arr; + pfdata.b = b_arr; + pfdata.n = n; + + /* Protocol 1: Iterate, halving vector size until it is 1 */ + for (halfwidth = n / 2, i = 0; halfwidth > IP_AB_SCALARS / 4; halfwidth /= 2, i++) { + secp256k1_gej tmplj, tmprj; + size_t j; + int overflow; + + pfdata.grouping = 1u << i; + + /* L */ + secp256k1_scalar_clear(&pfdata.g_sc); + for (j = 0; j < halfwidth; j++) { + secp256k1_scalar prod; + secp256k1_scalar_mul(&prod, &a_arr[2*j], &b_arr[2*j + 1]); + secp256k1_scalar_add(&pfdata.g_sc, &pfdata.g_sc, &prod); + } + secp256k1_scalar_mul(&pfdata.g_sc, &pfdata.g_sc, ux); + + secp256k1_scalar_set_int(&pfdata.yinvn, 1); + secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &tmplj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_l, (void *) &pfdata, n + 1); + secp256k1_ge_set_gej(&out_pt[(*pt_idx)++], &tmplj); + + /* R */ + secp256k1_scalar_clear(&pfdata.g_sc); + for (j = 0; j < halfwidth; j++) { + secp256k1_scalar prod; + secp256k1_scalar_mul(&prod, &a_arr[2*j + 1], &b_arr[2*j]); + secp256k1_scalar_add(&pfdata.g_sc, &pfdata.g_sc, &prod); + } + secp256k1_scalar_mul(&pfdata.g_sc, &pfdata.g_sc, ux); + + secp256k1_scalar_set_int(&pfdata.yinvn, 1); + secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &tmprj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_r, (void *) &pfdata, n + 1); + secp256k1_ge_set_gej(&out_pt[(*pt_idx)++], &tmprj); + + /* x, x^2, x^-1, x^-2 */ + secp256k1_bulletproof_update_commit(commit, &out_pt[*pt_idx - 2], &out_pt[*pt_idx] - 1); + secp256k1_scalar_set_b32(&pfdata.x[i], commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&pfdata.x[i])) { + return 0; + } + secp256k1_scalar_inverse_var(&pfdata.xinv[i], &pfdata.x[i]); + + /* update scalar array */ + for (j = 0; j < halfwidth; j++) { + secp256k1_scalar tmps; + secp256k1_scalar_mul(&a_arr[2*j], &a_arr[2*j], &pfdata.x[i]); + secp256k1_scalar_mul(&tmps, &a_arr[2*j + 1], &pfdata.xinv[i]); + secp256k1_scalar_add(&a_arr[j], &a_arr[2*j], &tmps); + + secp256k1_scalar_mul(&b_arr[2*j], &b_arr[2*j], &pfdata.xinv[i]); + secp256k1_scalar_mul(&tmps, &b_arr[2*j + 1], &pfdata.x[i]); + secp256k1_scalar_add(&b_arr[j], &b_arr[2*j], &tmps); + + } + + /* Combine G generators and recurse, if that would be more optimal */ + if ((n > 2048 && i == 3) || (n > 128 && i == 2) || (n > 32 && i == 1)) { + secp256k1_scalar yinv2; + + for (j = 0; j < halfwidth; j++) { + secp256k1_gej rj; + secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &rj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_g, (void *) &pfdata, 2u << i); + pfdata.geng += 2u << i; + secp256k1_ge_set_gej(&geng[j], &rj); + secp256k1_scalar_set_int(&pfdata.yinvn, 1); + secp256k1_ecmult_multi_var(ecmult_ctx, scratch, &rj, NULL, &secp256k1_bulletproof_innerproduct_pf_ecmult_callback_h, (void *) &pfdata, 2u << i); + pfdata.genh += 2u << i; + secp256k1_ge_set_gej(&genh[j], &rj); + } + + secp256k1_scalar_sqr(&yinv2, yinv); + for (j = 0; j < i; j++) { + secp256k1_scalar_sqr(&yinv2, &yinv2); + } + if (!secp256k1_bulletproof_inner_product_real_prove_impl(ecmult_ctx, scratch, out_pt, pt_idx, g, geng, genh, a_arr, b_arr, &yinv2, ux, halfwidth, commit)) { + return 0; + } + break; + } + } + return 1; +} + +static int secp256k1_bulletproof_inner_product_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, unsigned char *proof, size_t *proof_len, const secp256k1_bulletproof_generators *gens, const secp256k1_scalar *yinv, const size_t n, secp256k1_ecmult_multi_callback *cb, void *cb_data, const unsigned char *commit_inp) { + secp256k1_sha256 sha256; + size_t i; + unsigned char commit[32]; + secp256k1_scalar *a_arr; + secp256k1_scalar *b_arr; + secp256k1_ge *out_pt; + secp256k1_ge *geng; + secp256k1_ge *genh; + secp256k1_scalar ux; + int overflow; + size_t pt_idx = 0; + secp256k1_scalar dot; + size_t half_n_ab = n < IP_AB_SCALARS / 2 ? n : IP_AB_SCALARS / 2; + + if (*proof_len < secp256k1_bulletproof_innerproduct_proof_length(n)) { + return 0; + } + *proof_len = secp256k1_bulletproof_innerproduct_proof_length(n); + + /* Special-case lengths 0 and 1 whose proofs are just expliict lists of scalars */ + if (n <= IP_AB_SCALARS / 2) { + secp256k1_scalar a[IP_AB_SCALARS / 2]; + secp256k1_scalar b[IP_AB_SCALARS / 2]; + + for (i = 0; i < n; i++) { + cb(&a[i], NULL, 2*i, cb_data); + cb(&b[i], NULL, 2*i+1, cb_data); + } + + secp256k1_scalar_dot_product(&dot, a, b, n); + secp256k1_scalar_get_b32(proof, &dot); + + for (i = 0; i < n; i++) { + secp256k1_scalar_get_b32(&proof[32 * (i + 1)], &a[i]); + secp256k1_scalar_get_b32(&proof[32 * (i + n + 1)], &b[i]); + } + VERIFY_CHECK(*proof_len == 32 * (2 * n + 1)); + return 1; + } + + /* setup for nontrivial proofs */ + if (!secp256k1_scratch_allocate_frame(scratch, 2 * n * (sizeof(secp256k1_scalar) + sizeof(secp256k1_ge)) + 2 * secp256k1_floor_lg(n) * sizeof(secp256k1_ge), 5)) { + return 0; + } + + a_arr = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_scalar)); + b_arr = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_scalar)); + geng = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_ge)); + genh = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n * sizeof(secp256k1_ge)); + out_pt = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, 2 * secp256k1_floor_lg(n) * sizeof(secp256k1_ge)); + VERIFY_CHECK(a_arr != NULL); + VERIFY_CHECK(b_arr != NULL); + VERIFY_CHECK(gens != NULL); + + for (i = 0; i < n; i++) { + cb(&a_arr[i], NULL, 2*i, cb_data); + cb(&b_arr[i], NULL, 2*i+1, cb_data); + geng[i] = gens->gens[i]; + genh[i] = gens->gens[i + gens->n/2]; + } + + /* Record final dot product */ + secp256k1_scalar_dot_product(&dot, a_arr, b_arr, n); + secp256k1_scalar_get_b32(proof, &dot); + + /* Protocol 2: hash dot product to obtain G-randomizer */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit_inp, 32); + secp256k1_sha256_write(&sha256, proof, 32); + secp256k1_sha256_finalize(&sha256, commit); + + proof += 32; + + secp256k1_scalar_set_b32(&ux, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ux)) { + /* cryptographically unreachable */ + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + if (!secp256k1_bulletproof_inner_product_real_prove_impl(ecmult_ctx, scratch, out_pt, &pt_idx, gens->blinding_gen, geng, genh, a_arr, b_arr, yinv, &ux, n, commit)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + /* Final a/b values */ + for (i = 0; i < half_n_ab; i++) { + secp256k1_scalar_get_b32(&proof[32 * i], &a_arr[i]); + secp256k1_scalar_get_b32(&proof[32 * (i + half_n_ab)], &b_arr[i]); + } + proof += 64 * half_n_ab; + secp256k1_bulletproof_serialize_points(proof, out_pt, pt_idx); + + secp256k1_scratch_deallocate_frame(scratch); + return 1; +} + +#undef IP_AB_SCALARS + +#endif diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h new file mode 100644 index 000000000..8ffd69d05 --- /dev/null +++ b/src/modules/bulletproofs/main_impl.h @@ -0,0 +1,211 @@ +/********************************************************************** + * 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_BULLETPROOF_MAIN_IMPL +#define SECP256K1_MODULE_BULLETPROOF_MAIN_IMPL + +#include "group.h" +#include "scalar.h" + +#include "modules/commitment/main_impl.h" + +struct secp256k1_bulletproof_generators { + size_t n; + secp256k1_ge *gens; + secp256k1_ge *blinding_gen; +}; + +#include "modules/bulletproofs/inner_product_impl.h" +#include "modules/bulletproofs/rangeproof_impl.h" +#include "modules/bulletproofs/util.h" + +secp256k1_bulletproof_generators *secp256k1_bulletproof_generators_create(const secp256k1_context *ctx, const secp256k1_generator *blinding_gen, size_t n) { + secp256k1_bulletproof_generators *ret; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned char seed[64]; + secp256k1_gej precompj; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(blinding_gen != NULL); + + ret = (secp256k1_bulletproof_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->gens = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (n + 1) * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + ret->blinding_gen = &ret->gens[n]; + 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++) { + unsigned char tmp[32] = { 0 }; + secp256k1_generator gen; + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, tmp)); + secp256k1_generator_load(&ret->gens[i], &gen); + + secp256k1_gej_set_ge(&precompj, &ret->gens[i]); + } + + secp256k1_generator_load(&ret->blinding_gen[0], blinding_gen); + secp256k1_gej_set_ge(&precompj, &ret->blinding_gen[0]); + + return ret; +} + +void secp256k1_bulletproof_generators_destroy(const secp256k1_context* ctx, secp256k1_bulletproof_generators *gens) { + (void) ctx; + if (gens != NULL) { + free(gens->gens); + free(gens); + } +} + +int secp256k1_bulletproof_rangeproof_verify(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, const secp256k1_bulletproof_generators *gens, const unsigned char *proof, size_t plen, + const uint64_t *min_value, const secp256k1_pedersen_commitment* commit, size_t n_commits, size_t nbits, const secp256k1_generator *value_gen, const unsigned char *extra_commit, size_t extra_commit_len) { + int ret; + size_t i; + secp256k1_ge *commitp; + secp256k1_ge value_genp; + const secp256k1_ge *commitp_ptr; + const uint64_t *minvalue_ptr; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(scratch != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(gens->n >= 2 * nbits * n_commits); + ARG_CHECK(proof != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(n_commits > 0); + ARG_CHECK(nbits > 0); + ARG_CHECK(nbits <= 64); + ARG_CHECK(value_gen != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + + if (!secp256k1_scratch_allocate_frame(scratch, 2 * n_commits * sizeof(secp256k1_ge), 1)) { + return 0; + } + + commitp = (secp256k1_ge *)secp256k1_scratch_alloc(scratch, n_commits * sizeof(secp256k1_ge)); + for (i = 0; i < n_commits; i++) { + secp256k1_pedersen_commitment_load(&commitp[i], &commit[i]); + } + secp256k1_generator_load(&value_genp, value_gen); + + commitp_ptr = commitp; + minvalue_ptr = min_value; + ret = secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, &proof, 1, plen, nbits, &minvalue_ptr, &commitp_ptr, n_commits, &value_genp, gens, &extra_commit, &extra_commit_len); + secp256k1_scratch_deallocate_frame(scratch); + return ret; +} + +int secp256k1_bulletproof_rangeproof_verify_multi(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, const secp256k1_bulletproof_generators *gens, const unsigned char* const* proof, size_t n_proofs, size_t plen, const uint64_t* const* min_value, const secp256k1_pedersen_commitment* const* commit, size_t n_commits, size_t nbits, const secp256k1_generator *value_gen, const unsigned char* const* extra_commit, size_t *extra_commit_len) { + int ret; + secp256k1_ge **commitp; + secp256k1_ge *value_genp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(scratch != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(gens->n >= 2 * nbits * n_commits); + ARG_CHECK(commit != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(n_proofs > 0); + ARG_CHECK(n_commits > 0); + ARG_CHECK(nbits > 0); + ARG_CHECK(nbits <= 64); + ARG_CHECK(value_gen != NULL); + ARG_CHECK((extra_commit_len == NULL) == (extra_commit == NULL)); + if (extra_commit != NULL) { + for (i = 0; i < n_proofs; i++) { + ARG_CHECK(extra_commit[i] != NULL || extra_commit_len[i] == 0); + } + } + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + + if (!secp256k1_scratch_allocate_frame(scratch, n_proofs * (sizeof(*value_genp) + sizeof(*commitp) + n_commits * sizeof(**commitp)), 2 + n_proofs)) { + return 0; + } + + commitp = (secp256k1_ge **)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*commitp)); + value_genp = (secp256k1_ge *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*value_genp)); + for (i = 0; i < n_proofs; i++) { + size_t j; + commitp[i] = (secp256k1_ge *)secp256k1_scratch_alloc(scratch, n_commits * sizeof(*commitp[i])); + for (j = 0; j < n_commits; j++) { + secp256k1_pedersen_commitment_load(&commitp[i][j], &commit[i][j]); + } + secp256k1_generator_load(&value_genp[i], &value_gen[i]); + } + + ret = secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, proof, n_proofs, plen, nbits, min_value, (const secp256k1_ge **) commitp, n_commits, value_genp, gens, extra_commit, extra_commit_len); + secp256k1_scratch_deallocate_frame(scratch); + return ret; +} + +int secp256k1_bulletproof_rangeproof_prove(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, const secp256k1_bulletproof_generators *gens, unsigned char *proof, size_t *plen, const uint64_t *value, const uint64_t *min_value, const unsigned char* const* blind, size_t n_commits, const secp256k1_generator *value_gen, size_t nbits, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len) { + int ret; + secp256k1_ge *commitp; + secp256k1_scalar *blinds; + secp256k1_ge value_genp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(scratch != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(gens->n >= 2 * nbits * n_commits); + ARG_CHECK(proof != NULL); + ARG_CHECK(plen != NULL); + ARG_CHECK(value != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(value_gen != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(n_commits > 0 && n_commits); + ARG_CHECK(nbits <= 64); + if (nbits < 64) { + for (i = 0; i < n_commits; i++) { + ARG_CHECK(value[i] < (1ull << nbits)); + ARG_CHECK(blind[i] != NULL); + } + } + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + + if (!secp256k1_scratch_allocate_frame(scratch, n_commits * (sizeof(*commitp) + sizeof(*blinds)), 2)) { + return 0; + } + commitp = (secp256k1_ge *)secp256k1_scratch_alloc(scratch, n_commits * sizeof(*commitp)); + blinds = (secp256k1_scalar *)secp256k1_scratch_alloc(scratch, n_commits * sizeof(*blinds)); + + secp256k1_generator_load(&value_genp, value_gen); + for (i = 0; i < n_commits; i++) { + int overflow; + secp256k1_gej commitj; + secp256k1_scalar_set_b32(&blinds[i], blind[i], &overflow); + if (overflow || secp256k1_scalar_is_zero(&blinds[i])) { + return 0; + } + secp256k1_pedersen_ecmult(&commitj, &blinds[i], value[i], &value_genp, &gens->blinding_gen[0]); + secp256k1_ge_set_gej(&commitp[i], &commitj); + } + + ret = secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof, plen, nbits, value, min_value, blinds, commitp, n_commits, &value_genp, gens, nonce, extra_commit, extra_commit_len); + secp256k1_scratch_deallocate_frame(scratch); + return ret; +} + +#endif diff --git a/src/modules/bulletproofs/rangeproof_impl.h b/src/modules/bulletproofs/rangeproof_impl.h new file mode 100644 index 000000000..c40b53915 --- /dev/null +++ b/src/modules/bulletproofs/rangeproof_impl.h @@ -0,0 +1,650 @@ +/********************************************************************** + * 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_BULLETPROOF_RANGEPROOF_IMPL +#define SECP256K1_MODULE_BULLETPROOF_RANGEPROOF_IMPL + +#include "modules/bulletproofs/inner_product_impl.h" +#include "modules/bulletproofs/util.h" +#include "group.h" + +#define MAX_NBITS 64 + +typedef struct { + secp256k1_scalar yinv; + secp256k1_scalar yinvn; + secp256k1_scalar z; + secp256k1_scalar z_randomized; + secp256k1_scalar zsq; + secp256k1_scalar g_exponent; + secp256k1_scalar negz; + secp256k1_scalar x; + secp256k1_ge a; + secp256k1_ge s; + size_t n; + /* eq (61) stuff */ + size_t count; + secp256k1_scalar randomizer61; + secp256k1_scalar y; + secp256k1_scalar t; + const secp256k1_ge *asset; + const secp256k1_ge *commit; + const uint64_t *min_value; + size_t n_commits; + secp256k1_ge t1; + secp256k1_ge t2; +} secp256k1_bulletproof_vfy_ecmult_context; + +static int secp256k1_bulletproof_rangeproof_vfy_callback(secp256k1_scalar *sc, secp256k1_ge *pt, secp256k1_scalar *randomizer, size_t idx, void *data) { + secp256k1_bulletproof_vfy_ecmult_context *ctx = (secp256k1_bulletproof_vfy_ecmult_context *) data; + + if (idx == 0) { + secp256k1_scalar_mul(&ctx->g_exponent, &ctx->negz, randomizer); + secp256k1_scalar_mul(&ctx->z_randomized, &ctx->z, randomizer); + } + + if (idx < ctx->n) { + *sc = ctx->g_exponent; + } else if (idx < 2 * ctx->n) { + const size_t nbits = ctx->n / ctx->n_commits; + const size_t commit_idx = (idx - ctx->n) / nbits; + const size_t bit_idx = (idx - ctx->n) % nbits; + + if (bit_idx == 0) { + size_t i; + secp256k1_scalar tmp; + secp256k1_scalar_mul(&tmp, &ctx->z, &ctx->yinvn); + secp256k1_scalar_sqr(&ctx->zsq, &ctx->z); + for (i = 0; i < commit_idx; i++) { + secp256k1_scalar_mul(&ctx->zsq, &ctx->zsq, &tmp); + } + secp256k1_scalar_mul(&ctx->zsq, &ctx->zsq, randomizer); + } + secp256k1_scalar_add(sc, &ctx->zsq, &ctx->z_randomized); + + secp256k1_scalar_mul(&ctx->zsq, &ctx->zsq, &ctx->yinv); + secp256k1_scalar_add(&ctx->zsq, &ctx->zsq, &ctx->zsq); + } else { + switch(ctx->count) { + /* S^x in eq (62) */ + case 2: + *sc = ctx->x; + *pt = ctx->s; + break; + /* A in eq (62) */ + case 1: + *pt = ctx->a; + secp256k1_scalar_set_int(sc, 1); + break; + /* G^[k(y, z) + sum_i y^i - t] from eq (61) */ + case 0: { + size_t i; + secp256k1_scalar yn; + secp256k1_scalar twosum; + secp256k1_scalar tmp; + + secp256k1_scalar_clear(&twosum); + secp256k1_scalar_clear(&yn); + secp256k1_scalar_set_int(&tmp, 1); + + secp256k1_scalar_sqr(&ctx->zsq, &ctx->z); /* need to re-set this */ + secp256k1_scalar_negate(sc, &ctx->zsq); /* -z^2 */ + secp256k1_scalar_add(sc, sc, &ctx->z); /* z - z^2 */ + + for (i = 0; i < ctx->n_commits; i++) { + const size_t nbits = ctx->n / ctx->n_commits; + secp256k1_scalar negzn; + secp256k1_scalar twon; + size_t j; + + secp256k1_scalar_clear(&twon); + for (j = 0; j < nbits; j++) { + secp256k1_scalar_mul(&yn, &yn, &ctx->y); + secp256k1_scalar_add(&twon, &twon, &twon); + + secp256k1_scalar_add(&yn, &yn, &tmp); + secp256k1_scalar_add(&twon, &twon, &tmp); + } + + secp256k1_scalar_mul(&negzn, &ctx->zsq, &ctx->negz); + for (j = 0; j < i; j++) { + secp256k1_scalar_mul(&negzn, &negzn, &ctx->z); + } + if (ctx->min_value != NULL) { + secp256k1_scalar mv; + secp256k1_scalar_set_int(&mv, ctx->min_value[i]); + secp256k1_scalar_mul(&mv, &mv, &ctx->negz); + secp256k1_scalar_mul(&mv, &mv, &ctx->z); + for (j = 0; j < i; j++) { + secp256k1_scalar_mul(&negzn, &negzn, &ctx->z); + } + secp256k1_scalar_add(&twosum, &twosum, &mv); + } + secp256k1_scalar_mul(&twon, &twon, &negzn); + secp256k1_scalar_add(&twosum, &twosum, &twon); + } /* yn = 1 + y + ... + y^(n-1); twosum = (z^3 + ... + z^{2 + n_commits})(1 + 2 + ... + 2^(n-1)) */ + + + secp256k1_scalar_mul(sc, sc, &yn); /* (z - z^2)(1 + ... + y^(n-1)) */ + secp256k1_scalar_add(sc, sc, &twosum); /* (z - z^2)(1 + ... + y^(n-1)) - z^3(1 + ... + 2^(n-1)) */ + secp256k1_scalar_negate(&tmp, &ctx->t); + secp256k1_scalar_add(sc, sc, &tmp); /* (z - z^2)(1 + ... + y^n) - z^3(1 + ... + 2^n) - t */ + secp256k1_scalar_mul(sc, sc, &ctx->randomizer61); + *pt = *ctx->asset; + break; + } + /* T1^x in eq (61) */ + case 3: + secp256k1_scalar_mul(sc, &ctx->x, &ctx->randomizer61); + *pt = ctx->t1; + break; + /* T2^x^2 in eq (61) */ + case 4: + secp256k1_scalar_sqr(sc, &ctx->x); + secp256k1_scalar_mul(sc, sc, &ctx->randomizer61); + *pt = ctx->t2; + break; + /* V^z^2 in eq (61) */ + default: + VERIFY_CHECK(ctx->count < 5 + ctx->n_commits); + + secp256k1_scalar_mul(sc, &ctx->zsq, &ctx->randomizer61); + secp256k1_scalar_mul(&ctx->zsq, &ctx->zsq, &ctx->z); + *pt = ctx->commit[ctx->count - 5]; + break; + } + secp256k1_scalar_mul(sc, sc, randomizer); + ctx->count++; + } + return 1; +} + +static int secp256k1_bulletproof_rangeproof_verify_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, const unsigned char* const* proof, const size_t n_proofs, const size_t plen, size_t nbits, const uint64_t* const* min_value, const secp256k1_ge* const* commitp, size_t n_commits, const secp256k1_ge *value_gen, const secp256k1_bulletproof_generators *gens, const unsigned char* const* extra_commit, size_t *extra_commit_len) { + secp256k1_bulletproof_vfy_ecmult_context *ecmult_data; + secp256k1_bulletproof_innerproduct_context *innp_ctx; + int ret; + size_t i; + int same_generators = 1; + + /* sanity-check input */ + if (secp256k1_popcountl(nbits) != 1 || nbits > MAX_NBITS) { + return 0; + } + if (plen < 64 + 128 + 1 + 32) { /* inner product argument will do a more precise check */ + return 0; + } + if (plen > SECP256K1_BULLETPROOF_MAX_PROOF) { + return 0; + } + + if (!secp256k1_scratch_allocate_frame(scratch, n_proofs * (sizeof(*ecmult_data) + sizeof(*innp_ctx)), 2)) { + return 0; + } + ecmult_data = (secp256k1_bulletproof_vfy_ecmult_context *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*ecmult_data)); + innp_ctx = (secp256k1_bulletproof_innerproduct_context *)secp256k1_scratch_alloc(scratch, n_proofs * sizeof(*innp_ctx)); + + /* Check if all generators are equal. If so, we can amortize all their scalar multiplications + * by them and save one scalar-ge multiplication per proof. */ + VERIFY_CHECK(!secp256k1_ge_is_infinity(&value_gen[0])); + for (i = 1; i < n_proofs; i++) { + VERIFY_CHECK(!secp256k1_ge_is_infinity(&value_gen[i])); + if (!secp256k1_fe_equal_var(&value_gen[i].x, &value_gen[i - 1].x) || + !secp256k1_fe_equal_var(&value_gen[i].y, &value_gen[i - 1].y)) { + same_generators = 0; + } + } + + for (i = 0; i < n_proofs; i++) { + secp256k1_sha256 sha256; + unsigned char commit[32] = {0}; + unsigned char randomizer61[32] = {0}; /* randomizer for eq (61) so we can add it to eq (62) to save a separate multiexp */ + secp256k1_scalar taux, mu; + secp256k1_ge age, sge; + int overflow; + size_t j; + + /* Commit to all input data: min value, pedersen commit, asset generator, extra_commit */ + if (min_value != NULL && min_value[i] != NULL) { + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + for (j = 0; j < n_commits; j++) { + unsigned char vbuf[8]; + vbuf[0] = min_value[i][j]; + vbuf[1] = min_value[i][j] >> 8; + vbuf[2] = min_value[i][j] >> 16; + vbuf[3] = min_value[i][j] >> 24; + vbuf[4] = min_value[i][j] >> 32; + vbuf[5] = min_value[i][j] >> 40; + vbuf[6] = min_value[i][j] >> 48; + vbuf[7] = min_value[i][j] >> 56; + secp256k1_sha256_write(&sha256, vbuf, 8); + } + secp256k1_sha256_finalize(&sha256, commit); + } + for (j = 0; j < n_commits; j++) { + secp256k1_bulletproof_update_commit(commit, &commitp[i][j], &value_gen[i]); + } + if (extra_commit != NULL && extra_commit[i] != NULL) { + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, extra_commit[i], extra_commit_len[i]); + secp256k1_sha256_finalize(&sha256, commit); + } + + /* Compute y, z, x */ + if (!secp256k1_bulletproof_deserialize_point(&age, &proof[i][64], 0, 4) || + !secp256k1_bulletproof_deserialize_point(&sge, &proof[i][64], 1, 4)) { + return 0; + } + + secp256k1_bulletproof_update_commit(commit, &age, &sge); + secp256k1_scalar_set_b32(&ecmult_data[i].y, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ecmult_data[i].y)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_bulletproof_update_commit(commit, &age, &sge); + secp256k1_scalar_set_b32(&ecmult_data[i].z, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ecmult_data[i].z)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + if (!secp256k1_bulletproof_deserialize_point(&ecmult_data[i].t1, &proof[i][64], 2, 4) || + !secp256k1_bulletproof_deserialize_point(&ecmult_data[i].t2, &proof[i][64], 3, 4)) { + return 0; + } + + secp256k1_bulletproof_update_commit(commit, &ecmult_data[i].t1, &ecmult_data[i].t2); + secp256k1_scalar_set_b32(&ecmult_data[i].x, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ecmult_data[i].x)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + /* compute exponent offsets */ + secp256k1_scalar_inverse_var(&ecmult_data[i].yinv, &ecmult_data[i].y); /* TODO somehow batch this w the inner-product argument inverse */ + ecmult_data[i].yinvn = ecmult_data[i].yinv; + for (j = 0; j < secp256k1_floor_lg(nbits); j++) { + secp256k1_scalar_sqr(&ecmult_data[i].yinvn, &ecmult_data[i].yinvn); + } + secp256k1_scalar_sqr(&ecmult_data[i].zsq, &ecmult_data[i].z); + secp256k1_scalar_negate(&ecmult_data[i].negz, &ecmult_data[i].z); + + /* Update commit with remaining data for the inner product proof */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &proof[i][0], 64); + secp256k1_sha256_finalize(&sha256, commit); + + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_finalize(&sha256, randomizer61); + secp256k1_scalar_set_b32(&ecmult_data[i].randomizer61, randomizer61, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ecmult_data[i].randomizer61)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + /* Deserialize everything else */ + secp256k1_scalar_set_b32(&taux, &proof[i][0], &overflow); + if (overflow || secp256k1_scalar_is_zero(&taux)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_scalar_set_b32(&mu, &proof[i][32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&mu)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + /* A little sketchy, we read t (l(x) . r(x)) off the front of the inner product proof, + * which we otherwise treat as a black box */ + secp256k1_scalar_set_b32(&ecmult_data[i].t, &proof[i][64 + 128 + 1], &overflow); + if (overflow || secp256k1_scalar_is_zero(&ecmult_data[i].t)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + + /* Verify inner product proof */ + ecmult_data[i].a = age; + ecmult_data[i].s = sge; + ecmult_data[i].n = nbits * n_commits; + ecmult_data[i].count = 0; + ecmult_data[i].asset = &value_gen[i]; + ecmult_data[i].min_value = min_value == NULL ? NULL : min_value[i]; + ecmult_data[i].commit = commitp[i]; + ecmult_data[i].n_commits = n_commits; + secp256k1_scalar_mul(&taux, &taux, &ecmult_data[i].randomizer61); + secp256k1_scalar_add(&mu, &mu, &taux); + + innp_ctx[i].proof = &proof[i][64 + 128 + 1]; + innp_ctx[i].p_offs = mu; + memcpy(innp_ctx[i].commit, commit, 32); + innp_ctx[i].yinv = ecmult_data[i].yinv; + innp_ctx[i].rangeproof_cb = secp256k1_bulletproof_rangeproof_vfy_callback; + innp_ctx[i].rangeproof_cb_data = (void *) &ecmult_data[i]; + innp_ctx[i].n_extra_rangeproof_points = 5 + n_commits; + } + + ret = secp256k1_bulletproof_inner_product_verify_impl(ecmult_ctx, scratch, gens, nbits * n_commits, innp_ctx, n_proofs, plen - (64 + 128 + 1), same_generators); + secp256k1_scratch_deallocate_frame(scratch); + return ret; +} + +typedef struct { + const unsigned char *nonce; + secp256k1_scalar y; + secp256k1_scalar z; + secp256k1_scalar yn; + secp256k1_scalar z22n; + const uint64_t *val; + const uint64_t *min_val; + size_t n_vals; + size_t nbits; + size_t count; +} secp256k1_bulletproof_lr_generator; + +static void secp256k1_lr_generator_init(secp256k1_bulletproof_lr_generator *generator, const unsigned char *nonce, const secp256k1_scalar *y, const secp256k1_scalar *z, size_t nbits, const uint64_t *val, const uint64_t *min_val, size_t n_vals) { + generator->nonce = nonce; + generator->y = *y; + generator->z = *z; + secp256k1_scalar_set_int(&generator->yn, 1); + generator->nbits = nbits; + generator->val = val; + generator->min_val = min_val; + generator->n_vals = n_vals; + generator->count = 0; +} + +static void secp256k1_lr_generate(secp256k1_bulletproof_lr_generator *generator, secp256k1_scalar *lout, secp256k1_scalar *rout, const secp256k1_scalar *x) { + const size_t commit_idx = generator->count / generator->nbits; + const size_t bit_idx = generator->count % generator->nbits; + const uint64_t mv = generator->min_val == NULL ? 0 : generator->min_val[commit_idx]; + const int bit = ((generator->val[commit_idx] - mv) >> bit_idx) & 1; + secp256k1_scalar sl, sr; + secp256k1_scalar negz; + + if (bit_idx == 0) { + size_t i; + secp256k1_scalar_sqr(&generator->z22n, &generator->z); + for (i = 0; i < commit_idx; i++) { + secp256k1_scalar_mul(&generator->z22n, &generator->z22n, &generator->z); + } + } + + secp256k1_scalar_chacha20(&sl, &sr, generator->nonce, generator->count + 2); + secp256k1_scalar_mul(&sl, &sl, x); + secp256k1_scalar_mul(&sr, &sr, x); + + secp256k1_scalar_set_int(lout, bit); + secp256k1_scalar_negate(&negz, &generator->z); + secp256k1_scalar_add(lout, lout, &negz); + secp256k1_scalar_add(lout, lout, &sl); + + secp256k1_scalar_set_int(rout, 1 - bit); + secp256k1_scalar_negate(rout, rout); + secp256k1_scalar_add(rout, rout, &generator->z); + secp256k1_scalar_add(rout, rout, &sr); + secp256k1_scalar_mul(rout, rout, &generator->yn); + secp256k1_scalar_add(rout, rout, &generator->z22n); + + generator->count++; + secp256k1_scalar_mul(&generator->yn, &generator->yn, &generator->y); + secp256k1_scalar_add(&generator->z22n, &generator->z22n, &generator->z22n); +} + +typedef struct { + secp256k1_scalar x; + secp256k1_scalar cache; + secp256k1_bulletproof_lr_generator lr_gen; +} secp256k1_bulletproof_abgh_data; + +static int secp256k1_bulletproof_abgh_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_abgh_data *ctx = (secp256k1_bulletproof_abgh_data *) data; + const int is_g = idx % 2 == 0; + + (void) pt; + if (is_g) { + secp256k1_lr_generate(&ctx->lr_gen, sc, &ctx->cache, &ctx->x); + } else { + *sc = ctx->cache; + } + + return 1; +} + +/* Proof format: t, tau_x, mu, a, b, A, S, T_1, T_2, {L_i}, {R_i} + * 5 scalar + [4 + 2log(n)] ge + * + * The non-bold `h` in the Bulletproofs paper corresponds to our gens->blinding_gen + * while the non-bold `g` corresponds to the asset type `value_gen`. + */ +static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, unsigned char *proof, size_t *plen, const size_t nbits, const uint64_t *value, const uint64_t *min_value, const secp256k1_scalar *blind, const secp256k1_ge *commitp, size_t n_commits, const secp256k1_ge *value_gen, const secp256k1_bulletproof_generators *gens, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len) { + secp256k1_bulletproof_lr_generator lr_gen; + secp256k1_bulletproof_abgh_data abgh_data; + secp256k1_scalar zero; + secp256k1_sha256 sha256; + unsigned char commit[32] = {0}; + secp256k1_scalar alpha, rho; + secp256k1_scalar t0, t1, t2; + secp256k1_scalar tau1, tau2, taux, mu; + secp256k1_scalar y; + secp256k1_scalar z, zsq; + secp256k1_scalar x, xsq; + secp256k1_scalar tmps; + secp256k1_gej aj, sj; + secp256k1_gej tmpj; + size_t i, j; + int overflow; + /* inner product proof variables */ + secp256k1_ge out_pt[4]; + + if (secp256k1_popcountl(nbits) != 1 || nbits > MAX_NBITS) { + return 0; + } + for (i = 0; i < n_commits; i++) { + uint64_t mv = min_value == NULL ? 0 : min_value[i]; + if (mv > value[i]) { + return 0; + } + if (nbits < 64 && (value[i] - mv) >= (1ull << nbits)) { + return 0; + } + } + if (*plen < 128 + 64 + 1) { /* inner product argument will check and assign plen */ + return 0; + } + + secp256k1_scalar_clear(&zero); + + /* Commit to all input data: min value, pedersen commit, asset generator, extra_commit */ + if (min_value != NULL) { + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + for (i = 0; i < n_commits; i++) { + unsigned char vbuf[8]; + vbuf[0] = min_value[i]; + vbuf[1] = min_value[i] >> 8; + vbuf[2] = min_value[i] >> 16; + vbuf[3] = min_value[i] >> 24; + vbuf[4] = min_value[i] >> 32; + vbuf[5] = min_value[i] >> 40; + vbuf[6] = min_value[i] >> 48; + vbuf[7] = min_value[i] >> 56; + secp256k1_sha256_write(&sha256, vbuf, 8); + } + secp256k1_sha256_finalize(&sha256, commit); + } + for (i = 0; i < n_commits; i++) { + secp256k1_bulletproof_update_commit(commit, &commitp[i], value_gen); /* TODO be less stupid about this */ + } + if (extra_commit != NULL) { + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, extra_commit, extra_commit_len); + secp256k1_sha256_finalize(&sha256, commit); + } + + secp256k1_scalar_chacha20(&alpha, &rho, nonce, 0); + secp256k1_scalar_chacha20(&tau1, &tau2, nonce, 1); + + /* Compute A and S */ + secp256k1_ecmult_const(&aj, &gens->blinding_gen[0], &alpha, 256); + secp256k1_ecmult_const(&sj, &gens->blinding_gen[0], &rho, 256); + for (i = 0; i < n_commits; i++) { + for (j = 0; j < nbits; j++) { + secp256k1_scalar sl, sr; + uint64_t mv = min_value == NULL ? 0 : min_value[i]; + size_t al = !!((value[i] - mv) & (1ull << j)); + secp256k1_ge aterm = gens->gens[i * nbits + j + gens->n/2]; + secp256k1_ge sterm; + secp256k1_gej stermj; + + secp256k1_scalar_chacha20(&sl, &sr, nonce, i * nbits + j + 2); + + secp256k1_ge_neg(&aterm, &aterm); + secp256k1_fe_cmov(&aterm.x, &gens->gens[i * nbits + j].x, al); + secp256k1_fe_cmov(&aterm.y, &gens->gens[i * nbits + j].y, al); + + secp256k1_gej_add_ge(&aj, &aj, &aterm); + + secp256k1_ecmult_const(&stermj, &gens->gens[i * nbits + j], &sl, 256); + secp256k1_ge_set_gej(&sterm, &stermj); + secp256k1_gej_add_ge(&sj, &sj, &sterm); + secp256k1_ecmult_const(&stermj, &gens->gens[i * nbits + j + gens->n/2], &sr, 256); + secp256k1_ge_set_gej(&sterm, &stermj); + secp256k1_gej_add_ge(&sj, &sj, &sterm); + } + } + + /* get challenges y and z */ + secp256k1_ge_set_gej(&out_pt[0], &aj); + secp256k1_ge_set_gej(&out_pt[1], &sj); + + secp256k1_bulletproof_update_commit(commit, &out_pt[0], &out_pt[1]); + secp256k1_scalar_set_b32(&y, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&y)) { + return 0; + } + secp256k1_bulletproof_update_commit(commit, &out_pt[0], &out_pt[1]); /* TODO rehashing A and S to get a second challenge is overkill */ + secp256k1_scalar_set_b32(&z, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&z)) { + return 0; + } + secp256k1_scalar_sqr(&zsq, &z); + + /* Compute coefficients t0, t1, t2 of the polynomial */ + /* t0 = l(0) dot r(0) */ + secp256k1_lr_generator_init(&lr_gen, nonce, &y, &z, nbits, value, min_value, n_commits); + secp256k1_scalar_clear(&t0); + for (i = 0; i < nbits * n_commits; i++) { + secp256k1_scalar l, r; + secp256k1_lr_generate(&lr_gen, &l, &r, &zero); + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&t0, &t0, &l); + } + + /* A = t0 + t1 + t2 = l(1) dot r(1) */ + secp256k1_lr_generator_init(&lr_gen, nonce, &y, &z, nbits, value, min_value, n_commits); + secp256k1_scalar_clear(&t1); + for (i = 0; i < nbits * n_commits; i++) { + secp256k1_scalar one; + secp256k1_scalar l, r; + secp256k1_scalar_set_int(&one, 1); + secp256k1_lr_generate(&lr_gen, &l, &r, &one); + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&t1, &t1, &l); + } + + /* B = t0 - t1 + t2 = l(-1) dot r(-1) */ + secp256k1_lr_generator_init(&lr_gen, nonce, &y, &z, nbits, value, min_value, n_commits); + secp256k1_scalar_clear(&t2); + for (i = 0; i < nbits * n_commits; i++) { + secp256k1_scalar negone; + secp256k1_scalar l, r; + secp256k1_scalar_set_int(&negone, 1); + secp256k1_scalar_negate(&negone, &negone); + secp256k1_lr_generate(&lr_gen, &l, &r, &negone); + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&t2, &t2, &l); + } + + /* t1 = (A - B)/2 */ + secp256k1_scalar_set_int(&tmps, 2); + secp256k1_scalar_inverse_var(&tmps, &tmps); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&t1, &t1, &t2); + secp256k1_scalar_mul(&t1, &t1, &tmps); + + /* t2 = -(-B + t0) + t1 */ + secp256k1_scalar_add(&t2, &t2, &t0); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&t2, &t2, &t1); + + /* Compute Ti = t_i*A + tau_i*G for i = 1,2 */ + /* TODO surely we can improve this */ + secp256k1_ecmult_const(&tmpj, value_gen, &t1, 256); + secp256k1_ge_set_gej(&out_pt[2], &tmpj); + secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau1, 256); + secp256k1_gej_add_ge(&tmpj, &tmpj, &out_pt[2]); + secp256k1_ge_set_gej(&out_pt[2], &tmpj); + + secp256k1_ecmult_const(&tmpj, value_gen, &t2, 256); + secp256k1_ge_set_gej(&out_pt[3], &tmpj); + secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau2, 256); + secp256k1_gej_add_ge(&tmpj, &tmpj, &out_pt[3]); + secp256k1_ge_set_gej(&out_pt[3], &tmpj); + + /* get challenge x */ + secp256k1_bulletproof_update_commit(commit, &out_pt[2], &out_pt[3]); + secp256k1_scalar_set_b32(&x, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + return 0; + } + secp256k1_scalar_sqr(&xsq, &x); + + /* compute tau_x and mu */ + secp256k1_scalar_mul(&taux, &tau1, &x); + secp256k1_scalar_mul(&tmps, &tau2, &xsq); + secp256k1_scalar_add(&taux, &taux, &tmps); + for (i = 0; i < n_commits; i++) { + secp256k1_scalar_mul(&tmps, &zsq, &blind[i]); + secp256k1_scalar_add(&taux, &taux, &tmps); + secp256k1_scalar_mul(&zsq, &zsq, &z); + } + + secp256k1_scalar_mul(&mu, &rho, &x); + secp256k1_scalar_add(&mu, &mu, &alpha); + + /* Negate taux and mu so the verifier doesn't have to */ + secp256k1_scalar_negate(&taux, &taux); + secp256k1_scalar_negate(&mu, &mu); + + /* Encode rangeproof stuff */ + secp256k1_scalar_get_b32(&proof[0], &taux); + secp256k1_scalar_get_b32(&proof[32], &mu); + secp256k1_bulletproof_serialize_points(&proof[64], out_pt, 4); + + /* Mix this into the hash so the input to the inner product proof is fixed */ + /* TODO is this necessary? revisit */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, proof, 64); + secp256k1_sha256_finalize(&sha256, commit); + + /* Compute l and r, do inner product proof */ + abgh_data.x = x; + secp256k1_lr_generator_init(&abgh_data.lr_gen, nonce, &y, &z, nbits, value, min_value, n_commits); + *plen -= 64 + 128 + 1; + secp256k1_scalar_inverse_var(&y, &y); + if (secp256k1_bulletproof_inner_product_prove_impl(ecmult_ctx, scratch, &proof[64 + 128 + 1], plen, gens, &y, nbits * n_commits, secp256k1_bulletproof_abgh_callback, (void *) &abgh_data, commit) == 0) { + return 0; + } + *plen += 64 + 128 + 1; + + return 1; +} +#endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h new file mode 100644 index 000000000..6117791ad --- /dev/null +++ b/src/modules/bulletproofs/tests_impl.h @@ -0,0 +1,565 @@ +/********************************************************************** + * 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_BULLETPROOF_TESTS +#define SECP256K1_MODULE_BULLETPROOF_TESTS + +#include + +#include "group.h" +#include "scalar.h" +#include "testrand.h" +#include "util.h" + +#include "include/secp256k1_bulletproofs.h" + +static void test_bulletproof_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_scratch *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + secp256k1_generator value_gen; + secp256k1_bulletproof_generators *gens; + secp256k1_pedersen_commitment pcommit[4]; + const secp256k1_pedersen_commitment *pcommit_arr[1]; + unsigned char proof[2000]; + const unsigned char *proof_ptr = proof; + const unsigned char blind[32] = " i am not a blinding factor "; + const unsigned char *blind_ptr[4]; + size_t blindlen = sizeof(blind); + size_t plen = sizeof(proof); + uint64_t value[4] = { 1234, 4567, 8910, 1112 } ; + uint64_t min_value[4] = { 1000, 4567, 0, 5000 } ; + const uint64_t *mv_ptr = min_value; + + int32_t ecount = 0; + + blind_ptr[0] = blind; + blind_ptr[1] = blind; + blind_ptr[2] = blind; + blind_ptr[3] = blind; + pcommit_arr[0] = pcommit; + + 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); + + CHECK(secp256k1_generator_generate(both, &value_gen, blind) != 0); + CHECK(secp256k1_pedersen_commit(both, &pcommit[0], blind, value[0], &value_gen, &secp256k1_generator_const_h) != 0); + CHECK(secp256k1_pedersen_commit(both, &pcommit[1], blind, value[1], &value_gen, &secp256k1_generator_const_h) != 0); + CHECK(secp256k1_pedersen_commit(both, &pcommit[2], blind, value[2], &value_gen, &secp256k1_generator_const_h) != 0); + CHECK(secp256k1_pedersen_commit(both, &pcommit[3], blind, value[3], &value_gen, &secp256k1_generator_const_h) != 0); + + /* generators */ + gens = secp256k1_bulletproof_generators_create(none, NULL, 256); + CHECK(gens == NULL && ecount == 1); + gens = secp256k1_bulletproof_generators_create(none, &secp256k1_generator_const_h, 256); + CHECK(gens != NULL && ecount == 1); + + /* rangeproof_prove */ + ecount = 0; + CHECK(secp256k1_bulletproof_rangeproof_prove(none, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_bulletproof_rangeproof_prove(sign, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_prove(vrfy, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 1); + CHECK(ecount == 3); + plen = 2000; + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 2, &value_gen, 64, blind, NULL, 0) == 1); + CHECK(ecount == 3); + plen = 2000; + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 4, &value_gen, 64, blind, NULL, 0) == 0); /* too few gens */ + CHECK(ecount == 4); + + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, min_value, blind_ptr, 2, &value_gen, 64, blind, NULL, 0) == 1); /* mv = v, ok */ + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, &value[1], &min_value[1], blind_ptr, 2, &value_gen, 64, blind, NULL, 0) == 1); /* mv = 0, ok */ + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, &value[2], &min_value[2], blind_ptr, 2, &value_gen, 64, blind, NULL, 0) == 0); /* mv > v, !ok */ + CHECK(ecount == 4); + + CHECK(secp256k1_bulletproof_rangeproof_prove(both, NULL, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, NULL, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, NULL, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, NULL, value, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, NULL, NULL, blind_ptr, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, NULL, 1, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 0, &value_gen, 64, blind, NULL, 0) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, NULL, 64, blind, NULL, 0) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 0, blind, NULL, 0) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 65, blind, NULL, 0) == 0); + CHECK(ecount == 14); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, -1, blind, NULL, 0) == 0); + CHECK(ecount == 15); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, NULL, NULL, 0) == 0); + CHECK(ecount == 16); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, NULL, blind_ptr, 1, &value_gen, 64, blind, blind, 0) == 1); + CHECK(ecount == 16); + CHECK(secp256k1_bulletproof_rangeproof_prove(both, scratch, gens, proof, &plen, value, min_value, blind_ptr, 1, &value_gen, 64, blind, blind, 32) == 1); + CHECK(ecount == 16); + + /* rangeproof_verify */ + ecount = 0; + CHECK(secp256k1_bulletproof_rangeproof_verify(none, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_bulletproof_rangeproof_verify(sign, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(vrfy, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 1); + CHECK(ecount == 2); + + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 63, &value_gen, blind, 32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen - 1, min_value, pcommit, 1, 63, &value_gen, blind, 32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, 0, min_value, pcommit, 1, 63, &value_gen, blind, 32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 31) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, NULL, 0) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 2, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 4, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 3); + + CHECK(secp256k1_bulletproof_rangeproof_verify(both, NULL, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, NULL, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, NULL, plen, min_value, pcommit, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, NULL, pcommit, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, NULL, 1, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 0, 64, &value_gen, blind, 32) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 65, &value_gen, blind, 32) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 0, &value_gen, blind, 32) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 64, NULL, blind, 32) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, NULL, 32) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_bulletproof_rangeproof_verify(both, scratch, gens, proof, plen, min_value, pcommit, 1, 64, &value_gen, blind, 0) == 0); + CHECK(ecount == 12); + + /* verify_multi */ + ecount = 0; + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(none, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(sign, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(vrfy, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 1); + CHECK(ecount == 2); + + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, NULL, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, NULL, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, NULL, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 0, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, NULL, pcommit_arr, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, NULL, 1, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, NULL, blind_ptr, &blindlen) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, NULL, &blindlen) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, blind_ptr, NULL) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 64, &value_gen, NULL, NULL) == 0); + CHECK(ecount == 10); + + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 0, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 65, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 63, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 1, 0, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 2, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 4, 64, &value_gen, blind_ptr, &blindlen) == 0); + CHECK(ecount == 14); + + secp256k1_bulletproof_generators_destroy(none, gens); + secp256k1_bulletproof_generators_destroy(none, NULL); + secp256k1_scratch_destroy(scratch); + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +#define MAX_WIDTH (1ul << 20) +typedef struct { + const secp256k1_scalar *a; + const secp256k1_scalar *b; + const secp256k1_ge *g; + const secp256k1_ge *h; + size_t n; +} test_bulletproof_ecmult_context; + +static int test_bulletproof_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + test_bulletproof_ecmult_context *ecctx = (test_bulletproof_ecmult_context *) data; + if (idx < ecctx->n) { + *sc = ecctx->a[idx]; + *pt = ecctx->g[idx]; + } else { + VERIFY_CHECK(idx < 2*ecctx->n); + *sc = ecctx->b[idx - ecctx->n]; + *pt = ecctx->h[idx - ecctx->n]; + } + return 1; +} + +typedef struct { + secp256k1_scalar offs; + secp256k1_scalar ext_sc; + secp256k1_scalar skew_sc; + secp256k1_ge ext_pt; + secp256k1_ge p; + size_t n; + int parity; +} test_bulletproof_offset_context; + +static int test_bulletproof_offset_vfy_callback(secp256k1_scalar *sc, secp256k1_ge *pt, secp256k1_scalar *randomizer, size_t idx, void *data) { + test_bulletproof_offset_context *ecctx = (test_bulletproof_offset_context *) data; + secp256k1_scalar_set_int(&ecctx->offs, 1); + if (idx < 2 * ecctx->n) { + secp256k1_scalar idxsc; + secp256k1_scalar_set_int(&idxsc, idx); + secp256k1_scalar_mul(sc, &ecctx->skew_sc, &idxsc); + } else { + if (ecctx->parity) { + *sc = ecctx->ext_sc; + *pt = ecctx->ext_pt; + } else { + secp256k1_scalar_set_int(sc, 1); + *pt = ecctx->p; + } + } + secp256k1_scalar_mul(sc, sc, randomizer); + ecctx->parity = !ecctx->parity; + return 1; +} + +typedef struct { + const secp256k1_scalar *a_arr; + const secp256k1_scalar *b_arr; +} secp256k1_bulletproof_ip_test_abgh_data; + + +static int secp256k1_bulletproof_ip_test_abgh_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproof_ip_test_abgh_data *cbctx = (secp256k1_bulletproof_ip_test_abgh_data *) data; + const int is_g = idx % 2 == 0; + + (void) pt; + if (is_g) { + *sc = cbctx->a_arr[idx / 2]; + } else { + *sc = cbctx->b_arr[idx / 2]; + } + return 1; +} + +void test_bulletproof_inner_product(size_t n, const secp256k1_bulletproof_generators *gens) { + const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0,0,0,0,0,0,0,0); + secp256k1_gej pj; + secp256k1_gej tmpj, tmpj2; + secp256k1_scalar *a_arr = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, n * sizeof(*a_arr)); + secp256k1_scalar *b_arr = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, n * sizeof(*b_arr)); + unsigned char commit[32] = "hash of P, c, etc. all that jazz"; + secp256k1_scalar one; + size_t j; + test_bulletproof_offset_context offs_ctx; + secp256k1_bulletproof_ip_test_abgh_data abgh_data; + secp256k1_bulletproof_innerproduct_context innp_ctx; + unsigned char proof[2000]; + size_t plen = sizeof(proof); + + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 100000 + 256 * (2 * n + 2)); + + for (j = 0; j < n; j++) { + random_scalar_order(&a_arr[j]); + random_scalar_order(&b_arr[j]); + } + + abgh_data.a_arr = a_arr; + abgh_data.b_arr = b_arr; + + random_group_element_test(&offs_ctx.ext_pt); + random_scalar_order(&offs_ctx.ext_sc); + secp256k1_scalar_clear(&offs_ctx.skew_sc); + offs_ctx.n = n; + + secp256k1_scalar_set_int(&one, 1); + CHECK(secp256k1_bulletproof_inner_product_prove_impl(&ctx->ecmult_ctx, scratch, proof, &plen, gens, &one, n, secp256k1_bulletproof_ip_test_abgh_callback, (void *) &abgh_data, commit) == 1); + + innp_ctx.proof = proof; + memcpy(innp_ctx.commit, commit, 32); + secp256k1_scalar_set_int(&innp_ctx.yinv, 1); + innp_ctx.n_extra_rangeproof_points = 1; + innp_ctx.rangeproof_cb = test_bulletproof_offset_vfy_callback; + innp_ctx.rangeproof_cb_data = (void *) &offs_ctx; + + /* Manually do the multiexp to obtain the point P which commits to the inner product. + * The prover never computes this because it is implicit in the range/circuit proofs. */ + { + test_bulletproof_ecmult_context ecmult_data; + ecmult_data.n = n; + ecmult_data.a = a_arr; + ecmult_data.b = b_arr; + ecmult_data.g = gens->gens; + ecmult_data.h = gens->gens + gens->n/2; + CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &pj, &zero, test_bulletproof_ecmult_callback, (void*) &ecmult_data, 2 * n)); + secp256k1_ge_set_gej(&offs_ctx.p, &pj); + } + + /* Check proof with no offsets or other baubles */ + offs_ctx.parity = 0; + secp256k1_scalar_clear(&innp_ctx.p_offs); + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 1); + + /* skew P by a random amount and instruct the verifier to offset it */ + random_scalar_order(&innp_ctx.p_offs); + secp256k1_gej_set_ge(&tmpj2, &gens->blinding_gen[0]); + secp256k1_ecmult(&ctx->ecmult_ctx, &tmpj, &tmpj2, &innp_ctx.p_offs, &zero); + secp256k1_gej_add_var(&pj, &pj, &tmpj, NULL); + secp256k1_ge_set_gej(&offs_ctx.p, &pj); + + /* wrong p_offs should fail */ + offs_ctx.parity = 0; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 0); + + secp256k1_scalar_negate(&innp_ctx.p_offs, &innp_ctx.p_offs); + + offs_ctx.parity = 0; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 1); + /* check that verification did not trash anything */ + offs_ctx.parity = 0; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 1); + /* check that adding a no-op rangeproof skew function doesn't break anything */ + offs_ctx.parity = 0; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 1); + + /* Offset P by some random point and then try to undo this in the verification */ + secp256k1_gej_set_ge(&tmpj2, &offs_ctx.ext_pt); + secp256k1_ecmult(&ctx->ecmult_ctx, &tmpj, &tmpj2, &offs_ctx.ext_sc, &zero); + secp256k1_gej_neg(&tmpj, &tmpj); + secp256k1_gej_add_ge_var(&tmpj, &tmpj, &offs_ctx.p, NULL); + secp256k1_ge_set_gej(&offs_ctx.p, &tmpj); + offs_ctx.parity = 0; + innp_ctx.n_extra_rangeproof_points = 2; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 1); + + /* Offset each basis by some random point and try to undo this in the verification */ + secp256k1_gej_set_infinity(&tmpj2); + for (j = 0; j < n; j++) { + size_t k; + /* Offset by k-times the kth G basis and (k+n)-times the kth H basis */ + for (k = 0; k < j; k++) { + secp256k1_gej_add_ge_var(&tmpj2, &tmpj2, &gens->gens[j], NULL); + secp256k1_gej_add_ge_var(&tmpj2, &tmpj2, &gens->gens[j + gens->n/2], NULL); + } + for (k = 0; k < n; k++) { + secp256k1_gej_add_ge_var(&tmpj2, &tmpj2, &gens->gens[j + gens->n/2], NULL); + } + } + random_scalar_order(&offs_ctx.skew_sc); + secp256k1_ecmult(&ctx->ecmult_ctx, &tmpj, &tmpj2, &offs_ctx.skew_sc, &zero); + secp256k1_gej_add_ge_var(&tmpj, &tmpj, &offs_ctx.p, NULL); + secp256k1_ge_set_gej(&offs_ctx.p, &tmpj); + secp256k1_scalar_negate(&offs_ctx.skew_sc, &offs_ctx.skew_sc); + + offs_ctx.parity = 0; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, &innp_ctx, 1, plen, 1) == 1); + + /* Try to validate the same proof twice */ +{ + test_bulletproof_offset_context offs_ctxs[2]; + secp256k1_bulletproof_innerproduct_context innp_ctxs[2]; + offs_ctx.parity = 1; /* set parity to 1 so the common point will be returned first, as required by the multi-proof verifier */ + memcpy(&innp_ctxs[0], &innp_ctx, sizeof(innp_ctx)); + memcpy(&innp_ctxs[1], &innp_ctx, sizeof(innp_ctx)); + memcpy(&offs_ctxs[0], &offs_ctx, sizeof(offs_ctx)); + memcpy(&offs_ctxs[1], &offs_ctx, sizeof(offs_ctx)); + innp_ctxs[0].rangeproof_cb_data = (void *)&offs_ctxs[0]; + innp_ctxs[1].rangeproof_cb_data = (void *)&offs_ctxs[1]; + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, innp_ctxs, 2, plen, 1) == 1); + CHECK(secp256k1_bulletproof_inner_product_verify_impl(&ctx->ecmult_ctx, scratch, gens, n, innp_ctxs, 2, plen, 0) == 1); +} + + free(a_arr); + free(b_arr); + secp256k1_scratch_destroy(scratch); +} + +void test_bulletproof_rangeproof(size_t nbits, size_t expected_size, const secp256k1_bulletproof_generators *gens) { + secp256k1_scalar blind; + unsigned char proof[1024]; + unsigned char proof2[1024]; + unsigned char proof3[1024]; + const unsigned char *proof_ptr[3]; + size_t plen = sizeof(proof); + uint64_t v = 123456; + secp256k1_gej commitj; + secp256k1_ge commitp; + secp256k1_ge commitp2; + const secp256k1_ge *commitp_ptr[3]; + secp256k1_ge value_gen[3]; + unsigned char nonce[32] = "my kingdom for some randomness!!"; + + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 10000000); + + if (v >> nbits > 0) { + v = 0; + } + + proof_ptr[0] = proof; + proof_ptr[1] = proof2; + proof_ptr[2] = proof3; + + secp256k1_generator_load(&value_gen[0], &secp256k1_generator_const_g); + secp256k1_generator_load(&value_gen[1], &secp256k1_generator_const_g); + secp256k1_generator_load(&value_gen[2], &secp256k1_generator_const_h); + random_scalar_order(&blind); + + secp256k1_pedersen_ecmult(&commitj, &blind, v, &value_gen[0], &gens->blinding_gen[0]); + secp256k1_ge_set_gej(&commitp, &commitj); + secp256k1_pedersen_ecmult(&commitj, &blind, v, &value_gen[2], &gens->blinding_gen[0]); + secp256k1_ge_set_gej(&commitp2, &commitj); + commitp_ptr[0] = commitp_ptr[1] = &commitp; + commitp_ptr[2] = &commitp2; + + CHECK(secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof, &plen, nbits, &v, NULL, &blind, &commitp, 1, &value_gen[0], gens, nonce, NULL, 0) == 1); + CHECK(plen == expected_size); + nonce[0] ^= 1; + CHECK(secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof2, &plen, nbits, &v, NULL, &blind, &commitp, 1, &value_gen[1], gens, nonce, NULL, 0) == 1); + CHECK(plen == expected_size); + nonce[0] ^= 2; + CHECK(secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof3, &plen, nbits, &v, NULL, &blind, &commitp2, 1, &value_gen[2], gens, nonce, NULL, 0) == 1); + CHECK(plen == expected_size); + nonce[0] ^= 3; + /* Verify once */ + CHECK(secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, proof_ptr, 1, plen, nbits, NULL, commitp_ptr, 1, value_gen, gens, NULL, 0) == 1); + /* Verify twice at once to test batch validation */ + CHECK(secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, proof_ptr, 2, plen, nbits, NULL, commitp_ptr, 1, value_gen, gens, NULL, 0) == 1); + /* Verify thrice at once where one has a different asset type */ + CHECK(secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, proof_ptr, 3, plen, nbits, NULL, commitp_ptr, 1, value_gen, gens, NULL, 0) == 1); + + secp256k1_scratch_destroy(scratch); +} + +void test_bulletproof_rangeproof_aggregate(size_t nbits, size_t n_commits, size_t expected_size, const secp256k1_bulletproof_generators *gens) { + unsigned char proof[1024]; + const unsigned char *proof_ptr = proof; + size_t plen = sizeof(proof); + secp256k1_scalar *blind = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, n_commits * sizeof(*blind)); + uint64_t *v = (uint64_t *)checked_malloc(&ctx->error_callback, n_commits * sizeof(*v)); + secp256k1_ge *commitp = (secp256k1_ge *)checked_malloc(&ctx->error_callback, n_commits * sizeof(*commitp)); + const secp256k1_ge *constptr = commitp; + secp256k1_ge value_gen; + unsigned char commit[32] = {0}; + unsigned char nonce[32] = "mary, mary quite contrary how do"; + size_t i; + + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 10000000); + + secp256k1_generator_load(&value_gen, &secp256k1_generator_const_g); + for (i = 0; i < n_commits; i++) { + secp256k1_scalar vs; + secp256k1_gej commitj; + + v[i] = 223 * i; /* dice-roll random # */ + if (v[i] >> nbits > 0) { + v[i] = 0; + } + secp256k1_scalar_set_u64(&vs, v[i]); + random_scalar_order(&blind[i]); + secp256k1_pedersen_ecmult(&commitj, &blind[i], v[i], &value_gen, &gens->blinding_gen[0]); + secp256k1_ge_set_gej(&commitp[i], &commitj); + + secp256k1_bulletproof_update_commit(commit, &commitp[i], &value_gen); + } + + CHECK(secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof, &plen, nbits, v, NULL, blind, commitp, n_commits, &value_gen, gens, nonce, NULL, 0) == 1); + CHECK(plen == expected_size); + CHECK(secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, &proof_ptr, 1, plen, nbits, NULL, &constptr, n_commits, &value_gen, gens, NULL, 0) == 1); + + secp256k1_scratch_destroy(scratch); + free(commitp); + free(v); + free(blind); +} + +void run_bulletproofs_tests(void) { + size_t i; + + /* Make a ton of generators */ + secp256k1_bulletproof_generators *gens = secp256k1_bulletproof_generators_create(ctx, &secp256k1_generator_const_h, 32768); + test_bulletproof_api(); + + /* sanity checks */ + CHECK(secp256k1_bulletproof_innerproduct_proof_length(0) == 32); /* encoding of 1 */ + CHECK(secp256k1_bulletproof_innerproduct_proof_length(1) == 96); /* encoding a*b, a, b */ + CHECK(secp256k1_bulletproof_innerproduct_proof_length(2) == 160); /* dot prod, a, b, L, R, parity of L, R */ + CHECK(secp256k1_bulletproof_innerproduct_proof_length(4) == 225); /* dot prod, a, b, a, b, L, R, parity of L, R */ + CHECK(secp256k1_bulletproof_innerproduct_proof_length(8) == 289); /* dot prod, a, b, a, b, L, R, L, R, parity of L, R */ + + test_bulletproof_inner_product(0, gens); + test_bulletproof_inner_product(1, gens); + test_bulletproof_inner_product(2, gens); + test_bulletproof_inner_product(4, gens); + test_bulletproof_inner_product(8, gens); + for (i = 0; i < (size_t) count; i++) { + test_bulletproof_inner_product(32, gens); + test_bulletproof_inner_product(64, gens); + } + test_bulletproof_inner_product(1024, gens); + + test_bulletproof_rangeproof(1, 289, gens); + test_bulletproof_rangeproof(2, 353, gens); + test_bulletproof_rangeproof(16, 546, gens); + test_bulletproof_rangeproof(32, 610, gens); + test_bulletproof_rangeproof(64, 675, gens); + + test_bulletproof_rangeproof_aggregate(64, 1, 675, gens); + test_bulletproof_rangeproof_aggregate(8, 2, 546, gens); + test_bulletproof_rangeproof_aggregate(8, 4, 610, gens); + + secp256k1_bulletproof_generators_destroy(ctx, gens); +} +#undef MAX_WIDTH + +#endif diff --git a/src/modules/bulletproofs/util.h b/src/modules/bulletproofs/util.h new file mode 100644 index 000000000..8059ceee4 --- /dev/null +++ b/src/modules/bulletproofs/util.h @@ -0,0 +1,150 @@ +/********************************************************************** + * 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_BULLETPROOF_UTIL +#define SECP256K1_MODULE_BULLETPROOF_UTIL + +/* floor(log2(n)) which returns 0 for 0, since this is used to estimate proof sizes */ +SECP256K1_INLINE static size_t secp256k1_floor_lg(size_t n) { + switch (n) { + case 0: return 0; + case 1: return 0; + case 2: return 1; + case 3: return 1; + case 4: return 2; + case 5: return 2; + case 6: return 2; + case 7: return 2; + case 8: return 3; + default: { + size_t i = 0; + while (n > 1) { + n /= 2; + i++; + } + return i; + } + } +} + +SECP256K1_INLINE static size_t secp256k1_popcountl(unsigned long x) { +#ifdef HAVE_BUILTIN_POPCOUNTL + return __builtin_popcountl(x); +#else + size_t ret = 0; + size_t i; + for (i = 0; i < 64; i++) { + ret += x & 1; + x >>= 1; + } + return ret; +#endif +} + +SECP256K1_INLINE static size_t secp256k1_ctzl(unsigned long x) { +#ifdef HAVE_BUILTIN_CTZL + return __builtin_ctzl(x); +#else + size_t i; + for (i = 0; i < 64; i++) { + if (x & (1ull << i)) { + return i; + } + } + /* If no bits are set, the result is __builtin_ctzl is undefined, + * so we can return whatever we want here. */ + return 0; +#endif +} + +static void secp256k1_scalar_dot_product(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, size_t n) { + secp256k1_scalar_clear(r); + while(n--) { + secp256k1_scalar term; + secp256k1_scalar_mul(&term, &a[n], &b[n]); + secp256k1_scalar_add(r, r, &term); + } +} + +static void secp256k1_scalar_inverse_all_var(secp256k1_scalar *r, const secp256k1_scalar *a, size_t len) { + secp256k1_scalar u; + size_t i; + if (len < 1) { + return; + } + + VERIFY_CHECK((r + len <= a) || (a + len <= r)); + + r[0] = a[0]; + + i = 0; + while (++i < len) { + secp256k1_scalar_mul(&r[i], &r[i - 1], &a[i]); + } + + secp256k1_scalar_inverse_var(&u, &r[--i]); + + while (i > 0) { + size_t j = i--; + secp256k1_scalar_mul(&r[j], &r[i], &u); + secp256k1_scalar_mul(&u, &u, &a[j]); + } + + r[0] = u; +} + +SECP256K1_INLINE static void secp256k1_bulletproof_serialize_points(unsigned char *out, secp256k1_ge *pt, size_t n) { + const size_t bitveclen = (n + 7) / 8; + size_t i; + + memset(out, 0, bitveclen); + for (i = 0; i < n; i++) { + secp256k1_fe pointx; + pointx = pt[i].x; + secp256k1_fe_normalize(&pointx); + secp256k1_fe_get_b32(&out[bitveclen + i*32], &pointx); + if (!secp256k1_fe_is_quad_var(&pt[i].y)) { + out[i/8] |= (1ull << (i % 8)); + } + } +} + +SECP256K1_INLINE static int secp256k1_bulletproof_deserialize_point(secp256k1_ge *pt, const unsigned char *data, size_t i, size_t n) { + const size_t bitveclen = (n + 7) / 8; + const size_t offset = bitveclen + i*32; + secp256k1_fe fe; + + secp256k1_fe_set_b32(&fe, &data[offset]); + if (secp256k1_ge_set_xquad(pt, &fe)) { + if (data[i / 8] & (1 << (i % 8))) { + secp256k1_ge_neg(pt, pt); + } + return 1; + } else { + return 0; + } +} + +static void secp256k1_bulletproof_update_commit(unsigned char *commit, const secp256k1_ge *lpt, const secp256k1_ge *rpt) { + secp256k1_fe pointx; + secp256k1_sha256 sha256; + unsigned char lrparity; + lrparity = (!secp256k1_fe_is_quad_var(&lpt->y) << 1) + !secp256k1_fe_is_quad_var(&rpt->y); + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &lrparity, 1); + pointx = lpt->x; + secp256k1_fe_normalize(&pointx); + secp256k1_fe_get_b32(commit, &pointx); + secp256k1_sha256_write(&sha256, commit, 32); + pointx = rpt->x; + secp256k1_fe_normalize(&pointx); + secp256k1_fe_get_b32(commit, &pointx); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_finalize(&sha256, commit); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 468826124..2d60e57ca 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -31,6 +31,10 @@ # include "include/secp256k1_rangeproof.h" #endif +#ifdef ENABLE_MODULE_BULLETPROOF +# include "include/secp256k1_bulletproofs.h" +#endif + #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ @@ -661,6 +665,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/rangeproof/main_impl.h" #endif +#ifdef ENABLE_MODULE_BULLETPROOF +# include "modules/bulletproofs/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 9eb803c6b..0693bcff7 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5143,6 +5143,10 @@ void run_ecdsa_openssl(void) { # include "modules/rangeproof/tests_impl.h" #endif +#ifdef ENABLE_MODULE_BULLETPROOF +# include "modules/bulletproofs/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_WHITELIST # include "modules/whitelist/tests_impl.h" #endif @@ -5291,6 +5295,10 @@ int main(int argc, char **argv) { run_rangeproof_tests(); #endif +#ifdef ENABLE_MODULE_BULLETPROOF + run_bulletproofs_tests(); +#endif + #ifdef ENABLE_MODULE_WHITELIST /* Key whitelisting tests */ run_whitelist_tests(); From 14b003f6892f2b39e4de00d927f1bef81db13145 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 5 Apr 2018 02:30:16 +0000 Subject: [PATCH 52/58] bulletproofs: add rangeproof rewinding capability --- include/secp256k1_bulletproofs.h | 30 +++++ src/modules/bulletproofs/main_impl.h | 21 ++++ src/modules/bulletproofs/rangeproof_impl.h | 133 +++++++++++++++++++++ src/modules/bulletproofs/tests_impl.h | 43 +++++++ 4 files changed, 227 insertions(+) diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h index 8d29e4e03..654cb4040 100644 --- a/include/secp256k1_bulletproofs.h +++ b/include/secp256k1_bulletproofs.h @@ -103,6 +103,36 @@ SECP256K1_API int secp256k1_bulletproof_rangeproof_verify_multi( size_t *extra_commit_len ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(8); +/** Extracts the value and blinding factor from a single-commit rangeproof given a secret nonce + * Returns: 1: value and blinding factor were extracted and matched the input commit + * 0: one of the above was not true, extraction failed + * Args: ctx: pointer to a context object (cannot be NULL) + * gens: generator set used to make original proof (cannot be NULL) + * Out: value: pointer to value that will be extracted + * blind: pointer to 32-byte array for blinding factor to be extracted + * In: proof: byte-serialized rangeproof (cannot be NULL) + * plen: length of every individual proof + * min_value: minimum value that the proof ranges over + * commit: pedersen commitment that the rangeproof is over (cannot be NULL) + * value_gen: generator multiplied by value in pedersen commitments (cannot be NULL) + * nonce: random 32-byte seed used to derive blinding factors (cannot be NULL) + * extra_commit: additonal data committed to by the rangeproof + * extra_commit_len: length of additional data + */ +SECP256K1_API int secp256k1_bulletproof_rangeproof_rewind( + const secp256k1_context* ctx, + const secp256k1_bulletproof_generators* gens, + uint64_t* value, + unsigned char* blind, + const unsigned char* proof, + size_t plen, + uint64_t min_value, + const secp256k1_pedersen_commitment* commit, + const secp256k1_generator* value_gen, + const unsigned char* nonce, + const unsigned char* extra_commit, + size_t extra_commit_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9); /** Produces an aggregate Bulletproof rangeproof for a set of Pedersen commitments * Returns: 1: rangeproof was successfully created diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h index 8ffd69d05..1460ebfba 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bulletproofs/main_impl.h @@ -156,6 +156,27 @@ int secp256k1_bulletproof_rangeproof_verify_multi(const secp256k1_context* ctx, return ret; } +int secp256k1_bulletproof_rangeproof_rewind(const secp256k1_context* ctx, const secp256k1_bulletproof_generators *gens, uint64_t *value, unsigned char *blind, const unsigned char *proof, size_t plen, uint64_t min_value, const secp256k1_pedersen_commitment* commit, const secp256k1_generator *value_gen, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len) { + secp256k1_scalar blinds; + int ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(value != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(value_gen != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + + ret = secp256k1_bulletproof_rangeproof_rewind_impl(value, &blinds, proof, plen, min_value, commit, value_gen, gens->blinding_gen, nonce, extra_commit, extra_commit_len); + if (ret == 1) { + secp256k1_scalar_get_b32(blind, &blinds); + } + return ret; +} + int secp256k1_bulletproof_rangeproof_prove(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, const secp256k1_bulletproof_generators *gens, unsigned char *proof, size_t *plen, const uint64_t *value, const uint64_t *min_value, const unsigned char* const* blind, size_t n_commits, const secp256k1_generator *value_gen, size_t nbits, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len) { int ret; secp256k1_ge *commitp; diff --git a/src/modules/bulletproofs/rangeproof_impl.h b/src/modules/bulletproofs/rangeproof_impl.h index c40b53915..a9c22011b 100644 --- a/src/modules/bulletproofs/rangeproof_impl.h +++ b/src/modules/bulletproofs/rangeproof_impl.h @@ -490,6 +490,13 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co secp256k1_scalar_chacha20(&alpha, &rho, nonce, 0); secp256k1_scalar_chacha20(&tau1, &tau2, nonce, 1); + /* Encrypt value into alpha, so it will be recoverable from -mu by someone who knows `nonce` */ + if (n_commits == 1) { + secp256k1_scalar vals; + secp256k1_scalar_set_u64(&vals, value[0]); + secp256k1_scalar_negate(&vals, &vals); /* Negate so it'll be positive in -mu */ + secp256k1_scalar_add(&alpha, &alpha, &vals); + } /* Compute A and S */ secp256k1_ecmult_const(&aj, &gens->blinding_gen[0], &alpha, 256); @@ -647,4 +654,130 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co return 1; } + +static int secp256k1_bulletproof_rangeproof_rewind_impl(uint64_t *value, secp256k1_scalar *blind, const unsigned char *proof, const size_t plen, uint64_t min_value, const secp256k1_pedersen_commitment *pcommit, const secp256k1_generator *value_gen, const secp256k1_ge *blind_gen, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len) { + secp256k1_sha256 sha256; + static const unsigned char zero24[24] = { 0 }; + unsigned char commit[32] = { 0 }; + unsigned char lrparity; + secp256k1_scalar taux, mu; + secp256k1_scalar alpha, rho, tau1, tau2; + secp256k1_scalar x, z; + secp256k1_ge commitp, value_genp; + secp256k1_gej rewind_commitj; + int overflow; + + if (plen < 64 + 128 + 1 || plen > SECP256K1_BULLETPROOF_MAX_PROOF) { + return 0; + } + + /* Extract data from beginning of proof */ + secp256k1_scalar_set_b32(&taux, &proof[0], &overflow); + if (overflow || secp256k1_scalar_is_zero(&taux)) { + return 0; + } + secp256k1_scalar_set_b32(&mu, &proof[32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&mu)) { + return 0; + } + + secp256k1_scalar_chacha20(&alpha, &rho, nonce, 0); + secp256k1_scalar_chacha20(&tau1, &tau2, nonce, 1); + + if (min_value > 0) { + unsigned char vbuf[8]; + vbuf[0] = min_value; + vbuf[1] = min_value >> 8; + vbuf[2] = min_value >> 16; + vbuf[3] = min_value >> 24; + vbuf[4] = min_value >> 32; + vbuf[5] = min_value >> 40; + vbuf[6] = min_value >> 48; + vbuf[7] = min_value >> 56; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, vbuf, 8); + secp256k1_sha256_finalize(&sha256, commit); + } + + secp256k1_pedersen_commitment_load(&commitp, pcommit); + secp256k1_generator_load(&value_genp, value_gen); + secp256k1_bulletproof_update_commit(commit, &commitp, &value_genp); + + if (extra_commit != NULL) { + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, extra_commit, extra_commit_len); + secp256k1_sha256_finalize(&sha256, commit); + } + + /* Extract A and S to compute y and z */ + lrparity = 2 * !!(proof[64] & 1) + !!(proof[64] & 2); + /* y */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &lrparity, 1); + secp256k1_sha256_write(&sha256, &proof[65], 64); + secp256k1_sha256_finalize(&sha256, commit); + + /* z */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &lrparity, 1); + secp256k1_sha256_write(&sha256, &proof[65], 64); + secp256k1_sha256_finalize(&sha256, commit); + + secp256k1_scalar_set_b32(&z, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&z)) { + return 0; + } + + /* x */ + lrparity = 2 * !!(proof[64] & 4) + !!(proof[64] & 8); + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &lrparity, 1); + secp256k1_sha256_write(&sha256, &proof[129], 64); + secp256k1_sha256_finalize(&sha256, commit); + + secp256k1_scalar_set_b32(&x, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + return 0; + } + + /* Compute candidate mu and add to (negated) mu from proof to get value */ + secp256k1_scalar_mul(&rho, &rho, &x); + secp256k1_scalar_add(&mu, &mu, &rho); + secp256k1_scalar_add(&mu, &mu, &alpha); + + secp256k1_scalar_get_b32(commit, &mu); + if (memcmp(commit, zero24, 24) != 0) { + return 0; + } + *value = commit[31] + ((uint64_t) commit[30] << 8) + + ((uint64_t) commit[29] << 16) + ((uint64_t) commit[28] << 24) + + ((uint64_t) commit[27] << 32) + ((uint64_t) commit[26] << 40) + + ((uint64_t) commit[25] << 48) + ((uint64_t) commit[24] << 56); + + /* Derive blinding factor */ + secp256k1_scalar_mul(&tau1, &tau1, &x); + secp256k1_scalar_mul(&tau2, &tau2, &x); + secp256k1_scalar_mul(&tau2, &tau2, &x); + + secp256k1_scalar_add(&taux, &taux, &tau1); + secp256k1_scalar_add(&taux, &taux, &tau2); + + secp256k1_scalar_sqr(&z, &z); + secp256k1_scalar_inverse_var(&z, &z); + secp256k1_scalar_mul(blind, &taux, &z); + secp256k1_scalar_negate(blind, blind); + + /* Check blinding factor */ + secp256k1_pedersen_ecmult(&rewind_commitj, blind, *value, &value_genp, blind_gen); + secp256k1_gej_neg(&rewind_commitj, &rewind_commitj); + secp256k1_gej_add_ge_var(&rewind_commitj, &rewind_commitj, &commitp, NULL); + + return secp256k1_gej_is_infinity(&rewind_commitj); +} + #endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h index 6117791ad..8702e3d0b 100644 --- a/src/modules/bulletproofs/tests_impl.h +++ b/src/modules/bulletproofs/tests_impl.h @@ -35,6 +35,8 @@ static void test_bulletproof_api(void) { uint64_t value[4] = { 1234, 4567, 8910, 1112 } ; uint64_t min_value[4] = { 1000, 4567, 0, 5000 } ; const uint64_t *mv_ptr = min_value; + unsigned char rewind_blind[32]; + size_t rewind_v; int32_t ecount = 0; @@ -212,6 +214,35 @@ static void test_bulletproof_api(void) { CHECK(secp256k1_bulletproof_rangeproof_verify_multi(both, scratch, gens, &proof_ptr, 1, plen, &mv_ptr, pcommit_arr, 4, 64, &value_gen, blind_ptr, &blindlen) == 0); CHECK(ecount == 14); + /* Rewind */ + ecount = 0; + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, blind, blind, 32) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, NULL, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, NULL, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, NULL, proof, plen, min_value[0], pcommit, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, NULL, plen, min_value[0], pcommit, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, 0, min_value[0], pcommit, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, 0, pcommit, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], NULL, &value_gen, blind, blind, 32) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, NULL, blind, blind, 32) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, NULL, blind, 32) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, blind, NULL, 32) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, blind, blind, 0) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_bulletproof_rangeproof_rewind(none, gens, &rewind_v, rewind_blind, proof, plen, min_value[0], pcommit, &value_gen, blind, NULL, 0) == 0); + CHECK(ecount == 8); + secp256k1_bulletproof_generators_destroy(none, gens); secp256k1_bulletproof_generators_destroy(none, NULL); secp256k1_scratch_destroy(scratch); @@ -427,15 +458,18 @@ void test_bulletproof_inner_product(size_t n, const secp256k1_bulletproof_genera void test_bulletproof_rangeproof(size_t nbits, size_t expected_size, const secp256k1_bulletproof_generators *gens) { secp256k1_scalar blind; + secp256k1_scalar blind_recovered; unsigned char proof[1024]; unsigned char proof2[1024]; unsigned char proof3[1024]; const unsigned char *proof_ptr[3]; size_t plen = sizeof(proof); uint64_t v = 123456; + uint64_t v_recovered; secp256k1_gej commitj; secp256k1_ge commitp; secp256k1_ge commitp2; + secp256k1_pedersen_commitment pcommit; const secp256k1_ge *commitp_ptr[3]; secp256k1_ge value_gen[3]; unsigned char nonce[32] = "my kingdom for some randomness!!"; @@ -461,6 +495,7 @@ void test_bulletproof_rangeproof(size_t nbits, size_t expected_size, const secp2 secp256k1_ge_set_gej(&commitp2, &commitj); commitp_ptr[0] = commitp_ptr[1] = &commitp; commitp_ptr[2] = &commitp2; + secp256k1_pedersen_commitment_save(&pcommit, &commitp); CHECK(secp256k1_bulletproof_rangeproof_prove_impl(&ctx->ecmult_ctx, scratch, proof, &plen, nbits, &v, NULL, &blind, &commitp, 1, &value_gen[0], gens, nonce, NULL, 0) == 1); CHECK(plen == expected_size); @@ -478,6 +513,14 @@ void test_bulletproof_rangeproof(size_t nbits, size_t expected_size, const secp2 /* Verify thrice at once where one has a different asset type */ CHECK(secp256k1_bulletproof_rangeproof_verify_impl(&ctx->ecmult_ctx, scratch, proof_ptr, 3, plen, nbits, NULL, commitp_ptr, 1, value_gen, gens, NULL, 0) == 1); + /* Rewind */ + CHECK(secp256k1_bulletproof_rangeproof_rewind_impl(&v_recovered, &blind_recovered, proof, plen, 0, &pcommit, &secp256k1_generator_const_g, gens->blinding_gen, nonce, NULL, 0) == 1); + CHECK(v_recovered == v); + CHECK(secp256k1_scalar_eq(&blind_recovered, &blind) == 1); + + nonce[0] ^= 111; + CHECK(secp256k1_bulletproof_rangeproof_rewind_impl(&v_recovered, &blind_recovered, proof, plen, 0, &pcommit, &secp256k1_generator_const_g, gens->blinding_gen, nonce, NULL, 0) == 0); + secp256k1_scratch_destroy(scratch); } From 9601a6664c10c5b51ef6083abf4f1d60af9a9f7b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 26 Mar 2018 19:09:49 +0000 Subject: [PATCH 53/58] bulletproofs: add benchmark --- src/bench_bulletproof.c | 243 +++++++++++++++++++ src/modules/bulletproofs/Makefile.am.include | 6 + 2 files changed, 249 insertions(+) create mode 100644 src/bench_bulletproof.c diff --git a/src/bench_bulletproof.c b/src/bench_bulletproof.c new file mode 100644 index 000000000..315f57765 --- /dev/null +++ b/src/bench_bulletproof.c @@ -0,0 +1,243 @@ +/********************************************************************** + * Copyright (c) 2017 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_generator.h" +#include "include/secp256k1_commitment.h" +#include "include/secp256k1_bulletproofs.h" +#include "util.h" +#include "bench.h" + +#define MAX_PROOF_SIZE 2000 + +typedef struct { + secp256k1_context *ctx; + secp256k1_scratch_space *scratch; + unsigned char nonce[32]; + unsigned char **proof; + secp256k1_bulletproof_generators *generators; + secp256k1_generator *value_gen; + secp256k1_generator blind_gen; + size_t n_proofs; + size_t plen; + size_t iters; +} bench_bulletproof_t; + +typedef struct { + bench_bulletproof_t *common; + secp256k1_pedersen_commitment **commit; + const unsigned char **blind; + size_t *value; + size_t n_commits; + size_t nbits; +} bench_bulletproof_rangeproof_t; + +static void bench_bulletproof_common_setup(bench_bulletproof_t *data) { + size_t i; + const unsigned char nonce[32] = "my kingdom for some randomness!!"; + const unsigned char genbd[32] = "yet more blinding, for the asset"; + + memcpy(data->nonce, nonce, 32); + data->proof = (unsigned char **)malloc(data->n_proofs * sizeof(*data->proof)); + data->value_gen = (secp256k1_generator *)malloc(data->n_proofs * sizeof(*data->value_gen)); + for (i = 0; i < data->n_proofs; i++) { + data->proof[i] = (unsigned char *)malloc(MAX_PROOF_SIZE); + CHECK(secp256k1_generator_generate(data->ctx, &data->value_gen[i], genbd)); + } + data->plen = MAX_PROOF_SIZE; +} + +static void bench_bulletproof_rangeproof_setup(void* arg) { + bench_bulletproof_rangeproof_t *data = (bench_bulletproof_rangeproof_t*)arg; + size_t i; + size_t v; + + unsigned char blind[32] = "and my kingdom too for a blinder"; + + bench_bulletproof_common_setup (data->common); + + data->commit = (secp256k1_pedersen_commitment **)malloc(data->common->n_proofs * sizeof(*data->commit)); + data->blind = (const unsigned char **)malloc(data->n_commits * sizeof(*data->blind)); + data->value = (size_t *)malloc(data->n_commits * sizeof(*data->commit)); + + for (i = 0; i < data->common->n_proofs; i++) { + data->commit[i] = (secp256k1_pedersen_commitment *)malloc(data->n_commits * sizeof(*data->commit[i])); + } + + for (i = 0; i < data->n_commits; i++) { + data->blind[i] = malloc(32); + blind[0] = i; + blind[1] = i >> 8; + memcpy((unsigned char*) data->blind[i], blind, 32); + data->value[i] = i * 17; + CHECK(secp256k1_pedersen_commit(data->common->ctx, &data->commit[0][i], data->blind[i], data->value[i], &data->common->value_gen[0], &data->common->blind_gen)); + } + for (i = 1; i < data->common->n_proofs; i++) { + memcpy(data->commit[i], data->commit[0], data->n_commits * sizeof(*data->commit[0])); + } + + CHECK(secp256k1_bulletproof_rangeproof_prove(data->common->ctx, data->common->scratch, data->common->generators, data->common->proof[0], &data->common->plen, data->value, NULL, data->blind, data->n_commits, data->common->value_gen, data->nbits, data->common->nonce, NULL, 0) == 1); + for (i = 1; i < data->common->n_proofs; i++) { + memcpy(data->common->proof[i], data->common->proof[0], data->common->plen); + CHECK(secp256k1_bulletproof_rangeproof_verify(data->common->ctx, data->common->scratch, data->common->generators, data->common->proof[i], data->common->plen, NULL, data->commit[i], data->n_commits, data->nbits, &data->common->value_gen[0], NULL, 0) == 1); + } + CHECK(secp256k1_bulletproof_rangeproof_verify(data->common->ctx, data->common->scratch, data->common->generators, data->common->proof[0], data->common->plen, NULL, data->commit[0], data->n_commits, data->nbits, data->common->value_gen, NULL, 0) == 1); + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(data->common->ctx, data->common->scratch, data->common->generators, (const unsigned char **) data->common->proof, data->common->n_proofs, data->common->plen, NULL, (const secp256k1_pedersen_commitment **) data->commit, data->n_commits, data->nbits, data->common->value_gen, NULL, 0) == 1); + if (data->n_commits == 1) { + CHECK(secp256k1_bulletproof_rangeproof_rewind(data->common->ctx, data->common->generators, &v, blind, data->common->proof[0], data->common->plen, 0, data->commit[0], &data->common->value_gen[0], data->common->nonce, NULL, 0) == 1); + } +} + +static void bench_bulletproof_common_teardown(bench_bulletproof_t *data) { + size_t i; + + for (i = 0; i < data->n_proofs; i++) { + free(data->proof[i]); + } + free(data->proof); + free(data->value_gen); +} + +static void bench_bulletproof_rangeproof_teardown(void* arg) { + bench_bulletproof_rangeproof_t *data = (bench_bulletproof_rangeproof_t*)arg; + size_t i; + + if (data->blind != NULL) { + for (i = 0; i < data->n_commits; i++) { + free((unsigned char*) data->blind[i]); + } + } + if (data->commit != NULL) { + for (i = 0; i < data->common->n_proofs; i++) { + free(data->commit[i]); + } + free(data->commit); + } + free(data->blind); + free(data->value); + + bench_bulletproof_common_teardown(data->common); +} + +static void bench_bulletproof_rangeproof_prove(void* arg) { + bench_bulletproof_rangeproof_t *data = (bench_bulletproof_rangeproof_t*)arg; + size_t i; + for (i = 0; i < 25; i++) { + CHECK(secp256k1_bulletproof_rangeproof_prove(data->common->ctx, data->common->scratch, data->common->generators, data->common->proof[0], &data->common->plen, data->value, NULL, data->blind, data->n_commits, data->common->value_gen, data->nbits, data->common->nonce, NULL, 0) == 1); + } +} + +static void bench_bulletproof_rangeproof_verify(void* arg) { + size_t i; + bench_bulletproof_rangeproof_t *data = (bench_bulletproof_rangeproof_t*)arg; + + for (i = 0; i < data->common->iters; i++) { + CHECK(secp256k1_bulletproof_rangeproof_verify_multi(data->common->ctx, data->common->scratch, data->common->generators, (const unsigned char **) data->common->proof, data->common->n_proofs, data->common->plen, NULL, (const secp256k1_pedersen_commitment **) data->commit, data->n_commits, data->nbits, data->common->value_gen, NULL, 0) == 1); + } +} + +static void bench_bulletproof_rangeproof_rewind_succeed(void* arg) { + size_t i; + size_t v; + unsigned char blind[32]; + bench_bulletproof_rangeproof_t *data = (bench_bulletproof_rangeproof_t*)arg; + + for (i = 0; i < data->common->iters; i++) { + CHECK(secp256k1_bulletproof_rangeproof_rewind(data->common->ctx, data->common->generators, &v, blind, data->common->proof[0], data->common->plen, 0, data->commit[0], &data->common->value_gen[0], data->common->nonce, NULL, 0) == 1); + } +} + +static void bench_bulletproof_rangeproof_rewind_fail(void* arg) { + size_t i; + size_t v; + unsigned char blind[32]; + bench_bulletproof_rangeproof_t *data = (bench_bulletproof_rangeproof_t*)arg; + + data->common->nonce[0] ^= 1; + for (i = 0; i < data->common->iters; i++) { + CHECK(secp256k1_bulletproof_rangeproof_rewind(data->common->ctx, data->common->generators, &v, blind, data->common->proof[0], data->common->plen, 0, data->commit[0], &data->common->value_gen[0], data->common->nonce, NULL, 0) == 0); + } + data->common->nonce[0] ^= 1; +} + +static void run_rangeproof_test(bench_bulletproof_rangeproof_t *data, size_t nbits, size_t n_commits) { + char str[64]; + + data->nbits = nbits; + data->n_commits = n_commits; + data->common->iters = 100; + + data->common->n_proofs = 1; + sprintf(str, "bulletproof_prove, %i, %i, 0, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_prove, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, 25); + + data->common->n_proofs = 1; + sprintf(str, "bulletproof_verify, %i, %i, 1, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_verify, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + + if (n_commits == 1) { + sprintf(str, "bulletproof_rewind_succeed, %i, ", (int)nbits); + run_benchmark(str, bench_bulletproof_rangeproof_rewind_succeed, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + sprintf(str, "bulletproof_rewind_fail, %i, ", (int)nbits); + run_benchmark(str, bench_bulletproof_rangeproof_rewind_fail, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + } + + data->common->n_proofs = 2; + sprintf(str, "bulletproof_verify, %i, %i, 2, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_verify, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + + data->common->iters = 10; + data->common->n_proofs = 50; + sprintf(str, "bulletproof_verify, %i, %i, 50, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_verify, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + + data->common->iters = 1; + data->common->n_proofs = 100; + sprintf(str, "bulletproof_verify, %i, %i, 100, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_verify, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + + data->common->n_proofs = 500; + sprintf(str, "bulletproof_verify, %i, %i, 500, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_verify, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); + + data->common->n_proofs = 1000; + sprintf(str, "bulletproof_verify, %i, %i, 1000, ", (int)nbits, (int) n_commits); + run_benchmark(str, bench_bulletproof_rangeproof_verify, bench_bulletproof_rangeproof_setup, bench_bulletproof_rangeproof_teardown, (void *)data, 5, data->common->iters); +} + +int main(void) { + bench_bulletproof_t data; + bench_bulletproof_rangeproof_t rp_data; + + data.blind_gen = secp256k1_generator_const_g; + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024); + data.generators = secp256k1_bulletproof_generators_create(data.ctx, &data.blind_gen, 64 * 1024); + + rp_data.common = &data; + + run_rangeproof_test(&rp_data, 8, 1); + run_rangeproof_test(&rp_data, 16, 1); + run_rangeproof_test(&rp_data, 32, 1); + + run_rangeproof_test(&rp_data, 64, 1); + run_rangeproof_test(&rp_data, 64, 2); + run_rangeproof_test(&rp_data, 64, 4); + run_rangeproof_test(&rp_data, 64, 8); + run_rangeproof_test(&rp_data, 64, 16); + run_rangeproof_test(&rp_data, 64, 32); + run_rangeproof_test(&rp_data, 64, 64); + run_rangeproof_test(&rp_data, 64, 128); + run_rangeproof_test(&rp_data, 64, 256); + run_rangeproof_test(&rp_data, 64, 512); + /* to add more, increase the number of generators above in `data.generators = ...` */ + + secp256k1_bulletproof_generators_destroy(data.ctx, data.generators); + secp256k1_scratch_space_destroy(data.scratch); + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include index 2fa121b78..1bee88ef3 100644 --- a/src/modules/bulletproofs/Makefile.am.include +++ b/src/modules/bulletproofs/Makefile.am.include @@ -4,3 +4,9 @@ noinst_HEADERS += src/modules/bulletproofs/rangeproof_impl.h noinst_HEADERS += src/modules/bulletproofs/main_impl.h noinst_HEADERS += src/modules/bulletproofs/tests_impl.h noinst_HEADERS += src/modules/bulletproofs/util.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_bulletproof +bench_bulletproof_SOURCES = src/bench_bulletproof.c +bench_bulletproof_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_bulletproof_LDFLAGS = -static +endif From 849a2584e37a134c7c47d4a1267ebf6d9f0ff99e Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 4 Jun 2018 21:37:44 +0000 Subject: [PATCH 54/58] commitment and bulletproof: fix a bunch of typos and stuff (thanks Tim Ruffing) --- include/secp256k1_bulletproofs.h | 2 +- include/secp256k1_commitment.h | 28 ++++++++++++++-------------- src/modules/commitment/main_impl.h | 14 +++++++------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h index 654cb4040..5405e39e4 100644 --- a/include/secp256k1_bulletproofs.h +++ b/include/secp256k1_bulletproofs.h @@ -16,7 +16,7 @@ typedef struct secp256k1_bulletproof_generators secp256k1_bulletproof_generators #define SECP256K1_BULLETPROOF_MAX_DEPTH 31 /* Size of a hypothetical 31-depth rangeproof, in bytes */ -#define SECP256K1_BULLETPROOF_MAX_PROOF (160 + 66*32 + 7) +#define SECP256K1_BULLETPROOF_MAX_PROOF (160 + 36*32 + 7) /** Allocates and initializes a list of NUMS generators, along with precomputation data * Returns a list of generators, or NULL if allocation failed. diff --git a/include/secp256k1_commitment.h b/include/secp256k1_commitment.h index dd54e9a75..a03f2baf8 100644 --- a/include/secp256k1_commitment.h +++ b/include/secp256k1_commitment.h @@ -58,7 +58,7 @@ SECP256K1_API int secp256k1_pedersen_commitment_serialize( /** Initialize a context for usage with Pedersen commitments. */ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); -/** Generate a pedersen commitment. +/** 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 @@ -89,7 +89,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. - * npositive: how many of the initial factors should be treated with a positive sign. + * npositive: how many of the input 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( @@ -100,28 +100,28 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( size_t npositive ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Verify a tally of pedersen commitments +/** 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. + * In: ctx: pointer to a context object (cannot be NULL) + * pos: pointer to array of pointers to the commitments. (cannot be NULL if `n_pos` is non-zero) + * n_pos: number of commitments pointed to by `pos`. + * neg: pointer to array of pointers to the negative commitments. (cannot be NULL if `n_neg` is non-zero) + * n_neg: number of commitments pointed to by `neg`. * - * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. + * This computes sum(pos[0..n_pos)) - sum(neg[0..n_neg)) == 0. * - * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, + * 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 + const secp256k1_pedersen_commitment * const* pos, + size_t n_pos, + const secp256k1_pedersen_commitment * const* neg, + size_t n_neg ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); /** Sets the final Pedersen blinding factor correctly when the generators themselves diff --git a/src/modules/commitment/main_impl.h b/src/modules/commitment/main_impl.h index 4f3a444d7..a8c3ef1ce 100644 --- a/src/modules/commitment/main_impl.h +++ b/src/modules/commitment/main_impl.h @@ -139,22 +139,22 @@ 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) { +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* pos, size_t n_pos, const secp256k1_pedersen_commitment * const* neg, size_t n_neg) { secp256k1_gej accj; secp256k1_ge add; size_t i; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(!pcnt || (commits != NULL)); - ARG_CHECK(!ncnt || (ncommits != NULL)); + ARG_CHECK(!n_pos || (pos != NULL)); + ARG_CHECK(!n_neg || (neg != NULL)); (void) ctx; secp256k1_gej_set_infinity(&accj); - for (i = 0; i < ncnt; i++) { - secp256k1_pedersen_commitment_load(&add, ncommits[i]); + for (i = 0; i < n_neg; i++) { + secp256k1_pedersen_commitment_load(&add, neg[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]); + for (i = 0; i < n_pos; i++) { + secp256k1_pedersen_commitment_load(&add, pos[i]); secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); } return secp256k1_gej_is_infinity(&accj); From 6922c9170c7d1fe76f9836aa8d74abe2e5a6c9bc Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 15 Jun 2018 21:38:34 +0000 Subject: [PATCH 55/58] bulletproofs: extensively comment inner product proof, remove a couple expired TODOs --- include/secp256k1_bulletproofs.h | 8 +- src/modules/bulletproofs/inner_product_impl.h | 184 ++++++++++++------ src/modules/bulletproofs/main_impl.h | 5 + src/modules/bulletproofs/rangeproof_impl.h | 2 - 4 files changed, 132 insertions(+), 67 deletions(-) diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h index 5405e39e4..46df2b5a2 100644 --- a/include/secp256k1_bulletproofs.h +++ b/include/secp256k1_bulletproofs.h @@ -55,7 +55,7 @@ SECP256K1_API void secp256k1_bulletproof_generators_destroy( * extra_commit: additonal data committed to by the rangeproof (may be NULL if `extra_commit_len` is 0) * extra_commit_len: length of additional data */ -SECP256K1_API int secp256k1_bulletproof_rangeproof_verify( +SECP256K1_WARN_UNUSED_RESULT SECP256K1_API int secp256k1_bulletproof_rangeproof_verify( const secp256k1_context* ctx, secp256k1_scratch_space* scratch, const secp256k1_bulletproof_generators *gens, @@ -87,7 +87,7 @@ SECP256K1_API int secp256k1_bulletproof_rangeproof_verify( * extra_commit: additonal data committed to by the rangeproof (may be NULL if `extra_commit_len` is 0) * extra_commit_len: array of lengths of additional data */ -SECP256K1_API int secp256k1_bulletproof_rangeproof_verify_multi( +SECP256K1_WARN_UNUSED_RESULT SECP256K1_API int secp256k1_bulletproof_rangeproof_verify_multi( const secp256k1_context* ctx, secp256k1_scratch_space* scratch, const secp256k1_bulletproof_generators *gens, @@ -119,7 +119,7 @@ SECP256K1_API int secp256k1_bulletproof_rangeproof_verify_multi( * extra_commit: additonal data committed to by the rangeproof * extra_commit_len: length of additional data */ -SECP256K1_API int secp256k1_bulletproof_rangeproof_rewind( +SECP256K1_WARN_UNUSED_RESULT SECP256K1_API int secp256k1_bulletproof_rangeproof_rewind( const secp256k1_context* ctx, const secp256k1_bulletproof_generators* gens, uint64_t* value, @@ -152,7 +152,7 @@ SECP256K1_API int secp256k1_bulletproof_rangeproof_rewind( * extra_commit: additonal data committed to by the rangeproof * extra_commit_len: length of additional data */ -SECP256K1_API int secp256k1_bulletproof_rangeproof_prove( +SECP256K1_WARN_UNUSED_RESULT SECP256K1_API int secp256k1_bulletproof_rangeproof_prove( const secp256k1_context* ctx, secp256k1_scratch_space* scratch, const secp256k1_bulletproof_generators *gens, diff --git a/src/modules/bulletproofs/inner_product_impl.h b/src/modules/bulletproofs/inner_product_impl.h index 30cb8ba64..38773ccb2 100644 --- a/src/modules/bulletproofs/inner_product_impl.h +++ b/src/modules/bulletproofs/inner_product_impl.h @@ -13,8 +13,60 @@ #include "modules/bulletproofs/main_impl.h" #include "modules/bulletproofs/util.h" +/* Number of scalars that should remain at the end of a recursive proof. The paper + * uses 2, by reducing the scalars as far as possible. We stop one recursive step + * early, trading two points (L, R) for two scalars, which reduces verification + * and prover cost. + * + * For the most part, all comments assume this value is at 4. + */ #define IP_AB_SCALARS 4 +/* Bulletproof inner products consist of the four scalars and `2[log2(n) - 1]` points + * `a_1`, `a_2`, `b_1`, `b_2`, `L_i` and `R_i`, where `i` ranges from 0 to `log2(n)-1`. + * + * The prover takes as input a point `P` and scalar `c`. It proves that it knows + * scalars `a_i`, `b_i` for `i` ranging from 1 to `n`, such that + * `P = sum_i [a_i G_i + b_i H_i]` and `<{a_i}, {b_i}> = c`, + * where `G_i` and `H_i` are standard NUMS generators. + * + * Verification of the proof comes down to a single multiexponentiation of the form + * + * P + (c - a_1*b_1 - a_2*b_2)*x*G + * - sum_{i=1}^n [s'_i*G_i + s_i*H_i] + * + sum_{i=1}^log2(n) [x_i^-2 L_i + x_i^2 R_i] + * + * which will equal infinity if the inner product proof is correct. Here + * - `G` is the standard secp generator + * - `x` is a hash of `commit` and is used to rerandomize `c`. See Protocol 2 vs Protocol 1 in the paper. + * - `x_i = H(x_{i-1} || L_i || R_i)`, where `x_{-1}` is passed through the `commit` variable and + * must be a commitment to `P` and `c`. + * - `s_i` and `s'_i` are computed as follows. + * + * Letting `i_j` be defined as 1 if `i & 2^j == 1`, and -1 otherwise, + * - For `i` from `1` to `n/2`, `s'_i = a_1 * prod_{j=1}^log2(n) x_j^i_j` + * - For `i` from `n/2 + 1` to `n`, `s'_i = a_2 * prod_{j=1}^log2(n) x_j^i_j` + * - For `i` from `1` to `n/2`, `s_i = b_1 * prod_{j=1}^log2(n) x_j^-i_j` + * - For `i` from `n/2 + 1` to `n`, `s_i = b_2 * prod_{j=1}^log2(n) x_j^-i_j` + * + * Observe that these can be computed iteratively by labelling the coefficients `s_i` for `i` + * from `0` to `2n-1` rather than 1-indexing and distinguishing between `s_i'`s and `s_i`s: + * + * Start with `s_0 = a_1 * prod_{j=1}^log2(n) x_j^-1`, then for later `s_i`s, + * - For `i` from `1` to `n/2 - 1`, multiply some earlier `s'_j` by some `x_k^2` + * - For `i = n/2`, multiply `s_{i-1} by `a_2/a_1`. + * - For `i` from `n/2 + 1` to `n - 1`, multiply some earlier `s'_j` by some `x_k^2` + * - For `i = n`, multiply `s'_{i-1}` by `b_1/a_2` to get `s_i`. + * - For `i` from `n + 1` to `3n/2 - 1`, multiply some earlier `s_j` by some `x_k^-2` + * - For `i = 3n/2`, multiply `s_{i-1}` by `b_2/b_1`. + * - For `i` from `3n/2 + 1` to `2n - 1`, multiply some earlier `s_j` by some `x_k^-2` + * where of course, the indices `j` and `k` must be chosen carefully. + * + * The bulk of `secp256k1_bulletproof_innerproduct_vfy_ecmult_callback` involves computing + * these indices, given `a_2/a_1`, `b_1/a_1`, `b_2/b_1`, and the `x_k^2`s as input. It + * computes `x_k^-2` as a side-effect of its other computation. + */ + typedef int (secp256k1_bulletproof_vfy_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, secp256k1_scalar *randomizer, size_t idx, void *data); /* used by callers to wrap a proof with surrounding context */ @@ -64,73 +116,38 @@ size_t secp256k1_bulletproof_innerproduct_proof_length(size_t n) { } } -/* Bulletproof rangeproof verification comes down to a single multiexponentiation of the form - * - * P + (c-a*b)*x*G - sum_{i=1}^n [a*s'_i*G_i + b*s_i*H_i] + sum_{i=1}^log2(n) [x_i^-2 L_i + x_i^2 R_i - * - * which will equal infinity if the rangeproof is correct. Here - * - `G_i` and `H_i` are standard NUMS generators. `G` is the standard secp256k1 generator. - * - `P` and `c` are inputs to the proof, which claims that there exist `a_i` and `b_i`, `i` ranging - * from 0 to `n-1`, such that `P = sum_i [a_i G_i + b_i H_i]` and that `<{a_i}, {b_i}> = c`. - * - `a`, `b`, `L_i` and `R_i`are auxillary components of the proof, where `i` ranges from 0 to `log2(n)-1`. - * - `x_i = H(x_{i-1} || L_i || R_i)`, where `x_{-1}` is passed through the `commit` variable and - * must be a commitment to `P` and `c`. - * - `x` is a hash of `commit` and is used to rerandomize `c`. See Protocol 2 vs Protocol 1 in the paper. - * - `s_i` and `s'_i` are computed as follows. - * - * For each `i` between 0 and `n-1` inclusive, let `b_{ij}` be -1 (1) if the `j`th bit of `i` is zero (one). - * Here `j` ranges from 0 to `log2(n)-1`. Then for each such `i` we define - * - `s_i = prod_j x_j^{b_{ij}}` - * - `s'_i = 1/s_i` - * - * Alternately we can define `s_i` and `s'_i` recursively as follows: - * - `s_0 = s`_{n - 1} = 1 / prod_j x_j` - * - `s_i = s'_{n - 1 - i} = s_{i - 2^j} * x_j^2` where `j = i & (i - 1)` is `i` with its least significant 1 set to 0. - * - * Our ecmult_multi function takes `(c - a*b)*x` directly and multiplies this by `G`. For every other +/* Our ecmult_multi function takes `(c - a*b)*x` directly and multiplies this by `G`. For every other * (scalar, point) pair it calls the following callback function, which takes an index and outputs a * pair. The function therefore has three regimes: * - * For the first `2n` invocations, it alternately returns `(s'_{n - i}, G_{n - i})` and `(s_i, H_i)`, - * where `i` is `floor(idx / 2)`. The reason for the funny indexing is that we use the above recursive - * definition of `s_i` and `s'_i` which produces each element with only a single scalar multiplication, - * but in this mixed order. (We start with an array of `x_j^2` for each `x_j`.) - * - * As a side-effect, whenever `n - i = 2^j` for some `j`, `s_i = x_j^{-1} * prod_{j' != j} x_{j'}`, - * so `x_j^{-2} = s_i*s_0`. Therefore we compute an array of inverse squares during this computation, - * using only one multiplication per. We will need it in the following step. - * - * For the next `2*log2(n)` invocations it alternately returns `(x_i^-2, L_i)` and `(x_i^2, R_i)` - * where `i` is `idx - 2*n`. + * For the first `n` invocations, it returns `(s'_i, G_i)` for `i` from 1 to `n`. + * For the next `n` invocations, it returns `(s_i, H_i)` for `i` from 1 to `n`. + * For the next `2*log2(n)` invocations it returns `(x_i^-2, L_i)` and `(x_i^2, R_i)`, + * alternating between the two choices, for `i` from 1 to `log2(n)`. * * For the remaining invocations it passes through to another callback, `rangeproof_cb_data` which * computes `P`. The reason for this is that in practice `P` is usually defined by another multiexp * rather than being a known point, and it is more efficient to compute one exponentiation. * + * Inline we refer to the first `2n` coefficients as `s_i` for `i` from 0 to `2n-1`, since that + * is the more convenient indexing. In particular we describe (a) how the indices `j` and `k`, + * from the big comment block above, are chosen; and (b) when/how each `x_k^-2` is computed. */ - -/* For the G and H generators, we choose the ith generator with a scalar computed from the - * L/R hashes as follows: prod_{j=1}^m x_j^{e_j}, where each exponent e_j is either -1 or 1. - * The choice directly maps to the bits of i: for the G generators, a 0 bit means e_j is 1 - * and a 1 bit means e_j is -1. For the H generators it is the opposite. Finally, each of the - * G scalars is further multiplied by -a, while each of the H scalars is further multiplied - * by -b. - * - * These scalars are computed starting from I, the inverse of the product of every x_j, which - * is then selectively multiplied by x_j^2 for whichever j's are needed. As it turns out, by - * caching logarithmically many scalars, this can always be done by multiplying one of the - * cached values by a single x_j, rather than starting from I and doing multiple multiplications. - */ - static int secp256k1_bulletproof_innerproduct_vfy_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_bulletproof_innerproduct_vfy_ecmult_context *ctx = (secp256k1_bulletproof_innerproduct_vfy_ecmult_context *) data; - /* First 2N points use the standard Gi, Hi generators, and the scalars can be aggregated across proofs */ + /* First 2N points use the standard Gi, Hi generators, and the scalars can be aggregated across proofs. + * Inside this if clause, `idx` corresponds to the index `i` in the big comment, and runs from 0 to `2n-1`. + * Also `ctx->vec_len` corresponds to `n`. */ if (idx < 2 * ctx->vec_len) { + /* Number of `a` scalars in the proof (same as number of `b` scalars in the proof). Will + * be 2 except for very small proofs that have fewer than 2 scalars as input. */ const size_t grouping = ctx->vec_len < IP_AB_SCALARS / 2 ? ctx->vec_len : IP_AB_SCALARS / 2; const size_t lg_grouping = secp256k1_floor_lg(grouping); size_t i; - /* TODO zero this point when appropriate for non-2^n numbers of pairs */ + VERIFY_CHECK(lg_grouping == 0 || lg_grouping == 1); /* TODO support higher IP_AB_SCALARS */ + + /* Determine whether we're multiplying by `G_i`s or `H_i`s. */ if (idx < ctx->vec_len) { *pt = ctx->geng[idx]; } else { @@ -138,17 +155,51 @@ static int secp256k1_bulletproof_innerproduct_vfy_ecmult_callback(secp256k1_scal } secp256k1_scalar_clear(sc); + /* Loop over all the different inner product proofs we might be doing at once. Since they + * share generators `G_i` and `H_i`, we compute all of their scalars at once and add them. + * For each proof we start with the "seed value" `ctx->proof[i].xcache[0]` (see next comment + * for its meaning) from which every other scalar derived. We expect the caller to have + * randomized this to ensure that this wanton addition cannot enable cancellation attacks. + */ for (i = 0; i < ctx->n_proofs; i++) { + /* To recall from the introductory comment: most `s_i` values are computed by taking an + * earlier `s_j` value and multiplying it by some `x_k^2`. + * + * We now explain the index `j`: it is the largest number with one fewer 1-bits than `i`. + * Alternately, the most recently returned `s_j` where `j` has one fewer 1-bits than `i`. + * + * To ensure that `s_j` is available when we need it, on each iteration we define the + * variable `cache_idx` which simply counts the 1-bits in `i`; before returning `s_i` + * we store it in `ctx->proof[i].xcache[cache_idx]`. Then later, when we want "most + * recently returned `s_j` with one fewer 1-bits than `i`, it'll be sitting right + * there in `ctx->proof[i].xcache[cache_idx - 1]`. + * + * Note that `ctx->proof[i].xcache[0]` will always equal `-a_1 * prod_{i=1}^{n-1} x_i^-2`, + * and we expect the caller to have set this. + */ const size_t cache_idx = secp256k1_popcountl(idx); secp256k1_scalar term; VERIFY_CHECK(cache_idx < SECP256K1_BULLETPROOF_MAX_DEPTH); - /* Compute the normal inner-product scalar... */ + /* For the special case `cache_idx == 0` (which is true iff `idx == 0`) there is nothing to do. */ if (cache_idx > 0) { + /* Otherwise, check if this is one of the special indices where we transition from `a_1` to `a_2`, + * from `a_2` to `b_1`, or from `b_1` to `b_2`. (For small proofs there is only one transition, + * from `a` to `b`.) */ if (idx % (ctx->vec_len / grouping) == 0) { const size_t abinv_idx = idx / (ctx->vec_len / grouping) - 1; size_t prev_cache_idx; + /* Check if it's the even specialer index where we're transitioning from `a`s to `b`s, from + * `G`s to `H`s, and from `x_k^2`s to `x_k^-2`s. In rangeproof and circuit applications, + * the caller secretly has a variable `y` such that `H_i` is really `y^-i H_i` for `i` ranging + * from 0 to `n-1`. Rather than forcing the caller to tweak every `H_i` herself, which would + * be very slow and prevent precomputation, we instead multiply our cached `x_k^-2` values + * by `y^(-2^k)` respectively, which will ultimately result in every `s_i` we return having + * been multiplied by `y^-i`. + * + * This is an underhanded trick but the result is that all `n` powers of `y^-i` show up + * in the right place, and we only need log-many scalar squarings and multiplications. + */ if (idx == ctx->vec_len) { - /* Transition from G to H, a's to b's */ secp256k1_scalar yinvn = ctx->proof[i].proof->yinv; size_t j; prev_cache_idx = secp256k1_popcountl(idx - 1); @@ -156,19 +207,25 @@ static int secp256k1_bulletproof_innerproduct_vfy_ecmult_callback(secp256k1_scal secp256k1_scalar_mul(&ctx->proof[i].xsqinvy[j], &ctx->proof[i].xsqinv[j], &yinvn); secp256k1_scalar_sqr(&yinvn, &yinvn); } - for (j = 0; j < lg_grouping; j++) { - /* TODO this only does the right thing for lg_grouping = 0 or 1 */ + if (lg_grouping == 1) { secp256k1_scalar_mul(&ctx->proof[i].abinv[2], &ctx->proof[i].abinv[2], &yinvn); secp256k1_scalar_sqr(&yinvn, &yinvn); } } else { prev_cache_idx = cache_idx - 1; } + /* Regardless of specialness, we multiply by `a_2/a_1` or whatever the appropriate multiplier + * is. We expect the caller to have given these to us in the `ctx->proof[i].abinv` array. */ secp256k1_scalar_mul( &ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xcache[prev_cache_idx], &ctx->proof[i].abinv[abinv_idx] ); + /* If it's *not* a special index, just multiply by the appropriate `x_k^2`, or `x_k^-2` in case + * we're in the `H_i` half of the multiexp. At this point we can explain the index `k`, which + * is computed in the variable `xsq_idx` (`xsqinv_idx` respectively). In light of our discussion + * of `j`, we see that this should be "the least significant bit that's 1 in `i` but not `i-1`." + * In other words, it is the number of trailing 0 bits in the index `i`. */ } else if (idx < ctx->vec_len) { const size_t xsq_idx = secp256k1_ctzl(idx); secp256k1_scalar_mul(&ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xcache[cache_idx - 1], &ctx->proof[i].xsq[xsq_idx]); @@ -179,14 +236,19 @@ static int secp256k1_bulletproof_innerproduct_vfy_ecmult_callback(secp256k1_scal } term = ctx->proof[i].xcache[cache_idx]; - /* When going through the G generators, compute the x-inverses as side effects */ - if (idx < ctx->vec_len / grouping && secp256k1_popcountl(idx) == ctx->lg_vec_len - 1) { /* if the scalar has only one 0, i.e. only one inverse... */ + /* One last trick: compute `x_k^-2` while computing the `G_i` scalars, so that they'll be + * available when we need them for the `H_i` scalars. We can do this for every `i` value + * that has exactly one 0-bit, i.e. which is a product of all `x_i`s and one `x_k^-1`. By + * multiplying that by the special value `prod_{i=1}^n x_i^-1` we obtain simply `x_k^-2`. + * We expect the caller to give us this special value in `ctx->proof[i].xsqinv_mask`. */ + if (idx < ctx->vec_len / grouping && secp256k1_popcountl(idx) == ctx->lg_vec_len - 1) { const size_t xsqinv_idx = secp256k1_ctzl(~idx); - /* ...multiply it by the total inverse, to get x_j^-2 */ secp256k1_scalar_mul(&ctx->proof[i].xsqinv[xsqinv_idx], &ctx->proof[i].xcache[cache_idx], &ctx->proof[i].xsqinv_mask); } - /* ...add whatever offset the rangeproof wants... */ + /* Finally, if the caller, in its computation of `P`, wants to multiply `G_i` or `H_i` by some scalar, + * we add that to our sum as well. Again, we trust the randomization in `xcache[0]` to prevent any + * cancellation attacks here. */ if (ctx->proof[i].proof->rangeproof_cb != NULL) { secp256k1_scalar rangeproof_offset; if ((ctx->proof[i].proof->rangeproof_cb)(&rangeproof_offset, NULL, &ctx->randomizer[i], idx, ctx->proof[i].proof->rangeproof_cb_data) != 1) { @@ -416,7 +478,7 @@ static int secp256k1_bulletproof_inner_product_verify_impl(const secp256k1_ecmul for (j = n_ab - 1; j > 0; j--) { size_t prev_idx; if (j == n_ab / 2) { - prev_idx = j - 1; /* we go from a_n to b_0 */ + prev_idx = j - 1; /* we go from a_{n-1} to b_0 */ } else { prev_idx = j & (j - 1); /* but from a_i' to a_i, where i' is i with its lowest set bit unset */ } @@ -702,7 +764,7 @@ static int secp256k1_bulletproof_inner_product_prove_impl(const secp256k1_ecmult } *proof_len = secp256k1_bulletproof_innerproduct_proof_length(n); - /* Special-case lengths 0 and 1 whose proofs are just expliict lists of scalars */ + /* Special-case lengths 0 and 1 whose proofs are just explicit lists of scalars */ if (n <= IP_AB_SCALARS / 2) { secp256k1_scalar a[IP_AB_SCALARS / 2]; secp256k1_scalar b[IP_AB_SCALARS / 2]; diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h index 1460ebfba..4a675982c 100644 --- a/src/modules/bulletproofs/main_impl.h +++ b/src/modules/bulletproofs/main_impl.h @@ -14,7 +14,12 @@ struct secp256k1_bulletproof_generators { size_t n; + /* `G_i`, `H_i` generators, `n` each of them which are generated when creating this struct */ secp256k1_ge *gens; + /* `H` "alternate" generator, used in Pedersen commitments. Passed in by caller to + * `secp256k1_bulletproof_generators_create`; stored in this structure to allow consistent + * generators between functions using `secp256k1_bulletproof_generators` and functions + * using the Pedersen commitment module. */ secp256k1_ge *blinding_gen; }; diff --git a/src/modules/bulletproofs/rangeproof_impl.h b/src/modules/bulletproofs/rangeproof_impl.h index a9c22011b..6d8afda34 100644 --- a/src/modules/bulletproofs/rangeproof_impl.h +++ b/src/modules/bulletproofs/rangeproof_impl.h @@ -592,7 +592,6 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co secp256k1_scalar_add(&t2, &t2, &t1); /* Compute Ti = t_i*A + tau_i*G for i = 1,2 */ - /* TODO surely we can improve this */ secp256k1_ecmult_const(&tmpj, value_gen, &t1, 256); secp256k1_ge_set_gej(&out_pt[2], &tmpj); secp256k1_ecmult_const(&tmpj, &gens->blinding_gen[0], &tau1, 256); @@ -636,7 +635,6 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co secp256k1_bulletproof_serialize_points(&proof[64], out_pt, 4); /* Mix this into the hash so the input to the inner product proof is fixed */ - /* TODO is this necessary? revisit */ secp256k1_sha256_initialize(&sha256); secp256k1_sha256_write(&sha256, commit, 32); secp256k1_sha256_write(&sha256, proof, 64); From 81d22c98c0a3f94c43534537cae7ddfdceaa7e4f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 17 Sep 2018 22:01:48 +0000 Subject: [PATCH 56/58] bulletproofs: add `n_commits` to the hash of all rangeproof input data --- src/modules/bulletproofs/rangeproof_impl.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/modules/bulletproofs/rangeproof_impl.h b/src/modules/bulletproofs/rangeproof_impl.h index 6d8afda34..85dc0a057 100644 --- a/src/modules/bulletproofs/rangeproof_impl.h +++ b/src/modules/bulletproofs/rangeproof_impl.h @@ -208,8 +208,14 @@ static int secp256k1_bulletproof_rangeproof_verify_impl(const secp256k1_ecmult_c /* Commit to all input data: min value, pedersen commit, asset generator, extra_commit */ if (min_value != NULL && min_value[i] != NULL) { + unsigned char len[4]; secp256k1_sha256_initialize(&sha256); secp256k1_sha256_write(&sha256, commit, 32); + len[0] = n_commits; + len[1] = n_commits >> 8; + len[2] = n_commits >> 16; + len[3] = n_commits >> 24; + secp256k1_sha256_write(&sha256, len, 4); for (j = 0; j < n_commits; j++) { unsigned char vbuf[8]; vbuf[0] = min_value[i][j]; @@ -462,8 +468,14 @@ static int secp256k1_bulletproof_rangeproof_prove_impl(const secp256k1_ecmult_co /* Commit to all input data: min value, pedersen commit, asset generator, extra_commit */ if (min_value != NULL) { + unsigned char len[4]; secp256k1_sha256_initialize(&sha256); secp256k1_sha256_write(&sha256, commit, 32); + len[0] = n_commits; + len[1] = n_commits >> 8; + len[2] = n_commits >> 16; + len[3] = n_commits >> 24; + secp256k1_sha256_write(&sha256, len, 4); for (i = 0; i < n_commits; i++) { unsigned char vbuf[8]; vbuf[0] = min_value[i]; @@ -684,6 +696,7 @@ static int secp256k1_bulletproof_rangeproof_rewind_impl(uint64_t *value, secp256 if (min_value > 0) { unsigned char vbuf[8]; + const unsigned char len[4] = { 1, 0, 0, 0 }; vbuf[0] = min_value; vbuf[1] = min_value >> 8; vbuf[2] = min_value >> 16; @@ -694,6 +707,7 @@ static int secp256k1_bulletproof_rangeproof_rewind_impl(uint64_t *value, secp256 vbuf[7] = min_value >> 56; secp256k1_sha256_initialize(&sha256); secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, len, 4); secp256k1_sha256_write(&sha256, vbuf, 8); secp256k1_sha256_finalize(&sha256, commit); } From 5cda465593f871044154b422da1bc97b9a7d41d9 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 18 Sep 2018 15:13:53 +0000 Subject: [PATCH 57/58] add new modules to travis --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index dbb9d8409..622596a76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,14 +14,16 @@ env: - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no GENERATOR=no RANGEPROOF=no WHITELIST=no - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar 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 COMMITMENT=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes + - FIELD=64bit EXPERIMENTAL=yes COMMITMENT=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes - SCALAR=32bit RECOVERY=yes - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes GENERATOR=yes COMMITMENT=yes RANGEPROOF=yes BULLETPROOF=yes WHITELIST=yes SURJECTIONPROOF=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 GENERATOR=yes COMMITMENT=yes RANGEPROOF=yes BULLETPROOF=yes WHITELIST=yes SURJECTIONPROOF=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - FIELD=32bit ENDOMORPHISM=yes @@ -67,5 +69,5 @@ before_script: ./autogen.sh script: - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-module-rangeproof=$RANGEPROOF --enable-module-whitelist=$WHITELIST --enable-module-generator=$GENERATOR --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-commitment=$COMMITMENT --enable-module-recovery=$RECOVERY --enable-module-rangeproof=$RANGEPROOF --enable-module-whitelist=$WHITELIST --enable-module-generator=$GENERATOR --enable-module-bulletproof=$BULLETPROOF --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD os: linux From 6fb7e058dc03a54ad44f9bc8ead68686304ee978 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Oct 2018 07:23:43 +0000 Subject: [PATCH 58/58] rangeproof: reduce test iterations; add echo script to travis to prevent timeout --- .travis.yml | 1 + src/modules/rangeproof/tests_impl.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 622596a76..da50f2324 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,6 +67,7 @@ before_install: mkdir -p `dirname $GUAVA_JAR` install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi before_script: ./autogen.sh script: + - bash -c 'for ((i = 0; i < 3; i++)); do sleep 360 && echo '.'; done' & - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-commitment=$COMMITMENT --enable-module-recovery=$RECOVERY --enable-module-rangeproof=$RANGEPROOF --enable-module-whitelist=$WHITELIST --enable-module-generator=$GENERATOR --enable-module-bulletproof=$BULLETPROOF --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 9d5e1c8be..c64e376ad 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -516,7 +516,7 @@ 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 < 2*count; i++) { test_borromean(); } test_rangeproof();