From 6eebf82d8ac6231d4feee132e4eb335d8f4c2267 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 6 Jul 2016 15:44:09 +0000 Subject: [PATCH] rangeproof: add summing function for blinded generators; drop `excess` and `gen` from `verify_tally` --- include/secp256k1_rangeproof.h | 88 ++++++++++---- 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 + 10 files changed, 263 insertions(+), 84 deletions(-) diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index b8fca472..528b6662 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -16,8 +16,7 @@ extern "C" { * guaranteed to be portable between different platforms or versions. It is * however guaranteed to be 33 bytes in size, and can be safely copied/moved. * If you need to convert to a format suitable for storage or transmission, use - * the secp256k1_pedersen_commitment_serialize_* and - * secp256k1_pedersen_commitment_serialize_* functions. + * secp256k1_pedersen_commitment_serialize and secp256k1_pedersen_commitment_parse. * * Furthermore, it is guaranteed to identical signatures will have identical * representation, so they can be memcmp'ed. @@ -71,7 +70,7 @@ void secp256k1_pedersen_context_initialize(secp256k1_context* ctx); * * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, @@ -88,7 +87,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( * nneg: how many of the initial factors should be treated with a positive sign. * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, @@ -104,24 +103,57 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( * pcnt: number of commitments pointed to by commits. * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) * ncnt: number of commitments pointed to by ncommits. - * excess: signed 64bit amount to add to the total to bring it to zero, can be negative. * - * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) - excess*H == 0. + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. * - * A pedersen commitment is xG + vH where G and H are generators for the secp256k1 group and x is a blinding factor, - * while v is the committed value. For a collection of commitments to sum to zero both their blinding factors and - * values must sum to zero. + * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator + * A all blinding factors and all values must sum to zero. * */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, - size_t ncnt, - int64_t excess, - const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7); + size_t ncnt +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Sets the final Pedersen blinding factor correctly when the generators themselves + * have blinding factors. + * + * Consider a generator of the form A' = A + rG, where A is the "real" generator + * but A' is the generator provided to verifiers. Then a Pedersen commitment + * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') + * to sum to zero for multiple commitments, we take three arrays consisting of + * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s + * and `blinding_factor`s, and sum them. + * + * The function then subtracts the sum of all (vr + r') from the last element + * of the `blinding_factor` array, setting the total sum to zero. + * + * Returns 1 always. + * + * In: ctx: pointer to a context object + * value: array of asset values, `v` in the above paragraph. + * May not be NULL unless `n_total` is 0. + * generator_blind: array of asset blinding factors, `r` in the above paragraph + * May not be NULL unless `n_total` is 0. + * n_total: Total size of the above arrays + * n_inputs: How many of the initial array elements represent commitments that + * will be negated in the final sum + * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph + * May not be NULL unless `n_total` is 0. + * the last value will be modified to get the total sum to zero. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( + const secp256k1_context* ctx, + const uint64_t *value, + const unsigned char* const* generator_blind, + unsigned char* const* blinding_factor, + size_t n_total, + size_t n_inputs +); /** Initialize a context for usage with Pedersen commitments. */ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); @@ -133,18 +165,22 @@ void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx); * commit: the commitment being proved. (cannot be NULL) * proof: pointer to character array with the proof. (cannot be NULL) * plen: length of proof in bytes. + * extra_commit: additional data covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, + size_t extra_commit_len, const secp256k1_generator* gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(9); /** Verify a range proof proof and rewind the proof to recover information sent by its author. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. @@ -154,6 +190,8 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( * proof: pointer to character array with the proof. (cannot be NULL) * plen: length of proof in bytes. * nonce: 32-byte secret nonce used by the prover (cannot be NULL) + * extra_commit: additional data covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment * value_out: pointer to an unsigned int64 which has the exact value of the commitment. * message_out: pointer to a 4096 byte character array to receive message data from the proof author. @@ -161,7 +199,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( const secp256k1_context* ctx, unsigned char *blind_out, uint64_t *value_out, @@ -173,8 +211,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, + size_t extra_commit_len, const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(12); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(14); /** Author a proof that a committed value is within a range. * Returns 1: Proof successfully created. @@ -189,6 +229,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * (-1 is a special case that makes the value public. 0 is the most private.) * min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64). * value: Actual value of the commitment. + * message: pointer to a byte array of data to be embedded in the rangeproof that can be recovered by rewinding the proof + * msg_len: size of the message to be embedded in the rangeproof + * extra_commit: additional data to be covered in rangeproof signature + * extra_commit_len: length of extra_commit byte array (0 if NULL) * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. * * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. @@ -198,7 +242,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( * This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding. * */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( const secp256k1_context* ctx, unsigned char *proof, size_t *plen, @@ -211,8 +255,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( uint64_t value, const unsigned char *message, size_t msg_len, + const unsigned char *extra_commit, + size_t extra_commit_len, const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(13); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(15); /** Extract some basic information from a range-proof. * Returns 1: Information successfully extracted. @@ -225,7 +271,7 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) */ -SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( const secp256k1_context* ctx, int *exp, int *mantissa, diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index 31331266..ca8cbe5e 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -12,6 +12,7 @@ #include "field.h" #include "group.h" #include "hash.h" +#include "scalar.h" static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { secp256k1_fe fe; diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 34279887..4427667a 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -118,8 +118,7 @@ int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *bl } /* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ -int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt, int64_t excess, const secp256k1_generator* gen) { - secp256k1_ge genp; +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { secp256k1_gej accj; secp256k1_ge add; size_t i; @@ -127,17 +126,6 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k ARG_CHECK(!pcnt || (commits != NULL)); ARG_CHECK(!ncnt || (ncommits != NULL)); secp256k1_gej_set_infinity(&accj); - secp256k1_generator_load(&genp, gen); - if (excess) { - uint64_t ex; - int neg; - /* Take the absolute value, and negate the result if the input was negative. */ - neg = secp256k1_sign_and_abs64(&ex, excess); - secp256k1_pedersen_ecmult_small(&accj, ex, &genp); - if (neg) { - secp256k1_gej_neg(&accj, &accj); - } - } for (i = 0; i < ncnt; i++) { secp256k1_pedersen_commitment_load(&add, ncommits[i]); secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); @@ -150,6 +138,60 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k return secp256k1_gej_is_infinity(&accj); } +int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { + secp256k1_scalar sum; + secp256k1_scalar tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(n_total == 0 || value != NULL); + ARG_CHECK(n_total == 0 || generator_blind != NULL); + ARG_CHECK(n_total == 0 || blinding_factor != NULL); + ARG_CHECK(n_total > n_inputs); + (void) ctx; + + if (n_total == 0) { + return 1; + } + + secp256k1_scalar_set_int(&sum, 0); + for (i = 0; i < n_total; i++) { + int overflow = 0; + secp256k1_scalar addend; + secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ + + secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ + + secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ + secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ + secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ + secp256k1_scalar_clear(&addend); + } + + /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ + secp256k1_scalar_negate(&sum, &sum); + secp256k1_scalar_add(&tmp, &tmp, &sum); + secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); + + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&sum); + return 1; +} + int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { size_t offset; @@ -167,7 +209,7 @@ int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *manti int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const secp256k1_generator* gen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { secp256k1_ge commitp; secp256k1_ge genp; ARG_CHECK(ctx != NULL); @@ -180,11 +222,11 @@ int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, &genp); + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp); } int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value, - const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const secp256k1_generator* gen) { + const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) { secp256k1_ge commitp; secp256k1_ge genp; ARG_CHECK(ctx != NULL); @@ -196,12 +238,12 @@ int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_valu secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, - NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, &genp); + NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp); } int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value, const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, - const unsigned char *message, size_t msg_len, const secp256k1_generator* gen){ + const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen){ secp256k1_ge commitp; secp256k1_ge genp; ARG_CHECK(ctx != NULL); @@ -215,7 +257,7 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof secp256k1_pedersen_commitment_load(&commitp, commit); secp256k1_generator_load(&genp, gen); return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, - proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, &genp); + proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp); } #endif diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index 5aa3a68d..840a09ae 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -15,6 +15,7 @@ static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const secp256k1_ge* genp); + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, + const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp); #endif diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 29789e6a..39d21a5d 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -193,7 +193,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *proof, size_t *plen, uint64_t min_value, const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value, - const unsigned char *message, size_t msg_len, const secp256k1_ge* genp){ + const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp){ secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */ secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */ secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */ @@ -318,6 +318,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul npub += rsizes[i]; } secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); + if (extra_commit != NULL) { + secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); + } secp256k1_sha256_finalize(&sha256_m, tmp); if (!secp256k1_borromean_sign(ecmult_ctx, ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { return 0; @@ -538,7 +541,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(size_t *offset, SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, - uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const secp256k1_ge* genp) { + uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp) { secp256k1_gej accj; secp256k1_gej pubs[128]; secp256k1_ge c; @@ -640,6 +643,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm /*Extra data found, reject.*/ return 0; } + if (extra_commit != NULL) { + secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); + } secp256k1_sha256_finalize(&sha256_m, m); ret = secp256k1_borromean_verify(ecmult_ctx, nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); if (ret && nonce) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 82cf426d..29b0a659 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -16,7 +16,7 @@ #include "include/secp256k1_rangeproof.h" -void test_pedersen(void) { +static void test_pedersen(void) { secp256k1_pedersen_commitment commits[19]; const secp256k1_pedersen_commitment *cptr[19]; unsigned char blinds[32*19]; @@ -40,23 +40,12 @@ void test_pedersen(void) { values[i] = secp256k1_rands64(0, INT64_MAX - totalv); totalv += values[i]; } - if (secp256k1_rand32() & 1) { - for (i = 0; i < outputs; i++) { - int64_t max = INT64_MAX; - if (totalv < 0) { - max += totalv; - } - values[i + inputs] = secp256k1_rands64(0, max); - totalv -= values[i + inputs]; - } - } else { - for (i = 0; i < outputs - 1; i++) { - values[i + inputs] = secp256k1_rands64(0, totalv); - totalv -= values[i + inputs]; - } - values[total - 1] = totalv >> (secp256k1_rand32() & 1); - totalv -= values[total - 1]; + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_rands64(0, totalv); + totalv -= values[i + inputs]; } + values[total - 1] = totalv; + for (i = 0; i < total - 1; i++) { random_scalar_order(&s); secp256k1_scalar_get_b32(&blinds[i * 32], &s); @@ -65,8 +54,11 @@ void test_pedersen(void) { for (i = 0; i < total; i++) { CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); } - CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv, secp256k1_generator_h)); - CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); + if (inputs > 0 && values[0] > 0) { + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs)); + } random_scalar_order(&s); for (i = 0; i < 4; i++) { secp256k1_scalar_get_b32(&blinds[i * 32], &s); @@ -77,15 +69,11 @@ void test_pedersen(void) { for (i = 0; i < 3; i++) { CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); } - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1, 0, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[1], 1, INT64_MAX, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1, 0, secp256k1_generator_h)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[0], 1, -INT64_MAX, secp256k1_generator_h)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); } -void test_borromean(void) { +static void test_borromean(void) { unsigned char e0[32]; secp256k1_scalar s[64]; secp256k1_gej pubs[64]; @@ -150,7 +138,7 @@ void test_borromean(void) { } } -void test_rangeproof(void) { +static void test_rangeproof(void) { const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; secp256k1_pedersen_commitment commit; secp256k1_pedersen_commitment commit2; @@ -196,10 +184,10 @@ void test_rangeproof(void) { input_message_len = sizeof(message_long); } len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); if (input_message != NULL) { CHECK(memcmp(message, input_message, input_message_len) == 0); } @@ -212,9 +200,21 @@ void test_rangeproof(void) { CHECK(minv <= v); CHECK(maxv >= v); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 73); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv == v); + CHECK(maxv == v); + + /* Check with a committed message */ + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), secp256k1_generator_h)); + CHECK(len <= 73); + CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); CHECK(memcmp(blindout, blind, 32) == 0); CHECK(vout == v); CHECK(minv == v); @@ -226,11 +226,13 @@ void test_rangeproof(void) { CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); for (i = 0; i < 19; i++) { len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, secp256k1_generator_h)); - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); CHECK(minv <= v); CHECK(maxv >= v); + /* Make sure it fails when validating with a committed message */ + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h)); } secp256k1_rand256(blind); { @@ -238,14 +240,14 @@ void test_rangeproof(void) { v = secp256k1_rands64(0, 255); CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); len = 5134; - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); for (i = 0; i < len*8; i++) { proof[i >> 3] ^= 1 << (i & 7); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); proof[i >> 3] ^= 1 << (i & 7); } - CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); CHECK(minv <= v); CHECK(maxv >= v); } @@ -269,10 +271,10 @@ void test_rangeproof(void) { if (min_bits < 0) { min_bits = -min_bits; } - CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, NULL, 0, secp256k1_generator_h)); CHECK(len <= 5134); mlen = 4096; - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); for (j = 0; j < mlen; j++) { CHECK(message[j] == 0); } @@ -281,7 +283,7 @@ void test_rangeproof(void) { CHECK(vout == v); CHECK(minv <= v); CHECK(maxv >= v); - CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, secp256k1_generator_h)); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h)); memcpy(&commit2, &commit, sizeof(commit)); } for (j = 0; j < 10; j++) { @@ -290,13 +292,69 @@ void test_rangeproof(void) { } for (k = 0; k < 128; k++) { len = k; - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } len = secp256k1_rands64(0, 3072); - CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, secp256k1_generator_h)); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h)); } } +#define MAX_N_GENS 30 +void test_multiple_generators(void) { + const size_t n_inputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_outputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_generators = n_inputs + n_outputs; + unsigned char *generator_blind[MAX_N_GENS]; + unsigned char *pedersen_blind[MAX_N_GENS]; + secp256k1_generator generator[MAX_N_GENS]; + secp256k1_pedersen_commitment commit[MAX_N_GENS]; + const secp256k1_pedersen_commitment *commit_ptr[MAX_N_GENS]; + size_t i; + int64_t total_value; + uint64_t value[MAX_N_GENS]; + + secp256k1_scalar s; + + unsigned char generator_seed[32]; + random_scalar_order(&s); + secp256k1_scalar_get_b32(generator_seed, &s); + /* Create all the needed generators */ + for (i = 0; i < n_generators; i++) { + generator_blind[i] = (unsigned char*) malloc(32); + pedersen_blind[i] = (unsigned char*) malloc(32); + + random_scalar_order(&s); + secp256k1_scalar_get_b32(generator_blind[i], &s); + random_scalar_order(&s); + secp256k1_scalar_get_b32(pedersen_blind[i], &s); + + CHECK(secp256k1_generator_generate_blinded(ctx, &generator[i], generator_seed, generator_blind[i])); + + commit_ptr[i] = &commit[i]; + } + + /* Compute all the values -- can be positive or negative */ + total_value = 0; + for (i = 0; i < n_outputs; i++) { + value[n_inputs + i] = secp256k1_rands64(0, INT64_MAX - total_value); + total_value += value[n_inputs + i]; + } + for (i = 0; i < n_inputs - 1; i++) { + value[i] = secp256k1_rands64(0, total_value); + total_value -= value[i]; + } + value[i] = total_value; + + /* Correct for blinding factors and do the commitments */ + CHECK(secp256k1_pedersen_blind_generator_blind_sum(ctx, value, (const unsigned char * const *) generator_blind, pedersen_blind, n_generators, n_inputs)); + for (i = 0; i < n_generators; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commit[i], pedersen_blind[i], value[i], &generator[i])); + } + + /* Verify */ + CHECK(secp256k1_pedersen_verify_tally(ctx, &commit_ptr[0], n_inputs, &commit_ptr[n_inputs], n_outputs)); +} + void run_rangeproof_tests(void) { int i; for (i = 0; i < 10*count; i++) { @@ -306,6 +364,7 @@ void run_rangeproof_tests(void) { test_borromean(); } test_rangeproof(); + test_multiple_generators(); } #endif diff --git a/src/scalar.h b/src/scalar.h index 27e9d837..a052d19d 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 56e7bd82..1dc950e3 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 aae4f35c..f78bc7f1 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 4f94441f..fc91673c 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)