From 72e75918d6461d30a00bbdd28ac05edfe114abf2 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Oct 2021 00:16:57 +0000 Subject: [PATCH 1/7] rangeproof: add a bunch more testing Add two new fixed rangeproof vectors; check that various extracted values are correct; add a test for creating and verifying single-value proofs. --- src/modules/rangeproof/tests_impl.h | 269 +++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 8 deletions(-) diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index c2482550c..b534f495d 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -541,8 +541,95 @@ static void test_rangeproof(void) { } } +static void test_single_value_proof(uint64_t val) { + unsigned char proof[5000]; + secp256k1_pedersen_commitment commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + unsigned char nonce[32]; + const unsigned char message[1] = " "; /* no message will fit into a single-value proof */ + unsigned char message_out[sizeof(proof)] = { 0 }; + size_t plen = sizeof(proof); + uint64_t min_val_out = 0; + uint64_t max_val_out = 0; + uint64_t val_out = 0; + size_t m_len_out = 0; + + secp256k1_testrand256(blind); + secp256k1_testrand256(nonce); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, secp256k1_generator_h)); + + CHECK(secp256k1_rangeproof_sign( + ctx, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + message, sizeof(message), /* Will cause this to fail */ + NULL, 0, + secp256k1_generator_h + ) == 0); + + plen = sizeof(proof); + CHECK(secp256k1_rangeproof_sign( + ctx, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + NULL, 0, + NULL, 0, + secp256k1_generator_h + ) == 1); + + /* Different proof sizes are unfortunate but is caused by `min_value` of + * zero being special-cased and encoded more efficiently. */ + if (val == 0) { + CHECK(plen == 65); + } else { + CHECK(plen == 73); + } + + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + ) == 1); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + + memset(message_out, 0, sizeof(message_out)); + m_len_out = sizeof(message_out); + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind_out, &val_out, + message_out, &m_len_out, + nonce, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + )); + CHECK(val_out == val); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + CHECK(m_len_out == 0); + CHECK(memcmp(blind, blind_out, 32) == 0); + for (m_len_out = 0; m_len_out < sizeof(message_out); m_len_out++) { + CHECK(message_out[m_len_out] == 0); + } +} + #define MAX_N_GENS 30 -void test_multiple_generators(void) { +static void test_multiple_generators(void) { const size_t n_inputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1; const size_t n_outputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1; const size_t n_generators = n_inputs + n_outputs; @@ -604,7 +691,18 @@ void test_multiple_generators(void) { } void test_rangeproof_fixed_vectors(void) { - const unsigned char vector_1[] = { + size_t i; + unsigned char blind[32]; + uint64_t value; + uint64_t min_value; + uint64_t max_value; + secp256k1_pedersen_commitment pc; + unsigned char message[4000] = {0}; + size_t m_len = sizeof(message); + + /* Vector 1: no message */ +{ + static const unsigned char vector_1[] = { 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x02, 0x2a, 0x5c, 0x42, 0x0e, 0x1d, 0x51, 0xe1, 0xb7, 0xf3, 0x69, 0x04, 0xb5, 0xbb, 0x9b, 0x41, 0x66, 0x14, 0xf3, 0x64, 0x42, 0x26, 0xe3, 0xa7, 0x6a, 0x06, 0xbb, 0xa8, 0x5a, 0x49, 0x6f, 0x19, 0x76, 0xfb, 0xe5, 0x75, 0x77, 0x88, @@ -647,25 +745,175 @@ void test_rangeproof_fixed_vectors(void) { 0xa6, 0x45, 0xf6, 0xce, 0xcf, 0x48, 0xf6, 0x1e, 0x3d, 0xd2, 0xcf, 0xcb, 0x3a, 0xcd, 0xbb, 0x92, 0x29, 0x24, 0x16, 0x7f, 0x8a, 0xa8, 0x5c, 0x0c, 0x45, 0x71, 0x33 }; - const unsigned char commit_1[] = { + static const unsigned char commit_1[] = { 0x08, 0xf5, 0x1e, 0x0d, 0xc5, 0x86, 0x78, 0x51, 0xa9, 0x00, 0x00, 0xef, 0x4d, 0xe2, 0x94, 0x60, 0x89, 0x83, 0x04, 0xb4, 0x0e, 0x90, 0x10, 0x05, 0x1c, 0x7f, 0xd7, 0x33, 0x92, 0x1f, 0xe7, 0x74, 0x59 }; - uint64_t min_value_1; - uint64_t max_value_1; - secp256k1_pedersen_commitment pc; + static const unsigned char blind_1[] = { + 0x98, 0x44, 0xfc, 0x7a, 0x64, 0xa9, 0xca, 0xdf, 0xf3, 0x2f, 0x9f, 0x02, 0xba, 0x46, 0xc7, 0xd9, + 0x77, 0x47, 0xa4, 0xd3, 0x53, 0x17, 0xc6, 0x44, 0x30, 0x73, 0x84, 0xeb, 0x1f, 0xbe, 0xa1, 0xfb + }; CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_1)); - CHECK(secp256k1_rangeproof_verify( ctx, - &min_value_1, &max_value_1, + &min_value, &max_value, &pc, vector_1, sizeof(vector_1), NULL, 0, secp256k1_generator_h )); + CHECK(min_value == 86); + CHECK(max_value == 25586); + + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind, &value, + message, &m_len, + pc.data, + &min_value, &max_value, + &pc, + vector_1, sizeof(vector_1), + NULL, 0, + secp256k1_generator_h + )); + + CHECK(memcmp(blind, blind_1, 32) == 0); + CHECK(value == 86); + CHECK(min_value == 86); + CHECK(max_value == 25586); + CHECK(m_len == 448); /* length of the sidechannel in the proof */ + for (i = 0; i < m_len; i++) { + /* No message encoded in this vector */ + CHECK(message[i] == 0); + } +} + + /* Vector 2: embedded message */ +{ + static const unsigned char vector_2[] = { + 0x40, 0x03, 0x00, 0x90, 0x1a, 0x61, 0x64, 0xbb, 0x85, 0x1a, 0x78, 0x35, 0x1e, 0xe0, 0xd5, 0x96, + 0x71, 0x0f, 0x18, 0x8e, 0xf3, 0x33, 0xf0, 0x75, 0xfe, 0xd6, 0xc6, 0x11, 0x6b, 0x42, 0x89, 0xea, + 0xa2, 0x0c, 0x89, 0x25, 0x37, 0x81, 0x10, 0xf9, 0xf0, 0x9b, 0xda, 0x68, 0x2a, 0xd9, 0x2e, 0x0c, + 0x45, 0x17, 0x54, 0x6d, 0x02, 0xd2, 0x21, 0x5d, 0xbc, 0x10, 0xf8, 0x8f, 0xf1, 0x92, 0x40, 0xa9, + 0xc7, 0x24, 0x00, 0x1b, 0xc8, 0x75, 0x0f, 0xf6, 0x8f, 0x93, 0x8b, 0x78, 0x62, 0x73, 0x3c, 0x86, + 0x4b, 0x61, 0x7c, 0x0f, 0xc6, 0x41, 0xc9, 0xb3, 0xc1, 0x30, 0x7f, 0xd4, 0xee, 0x9f, 0x37, 0x08, + 0x9b, 0x64, 0x23, 0xd5, 0xe6, 0x1a, 0x03, 0x54, 0x74, 0x9b, 0x0b, 0xae, 0x6f, 0x2b, 0x1e, 0xf5, + 0x40, 0x44, 0xaa, 0x12, 0xe8, 0xbd, 0xe0, 0xa6, 0x85, 0x89, 0xf1, 0xa9, 0xd0, 0x3f, 0x2e, 0xc6, + 0x1f, 0x11, 0xf5, 0x44, 0x69, 0x99, 0x31, 0x10, 0x2e, 0x64, 0xc6, 0x44, 0xdb, 0x47, 0x06, 0x6d, + 0xd5, 0xf2, 0x8d, 0x19, 0x00, 0x39, 0xb8, 0xca, 0xda, 0x5c, 0x1d, 0x83, 0xbd, 0xa3, 0x6d, 0xbf, + 0x97, 0xdd, 0x83, 0x86, 0xc9, 0x56, 0xe2, 0xbb, 0x37, 0x4b, 0x2d, 0xb5, 0x9d, 0xf2, 0x7a, 0x6a, + 0x25, 0x47, 0xfa, 0x03, 0x05, 0xc5, 0xda, 0x73, 0xe1, 0x96, 0x15, 0x21, 0x23, 0xe5, 0xef, 0x55, + 0x36, 0xdd, 0xf1, 0xb1, 0x3f, 0x33, 0x1a, 0x91, 0x6c, 0x73, 0x64, 0xd3, 0x88, 0xe7, 0xc6, 0xc9, + 0x04, 0x29, 0xae, 0x55, 0x27, 0xa0, 0x80, 0x60, 0xaf, 0x0c, 0x09, 0x2f, 0xc8, 0x1b, 0xe6, 0x16, + 0x9e, 0xed, 0x29, 0xc7, 0x93, 0xce, 0xc7, 0x0d, 0xdf, 0x1f, 0x28, 0xba, 0xf3, 0x38, 0xc3, 0xaa, + 0x99, 0xd9, 0x21, 0x41, 0xb8, 0x10, 0xa5, 0x48, 0x37, 0xec, 0x60, 0xda, 0x64, 0x5a, 0x73, 0x55, + 0xd7, 0xff, 0x23, 0xfa, 0xf6, 0xc6, 0xf4, 0xe2, 0xca, 0x99, 0x2f, 0x30, 0x36, 0x48, 0x73, 0x8b, + 0x57, 0xa6, 0x62, 0x12, 0xa3, 0xe7, 0x5c, 0xa8, 0xd1, 0xe6, 0x85, 0x05, 0x59, 0xfe, 0x2b, 0x44, + 0xe4, 0x73, 0x1c, 0xc3, 0x56, 0x32, 0x07, 0x65, 0x4a, 0x58, 0xaf, 0x2b, 0x3f, 0x36, 0xca, 0xb4, + 0x1d, 0x5c, 0x2a, 0x46, 0x1f, 0xf7, 0x63, 0x59, 0x4f, 0x2b, 0xd0, 0xf6, 0xfc, 0xcf, 0x04, 0x09, + 0xb7, 0x65, 0x1b + }; + static const unsigned char commit_2[] = { + 0x09, + 0x25, 0xa4, 0xbd, 0xc4, 0x57, 0x69, 0xeb, 0x4f, 0x34, 0x0f, 0xea, 0xb8, 0xe4, 0x72, 0x04, 0x54, + 0x06, 0xe5, 0xd6, 0x85, 0x15, 0x42, 0xea, 0x6e, 0x1d, 0x11, 0x11, 0x9c, 0x56, 0xf8, 0x10, 0x45 + }; + static const unsigned char blind_2[] = { + 0xdc, 0x79, 0x07, 0x89, 0x2d, 0xc4, 0xe3, 0x76, 0xf9, 0x13, 0x38, 0xd6, 0x4b, 0x46, 0xed, 0x9d, + 0x9b, 0xf6, 0x70, 0x3d, 0x04, 0xcf, 0x96, 0x8c, 0xfd, 0xb5, 0xff, 0x0a, 0x06, 0xc7, 0x08, 0x8b + }; + static const unsigned char message_2[] = "When I see my own likeness in the depths of someone else's consciousness, I always experience a moment of panic."; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_2)); + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_value, &max_value, + &pc, + vector_2, sizeof(vector_2), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == 0); + CHECK(max_value == 15); + + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind, &value, + message, &m_len, + pc.data, + &min_value, &max_value, + &pc, + vector_2, sizeof(vector_2), + NULL, 0, + secp256k1_generator_h + )); + + CHECK(memcmp(blind, blind_2, 32) == 0); + CHECK(value == 11); + CHECK(min_value == 0); + CHECK(max_value == 15); + CHECK(m_len == 192); /* length of the sidechannel in the proof */ + CHECK(memcmp(message, message_2, sizeof(message_2)) == 0); + for (i = sizeof(message_2); i < m_len; i++) { + /* No message encoded in this vector */ + CHECK(message[i] == 0); + } +} + + /* Vector 3: single-value proof of UINT64_MAX */ +{ + static const unsigned char vector_3[] = { + 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0x7d, 0x0b, 0x79, 0x0e, 0xaf, 0x41, + 0xa5, 0x8e, 0x9b, 0x0c, 0x5b, 0xa3, 0xee, 0x7d, 0xfd, 0x3d, 0x6b, 0xf3, 0xac, 0x04, 0x8a, 0x43, + 0x75, 0xb0, 0xb7, 0x0e, 0x92, 0xd7, 0xdf, 0xf0, 0x76, 0xc4, 0xa5, 0xb6, 0x2f, 0xf1, 0xb5, 0xfb, + 0xb4, 0xb6, 0x29, 0xea, 0x34, 0x9b, 0x16, 0x30, 0x0d, 0x06, 0xf1, 0xb4, 0x3f, 0x0d, 0x73, 0x59, + 0x75, 0xbf, 0x5d, 0x19, 0x59, 0xef, 0x11, 0xf0, 0xbf + }; + static const unsigned char commit_3[] = { + 0x08, + 0xc7, 0xea, 0x40, 0x7d, 0x26, 0x38, 0xa2, 0x99, 0xb9, 0x40, 0x22, 0x78, 0x17, 0x57, 0x65, 0xb3, + 0x36, 0x82, 0x18, 0x42, 0xc5, 0x57, 0x04, 0x5e, 0x58, 0x5e, 0xf6, 0x40, 0x8b, 0x24, 0x73, 0x10 + }; + static const unsigned char nonce_3[] = { + 0x84, 0x50, 0x94, 0x69, 0xa3, 0x4b, 0x6c, 0x62, 0x1a, 0xc7, 0xe2, 0x0e, 0x07, 0x9a, 0x6f, 0x85, + 0x5f, 0x26, 0x50, 0xcd, 0x88, 0x5a, 0x9f, 0xaa, 0x23, 0x5e, 0x0a, 0xe0, 0x7e, 0xc5, 0xe9, 0xf1 + }; + static const unsigned char blind_3[] = { + 0x68, 0x89, 0x47, 0x8c, 0x77, 0xec, 0xcc, 0x2b, 0x65, 0x01, 0x78, 0x6b, 0x06, 0x8b, 0x38, 0x94, + 0xc0, 0x6b, 0x9b, 0x4c, 0x02, 0xa6, 0xc8, 0xf6, 0xc0, 0x34, 0xea, 0x35, 0x57, 0xf4, 0xe1, 0x37 + }; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_3)); + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_value, &max_value, + &pc, + vector_3, sizeof(vector_3), + NULL, 0, + secp256k1_generator_h + )); + CHECK(min_value == UINT64_MAX); + CHECK(max_value == UINT64_MAX); + + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind, &value, + message, &m_len, + nonce_3, + &min_value, &max_value, + &pc, + vector_3, sizeof(vector_3), + NULL, 0, + secp256k1_generator_h + )); + CHECK(memcmp(blind, blind_3, 32) == 0); + CHECK(value == UINT64_MAX); + CHECK(min_value == UINT64_MAX); + CHECK(max_value == UINT64_MAX); + CHECK(m_len == 0); +} } void test_pedersen_commitment_fixed_vector(void) { @@ -690,6 +938,11 @@ void test_pedersen_commitment_fixed_vector(void) { void run_rangeproof_tests(void) { int i; test_api(); + + test_single_value_proof(0); + test_single_value_proof(12345678); + test_single_value_proof(UINT64_MAX); + test_rangeproof_fixed_vectors(); test_pedersen_commitment_fixed_vector(); for (i = 0; i < count / 2 + 1; i++) { From 058863a2c8ef7758bc85ada490b21cdfd5ea4d74 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 17 Oct 2021 14:37:21 +0000 Subject: [PATCH 2/7] add bench_whitelist to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c4e5d9ccd..673768efc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ bench_schnorrsig bench_sign bench_verify bench_recover +bench_whitelist bench_internal tests exhaustive_tests From 138135dcb5bd920aec137c0f3f49d5eaee42bd9a Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 16 Oct 2021 14:39:55 +0000 Subject: [PATCH 3/7] borromean: use closures to compute secret indices This decreases rangeproof proving stack size by 224 bytes on my system. Though the point is not to show large stack reductions (yet) but to demo the closure API on something simple/non-invasive. Future commits will use this for scalars and public keys, and then we will see some serious stack reduction. --- src/modules/rangeproof/borromean.h | 4 ++- src/modules/rangeproof/borromean_impl.h | 10 +++--- src/modules/rangeproof/borromean_util.h | 32 ++++++++++++++++++ src/modules/rangeproof/borromean_util_impl.h | 26 +++++++++++++++ src/modules/rangeproof/main_impl.h | 1 + src/modules/rangeproof/rangeproof_impl.h | 34 ++++++++++++++------ src/modules/rangeproof/tests_impl.h | 16 ++++++++- src/modules/surjection/main_impl.h | 6 ++-- src/modules/whitelist/main_impl.h | 3 +- 9 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 src/modules/rangeproof/borromean_util.h create mode 100644 src/modules/rangeproof/borromean_util_impl.h diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h index efd4da162..79204902a 100644 --- a/src/modules/rangeproof/borromean.h +++ b/src/modules/rangeproof/borromean.h @@ -14,11 +14,13 @@ #include "ecmult.h" #include "ecmult_gen.h" +#include "modules/rangeproof/borromean_util.h" + int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e0, const secp256k1_scalar *s, const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen); int secp256k1_borromean_sign(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 size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen); + const size_t *rsizes, const secp256k1_borromean_sz_closure* secidx_closure, 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 0b8dcd478..637a9db94 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -110,7 +110,7 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e int secp256k1_borromean_sign(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 size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen) { + const size_t *rsizes, const secp256k1_borromean_sz_closure* secidx_closure, size_t nrings, const unsigned char *m, size_t mlen) { secp256k1_gej rgej; secp256k1_ge rge; secp256k1_scalar ens; @@ -128,12 +128,13 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, VERIFY_CHECK(k != NULL); VERIFY_CHECK(sec != NULL); VERIFY_CHECK(rsizes != NULL); - VERIFY_CHECK(secidx != NULL); + VERIFY_CHECK(secidx_closure != NULL); VERIFY_CHECK(nrings > 0); VERIFY_CHECK(m != NULL); secp256k1_sha256_initialize(&sha256_e0); count = 0; for (i = 0; i < nrings; i++) { + size_t secidx_i = secidx_closure->call(secidx_closure, i); VERIFY_CHECK(INT_MAX - count > rsizes[i]); secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &k[i]); secp256k1_ge_set_gej(&rge, &rgej); @@ -141,7 +142,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, return 0; } secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); - for (j = secidx[i] + 1; j < rsizes[i]; j++) { + 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)) { @@ -165,13 +166,14 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_sha256_finalize(&sha256_e0, e0); count = 0; for (i = 0; i < nrings; i++) { + size_t secidx_i = secidx_closure->call(secidx_closure, 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++) { + for (j = 0; j < secidx_i; j++) { secp256k1_ecmult(&rgej, &pubs[count + j], &ens, &s[count + j]); if (secp256k1_gej_is_infinity(&rgej)) { return 0; diff --git a/src/modules/rangeproof/borromean_util.h b/src/modules/rangeproof/borromean_util.h new file mode 100644 index 000000000..f6e08b0f8 --- /dev/null +++ b/src/modules/rangeproof/borromean_util.h @@ -0,0 +1,32 @@ +/********************************************************************** + * Copyright (c) 2021 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BORROMEAN_UTIL_H_ +#define _SECP256K1_BORROMEAN_UTIL_H_ + +#include /* for size_t */ +#include /* for uint64_t */ + +/** A pointer to a function that returns a size_t given a ring index + * + * Used by borromean_sign to look up the size of each ring and the secret + * index, to avoid caching these values which would take excessive stack. + * As it turns out, both these values can be determined from the mantissa (for + * rangeproofs) or are constant (for surjection proofs or ring signatures) + * + * In: input: a single closed-over value + * index: which ring in the borromean ring signature this lookup + * function should look up + */ +typedef struct secp256k1_borromean_sz_closure { + uint64_t input; + size_t (*call)(const struct secp256k1_borromean_sz_closure* self, size_t index); +} secp256k1_borromean_sz_closure; + +/** Create a sz_closure that just returns a constant */ +secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_const(uint64_t c); + +#endif diff --git a/src/modules/rangeproof/borromean_util_impl.h b/src/modules/rangeproof/borromean_util_impl.h new file mode 100644 index 000000000..c81d04188 --- /dev/null +++ b/src/modules/rangeproof/borromean_util_impl.h @@ -0,0 +1,26 @@ +/********************************************************************** + * Copyright (c) 2021 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BORROMEAN_UTIL_IMPL_H_ +#define _SECP256K1_BORROMEAN_UTIL_IMPL_H_ + +#include "modules/rangeproof/borromean_util.h" +#include "util.h" + +static size_t secp256k1_borromean_sz_closure_const_call(const secp256k1_borromean_sz_closure* self, size_t index) { + (void) index; + return self->input; +} + +secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_const(uint64_t c) { + secp256k1_borromean_sz_closure ret; + VERIFY_CHECK(c < SIZE_MAX); + ret.input = c; + ret.call = secp256k1_borromean_sz_closure_const_call; + return ret; +} + +#endif diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 9129e4c30..5d62c53c0 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -11,6 +11,7 @@ #include "modules/rangeproof/pedersen_impl.h" #include "modules/rangeproof/borromean_impl.h" +#include "modules/rangeproof/borromean_util_impl.h" #include "modules/rangeproof/rangeproof_impl.h" /** Alternative generator for secp256k1. diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 184b79882..5df7ce54d 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -18,6 +18,18 @@ #include "modules/rangeproof/pedersen.h" #include "modules/rangeproof/borromean.h" +static size_t secp256k1_borromean_sz_closure_secidx_call(const secp256k1_borromean_sz_closure* self, size_t index) { + return (self->input >> (index * 2)) & 3; +} + +/** Create a sz_closure for the secret index within each ring */ +static secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_secidx(uint64_t value) { + secp256k1_borromean_sz_closure ret; + ret.input = value; + ret.call = secp256k1_borromean_sz_closure_secidx_call; + return ret; +} + SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { secp256k1_gej base; @@ -111,12 +123,11 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, return ret; } -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, +SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rings, size_t *rsizes, size_t *npub, secp256k1_borromean_sz_closure* secidx_closure, uint64_t *min_value, int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) { size_t i; *rings = 1; rsizes[0] = 1; - secidx[0] = 0; *scale = 1; *mantissa = 0; *npub = 0; @@ -170,16 +181,17 @@ SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rin 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? */ + *secidx_closure = secp256k1_borromean_sz_closure_secidx(*v); } else { /* A proof for an exact value. */ *exp = 0; *min_value = value; *v = 0; *npub = 2; + *secidx_closure = secp256k1_borromean_sz_closure_const(0); } VERIFY_CHECK(*v * *scale + *min_value == value); VERIFY_CHECK(*rings > 0); @@ -207,7 +219,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul int mantissa; /* Number of bits proven in the blinded value. */ 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. */ + secp256k1_borromean_sz_closure secidx_closure; size_t len; /* Number of bytes used so far. */ size_t i; int overflow; @@ -216,7 +228,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul 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)) { + if (!secp256k1_range_proveparams(&v, &rings, rsizes, &npub, &secidx_closure, &min_value, &mantissa, &scale, &exp, &min_bits, value)) { return 0; } proof[len] = (rsizes[0] > 1 ? (64 | exp) : 0) | (min_value ? 32 : 0); @@ -259,7 +271,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul size_t idx; /* Value encoding sidechannel. */ idx = rsizes[rings - 1] - 1; - idx -= secidx[rings - 1] == idx; + idx -= secidx_closure.call(&secidx_closure, 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; @@ -272,9 +284,10 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } memset(prep, 0, 4096); for (i = 0; i < rings; i++) { + size_t secidx_i = secidx_closure.call(&secidx_closure, 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]]); + 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 @@ -295,8 +308,9 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } npub = 0; for (i = 0; i < rings; i++) { + uint64_t secidx_i = secidx_closure.call(&secidx_closure, 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(ecmult_gen_ctx, &pubs[npub], &sec[i], (secidx_i * scale) << (i*2), genp); if (secp256k1_gej_is_infinity(&pubs[npub])) { return 0; } @@ -321,7 +335,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); } secp256k1_sha256_finalize(&sha256_m, tmp); - if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { + if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, &secidx_closure, rings, tmp, 32)) { return 0; } len += 32; diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index b534f495d..0bb129ad6 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -312,6 +312,19 @@ static void test_pedersen(void) { CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); } +/* Hack size_t closure that will just index into a given array */ +static size_t secp256k1_borromean_sz_closure_test_call(const secp256k1_borromean_sz_closure* self, size_t index) { + const size_t* secidx = (const size_t*)self->input; + return secidx[index]; +} + +static secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_test(const size_t* secidx) { + secp256k1_borromean_sz_closure ret; + ret.input = (uint64_t) secidx; + ret.call = secp256k1_borromean_sz_closure_test_call; + return ret; +} + static void test_borromean(void) { unsigned char e0[32]; secp256k1_scalar s[64]; @@ -327,6 +340,7 @@ static void test_borromean(void) { size_t i; size_t j; int c; + secp256k1_borromean_sz_closure secidx_closure = secp256k1_borromean_sz_closure_test(secidx); secp256k1_testrand256_test(m); nrings = 1 + (secp256k1_testrand32()&7); c = 0; @@ -359,7 +373,7 @@ static void test_borromean(void) { } c += rsizes[i]; } - CHECK(secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, secidx, nrings, m, 32)); + CHECK(secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, &secidx_closure, nrings, m, 32)); CHECK(secp256k1_borromean_verify(NULL, e0, s, pubs, rsizes, nrings, m, 32)); i = secp256k1_testrand32() % c; secp256k1_scalar_negate(&s[i],&s[i]); diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index b8f145e5f..22fa8fd98 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -278,7 +278,6 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s 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; @@ -286,6 +285,7 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; unsigned char msg32[32]; + secp256k1_borromean_sz_closure secidx_closure; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); @@ -328,7 +328,6 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s /* Produce signature */ rsizes[0] = (int) n_used_pubkeys; - indices[0] = (int) ring_input_index; secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) { return 0; @@ -338,7 +337,8 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s * 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_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &nonce, &blinding_key, rsizes, indices, 1, msg32, 32) == 0) { + secidx_closure = secp256k1_borromean_sz_closure_const(ring_input_index); + if (secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &nonce, &blinding_key, rsizes, &secidx_closure, 1, msg32, 32) == 0) { return 0; } for (i = 0; i < n_used_pubkeys; i++) { diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 5ce780d45..6c0462cae 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -88,8 +88,9 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s } /* Actually sign */ if (ret) { + secp256k1_borromean_sz_closure secidx_closure = secp256k1_borromean_sz_closure_const(index); sig->n_keys = n_keys; - ret = secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &non, &sec, &n_keys, &index, 1, msg32, 32); + ret = secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &non, &sec, &n_keys, &secidx_closure, 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]); } From 1bde70575e39462939b5acfb5bb9069ce639168b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 16 Oct 2021 16:02:57 +0000 Subject: [PATCH 4/7] borromean: move k-value trickery into borromean_sign Reduces stack usage of rangeproof_sign by 1056 bytes, is a bit safer as it doesn't require the caller of borromean_sign to know which indices are going to be overwritten, is a net-negative code diff, and will simplify things for the next step of closure-ification. --- src/modules/rangeproof/borromean.h | 2 +- src/modules/rangeproof/borromean_impl.h | 12 ++++++++---- src/modules/rangeproof/rangeproof_impl.h | 9 +-------- src/modules/rangeproof/tests_impl.h | 7 +------ src/modules/surjection/main_impl.h | 8 +------- src/modules/whitelist/main_impl.h | 19 +++---------------- 6 files changed, 15 insertions(+), 42 deletions(-) diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h index 79204902a..917225ed4 100644 --- a/src/modules/rangeproof/borromean.h +++ b/src/modules/rangeproof/borromean.h @@ -20,7 +20,7 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e 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_gen_context *ecmult_gen_ctx, - unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec, + unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *sec, const size_t *rsizes, const secp256k1_borromean_sz_closure* secidx_closure, 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 637a9db94..97117962e 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -109,7 +109,7 @@ int secp256k1_borromean_verify(secp256k1_scalar *evalues, const unsigned char *e } int secp256k1_borromean_sign(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, + unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *sec, const size_t *rsizes, const secp256k1_borromean_sz_closure* secidx_closure, size_t nrings, const unsigned char *m, size_t mlen) { secp256k1_gej rgej; secp256k1_ge rge; @@ -125,7 +125,6 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, 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_closure != NULL); @@ -136,7 +135,8 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, for (i = 0; i < nrings; i++) { size_t secidx_i = secidx_closure->call(secidx_closure, i); VERIFY_CHECK(INT_MAX - count > rsizes[i]); - secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &k[i]); + /* We have been provided an s value that we will just overwrite, so use it as a nonce */ + secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &s[count + secidx_i]); secp256k1_ge_set_gej(&rge, &rgej); if (secp256k1_gej_is_infinity(&rgej)) { return 0; @@ -167,6 +167,10 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, count = 0; for (i = 0; i < nrings; i++) { size_t secidx_i = secidx_closure->call(secidx_closure, i); + /* We have been provided an s value that we will just overwrite, so use it as a nonce */ + secp256k1_scalar k = s[count + secidx_i]; + secp256k1_scalar_clear(&s[count + secidx_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); @@ -188,7 +192,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, } 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]); + secp256k1_scalar_add(&s[count + j], &s[count + j], &k); if (secp256k1_scalar_is_zero(&s[count + j])) { return 0; } diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 5df7ce54d..1a1a2119d 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -208,7 +208,6 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul 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]; @@ -283,12 +282,6 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul return 0; } memset(prep, 0, 4096); - for (i = 0; i < rings; i++) { - size_t secidx_i = secidx_closure.call(&secidx_closure, 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 @@ -335,7 +328,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); } secp256k1_sha256_finalize(&sha256_m, tmp); - if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, &secidx_closure, rings, tmp, 32)) { + if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, sec, rsizes, &secidx_closure, rings, tmp, 32)) { return 0; } len += 32; diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 0bb129ad6..d936e1675 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -329,7 +329,6 @@ static 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; @@ -352,13 +351,9 @@ static void test_borromean(void) { rsizes[i] = 1 + (secp256k1_testrand32()&7); secidx[i] = secp256k1_testrand32() % rsizes[i]; random_scalar_order(&sec[i]); - random_scalar_order(&k[i]); if(secp256k1_testrand32()&7) { sec[i] = one; } - if(secp256k1_testrand32()&7) { - k[i] = one; - } for (j = 0; j < rsizes[i]; j++) { random_scalar_order(&s[c + j]); if(secp256k1_testrand32()&7) { @@ -373,7 +368,7 @@ static void test_borromean(void) { } c += rsizes[i]; } - CHECK(secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, &secidx_closure, nrings, m, 32)); + CHECK(secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, e0, s, pubs, sec, rsizes, &secidx_closure, nrings, m, 32)); CHECK(secp256k1_borromean_verify(NULL, e0, s, pubs, rsizes, nrings, m, 32)); i = secp256k1_testrand32() % c; secp256k1_scalar_negate(&s[i],&s[i]); diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 22fa8fd98..0521ecd07 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -275,7 +275,6 @@ int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1 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 i; @@ -332,13 +331,8 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s 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]); secidx_closure = secp256k1_borromean_sz_closure_const(ring_input_index); - if (secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &nonce, &blinding_key, rsizes, &secidx_closure, 1, msg32, 32) == 0) { + if (secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &blinding_key, rsizes, &secidx_closure, 1, msg32, 32) == 0) { return 0; } for (i = 0; i < n_used_pubkeys; i++) { diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index 6c0462cae..00c841c1b 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -15,7 +15,7 @@ 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; + secp256k1_scalar sec; unsigned char msg32[32]; int ret; @@ -51,19 +51,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s 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; + int done = 1; for (i = 0; i < n_keys; i++) { msg32[0] ^= i + 1; msg32[1] ^= (i + 1) / 0x100; @@ -90,12 +78,11 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s if (ret) { secp256k1_borromean_sz_closure secidx_closure = secp256k1_borromean_sz_closure_const(index); sig->n_keys = n_keys; - ret = secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &non, &sec, &n_keys, &secidx_closure, 1, msg32, 32); + ret = secp256k1_borromean_sign(&ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &sec, &n_keys, &secidx_closure, 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; } From 6f04f553ebc98b9a1d0d11fdd23c435e3f1a3e49 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 19 Oct 2021 00:07:09 +0000 Subject: [PATCH 5/7] rangeproof: introduce rangeproof_header structure and use it for verification/rewind --- src/modules/rangeproof/main_impl.h | 12 +- src/modules/rangeproof/rangeproof.h | 58 ++++++ src/modules/rangeproof/rangeproof_impl.h | 244 ++++++++++++----------- 3 files changed, 196 insertions(+), 118 deletions(-) diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 5d62c53c0..e3ee103e1 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -232,17 +232,23 @@ int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, c 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) { + secp256k1_rangeproof_header header; size_t offset; - uint64_t scale; ARG_CHECK(exp != NULL); ARG_CHECK(mantissa != NULL); ARG_CHECK(min_value != NULL); ARG_CHECK(max_value != NULL); ARG_CHECK(proof != NULL); offset = 0; - scale = 1; (void)ctx; - return secp256k1_rangeproof_getheader_impl(&offset, exp, mantissa, &scale, min_value, max_value, proof, plen); + if (!secp256k1_rangeproof_header_parse(&header, &offset, proof, plen)) { + return 0; + } + *exp = header.exp; + *mantissa = header.mantissa; + *min_value = header.min_value; + *max_value = header.max_value; + return 1; } int secp256k1_rangeproof_rewind(const secp256k1_context* ctx, diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index fd8cf9505..3226d9e23 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -12,6 +12,64 @@ #include "ecmult.h" #include "ecmult_gen.h" +/** Structure representing data directly encoded into a rangeproof header + * + * A rangeproof is a proof, associated with a Pedersen commitment, that a + * "proven value" in is the range [0, 2^mantissa]. The committed value is + * related to the proven value by the contents of this header, as + * + * committed = min_value + 10^exp * proven + */ +typedef struct secp256k1_rangeproof_header { + /** Power of ten to multiply the proven value by, or -1 for an exact proof + * + * Encoded in the header. */ + int exp; + /** Number of bits used to represent the proven value + * + * Encoded in the header. */ + int mantissa; + /** 10 to the power of exp, or 1 for a proof of an exact value. + * + * Implied by `exp`, not encoded. */ + uint64_t scale; + /** Minimum value for the range (added to the proven value). + * + * Encoded in the header. */ + uint64_t min_value; + /** Maximum value for the range (min_value + 10^exp * 2^mantissa). + * + * Implied by `min_value`, `exp`, `mantissa`. Not encoded. */ + uint64_t max_value; + /** Number of rings to use in the underlying borromean ring signature + * + * Implied by `mantissa`. Not encoded. */ + size_t n_rings; + /** Number of public keys to use in the underlying borromean ring signature + * + * Implied by `mantissa`. Not encoded. */ + size_t n_pubs; + /** Number of keys in each ring + * + * Implied by `mantissa`. Not encoded. */ + size_t rsizes[32]; +} secp256k1_rangeproof_header; + +/** Parses out a rangeproof header from a rangeproof and fills in all fields + * + * Returns: 1 on success, 0 on failure + * Out: header: the parsed header + * offset: the number of bytes of `proof` that the header occupied + * In: proof: the proof to parse the header out of + * plen: the length of the proof + */ +static int secp256k1_rangeproof_header_parse( + secp256k1_rangeproof_header* header, + size_t* offset, + const unsigned char* proof, + size_t plen +); + static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 1a1a2119d..50f455a86 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -17,11 +17,12 @@ #include "modules/rangeproof/pedersen.h" #include "modules/rangeproof/borromean.h" +#include "modules/rangeproof/rangeproof.h" static size_t secp256k1_borromean_sz_closure_secidx_call(const secp256k1_borromean_sz_closure* self, size_t index) { return (self->input >> (index * 2)) & 3; } - + /** Create a sz_closure for the secret index within each ring */ static secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_secidx(uint64_t value) { secp256k1_borromean_sz_closure ret; @@ -29,7 +30,97 @@ static secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_secidx(uint ret.call = secp256k1_borromean_sz_closure_secidx_call; return ret; } - + +/** Takes a header with `exp`, `mantissa` and `min_value` set and fills in all other fields + * + * Returns 1 on success, 0 if the `max_value` field would exceed UINT64_MAX */ +static int secp256k1_rangeproof_header_expand(secp256k1_rangeproof_header* header) { + if (header->mantissa == 0) { + header->n_rings = 1; + header->n_pubs = 1; + header->max_value = 0; + header->rsizes[0] = 1; + } else { + size_t i; + + header->n_rings = header->mantissa / 2; + header->n_pubs = 4 * header->n_rings; + header->max_value = UINT64_MAX >> (64 - header->mantissa); + for (i = 0; i < header->n_rings; i++) { + header->rsizes[i] = 4; + } + + if (header->mantissa & 1) { + header->rsizes[header->n_rings] = 2; + header->n_pubs += 2; + header->n_rings++; + } + } + VERIFY_CHECK(header->n_rings <= 32); + + header->scale = 1; + if (header->exp > 0) { + int i; + for (i = 0; i < header->exp; i++) { + if (header->max_value > UINT64_MAX / 10) { + return 0; + } + header->max_value *= 10; + header->scale *= 10; + } + } + + if (header->max_value > UINT64_MAX - header->min_value) { + return 0; + } + header->max_value += header->min_value; + + return 1; +} + +static int secp256k1_rangeproof_header_parse( + secp256k1_rangeproof_header* header, + size_t* offset, + const unsigned char* proof, + size_t plen +) { + memset(header, 0, sizeof(*header)); + *offset = 0; + + if (plen < 65 || ((proof[0] & 128) != 0)) { + return 0; + } + /* Read `exp` and `mantissa` */ + if (proof[0] & 64) { + *offset += 1; + header->exp = proof[0] & 31; + if (header->exp > 18) { + return 0; + } + header->mantissa = proof[1] + 1; + if (header->mantissa > 64) { + return 0; + } + } else { + /* single-value proof */ + header->mantissa = 0; + header->exp = -1; + } + *offset += 1; + /* Read `min_value` */ + if (proof[0] & 32) { + size_t i; + for (i = 0; i < 8; i++) { + header->min_value = (header->min_value << 8) | proof[*offset + i]; + } + *offset += 8; + } else { + header->min_value = 0; + } + + return secp256k1_rangeproof_header_expand(header); +} + SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { secp256k1_gej base; @@ -369,7 +460,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, const secp256k1_ge *genp) { + secp256k1_rangeproof_header* header, 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; @@ -383,15 +474,12 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * size_t skip1; size_t skip2; size_t 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, genp); + secp256k1_rangeproof_genrand(sec, s_orig, prep, header->rsizes, header->n_rings, nonce, commit, proof, len, genp); *v = UINT64_MAX; secp256k1_scalar_clear(blind); - if (rings == 1 && rsizes[0] == 1) { + if (header->n_rings == 1 && header->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) { @@ -402,11 +490,10 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } return 1; } - npub = (rings - 1) << 2; for (j = 0; j < 2; j++) { size_t idx; /* Look for a value encoding in the last ring. */ - idx = npub + rsizes[rings - 1] - 1 - j; + idx = 4 * (header->n_rings - 1) + header->rsizes[header->n_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)) { @@ -428,8 +515,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } return 0; } - skip1 = rsizes[rings - 1] - 1 - j; - skip2 = ((value >> ((rings - 1) << 1)) & 3); + skip1 = header->rsizes[header->n_rings - 1] - 1 - j; + skip2 = ((value >> ((header->n_rings - 1) << 1)) & 3); if (skip1 == skip2) { /*Value is in wrong position.*/ if (mlen) { @@ -437,12 +524,12 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } return 0; } - skip1 += (rings - 1) << 2; - skip2 += (rings - 1) << 2; + skip1 += (header->n_rings - 1) << 2; + skip2 += (header->n_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]); + secp256k1_scalar_negate(&sec[header->n_rings - 1], &sec[header->n_rings - 1]); + secp256k1_scalar_add(blind, &stmp, &sec[header->n_rings - 1]); if (!m || !mlen || *mlen == 0) { if (mlen) { *mlen = 0; @@ -452,10 +539,10 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } offset = 0; npub = 0; - for (i = 0; i < rings; i++) { + for (i = 0; i < header->n_rings; i++) { size_t idx; idx = (value >> (i << 1)) & 3; - for (j = 0; j < rsizes[i]; j++) { + for (j = 0; j < header->rsizes[i]; j++) { if (npub == skip1 || npub == skip2) { npub++; continue; @@ -490,105 +577,31 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * return 1; } -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; - 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 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_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 unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp) { 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; - size_t rsizes[32]; + secp256k1_rangeproof_header header; int ret; size_t i; - int exp; - int mantissa; + size_t pub_idx; size_t offset; - size_t rings; - int overflow; - size_t npub; - int offset_post_header; - uint64_t scale; + size_t offset_post_header; 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)) { + if (!secp256k1_rangeproof_header_parse(&header, &offset, proof, plen)) { return 0; } + *min_value = header.min_value; + *max_value = header.max_value; 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)) { + if (plen - offset < 32 * (header.n_pubs + header.n_rings - 1) + 32 + ((header.n_rings + 6) >> 3)) { return 0; } secp256k1_sha256_initialize(&sha256_m); @@ -597,23 +610,24 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm 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++) { + for(i = 0; i < header.n_rings - 1; i++) { signs[i] = (proof[offset + ( i>> 3)] & (1 << (i & 7))) != 0; } - offset += (rings + 6) >> 3; - if ((rings - 1) & 7) { + offset += (header.n_rings + 6) >> 3; + if ((header.n_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) { + if ((proof[offset - 1] >> ((header.n_rings - 1) & 7)) != 0) { return 0; } } - npub = 0; + pub_idx = 0; secp256k1_gej_set_infinity(&accj); if (*min_value) { secp256k1_pedersen_ecmult_small(&accj, *min_value, genp); } - for(i = 0; i < rings - 1; i++) { + for(i = 0; i < header.n_rings - 1; i++) { secp256k1_fe fe; + secp256k1_ge c; if (!secp256k1_fe_set_b32(&fe, &proof[offset]) || !secp256k1_ge_set_xquad(&c, &fe)) { return 0; @@ -625,21 +639,21 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm * 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_set_ge(&pubs[pub_idx], &c); secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL); offset += 32; - npub += rsizes[i]; + pub_idx += header.rsizes[i]; } secp256k1_gej_neg(&accj, &accj); - secp256k1_gej_add_ge_var(&pubs[npub], &accj, commit, NULL); - if (secp256k1_gej_is_infinity(&pubs[npub])) { + secp256k1_gej_add_ge_var(&pubs[pub_idx], &accj, commit, NULL); + if (secp256k1_gej_is_infinity(&pubs[pub_idx])) { return 0; } - secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); - npub += rsizes[rings - 1]; + secp256k1_rangeproof_pub_expand(pubs, header.exp, header.rsizes, header.n_rings, genp); e0 = &proof[offset]; offset += 32; - for (i = 0; i < npub; i++) { + for (i = 0; i < header.n_pubs; i++) { + int overflow; secp256k1_scalar_set_b32(&s[i], &proof[offset], &overflow); if (overflow) { return 0; @@ -654,7 +668,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecm secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len); } secp256k1_sha256_finalize(&sha256_m, m); - ret = secp256k1_borromean_verify(nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); + ret = secp256k1_borromean_verify(nonce ? evalues : NULL, e0, s, pubs, header.rsizes, header.n_rings, m, 32); if (ret && nonce) { /* Given the nonce, try rewinding the witness to recover its initial state. */ secp256k1_scalar blind; @@ -662,12 +676,12 @@ 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, genp)) { + if (!secp256k1_rangeproof_rewind_inner(&blind, &vv, message_out, outlen, evalues, s, &header, 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; + vv = (vv * header.scale) + header.min_value; secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv, genp); if (secp256k1_gej_is_infinity(&accj)) { return 0; From 65d4c83da7c5508ab4c886cb103b4b1facea1871 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 19 Oct 2021 13:39:04 +0000 Subject: [PATCH 6/7] rangeproof: use rangeproof_header structure for proving --- src/modules/rangeproof/rangeproof.h | 17 +- src/modules/rangeproof/rangeproof_impl.h | 256 ++++++++++++----------- src/modules/rangeproof/tests_impl.h | 4 +- 3 files changed, 156 insertions(+), 121 deletions(-) diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h index 3226d9e23..dd0d71bf0 100644 --- a/src/modules/rangeproof/rangeproof.h +++ b/src/modules/rangeproof/rangeproof.h @@ -28,7 +28,7 @@ typedef struct secp256k1_rangeproof_header { /** Number of bits used to represent the proven value * * Encoded in the header. */ - int mantissa; + size_t mantissa; /** 10 to the power of exp, or 1 for a proof of an exact value. * * Implied by `exp`, not encoded. */ @@ -70,6 +70,21 @@ static int secp256k1_rangeproof_header_parse( size_t plen ); +/** Serializes out a rangeproof header which has at least `exp`, `min_value` and `mantissa` set + * + * Returns: 1 on success, 0 on failure + * Out: proof: the buffer to serialize into + * offset: the number of bytes of `proof` that the header occupies + * In: plen: the length of the proof buffer + * header: the header to serialize + */ +static int secp256k1_rangeproof_header_serialize( + unsigned char* proof, + size_t plen, + size_t* offset, + const secp256k1_rangeproof_header* header +); + static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce, uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index 50f455a86..a17cbbf8a 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -121,6 +121,105 @@ static int secp256k1_rangeproof_header_parse( return secp256k1_rangeproof_header_expand(header); } +static int secp256k1_rangeproof_header_set_for_value( + secp256k1_rangeproof_header* header, + uint64_t* proven_value, + const uint64_t min_value, + const uint64_t min_bits, + const int exp, + const uint64_t value +) { + memset(header, 0, sizeof(*header)); + *proven_value = 0; + + /* Sanity checks */ + if (min_value > value || min_bits > 64 || exp < -1 || exp > 18) { + return 0; + } + + /* Start by just using the user's requested values, then adjust them in + * various ways to make them compatible. This is probably not advisable + * from a privacy point-of-view but it's important to be compatible with + * the 2015-era API, and all of these issues will go away when we merge + * Bulletproofs. */ + header->exp = exp; + header->min_value = min_value; + header->mantissa = min_bits ? min_bits : 1; /* force mantissa to be nonzero */ + + /* Special-case single-value proofs */ + if (header->exp == -1) { + header->mantissa = 0; /* ignore user's min_bits */ + return secp256k1_rangeproof_header_expand(header); + } + + /* Deal with extreme values (copied directly from 2015 code) */ + 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. + */ + header->exp = 0; + } + { + /* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */ + uint64_t max = min_bits ? (UINT64_MAX >> (64 - min_bits)) : 0; + int i; + for (i = 0; i < header->exp && max <= UINT64_MAX / 10; i++) { + max *= 10; + } + header->exp = i; + } + + + /* Increase the mantissa from min_bits until it actually covers the proven value */ + if (!secp256k1_rangeproof_header_expand(header)) { + return 0; + } + *proven_value = (value - header->min_value) / header->scale; + while (header->mantissa < 64 && (*proven_value >> header->mantissa) > 0) { + header->mantissa++; + } + /* Fudge min_value so we don't lose the low-order digits of `value` */ + header->min_value = value - (*proven_value * header->scale); + + /* Increasing the mantissa will have increased the number of rings etc + * so re-expand the header to recompute the other derived values. */ + return secp256k1_rangeproof_header_expand(header); +} + +static int secp256k1_rangeproof_header_serialize( + unsigned char* proof, + size_t plen, + size_t* offset, + const secp256k1_rangeproof_header* header +) { + *offset = 0; + if (plen < 65) { + return 0; + } + + /* Write control byte */ + proof[0] = (header->exp >= 0 ? (64 | header->exp) : 0) | (header->min_value ? 32 : 0); + *offset += 1; + /* Write mantissa, for non-exact-value proofs */ + if (header->exp >= 0) { + VERIFY_CHECK(header->mantissa > 0 && header->mantissa <= 64); + proof[1] = header->mantissa - 1; + *offset += 1; + } + /* Write min_value, if present */ + if (header->min_value > 0) { + size_t i; + for (i = 0; i < 8; i++) { + proof[*offset + i] = (header->min_value >> ((7-i) * 8)) & 255; + } + *offset += 8; + } + + return 1; +} + SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { secp256k1_gej base; @@ -214,88 +313,12 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, return ret; } -SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rings, size_t *rsizes, size_t *npub, secp256k1_borromean_sz_closure* secidx_closure, uint64_t *min_value, - int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) { - size_t i; - *rings = 1; - rsizes[0] = 1; - *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; (int) i < *exp && (v2 <= UINT64_MAX / 10); i++) { - *v /= 10; - v2 *= 10; - } - *exp = i; - v2 = *v; - for (i = 0; (int) 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]; - } - VERIFY_CHECK(*mantissa>0); - VERIFY_CHECK((*v & ~(UINT64_MAX>>(64-*mantissa))) == 0); /* Did this get all the bits? */ - *secidx_closure = secp256k1_borromean_sz_closure_secidx(*v); - } else { - /* A proof for an exact value. */ - *exp = 0; - *min_value = value; - *v = 0; - *npub = 2; - *secidx_closure = secp256k1_borromean_sz_closure_const(0); - } - 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_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 *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp){ + secp256k1_rangeproof_header header; 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. */ @@ -305,44 +328,41 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul 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. */ - size_t rings; /* How many digits will our proof cover. */ - size_t rsizes[32]; /* How many possible values there are for each place. */ secp256k1_borromean_sz_closure secidx_closure; size_t len; /* Number of bytes used so far. */ size_t i; + size_t pub_idx; int overflow; - size_t npub; len = 0; - if (*plen < 65 || min_value > value || min_bits > 64 || min_bits < 0 || exp < -1 || exp > 18) { + if (*plen < 65) { return 0; } - if (!secp256k1_range_proveparams(&v, &rings, rsizes, &npub, &secidx_closure, &min_value, &mantissa, &scale, &exp, &min_bits, value)) { + + if (!secp256k1_rangeproof_header_set_for_value(&header, &v, min_value, min_bits, exp, 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; + if (header.exp >= 0) { + secidx_closure = secp256k1_borromean_sz_closure_secidx(v); + } else { + secidx_closure = secp256k1_borromean_sz_closure_const(0); } + + VERIFY_CHECK(v * header.scale + header.min_value == value); + VERIFY_CHECK(header.n_rings > 0); + VERIFY_CHECK(header.n_rings <= 32); + VERIFY_CHECK(header.n_pubs <= 128); + + secp256k1_rangeproof_header_serialize (proof, *plen, &len, &header); + /* 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)) { + if (msg_len > 0 && msg_len > 128 * (header.n_rings - 1)) { return 0; } /* Do we have enough room for the proof? */ - if (*plen - len < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + if (*plen - len < 32 * (header.n_pubs + header.n_rings - 1) + 32 + ((header.n_rings + 6) >> 3)) { return 0; } secp256k1_sha256_initialize(&sha256_m); @@ -357,19 +377,19 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul memcpy(prep, message, msg_len); } /* Note, the data corresponding to the blinding factors must be zero. */ - if (rsizes[rings - 1] > 1) { + if (header.rsizes[header.n_rings - 1] > 1) { size_t idx; /* Value encoding sidechannel. */ - idx = rsizes[rings - 1] - 1; - idx -= secidx_closure.call(&secidx_closure, rings - 1) == idx; - idx = ((rings - 1) * 4 + idx) * 32; + idx = header.rsizes[header.n_rings - 1] - 1; + idx -= secidx_closure.call(&secidx_closure, header.n_rings - 1) == idx; + idx = ((header.n_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, genp)) { + if (!secp256k1_rangeproof_genrand(sec, s, prep, header.rsizes, header.n_rings, nonce, commit, proof, len, genp)) { return 0; } memset(prep, 0, 4096); @@ -380,31 +400,31 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul * 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])) { + secp256k1_scalar_add(&sec[header.n_rings - 1], &sec[header.n_rings - 1], &stmp); + if (overflow || secp256k1_scalar_is_zero(&sec[header.n_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++) { + for (i = 0; i < (header.n_rings + 6) >> 3; i++) { signs[i] = 0; len++; } - npub = 0; - for (i = 0; i < rings; i++) { + pub_idx = 0; + for (i = 0; i < header.n_rings; i++) { uint64_t secidx_i = secidx_closure.call(&secidx_closure, i); /*OPT: Use the precomputed gen2 basis?*/ - secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], (secidx_i * scale) << (i*2), genp); - if (secp256k1_gej_is_infinity(&pubs[npub])) { + secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[pub_idx], &sec[i], (secidx_i * header.scale) << (i*2), genp); + if (secp256k1_gej_is_infinity(&pubs[pub_idx])) { return 0; } - if (i < rings - 1) { + if (i < header.n_rings - 1) { 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]); + /*OPT: do not compute full pubs[pub_idx] in ge form; we only need x */ + secp256k1_ge_set_gej_var(&c, &pubs[pub_idx]); secp256k1_rangeproof_serialize_point(tmpc, &c); quadness = tmpc[0]; secp256k1_sha256_write(&sha256_m, tmpc, 33); @@ -412,19 +432,19 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul memcpy(&proof[len], tmpc + 1, 32); len += 32; } - npub += rsizes[i]; + pub_idx += header.rsizes[i]; } - secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp); + secp256k1_rangeproof_pub_expand(pubs, header.exp, header.rsizes, header.n_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_gen_ctx, &proof[len], s, pubs, sec, rsizes, &secidx_closure, rings, tmp, 32)) { + if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, sec, header.rsizes, &secidx_closure, header.n_rings, tmp, 32)) { return 0; } len += 32; - for (i = 0; i < npub; i++) { - secp256k1_scalar_get_b32(&proof[len],&s[i]); + for (i = 0; i < pub_idx; i++) { + secp256k1_scalar_get_b32(&proof[len], &s[i]); len += 32; } VERIFY_CHECK(len <= *plen); diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index d936e1675..f6a9f82c9 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -314,13 +314,13 @@ static void test_pedersen(void) { /* Hack size_t closure that will just index into a given array */ static size_t secp256k1_borromean_sz_closure_test_call(const secp256k1_borromean_sz_closure* self, size_t index) { - const size_t* secidx = (const size_t*)self->input; + const size_t* secidx = (const size_t*) (size_t) self->input; return secidx[index]; } static secp256k1_borromean_sz_closure secp256k1_borromean_sz_closure_test(const size_t* secidx) { secp256k1_borromean_sz_closure ret; - ret.input = (uint64_t) secidx; + ret.input = (uint64_t) (size_t) secidx; ret.call = secp256k1_borromean_sz_closure_test_call; return ret; } From 6c94f4553ab998a35e235d6455580a3d22b21088 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 19 Oct 2021 14:29:39 +0000 Subject: [PATCH 7/7] rangeproof: refactor `rangeproof_genrand` into two functions This is purely to reduce the number of arguments being passed into one function at once. Also improves const-correctness. --- src/modules/rangeproof/rangeproof_impl.h | 59 ++++++++++++++++-------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index a17cbbf8a..78afcbb33 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -261,11 +261,32 @@ SECP256K1_INLINE static void secp256k1_rangeproof_serialize_point(unsigned char* secp256k1_fe_get_b32(data + 1, &pointx); } -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, const secp256k1_ge* genp) { - unsigned char tmp[32]; +SECP256K1_INLINE static void secp256k1_rangeproof_init_rng( + secp256k1_rfc6979_hmac_sha256* rng, + const unsigned char* nonce, + const secp256k1_ge* commit, + const unsigned char *proof, + const size_t len, + const secp256k1_ge* genp +) { unsigned char rngseed[32 + 33 + 33 + 10]; - secp256k1_rfc6979_hmac_sha256 rng; + VERIFY_CHECK(len <= 10); + + memcpy(rngseed, nonce, 32); + secp256k1_rangeproof_serialize_point(rngseed + 32, commit); + 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_INLINE static int secp256k1_rangeproof_genrand( + secp256k1_scalar *sec, + secp256k1_scalar *s, + unsigned char *message, + const secp256k1_rangeproof_header* header, + secp256k1_rfc6979_hmac_sha256* rng +) { + unsigned char tmp[32]; secp256k1_scalar acc; int overflow; int ret; @@ -273,20 +294,14 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, size_t j; int b; size_t npub; - VERIFY_CHECK(len <= 10); - memcpy(rngseed, nonce, 32); - secp256k1_rangeproof_serialize_point(rngseed + 32, commit); - 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; - for (i = 0; i < rings; i++) { - if (i < rings - 1) { - secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + for (i = 0; i < header->n_rings; i++) { + if (i < header->n_rings - 1) { + secp256k1_rfc6979_hmac_sha256_generate(rng, tmp, 32); do { - secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + 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]); @@ -294,8 +309,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_scalar_negate(&acc, &acc); sec[i] = acc; } - for (j = 0; j < rsizes[i]; j++) { - secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + for (j = 0; j < header->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]; @@ -307,7 +322,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, npub++; } } - secp256k1_rfc6979_hmac_sha256_finalize(&rng); + secp256k1_rfc6979_hmac_sha256_finalize(rng); secp256k1_scalar_clear(&acc); memset(tmp, 0, 32); return ret; @@ -328,6 +343,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul unsigned char tmp[33]; unsigned char *signs; /* Location of sign flags in the proof. */ uint64_t v; + secp256k1_rfc6979_hmac_sha256 genrand_rng; secp256k1_borromean_sz_closure secidx_closure; size_t len; /* Number of bytes used so far. */ size_t i; @@ -389,7 +405,8 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } prep[idx] = 128; } - if (!secp256k1_rangeproof_genrand(sec, s, prep, header.rsizes, header.n_rings, nonce, commit, proof, len, genp)) { + secp256k1_rangeproof_init_rng(&genrand_rng, nonce, commit, proof, len, genp); + if (!secp256k1_rangeproof_genrand(sec, s, prep, &header, &genrand_rng)) { return 0; } memset(prep, 0, 4096); @@ -481,6 +498,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, secp256k1_rangeproof_header* header, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge *genp) { + secp256k1_rfc6979_hmac_sha256 genrand_rng; secp256k1_scalar s_orig[128]; secp256k1_scalar sec[32]; secp256k1_scalar stmp; @@ -496,7 +514,10 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * size_t npub; memset(prep, 0, 4096); /* Reconstruct the provers random values. */ - secp256k1_rangeproof_genrand(sec, s_orig, prep, header->rsizes, header->n_rings, nonce, commit, proof, len, genp); + secp256k1_rangeproof_init_rng(&genrand_rng, nonce, commit, proof, len, genp); + if (!secp256k1_rangeproof_genrand(sec, s_orig, prep, header, &genrand_rng)) { + return 0; + } *v = UINT64_MAX; secp256k1_scalar_clear(blind); if (header->n_rings == 1 && header->rsizes[0] == 1) {