From a118acc02beac099f63f82d1e619c2bf33451b13 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 30 Apr 2019 22:46:05 +0000 Subject: [PATCH 1/4] surjectionproof: reduce stack usage --- src/modules/surjection/main_impl.h | 22 ++++------------------ src/modules/surjection/surjection_impl.h | 19 +++++++++++-------- src/modules/surjection/tests_impl.h | 4 +--- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 13e8f97e..d08712a0 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -272,8 +272,6 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s size_t ring_input_index = 0; secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge output; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); @@ -312,17 +310,12 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s return 0; } - secp256k1_generator_load(&output, ephemeral_output_tag); - for (i = 0; i < n_total_pubkeys; i++) { - secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]); - } - - secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, input_index, &ring_input_index); + secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index); /* Produce signature */ rsizes[0] = (int) n_used_pubkeys; indices[0] = (int) ring_input_index; - secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output); + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) { return 0; } @@ -347,8 +340,6 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 size_t n_used_pubkeys; secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_ge output; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); @@ -364,12 +355,7 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return 0; } - secp256k1_generator_load(&output, ephemeral_output_tag); - for (i = 0; i < n_total_pubkeys; i++) { - secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]); - } - - if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, 0, NULL) == 0) { + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 0, NULL) == 0) { return 0; } @@ -382,7 +368,7 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return 0; } } - secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output); + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32); } diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index f58026de..c90c5767 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -15,7 +15,7 @@ #include "scalar.h" #include "hash.h" -SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag) { +SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, const secp256k1_generator *ephemeral_input_tags, size_t n_input_tags, const secp256k1_generator *ephemeral_output_tag) { /* compute message */ size_t i; unsigned char pk_ser[33]; @@ -24,12 +24,12 @@ SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg3 secp256k1_sha256_initialize(&sha256_en); for (i = 0; i < n_input_tags; i++) { - secp256k1_eckey_pubkey_serialize(&ephemeral_input_tags[i], pk_ser, &pk_len, 1); - assert(pk_len == sizeof(pk_ser)); + pk_ser[0] = 2 + (ephemeral_input_tags[i].data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_input_tags[i].data[0], 32); secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); } - secp256k1_eckey_pubkey_serialize(ephemeral_output_tag, pk_ser, &pk_len, 1); - assert(pk_len == sizeof(pk_ser)); + pk_ser[0] = 2 + (ephemeral_output_tag->data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_output_tag->data[0], 32); secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); secp256k1_sha256_finalize(&sha256_en, msg32); } @@ -61,15 +61,18 @@ SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, si return 1; } -SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index) { +SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_generator *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_generator *output_tag, size_t input_index, size_t *ring_input_index) { size_t i; size_t j = 0; for (i = 0; i < n_input_tags; i++) { if (used_tags[i / 8] & (1 << (i % 8))) { secp256k1_ge tmpge; - secp256k1_ge_neg(&tmpge, &input_tags[i]); + secp256k1_generator_load(&tmpge, &input_tags[i]); + secp256k1_ge_neg(&tmpge, &tmpge); secp256k1_gej_set_ge(&pubkeys[j], &tmpge); - secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], output_tag, NULL); + + secp256k1_generator_load(&tmpge, output_tag); + secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], &tmpge, NULL); if (ring_input_index != NULL && input_index == i) { *ring_input_index = j; } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index c6f0c64b..49f36847 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -456,7 +456,6 @@ static void test_no_used_inputs_verify(void) { size_t n_ephemeral_input_tags = 1; secp256k1_generator ephemeral_output_tag; unsigned char blinding_key[32]; - secp256k1_ge inputs[1]; secp256k1_ge output; secp256k1_sha256 sha256_e0; int result; @@ -477,8 +476,7 @@ static void test_no_used_inputs_verify(void) { /* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */ secp256k1_generator_load(&output, &ephemeral_output_tag); - secp256k1_generator_load(&inputs[0], &ephemeral_input_tags[0]); - secp256k1_surjection_genmessage(proof.data, inputs, 1, &output); + secp256k1_surjection_genmessage(proof.data, ephemeral_input_tags, 1, &ephemeral_output_tag); secp256k1_sha256_initialize(&sha256_e0); secp256k1_sha256_write(&sha256_e0, proof.data, 32); secp256k1_sha256_finalize(&sha256_e0, proof.data); From d512d786535caebb08950b6295f9164a81383642 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 30 Apr 2019 23:04:08 +0000 Subject: [PATCH 2/4] surjectionproof: introduce `SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS` constant and set it to 16 --- include/secp256k1_surjectionproof.h | 10 +++++++--- src/modules/surjection/main_impl.h | 17 ++++++++++------- src/modules/surjection/surjection_impl.h | 2 +- src/modules/surjection/tests_impl.h | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index d9a38e40..4f83458d 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -11,6 +11,9 @@ extern "C" { /** Maximum number of inputs that may be given in a surjection proof */ #define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256 +/** Maximum number of inputs that may be used in a surjection proof */ +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 + /** Number of bytes a serialized surjection proof requires given the * number of inputs and the number of used inputs. */ @@ -19,7 +22,7 @@ extern "C" { /** Maximum number of bytes a serialized surjection proof requires. */ #define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX \ - SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) + SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) /** Opaque data structure that holds a parsed surjection proof * @@ -46,7 +49,7 @@ typedef struct { /** Bitmap of which input tags are used in the surjection proof */ unsigned char used_inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8]; /** Borromean signature: e0, scalars */ - unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)]; + unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)]; } secp256k1_surjectionproof; /** Parse a surjection proof @@ -143,7 +146,8 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size( * e.g. in a coinjoin with others' inputs, an ephemeral tag can be given; * this won't match the output tag but might be used in the anonymity set.) * n_input_tags: the number of entries in the fixed_input_tags array - * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set + * Must be <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS * fixed_output_tag: fixed output tag * max_n_iterations: the maximum number of iterations to do before giving up. Because the * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index d08712a0..832377bc 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -9,11 +9,12 @@ #include #include +#include "include/secp256k1_rangeproof.h" +#include "include/secp256k1_surjectionproof.h" + #include "modules/rangeproof/borromean.h" #include "modules/surjection/surjection_impl.h" #include "hash.h" -#include "include/secp256k1_rangeproof.h" -#include "include/secp256k1_surjectionproof.h" static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) { size_t ret = 0; @@ -270,8 +271,8 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s size_t n_total_pubkeys; size_t n_used_pubkeys; size_t ring_input_index = 0; - secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); @@ -310,7 +311,9 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s return 0; } - secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index); + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index) == 0) { + return 0; + } /* Produce signature */ rsizes[0] = (int) n_used_pubkeys; @@ -338,8 +341,8 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 size_t i; size_t n_total_pubkeys; size_t n_used_pubkeys; - secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; - secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS]; + secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; unsigned char msg32[32]; VERIFY_CHECK(ctx != NULL); diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index c90c5767..1a839ff8 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -77,7 +77,7 @@ SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_g *ring_input_index = j; } j++; - if (j > n_pubkeys) { + if (j > n_pubkeys || j > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { return 0; } } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 49f36847..dbab9576 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -671,7 +671,7 @@ void run_surjection_tests(void) { test_input_selection_distribution(); test_gen_verify(10, 3); - test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); test_no_used_inputs_verify(); test_bad_serialize(); test_bad_parse(); From 112edb2c6a28eb95423249ea4efd4609af4f95ce Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Thu, 30 May 2019 09:04:40 +0300 Subject: [PATCH 3/4] allow reducing surjection proof size (to lower generation stack usage) --- configure.ac | 10 ++++++++++ include/secp256k1_surjectionproof.h | 6 +++++- src/modules/surjection/main_impl.h | 17 ++++++++++++++++- src/modules/surjection/tests_impl.h | 3 +-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 2ba2d749..2874c79c 100644 --- a/configure.ac +++ b/configure.ac @@ -174,6 +174,11 @@ AC_ARG_ENABLE(module_surjectionproof, [enable_module_surjectionproof=$enableval], [enable_module_surjectionproof=no]) +AC_ARG_ENABLE(reduced_surjection_proof_size, + AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), + [use_reduced_surjection_proof_size=$enableval], + [use_reduced_surjection_proof_size=no]) + AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], [finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto]) @@ -568,6 +573,10 @@ if test x"$use_external_default_callbacks" = x"yes"; then AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used]) fi +if test x"$use_reduced_surjection_proof_size" = x"yes"; then + AC_DEFINE(USE_REDUCED_SURJECTION_PROOF_SIZE, 1, [Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, disabling parsing and verification]) +fi + if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([******]) AC_MSG_NOTICE([WARNING: experimental build]) @@ -652,6 +661,7 @@ AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) +AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = x"yes"]) dnl make sure nothing new is exported so that we don't break the cache PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index 4f83458d..ab7a4a9e 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -12,7 +12,7 @@ extern "C" { #define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256 /** Maximum number of inputs that may be used in a surjection proof */ -#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 256 /** Number of bytes a serialized surjection proof requires given the * number of inputs and the number of used inputs. @@ -52,6 +52,7 @@ typedef struct { unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)]; } secp256k1_surjectionproof; +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE /** Parse a surjection proof * * Returns: 1 when the proof could be parsed, 0 otherwise. @@ -73,6 +74,7 @@ SECP256K1_API int secp256k1_surjectionproof_parse( const unsigned char *input, size_t inputlen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +#endif /** Serialize a surjection proof * @@ -241,6 +243,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_generat ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE /** Surjection proof verification function * Returns 0: proof was invalid * 1: proof was valid @@ -258,6 +261,7 @@ SECP256K1_API int secp256k1_surjectionproof_verify( size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); +#endif #ifdef __cplusplus } diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index 832377bc..e76ebbf9 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -9,13 +9,21 @@ #include #include +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + #include "include/secp256k1_rangeproof.h" #include "include/secp256k1_surjectionproof.h" - #include "modules/rangeproof/borromean.h" #include "modules/surjection/surjection_impl.h" #include "hash.h" +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +#undef SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 +#endif + static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) { size_t ret = 0; size_t i; @@ -36,6 +44,9 @@ static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) return ret; } +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surjectionproof *proof, const unsigned char *input, size_t inputlen) { size_t n_inputs; size_t signature_len; @@ -214,6 +225,7 @@ int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1 ARG_CHECK(fixed_output_tag != NULL); ARG_CHECK(random_seed32 != NULL); ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + ARG_CHECK(n_input_tags_to_use <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); ARG_CHECK(n_input_tags_to_use <= n_input_tags); (void) ctx; @@ -336,6 +348,9 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s return 1; } +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag) { size_t rsizes[1]; /* array needed for borromean sig API */ size_t i; diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index dbab9576..4885a8e8 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -666,8 +666,7 @@ void run_surjection_tests(void) { test_input_selection(0); test_input_selection(1); test_input_selection(5); - test_input_selection(100); - test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); test_input_selection_distribution(); test_gen_verify(10, 3); From f7e4d08dae231fa4bec0cd2a785f78e985232d15 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 5 Jun 2019 11:15:11 +0200 Subject: [PATCH 4/4] surjection proof: Reject proofs with too many used inputs in reduced mode --- src/modules/surjection/main_impl.h | 5 +++++ src/modules/surjection/surjection_impl.h | 8 +++++--- src/modules/surjection/tests_impl.h | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index e76ebbf9..9614e5f7 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -373,6 +373,11 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return 0; } + /* Reject proofs with too many used inputs in USE_REDUCED_SURJECTION_PROOF_SIZE mode */ + if (n_used_pubkeys > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { + return 0; + } + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 0, NULL) == 0) { return 0; } diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index 1a839ff8..f3652567 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -69,6 +69,9 @@ SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_g secp256k1_ge tmpge; secp256k1_generator_load(&tmpge, &input_tags[i]); secp256k1_ge_neg(&tmpge, &tmpge); + + VERIFY_CHECK(j < SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); + VERIFY_CHECK(j < n_pubkeys); secp256k1_gej_set_ge(&pubkeys[j], &tmpge); secp256k1_generator_load(&tmpge, output_tag); @@ -77,11 +80,10 @@ SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_g *ring_input_index = j; } j++; - if (j > n_pubkeys || j > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { - return 0; - } } } + /* Caller needs to ensure that the number of set bits in used_tags (which we counted in j) equals n_pubkeys. */ + VERIFY_CHECK(j == n_pubkeys); return 1; } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index 4885a8e8..ca0b09a0 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -427,6 +427,7 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); CHECK(result == 1); + /* various fail cases */ if (n_inputs > 1) { result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]); @@ -441,6 +442,15 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { n_inputs += 1; } + for (i = 0; i < n_inputs; i++) { + /* flip bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); + CHECK(result == 0); + /* reset the bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + } + /* cleanup */ for (i = 0; i < n_inputs + 1; i++) { free(input_blinding_key[i]);