diff --git a/.gitignore b/.gitignore index 1ec887ded..3c0494d55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ bench +bench_bulletproofs bench_ecmult bench_generator bench_rangeproof bench_internal +bench_whitelist tests +example_musig exhaustive_tests precompute_ecmult_gen precompute_ecmult @@ -66,4 +69,4 @@ src/stamp-h1 libsecp256k1.pc contrib/gh-pr-create.sh -musig_example \ No newline at end of file +musig_example diff --git a/Makefile.am b/Makefile.am index 0b50f7a8b..722dfac3a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,10 @@ clean-precomp: EXTRA_DIST = autogen.sh SECURITY.md +if ENABLE_MODULE_BULLETPROOFS +include src/modules/bulletproofs/Makefile.am.include +endif + if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 431d35e46..74e8ab5ba 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -19,6 +19,7 @@ valgrind --version || true --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-ecdsa-s2c="$ECDSA_S2C" \ + --enable-module-bulletproofs="$BULLETPROOFS" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --enable-module-schnorrsig="$SCHNORRSIG" \ diff --git a/configure.ac b/configure.ac index 3ab35ed8c..0b0a9d78e 100644 --- a/configure.ac +++ b/configure.ac @@ -140,6 +140,11 @@ AC_ARG_ENABLE(examples, AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) +AC_ARG_ENABLE(module_bulletproofs, + AS_HELP_STRING([--enable-module-bulletproofs],[enable Bulletproofs module (experimental)]), + [], + [SECP_SET_DEFAULT([enable_module_bulletproofs], [no], [yes])]) + AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_ecdh], [no], [yes])]) @@ -417,6 +422,11 @@ if test x"$enable_module_rangeproof" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module]) fi +if test x"$enable_module_bulletproofs" = x"yes"; then + enable_module_generator=yes + AC_DEFINE(ENABLE_MODULE_BULLETPROOFS, 1, [Define this symbol to enable the Bulletproofs module]) +fi + if test x"$enable_module_generator" = x"yes"; then AC_DEFINE(ENABLE_MODULE_GENERATOR, 1, [Define this symbol to enable the NUMS generator module]) fi @@ -460,6 +470,9 @@ else # module (which automatically enables the module dependencies) we want to # print an error for the dependent module, not the module dependency. Hence, # we first test dependent modules. + if test x"$enable_module_bulletproofs" = x"yes"; then + AC_MSG_ERROR([Bulletproofs module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_whitelist" = x"yes"; then AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.]) fi @@ -502,6 +515,7 @@ AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"]) AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_BULLETPROOFS], [test x"$enable_module_bulletproofs" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) @@ -541,6 +555,7 @@ echo " module whitelist = $enable_module_whitelist" echo " module musig = $enable_module_musig" echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" +echo " module bulletproofs = $enable_module_bulletproofs" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/include/secp256k1_bulletproofs.h b/include/secp256k1_bulletproofs.h new file mode 100644 index 000000000..ce271646f --- /dev/null +++ b/include/secp256k1_bulletproofs.h @@ -0,0 +1,170 @@ +#ifndef _SECP256K1_BULLETPROOFS_ +# define _SECP256K1_BULLETPROOFS_ + +# include "secp256k1.h" +# include "secp256k1_generator.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** A function-like macro returning the size, in bytes, of an uncompressed + * Bulletproof proving that a value lies in the range [0, 2^(n_bits) - 1] + * + * A proof contains: 65 bytes for (A, S); 65 for (T1, T2); 64 for (tau_x, mu) + * followed by n_bits-many scalar pairs (l(i), r(i)). + */ +#define SECP256K1_BULLETPROOFS_UNCOMPRESSED_SIZE(n_bits) (65UL + 65 + 64 + (n_bits) * 64) + +/** Maximum size, in bytes, of an uncompressed rangeproof */ +extern const size_t SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH; + +/** The same value, as a C macro so it can be used as C89 array size */ +#define SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH_ \ + SECP256K1_BULLETPROOFS_UNCOMPRESSED_SIZE(64) + +/** Opaque data structure that holds the current state of an uncompressed + * Bulletproof proof generation. This data is not secret and does not need + * to be handled carefully, but neither does it have any meaning outside + * of the API functions that use it. + * + * Obviously you should not modify it or else you will get invalid proofs. + * + * Typical users do not need this structure. If you have more than a few + * hundred bytes of memory to spare create a proof in one shot with the + * TODO function instead. + */ +typedef struct { + unsigned char data[160]; +} secp256k1_bulletproofs_prover_context; + +/** Opaque structure representing a large number of NUMS generators */ +typedef struct secp256k1_bulletproofs_generators secp256k1_bulletproofs_generators; + +/** Allocates and initializes a list of NUMS generators + * Returns a list of generators, or NULL if allocation failed. + * Args: ctx: pointer to a context object + * n: number of NUMS generators to produce. Should be 128 to allow for + * 64-bit rangeproofs + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create( + const secp256k1_context* ctx, + size_t n +) SECP256K1_ARG_NONNULL(1); + +/** Allocates a list of generators from a static array + * Returns a list of generators, or NULL if allocation or parsing failed. + * Args: ctx: pointer to a context object + * In: data: data that came from `secp256k1_bulletproofs_generators_serialize` + * data_len: the length of the `data` buffer + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse( + const secp256k1_context* ctx, + const unsigned char* data, + size_t data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Serializes a list of generators to an array + * Returns 1 on success, 0 if the provided array was not large enough + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be serialized + * Out: data: pointer to buffer into which the generators will be serialized + * In/Out: data_len: the length of the `data` buffer. Should be initially set to at + * least 33 times the number of generators; will be set to 33 times + * the number of generators on successful return + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_bulletproofs_generators_serialize( + const secp256k1_context* ctx, + const secp256k1_bulletproofs_generators* gen, + unsigned char* data, + size_t *data_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Destroys a list of NUMS generators, freeing allocated memory + * Args: ctx: pointer to a context object + * gen: pointer to the generator set to be destroyed + * (can be NULL, in which case this function is a no-op) + */ +SECP256K1_API void secp256k1_bulletproofs_generators_destroy( + const secp256k1_context* ctx, + secp256k1_bulletproofs_generators* gen +) SECP256K1_ARG_NONNULL(1); + +/** Returns the serialized size of an uncompressed proof of a given number of bits + * Args: ctx: pointer to a context object + * In: n_bits: number of bits to prove (max 64, should usually be 64) + */ +SECP256K1_API size_t secp256k1_bulletproofs_rangeproof_uncompressed_proof_length( + const secp256k1_context* ctx, + size_t n_bits +) SECP256K1_ARG_NONNULL(1); + +/** Produces an uncompressed rangeproof. Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object + * gens: pointer to the generator set to use, which must have at least 2*n_bits generators + * asset_gen: pointer to the asset generator for the Pedersen/CT commitment + * Out: proof: pointer to a byte array to output the proof into + * In/Out: plen: pointer to the size of the above array; will be set to the actual size of + * the serialized proof. To learn this value in advance, to allocate a sufficient + * buffer, call `secp256k1_bulletproofs_rangeproof_uncompressed_proof_length` or + * use `SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH` + * In: n_bits: size of range being proven, in bits + * value: value committed in the Pedersen commitment + * min_value: minimum value of the range being proven + * commit: the Pedersen commitment being proven + * blind: blinding factor for the Pedersen commitment + * nonce: seed for the RNG used to generate random data during proving + * enc_data: 32-byte array of extra data which may be decrypted by a verifier who knows `nonce` + * (may be NULL if no data is to be encrypted) + * extra_commit: arbitrary extra data that the proof commits to (may be NULL if extra_commit_len is 0) + * extra_commit_len: length of the arbitrary extra data + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_bulletproofs_rangeproof_uncompressed_prove( + const secp256k1_context* ctx, + const secp256k1_bulletproofs_generators* gens, + const secp256k1_generator* asset_gen, + unsigned char* proof, + size_t* plen, + const size_t n_bits, + const uint64_t value, + const uint64_t min_value, + const secp256k1_pedersen_commitment* commit, + const unsigned char* blind, + const unsigned char* nonce, + const unsigned char* enc_data, + const unsigned char* extra_commit, + size_t extra_commit_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(11); + +/** Verifies an uncompressed rangeproof. Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object + * scratch: pointer to a scratch space + * gens: pointer to the generator set to use, which must have at least 2*n_bits generators + * asset_gen: pointer to the asset generator for the CT commitment + * In: proof: pointer to a byte array containing the serialized proof + * plen: length of the serialized proof + * min_value: minimum value of the range being proven + * commit: the Pedersen commitment being proven + * extra_commit: arbitrary extra data that the proof commits to (may be NULL if extra_commit_len is 0) + * extra_commit_len: length of the arbitrary extra data + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_bulletproofs_rangeproof_uncompressed_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_bulletproofs_generators* gens, + const secp256k1_generator* asset_gen, + const unsigned char* proof, + const size_t plen, + const uint64_t min_value, + const secp256k1_pedersen_commitment* commit, + const unsigned char* extra_commit, + size_t extra_commit_len +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(8); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h index cb55af91c..5479fc819 100644 --- a/include/secp256k1_generator.h +++ b/include/secp256k1_generator.h @@ -21,6 +21,11 @@ typedef struct { unsigned char data[64]; } secp256k1_generator; +/** + * Static constant generator 'h' maintained for historical reasons. + */ +SECP256K1_API extern const secp256k1_generator *secp256k1_generator_h; + /** Parse a 33-byte generator byte sequence into a generator object. * * Returns: 1 if input contains a valid generator. @@ -86,6 +91,149 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blin const unsigned char *blind32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Opaque data structure that stores a Pedersen commitment + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_pedersen_commitment_serialize and + * secp256k1_pedersen_commitment_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pedersen_commitment; + +/** Parse a 33-byte commitment into a commitment object. + * + * Returns: 1 if input contains a valid commitment. + * Args: ctx: a secp256k1 context object. + * Out: commit: pointer to the output commitment object + * In: input: pointer to a 33-byte serialized commitment key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( + const secp256k1_context* ctx, + secp256k1_pedersen_commitment* commit, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a commitment object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 33-byte byte array + * In: commit: a pointer to a secp256k1_pedersen_commitment containing an + * initialized commitment + */ +SECP256K1_API int secp256k1_pedersen_commitment_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_pedersen_commitment* commit +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Generate a pedersen commitment. + * Returns 1: Commitment successfully created. + * 0: Error. The blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127) or results in the + * point at infinity. Retry with a different factor. + * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) + * blind: pointer to a 32-byte blinding factor (cannot be NULL) + * value: unsigned 64-bit integer value to commit to. + * gen: additional generator 'h' + * Out: commit: pointer to the commitment (cannot be NULL) + * + * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( + const secp256k1_context* ctx, + secp256k1_pedersen_commitment *commit, + const unsigned char *blind, + uint64_t value, + const secp256k1_generator *gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Computes the sum of multiple positive and negative blinding factors. + * Returns 1: Sum successfully computed. + * 0: Error. A blinding factor is larger than the group order + * (probability for random 32 byte number < 2^-127). Retry with + * different factors. + * In: ctx: pointer to a context object (cannot be NULL) + * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) + * n: number of factors pointed to by blinds. + * npositive: how many of the initial factors should be treated with a positive sign. + * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( + const secp256k1_context* ctx, + unsigned char *blind_out, + const unsigned char * const *blinds, + size_t n, + size_t npositive +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a tally of pedersen commitments + * Returns 1: commitments successfully sum to zero. + * 0: Commitments do not sum to zero or other error. + * In: ctx: pointer to a context object (cannot be NULL) + * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) + * pcnt: number of commitments pointed to by commits. + * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) + * ncnt: number of commitments pointed to by ncommits. + * + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. + * + * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator + * A all blinding factors and all values must sum to zero. + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( + const secp256k1_context* ctx, + const secp256k1_pedersen_commitment * const* commits, + size_t pcnt, + const secp256k1_pedersen_commitment * const* ncommits, + size_t ncnt +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Sets the final Pedersen blinding factor correctly when the generators themselves + * have blinding factors. + * + * Consider a generator of the form A' = A + rG, where A is the "real" generator + * but A' is the generator provided to verifiers. Then a Pedersen commitment + * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') + * to sum to zero for multiple commitments, we take three arrays consisting of + * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s + * and `blinding_factor`s, and sum them. + * + * The function then subtracts the sum of all (vr + r') from the last element + * of the `blinding_factor` array, setting the total sum to zero. + * + * Returns 1: Blinding factor successfully computed. + * 0: Error. A blinding_factor or generator_blind are larger than the group + * order (probability for random 32 byte number < 2^-127). Retry with + * different values. + * + * In: ctx: pointer to a context object + * value: array of asset values, `v` in the above paragraph. + * May not be NULL unless `n_total` is 0. + * generator_blind: array of asset blinding factors, `r` in the above paragraph + * May not be NULL unless `n_total` is 0. + * n_total: Total size of the above arrays + * n_inputs: How many of the initial array elements represent commitments that + * will be negated in the final sum + * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph + * May not be NULL unless `n_total` is 0. + * the last value will be modified to get the total sum to zero. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( + const secp256k1_context* ctx, + const uint64_t *value, + const unsigned char* const* generator_blind, + unsigned char* const* blinding_factor, + size_t n_total, + size_t n_inputs +); + # ifdef __cplusplus } # endif diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 9bb014545..2d86ab067 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -19,154 +19,6 @@ extern "C" { */ #define SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN 3968 -/** Opaque data structure that stores a Pedersen commitment - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, or - * comparison, use secp256k1_pedersen_commitment_serialize and - * secp256k1_pedersen_commitment_parse. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_pedersen_commitment; - -/** - * Static constant generator 'h' maintained for historical reasons. - */ -SECP256K1_API extern const secp256k1_generator *secp256k1_generator_h; - -/** Parse a 33-byte commitment into a commitment object. - * - * Returns: 1 if input contains a valid commitment. - * Args: ctx: a secp256k1 context object. - * Out: commit: pointer to the output commitment object - * In: input: pointer to a 33-byte serialized commitment key - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( - const secp256k1_context* ctx, - secp256k1_pedersen_commitment* commit, - const unsigned char *input -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Serialize a commitment object into a serialized byte sequence. - * - * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 33-byte byte array - * In: commit: a pointer to a secp256k1_pedersen_commitment containing an - * initialized commitment - */ -SECP256K1_API int secp256k1_pedersen_commitment_serialize( - const secp256k1_context* ctx, - unsigned char *output, - const secp256k1_pedersen_commitment* commit -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Generate a pedersen commitment. - * Returns 1: Commitment successfully created. - * 0: Error. The blinding factor is larger than the group order - * (probability for random 32 byte number < 2^-127) or results in the - * point at infinity. Retry with a different factor. - * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) - * blind: pointer to a 32-byte blinding factor (cannot be NULL) - * value: unsigned 64-bit integer value to commit to. - * gen: additional generator 'h' - * Out: commit: pointer to the commitment (cannot be NULL) - * - * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( - const secp256k1_context* ctx, - secp256k1_pedersen_commitment *commit, - const unsigned char *blind, - uint64_t value, - const secp256k1_generator *gen -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); - -/** Computes the sum of multiple positive and negative blinding factors. - * Returns 1: Sum successfully computed. - * 0: Error. A blinding factor is larger than the group order - * (probability for random 32 byte number < 2^-127). Retry with - * different factors. - * In: ctx: pointer to a context object (cannot be NULL) - * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) - * n: number of factors pointed to by blinds. - * npositive: how many of the initial factors should be treated with a positive sign. - * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( - const secp256k1_context* ctx, - unsigned char *blind_out, - const unsigned char * const *blinds, - size_t n, - size_t npositive -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Verify a tally of pedersen commitments - * Returns 1: commitments successfully sum to zero. - * 0: Commitments do not sum to zero or other error. - * In: ctx: pointer to a context object (cannot be NULL) - * commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero) - * pcnt: number of commitments pointed to by commits. - * ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero) - * ncnt: number of commitments pointed to by ncommits. - * - * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0. - * - * A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor, - * while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator - * A all blinding factors and all values must sum to zero. - * - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( - const secp256k1_context* ctx, - const secp256k1_pedersen_commitment * const* commits, - size_t pcnt, - const secp256k1_pedersen_commitment * const* ncommits, - size_t ncnt -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); - -/** Sets the final Pedersen blinding factor correctly when the generators themselves - * have blinding factors. - * - * Consider a generator of the form A' = A + rG, where A is the "real" generator - * but A' is the generator provided to verifiers. Then a Pedersen commitment - * P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r') - * to sum to zero for multiple commitments, we take three arrays consisting of - * the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s - * and `blinding_factor`s, and sum them. - * - * The function then subtracts the sum of all (vr + r') from the last element - * of the `blinding_factor` array, setting the total sum to zero. - * - * Returns 1: Blinding factor successfully computed. - * 0: Error. A blinding_factor or generator_blind are larger than the group - * order (probability for random 32 byte number < 2^-127). Retry with - * different values. - * - * In: ctx: pointer to a context object - * value: array of asset values, `v` in the above paragraph. - * May not be NULL unless `n_total` is 0. - * generator_blind: array of asset blinding factors, `r` in the above paragraph - * May not be NULL unless `n_total` is 0. - * n_total: Total size of the above arrays - * n_inputs: How many of the initial array elements represent commitments that - * will be negated in the final sum - * In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph - * May not be NULL unless `n_total` is 0. - * the last value will be modified to get the total sum to zero. - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum( - const secp256k1_context* ctx, - const uint64_t *value, - const unsigned char* const* generator_blind, - unsigned char* const* blinding_factor, - size_t n_total, - size_t n_inputs -); - /** Verify a proof that a committed value is within a range. * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. * 0: Proof failed or other error. diff --git a/src/bench_bulletproofs.c b/src/bench_bulletproofs.c new file mode 100644 index 000000000..6c2f9b0af --- /dev/null +++ b/src/bench_bulletproofs.c @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_bulletproofs.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + secp256k1_bulletproofs_generators* gens; + secp256k1_scratch_space *scratch; + secp256k1_pedersen_commitment commit; + unsigned char proof[SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH_]; + unsigned char blind[32]; + unsigned char nonce[32]; + size_t proof_len; + size_t n_bits; + uint64_t min_value; + uint64_t value; +} bench_bulletproofs_data; + +static void bench_bulletproofs_setup(void* arg) { + bench_bulletproofs_data *data = (bench_bulletproofs_data*)arg; + + data->min_value = 99; + data->value = 100; + data->proof_len = sizeof(data->proof); + memset(data->blind, 0x77, 32); + memset(data->nonce, 0x0, 32); + CHECK(secp256k1_pedersen_commit(data->ctx, &data->commit, data->blind, data->value, secp256k1_generator_h)); + + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(data->ctx, data->gens, secp256k1_generator_h, data->proof, &data->proof_len, data->n_bits, data->value, data->min_value, &data->commit, data->blind, data->nonce, NULL, NULL, 0)); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(data->ctx, data->scratch, data->gens, secp256k1_generator_h, data->proof, data->proof_len, data->min_value, &data->commit, NULL, 0)); +} + +static void bench_bulletproofs_prove(void* arg, int iters) { + bench_bulletproofs_data *data = (bench_bulletproofs_data*)arg; + int i; + + for (i = 0; i < iters; i++) { + data->nonce[1] = i; + data->nonce[2] = i >> 8; + data->nonce[3] = i >> 16; + data->proof_len = sizeof(data->proof); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(data->ctx, data->gens, secp256k1_generator_h, data->proof, &data->proof_len, data->n_bits, data->value, data->min_value, &data->commit, data->blind, data->nonce, NULL, NULL, 0)); + } +} + +static void bench_bulletproofs_verify(void* arg, int iters) { + bench_bulletproofs_data *data = (bench_bulletproofs_data*)arg; + int i; + + for (i = 0; i < iters; i++) { + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(data->ctx, data->scratch, data->gens, secp256k1_generator_h, data->proof, data->proof_len, data->min_value, &data->commit, NULL, 0)); + } +} + +int main(void) { + bench_bulletproofs_data data; + int iters = get_iters(32); + int i; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + data.gens = secp256k1_bulletproofs_generators_create(data.ctx, 128); + data.scratch = secp256k1_scratch_space_create(data.ctx, 100 * 1024); + + for (i = 0; i <= 6; i++) { + char test_name[64]; + data.n_bits = 1ul << i; + sprintf(test_name, "bulletproofs_uncompressed_prove_%i", 1 << i); + run_benchmark(test_name, bench_bulletproofs_prove, bench_bulletproofs_setup, NULL, &data, 20, iters); + } + + for (i = 0; i <= 6; i++) { + char test_name[64]; + data.n_bits = 1ul << i; + sprintf(test_name, "bulletproofs_uncompressed_verif_%i", 1 << i); + run_benchmark(test_name, bench_bulletproofs_verify, bench_bulletproofs_setup, NULL, &data, 20, iters); + } + + secp256k1_scratch_space_destroy(data.ctx, data.scratch); + secp256k1_bulletproofs_generators_destroy(data.ctx, data.gens); + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/bench_internal.c b/src/bench_internal.c index 61400519e..898c7801c 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -98,6 +98,15 @@ void bench_scalar_negate(void* arg, int iters) { } } +void bench_scalar_sqr(void* arg, int iters) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]); + } +} + void bench_scalar_mul(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -376,6 +385,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters); diff --git a/src/modules/bulletproofs/Makefile.am.include b/src/modules/bulletproofs/Makefile.am.include new file mode 100644 index 000000000..9e0205c8a --- /dev/null +++ b/src/modules/bulletproofs/Makefile.am.include @@ -0,0 +1,12 @@ +include_HEADERS += include/secp256k1_bulletproofs.h +noinst_HEADERS += src/modules/bulletproofs/bulletproofs_util.h +noinst_HEADERS += src/modules/bulletproofs/main_impl.h +noinst_HEADERS += src/modules/bulletproofs/rangeproof_uncompressed_impl.h +noinst_HEADERS += src/modules/bulletproofs/tests_impl.h + +if USE_BENCHMARK +noinst_PROGRAMS += bench_bulletproofs +bench_bulletproofs_SOURCES = src/bench_bulletproofs.c +bench_bulletproofs_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_bulletproofs_LDFLAGS = -static +endif diff --git a/src/modules/bulletproofs/bulletproofs_util.h b/src/modules/bulletproofs/bulletproofs_util.h new file mode 100644 index 000000000..81a456e62 --- /dev/null +++ b/src/modules/bulletproofs/bulletproofs_util.h @@ -0,0 +1,177 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_UTIL_ +#define _SECP256K1_MODULE_BULLETPROOFS_UTIL_ + +#include "field.h" +#include "group.h" +#include "hash.h" + +/* Outputs a pair of points, amortizing the parity byte between them + * Assumes both points' coordinates have been normalized. + */ +static void secp256k1_bulletproofs_serialize_points(unsigned char *output, const secp256k1_ge *lpt, const secp256k1_ge *rpt) { + output[0] = (secp256k1_fe_is_odd(&lpt->y) << 1) + secp256k1_fe_is_odd(&rpt->y); + secp256k1_fe_get_b32(&output[1], &lpt->x); + secp256k1_fe_get_b32(&output[33], &rpt->x); +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("Bulletproofs/commitment")||SHA256("Bulletproofs/commitment"). */ +static void secp256k1_bulletproofs_sha256_tagged_commitment(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x50b6a879ul; + sha->s[1] = 0x0d9a7470ul; + sha->s[2] = 0xb4400e54ul; + sha->s[3] = 0x32d29ac7ul; + sha->s[4] = 0xde938408ul; + sha->s[5] = 0x923fc797ul; + sha->s[6] = 0x29f973a6ul; + sha->s[7] = 0xa25e1a1cul; + + sha->bytes = 64; +} + +/* little-endian encodes a uint64 */ +static void secp256k1_bulletproofs_le64(unsigned char *output, const uint64_t n) { + output[0] = n; + output[1] = n >> 8; + output[2] = n >> 16; + output[3] = n >> 24; + output[4] = n >> 32; + output[5] = n >> 40; + output[6] = n >> 48; + output[7] = n >> 56; +} + +static void secp256k1_bulletproofs_commit_initial_data( + unsigned char* commit, + const uint64_t n_bits, + const uint64_t min_value, + const secp256k1_ge* commitp, + const secp256k1_ge* asset_genp, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + unsigned char scratch[65]; + secp256k1_sha256 sha256; + secp256k1_bulletproofs_sha256_tagged_commitment(&sha256); + secp256k1_bulletproofs_le64(scratch, n_bits); + secp256k1_sha256_write(&sha256, scratch, 8); + secp256k1_bulletproofs_le64(scratch, min_value); + secp256k1_sha256_write(&sha256, scratch, 8); + secp256k1_bulletproofs_serialize_points(scratch, commitp, asset_genp); + secp256k1_sha256_write(&sha256, scratch, 65); + if (extra_commit != NULL) { + secp256k1_bulletproofs_le64(scratch, (uint64_t) extra_commit_len); + secp256k1_sha256_write(&sha256, scratch, 8); + secp256k1_sha256_write(&sha256, extra_commit, extra_commit_len); + } + secp256k1_sha256_finalize(&sha256, commit); +} + +/* Iterator which produces each l,r pair in succession with a minimum + * of scalar operations */ +typedef struct { + const unsigned char *nonce; + const secp256k1_scalar *y; + const secp256k1_scalar *z; + secp256k1_scalar yn; + secp256k1_scalar z22n; + uint64_t val_less_min; + size_t count; +} secp256k1_bulletproofs_bulletproofs_lrgen; + +static void secp256k1_bulletproofs_lrgen_init(secp256k1_bulletproofs_bulletproofs_lrgen *generator, const unsigned char *nonce, const secp256k1_scalar *y, const secp256k1_scalar *z, const uint64_t val_less_min) { + generator->nonce = nonce; + generator->y = y; + generator->z = z; + secp256k1_scalar_set_int(&generator->yn, 1); + generator->val_less_min = val_less_min; + generator->count = 0; +} + +/* Serializes yn and z22n as a 64-byte char array */ +static void secp256k1_bulletproofs_lrgen_serialize(unsigned char* output, const secp256k1_bulletproofs_bulletproofs_lrgen *generator) { + secp256k1_scalar_get_b32(&output[0], &generator->yn); + secp256k1_scalar_get_b32(&output[32], &generator->z22n); +} + +/* Deserializes yn and z22n from a 64-byte char array, otherwise the same as `secp256k1_bulletproofs_lrgen_init` */ +static int secp256k1_bulletproofs_lrgen_deserialize(secp256k1_bulletproofs_bulletproofs_lrgen *generator, const unsigned char *saved_state, size_t idx, const unsigned char *nonce, const secp256k1_scalar *y, const secp256k1_scalar *z, const uint64_t val_less_min) { + int overflow; + secp256k1_bulletproofs_lrgen_init(generator, nonce, y, z, val_less_min); + secp256k1_scalar_set_b32(&generator->yn, &saved_state[0], &overflow); + if (overflow || secp256k1_scalar_is_zero(&generator->yn)) { + return 0; + } + secp256k1_scalar_set_b32(&generator->z22n, &saved_state[32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&generator->z22n)) { + return 0; + } + generator->count = idx; + return 1; +} + +static void secp256k1_lr_generate(secp256k1_bulletproofs_bulletproofs_lrgen *generator, secp256k1_scalar *lout, secp256k1_scalar *rout, const secp256k1_scalar *x) { + const int al = (generator->val_less_min >> generator->count) & 1; + secp256k1_scalar sl, sr; + secp256k1_scalar negz; + + if (generator->count == 0) { + secp256k1_scalar_sqr(&generator->z22n, generator->z); + } + + secp256k1_scalar_chacha20(&sl, &sr, generator->nonce, generator->count + 2); + secp256k1_scalar_mul(&sl, &sl, x); + secp256k1_scalar_mul(&sr, &sr, x); + + secp256k1_scalar_set_int(lout, al); + secp256k1_scalar_negate(&negz, generator->z); + secp256k1_scalar_add(lout, lout, &negz); + secp256k1_scalar_add(lout, lout, &sl); + + secp256k1_scalar_set_int(rout, 1 - al); + secp256k1_scalar_negate(rout, rout); + secp256k1_scalar_add(rout, rout, generator->z); + secp256k1_scalar_add(rout, rout, &sr); + secp256k1_scalar_mul(rout, rout, &generator->yn); + secp256k1_scalar_add(rout, rout, &generator->z22n); + + generator->count++; + secp256k1_scalar_mul(&generator->yn, &generator->yn, generator->y); + secp256k1_scalar_add(&generator->z22n, &generator->z22n, &generator->z22n); +} + +/* Computes delta(y, z) as defined in eq (39) of the BPs paper and adds it to `inout` */ +static void secp256k1_bulletproofs_add_delta(secp256k1_scalar* inout, const secp256k1_scalar* y, const secp256k1_scalar* z, const secp256k1_scalar* z_sq, size_t n) { + size_t i; + secp256k1_scalar term = *z_sq; + + /* (z - z^2) */ + secp256k1_scalar_negate(&term, &term); + secp256k1_scalar_add(&term, &term, z); + /* (z - z^2) * (1^n dot y^n) */ + secp256k1_scalar_add(inout, inout, &term); + for (i = 1; i < n; i++) { /* iterate n-1 times */ + secp256k1_scalar_mul(&term, &term, y); + secp256k1_scalar_add(inout, inout, &term); + } + /* z^3 * (1^n dot 2^n) */ + if (n == 64) { + secp256k1_scalar_set_u64(&term, ~(uint64_t)0); + } else { + secp256k1_scalar_set_u64(&term, ((uint64_t)1 << n) - 1); + } + secp256k1_scalar_mul(&term, &term, z); + secp256k1_scalar_mul(&term, &term, z_sq); + secp256k1_scalar_negate(&term, &term); + /* Sum of the previous two expressions */ + secp256k1_scalar_add(inout, inout, &term); +} + +#endif diff --git a/src/modules/bulletproofs/main_impl.h b/src/modules/bulletproofs/main_impl.h new file mode 100644 index 000000000..15eb9764a --- /dev/null +++ b/src/modules/bulletproofs/main_impl.h @@ -0,0 +1,335 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_MAIN_ +#define _SECP256K1_MODULE_BULLETPROOFS_MAIN_ + +#include "include/secp256k1_bulletproofs.h" + +const size_t SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH + = SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH_; + +/* this type must be completed before any of the modules/bulletproofs includes */ +struct secp256k1_bulletproofs_generators { + size_t n; + /* n total generators; set n = 2*k to get G_i and H_i values for i in [1..k] */ + secp256k1_ge* gens; +}; + +#include "include/secp256k1_bulletproofs.h" +#include "include/secp256k1_generator.h" +#include "modules/bulletproofs/rangeproof_uncompressed_impl.h" +#include "modules/generator/main_impl.h" /* for generator_{load, save} and pedersen_commitment_load */ +#include "hash.h" +#include "util.h" + +secp256k1_bulletproofs_generators *secp256k1_bulletproofs_generators_create(const secp256k1_context *ctx, size_t n) { + secp256k1_bulletproofs_generators *ret; + secp256k1_rfc6979_hmac_sha256 rng; + unsigned char seed[64]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + ret->n = n; + + secp256k1_fe_get_b32(&seed[0], &secp256k1_ge_const_g.x); + secp256k1_fe_get_b32(&seed[32], &secp256k1_ge_const_g.y); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed, 64); + for (i = 0; i < n; i++) { + secp256k1_generator gen; + unsigned char tmp[32] = { 0 }; + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, tmp)); + secp256k1_generator_load(&ret->gens[i], &gen); + } + + return ret; +} + +secp256k1_bulletproofs_generators* secp256k1_bulletproofs_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { + size_t n = data_len / 33; + secp256k1_bulletproofs_generators* ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(data != NULL); + + if (data_len % 33 != 0) { + return NULL; + } + + ret = (secp256k1_bulletproofs_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); + if (ret == NULL) { + return NULL; + } + ret->n = n; + ret->gens = (secp256k1_ge*)checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens)); + if (ret->gens == NULL) { + free(ret); + return NULL; + } + + while (n--) { + secp256k1_generator gen; + if (!secp256k1_generator_parse(ctx, &gen, &data[33 * n])) { + free(ret->gens); + free(ret); + return NULL; + } + secp256k1_generator_load(&ret->gens[n], &gen); + } + return ret; +} + +int secp256k1_bulletproofs_generators_serialize(const secp256k1_context* ctx, const secp256k1_bulletproofs_generators* gens, unsigned char* data, size_t *data_len) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(data != NULL); + ARG_CHECK(data_len != NULL); + + memset(data, 0, *data_len); + if (*data_len < 33 * gens->n) { + return 0; + } + for (i = 0; i < gens->n; i++) { + secp256k1_generator gen; + secp256k1_generator_save(&gen, &gens->gens[i]); + secp256k1_generator_serialize(ctx, &data[33 * i], &gen); + } + + *data_len = 33 * gens->n; + return 1; +} + +void secp256k1_bulletproofs_generators_destroy(const secp256k1_context* ctx, secp256k1_bulletproofs_generators *gens) { + VERIFY_CHECK(ctx != NULL); + (void) ctx; + if (gens != NULL) { + free(gens->gens); + free(gens); + } +} + +size_t secp256k1_bulletproofs_rangeproof_uncompressed_proof_length(const secp256k1_context* ctx, size_t n_bits) { + VERIFY_CHECK(ctx != NULL); + if (n_bits > 64) { + return 0; + } + return SECP256K1_BULLETPROOFS_UNCOMPRESSED_SIZE(n_bits); +} + +int secp256k1_bulletproofs_rangeproof_uncompressed_prove( + const secp256k1_context* ctx, + const secp256k1_bulletproofs_generators* gens, + const secp256k1_generator* asset_gen, + unsigned char* proof, + size_t* plen, + const size_t n_bits, + const uint64_t value, + const uint64_t min_value, + const secp256k1_pedersen_commitment* commit, + const unsigned char* blind, + const unsigned char* nonce, + const unsigned char* enc_data, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + secp256k1_bulletproofs_prover_context prover_ctx; + secp256k1_ge commitp, asset_genp; + secp256k1_scalar blinds, enc_datas; + size_t i; + int overflow; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(gens != NULL); + ARG_CHECK(asset_gen != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(plen != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + + i = secp256k1_bulletproofs_rangeproof_uncompressed_proof_length(ctx, n_bits); + if (*plen >= i) { + *plen = i; + } else { + return 0; + } + + secp256k1_scalar_set_b32(&blinds, blind, &overflow); + if (overflow) { + return 0; + } + + if (enc_data == NULL) { + secp256k1_scalar_clear(&enc_datas); + } else { + secp256k1_scalar_set_b32(&enc_datas, enc_data, &overflow); + if (overflow) { + return 0; + } + } + + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&asset_genp, asset_gen); + + ret = ret && secp256k1_bulletproofs_rangeproof_uncompressed_prove_step0_impl( + &ctx->ecmult_gen_ctx, + &prover_ctx, + &proof[0], + n_bits, + value, + min_value, + &commitp, + &asset_genp, + gens, + nonce, + &enc_datas, + extra_commit, + extra_commit_len + ); + + ret = ret && secp256k1_bulletproofs_rangeproof_uncompressed_prove_step1_impl( + &ctx->ecmult_gen_ctx, + &prover_ctx, + &proof[65], + n_bits, + value, + min_value, + &asset_genp, + nonce + ); + + ret = ret && secp256k1_bulletproofs_rangeproof_uncompressed_prove_step2_impl( + &prover_ctx, + &proof[130], + nonce, + &blinds, + &enc_datas + ); + + for (i = 0; i < n_bits; i++) { + ret = ret && secp256k1_bulletproofs_rangeproof_uncompressed_prove_step3_impl( + &prover_ctx, + &proof[194 + 64 * i], + i, + value, + min_value, + nonce + ); + } + + return ret; +} + +int secp256k1_bulletproofs_rangeproof_uncompressed_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_bulletproofs_generators* gens, + const secp256k1_generator* asset_gen, + const unsigned char* proof, + const size_t plen, + const uint64_t min_value, + const secp256k1_pedersen_commitment* commit, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + unsigned char pk_buf[33]; + const size_t n_bits = (plen - 194) / 64; + secp256k1_ge commitp, asset_genp; + secp256k1_ge ap, sp; + secp256k1_ge t1p, t2p; + secp256k1_scalar l_dot_r; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(scratch != NULL); + ARG_CHECK(gens != NULL); + ARG_CHECK(asset_gen != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(extra_commit != NULL || extra_commit_len == 0); + + if (n_bits > 64 || plen < 194 || (plen - 194) % 64 != 0) { + return 0; + } + + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&asset_genp, asset_gen); + + pk_buf[0] = 2 | (proof[0] >> 1); + memcpy(&pk_buf[1], &proof[1], 32); + if (!secp256k1_eckey_pubkey_parse(&ap, pk_buf, sizeof(pk_buf))) { + return 0; + } + pk_buf[0] = 2 | (proof[0] & 1); + memcpy(&pk_buf[1], &proof[33], 32); + if (!secp256k1_eckey_pubkey_parse(&sp, pk_buf, sizeof(pk_buf))) { + return 0; + } + + pk_buf[0] = 2 | (proof[65] >> 1); + memcpy(&pk_buf[1], &proof[66], 32); + if (!secp256k1_eckey_pubkey_parse(&t1p, pk_buf, sizeof(pk_buf))) { + return 0; + } + pk_buf[0] = 2 | (proof[65] & 1); + memcpy(&pk_buf[1], &proof[98], 32); + if (!secp256k1_eckey_pubkey_parse(&t2p, pk_buf, sizeof(pk_buf))) { + return 0; + } + + secp256k1_scalar_clear(&l_dot_r); + for (i = 0; i < n_bits; i++) { + int overflow; + secp256k1_scalar l, r; + secp256k1_scalar_set_b32(&l, &proof[194 + 64 * i], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&r, &proof[194 + 64 * i + 32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&l_dot_r, &l_dot_r, &l); + } + + return secp256k1_bulletproofs_rangeproof_uncompressed_verify_impl( + ctx, + scratch, + proof, + &l_dot_r, + n_bits, + min_value, + &commitp, + &asset_genp, + &ap, + &sp, + &t1p, + &t2p, + gens, + extra_commit, + extra_commit_len + ); +} + +#endif diff --git a/src/modules/bulletproofs/rangeproof_uncompressed_impl.h b/src/modules/bulletproofs/rangeproof_uncompressed_impl.h new file mode 100644 index 000000000..a0f5fd3ac --- /dev/null +++ b/src/modules/bulletproofs/rangeproof_uncompressed_impl.h @@ -0,0 +1,616 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_RP_UNCOMPRESSED_ +#define _SECP256K1_MODULE_BULLETPROOFS_RP_UNCOMPRESSED_ + +#include "group.h" +#include "scalar.h" + +#include "modules/bulletproofs/bulletproofs_util.h" + +/* Prover context data: + * bytes 0-32: x (hash challenge) + * bytes 32-64: y (hash challenge) + * bytes 64-96: z (hash challenge) + * bytes 96-160: lr_generator data + */ + +/* Generators (paper -> implementation): + * g -> asset_gen + * h -> default secp256k1 generator + * *g*, *h* vectors -> gens + */ + +/* Step 0 of the proof. + * Uses the value but not the blinding factor. Takes the complete commitment, + * but only to put it into the hash. + * Outputs the points A and S, encoded in 65 bytes (one byte with A's parity + * in the LSB, S's parity in the second-LSB, 32-byte A.x, 32-byte S.x). + * Updates the state to include the hashes y and z. + */ +static int secp256k1_bulletproofs_rangeproof_uncompressed_prove_step0_impl( + const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + secp256k1_bulletproofs_prover_context* prover_ctx, + unsigned char* output, + const size_t n_bits, + const uint64_t value, + const uint64_t min_value, + const secp256k1_ge* commitp, + const secp256k1_ge* asset_genp, + const secp256k1_bulletproofs_generators* gens, + const unsigned char* nonce, + const secp256k1_scalar* enc_data, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + secp256k1_sha256 sha256; + unsigned char commit[32]; + secp256k1_scalar alpha, rho; + secp256k1_scalar tmp_l, tmp_r; + secp256k1_gej gej; + secp256k1_ge ge; + size_t i; + int overflow; + + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 65); + + /* Sanity checks */ + if (n_bits > 64) { + return 0; + } + if (gens->n < n_bits * 2) { + return 0; + } + if (value < min_value) { + return 0; + } + if (n_bits < 64 && (value - min_value) >= (1ull << n_bits)) { + return 0; + } + if (extra_commit_len > 0 && extra_commit == NULL) { + return 0; + } + + /* Commit to all input data: min value, pedersen commit, asset generator, extra_commit */ + secp256k1_bulletproofs_commit_initial_data(commit, n_bits, min_value, commitp, asset_genp, extra_commit, extra_commit_len); + + /* Compute alpha and rho, adding encrypted data to alpha (effectively adding + * it to mu, which is one of the scalars in the final proof) */ + secp256k1_scalar_chacha20(&alpha, &rho, nonce, 0); + secp256k1_scalar_add(&alpha, &alpha, enc_data); + + /* Compute and output A */ + secp256k1_ecmult_gen(ecmult_gen_ctx, &gej, &alpha); + for (i = 0; i < n_bits; i++) { + secp256k1_ge aterm = gens->gens[2 * i + 1]; + int al = !!((value - min_value) & (1ull << i)); + + secp256k1_ge_neg(&aterm, &aterm); + secp256k1_fe_cmov(&aterm.x, &gens->gens[2 * i].x, al); + secp256k1_fe_cmov(&aterm.y, &gens->gens[2 * i].y, al); + secp256k1_gej_add_ge(&gej, &gej, &aterm); + } + secp256k1_ge_set_gej(&ge, &gej); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_normalize_var(&ge.y); + output[0] = secp256k1_fe_is_odd(&ge.y) << 1; + secp256k1_fe_get_b32(&output[1], &ge.x); + + /* Compute and output S */ + secp256k1_ecmult_gen(ecmult_gen_ctx, &gej, &rho); + for (i = 0; i < n_bits; i++) { + secp256k1_ge sterm; + secp256k1_gej stermj; + + secp256k1_scalar_chacha20(&tmp_l, &tmp_r, nonce, i + 2); + + secp256k1_ecmult_const(&stermj, &gens->gens[2 * i], &tmp_l, 256); + secp256k1_ge_set_gej(&sterm, &stermj); + secp256k1_gej_add_ge(&gej, &gej, &sterm); + secp256k1_ecmult_const(&stermj, &gens->gens[2 * i + 1], &tmp_r, 256); + secp256k1_ge_set_gej(&sterm, &stermj); + secp256k1_gej_add_ge(&gej, &gej, &sterm); + } + secp256k1_ge_set_gej(&ge, &gej); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_normalize_var(&ge.y); + output[0] |= secp256k1_fe_is_odd(&ge.y); + secp256k1_fe_get_b32(&output[33], &ge.x); + + /* get challenges y and z, store them in the prover context */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, output, 65); + secp256k1_sha256_finalize(&sha256, &prover_ctx->data[32]); + secp256k1_scalar_set_b32(&tmp_l, &prover_ctx->data[32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&tmp_l)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 65); + return 0; + } + + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, &prover_ctx->data[32], 32); + secp256k1_sha256_finalize(&sha256, &prover_ctx->data[64]); + secp256k1_scalar_set_b32(&tmp_l, &prover_ctx->data[64], &overflow); + if (overflow || secp256k1_scalar_is_zero(&tmp_l)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 65); + return 0; + } + + /* Success */ + return 1; +} + +/* Step 1 of the proof. + * Does not use the Pedersen commitment at all. + * Outputs the points T1 and T2, encoded in 65 bytes (one byte with T1's parity + * in the LSB, T2's parity in the second-LSB, 32-byte T1.x, 32-byte T2.x). + * Updates the state to include the hash x. + */ +static int secp256k1_bulletproofs_rangeproof_uncompressed_prove_step1_impl( + const secp256k1_ecmult_gen_context* ecmult_gen_ctx, + secp256k1_bulletproofs_prover_context* prover_ctx, + unsigned char* output, + const size_t n_bits, + const uint64_t value, + const uint64_t min_value, + const secp256k1_ge* asset_genp, + const unsigned char* nonce +) { + secp256k1_bulletproofs_bulletproofs_lrgen lr_gen; + secp256k1_sha256 sha256; + secp256k1_scalar tau1, tau2; + secp256k1_scalar t0, t1, t2; + secp256k1_scalar y, z; + secp256k1_scalar tmps; + secp256k1_gej gej; + secp256k1_ge ge; + size_t i; + int overflow; + + memset(output, 0, 65); + + /* Unpack challenges y and z from step 0 */ + secp256k1_scalar_set_b32(&y, &prover_ctx->data[32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&y)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + return 0; + } + secp256k1_scalar_set_b32(&z, &prover_ctx->data[64], &overflow); + if (overflow || secp256k1_scalar_is_zero(&z)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 65); + return 0; + } + + /* Generate new blinding factors tau1 and tau2 */ + secp256k1_scalar_chacha20(&tau1, &tau2, nonce, 1); + + /* Compute coefficients t0, t1, t2 of the (l dot r) polynomial */ + /* t0 := l(0) dot r(0) */ + secp256k1_bulletproofs_lrgen_init(&lr_gen, nonce, &y, &z, value - min_value); + secp256k1_scalar_clear(&t0); + secp256k1_scalar_clear(&tmps); + for (i = 0; i < n_bits; i++) { + secp256k1_scalar l, r; + secp256k1_lr_generate(&lr_gen, &l, &r, &tmps); + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&t0, &t0, &l); + } + + /* A := t0 + t1 + t2 = l(1) dot r(1) */ + secp256k1_bulletproofs_lrgen_init(&lr_gen, nonce, &y, &z, value - min_value); + secp256k1_scalar_clear(&t1); + for (i = 0; i < n_bits; i++) { + secp256k1_scalar one; + secp256k1_scalar l, r; + secp256k1_scalar_set_int(&one, 1); + secp256k1_lr_generate(&lr_gen, &l, &r, &one); + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&t1, &t1, &l); + } + + /* B := t0 - t1 + t2 = l(-1) dot r(-1) */ + secp256k1_bulletproofs_lrgen_init(&lr_gen, nonce, &y, &z, value - min_value); + secp256k1_scalar_clear(&t2); + for (i = 0; i < n_bits; i++) { + secp256k1_scalar negone; + secp256k1_scalar l, r; + secp256k1_scalar_set_int(&negone, 1); + secp256k1_scalar_negate(&negone, &negone); + secp256k1_lr_generate(&lr_gen, &l, &r, &negone); + secp256k1_scalar_mul(&l, &l, &r); + secp256k1_scalar_add(&t2, &t2, &l); + } + + /* t1 currently contains A + * t2 currently contains B + * Set t1 := (A - B)/2 */ + secp256k1_scalar_set_int(&tmps, 2); + secp256k1_scalar_inverse_var(&tmps, &tmps); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&t1, &t1, &t2); + secp256k1_scalar_mul(&t1, &t1, &tmps); + + /* t2 currently contains -B + * Set t2 := -(-B + t0) + t1 */ + secp256k1_scalar_add(&t2, &t2, &t0); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&t2, &t2, &t1); + + /* Compute and output Ti := t_i*A + tau_i*G for i = 1 */ + secp256k1_ecmult_const(&gej, asset_genp, &t1, 256); + secp256k1_ge_set_gej(&ge, &gej); + secp256k1_ecmult_gen(ecmult_gen_ctx, &gej, &tau1); + secp256k1_gej_add_ge(&gej, &gej, &ge); + secp256k1_ge_set_gej(&ge, &gej); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_normalize_var(&ge.y); + output[0] = secp256k1_fe_is_odd(&ge.y) << 1; + secp256k1_fe_get_b32(&output[1], &ge.x); + + /* Compute and output Ti := t_i*A + tau_i*G for i = 2 */ + secp256k1_ecmult_const(&gej, asset_genp, &t2, 256); + secp256k1_ge_set_gej(&ge, &gej); + secp256k1_ecmult_gen(ecmult_gen_ctx, &gej, &tau2); + secp256k1_gej_add_ge(&gej, &gej, &ge); + secp256k1_ge_set_gej(&ge, &gej); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_normalize_var(&ge.y); + output[0] |= secp256k1_fe_is_odd(&ge.y); + secp256k1_fe_get_b32(&output[33], &ge.x); + + /* get challenge x */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, &prover_ctx->data[64], 32); /* z, which commits to all data from before this step */ + secp256k1_sha256_write(&sha256, output, 65); + secp256k1_sha256_finalize(&sha256, &prover_ctx->data[0]); + secp256k1_scalar_set_b32(&tmps, &prover_ctx->data[0], &overflow); + if (overflow || secp256k1_scalar_is_zero(&tmps)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 65); + return 0; + } + + /* Success */ + return 1; +} + +/* Step 2 of the proof. + * Only step to use the blinding factor of the commitment. + * Outputs the scalars tau_x and mu, encoded in 64 bytes (tau_x first then mu). + * Does not update the state except to zero it out if something goes wrong + */ +static int secp256k1_bulletproofs_rangeproof_uncompressed_prove_step2_impl( + secp256k1_bulletproofs_prover_context* prover_ctx, + unsigned char* output, + const unsigned char* nonce, + const secp256k1_scalar* blind, + const secp256k1_scalar* enc_data +) { + secp256k1_scalar alpha, rho; + secp256k1_scalar tau1, tau2, taux, mu; + secp256k1_scalar x, z, xsq, zsq; + secp256k1_scalar tmps; + int overflow; + + /* Recompute alpha, rho, tau1, tau2 */ + secp256k1_scalar_chacha20(&alpha, &rho, nonce, 0); + secp256k1_scalar_add(&alpha, &alpha, enc_data); + secp256k1_scalar_chacha20(&tau1, &tau2, nonce, 1); + + /* Extract challenges x and z */ + secp256k1_scalar_set_b32(&x, &prover_ctx->data[0], &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 64); + return 0; + } + secp256k1_scalar_mul(&xsq, &x, &x); + + secp256k1_scalar_set_b32(&z, &prover_ctx->data[64], &overflow); + if (overflow || secp256k1_scalar_is_zero(&z)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 64); + return 0; + } + secp256k1_scalar_mul(&zsq, &z, &z); + + /* compute tau_x and mu */ + secp256k1_scalar_mul(&taux, &tau1, &x); + secp256k1_scalar_mul(&tmps, &tau2, &xsq); + secp256k1_scalar_add(&taux, &taux, &tmps); + + secp256k1_scalar_mul(&tmps, &zsq, blind); + secp256k1_scalar_add(&taux, &taux, &tmps); + + secp256k1_scalar_mul(&mu, &rho, &x); + secp256k1_scalar_add(&mu, &mu, &alpha); + + /* Negate taux and mu so the verifier doesn't have to */ + secp256k1_scalar_negate(&taux, &taux); + secp256k1_scalar_negate(&mu, &mu); + + /* Success */ + secp256k1_scalar_get_b32(&output[0], &taux); + secp256k1_scalar_get_b32(&output[32], &mu); + return 1; +} + +/* Step 3 of the proof. Should be called n_bits many times. + * Outputs the scalars l(i) and r(i), encoded in 64 bytes (l first then r). + * Does not update the state when idx > 0. + */ +static int secp256k1_bulletproofs_rangeproof_uncompressed_prove_step3_impl( + secp256k1_bulletproofs_prover_context* prover_ctx, + unsigned char* output, + size_t idx, + const uint64_t value, + const uint64_t min_value, + const unsigned char* nonce +) { + secp256k1_bulletproofs_bulletproofs_lrgen lr_gen; + secp256k1_scalar x, y, z, l, r; + int overflow; + + /* Extract challenges */ + secp256k1_scalar_set_b32(&x, &prover_ctx->data[0], &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 64); + return 0; + } + secp256k1_scalar_set_b32(&y, &prover_ctx->data[32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&y)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 64); + return 0; + } + secp256k1_scalar_set_b32(&z, &prover_ctx->data[64], &overflow); + if (overflow || secp256k1_scalar_is_zero(&z)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 64); + return 0; + } + + /* Restore lrgen state */ + if (idx == 0) { + secp256k1_bulletproofs_lrgen_init(&lr_gen, nonce, &y, &z, value - min_value); + } else { + if (!secp256k1_bulletproofs_lrgen_deserialize(&lr_gen, &prover_ctx->data[96], idx, nonce, &y, &z, value - min_value)) { + memset(prover_ctx->data, 0, sizeof(prover_ctx->data)); + memset(output, 0, 64); + return 0; + } + } + + /* Generate l(x) and r(x) */ + secp256k1_lr_generate(&lr_gen, &l, &r, &x); + + /* Success */ + secp256k1_bulletproofs_lrgen_serialize(&prover_ctx->data[96], &lr_gen); + secp256k1_scalar_get_b32(&output[0], &l); + secp256k1_scalar_get_b32(&output[32], &r); + return 1; +} + +typedef struct { + const secp256k1_ge *t1p; + const secp256k1_ge *t2p; + const secp256k1_ge *asset_genp; + const secp256k1_ge *commitp; + secp256k1_scalar neg_t; + secp256k1_scalar z_sq; + secp256k1_scalar delta_y_z__minus_t__minus_mv_z_sq; + secp256k1_scalar x; +} secp256k1_bulletproofs_eq65_data; + +/** Equation (65) is the sum of these 4 scalar-point multiplications, minus tau_x*G. */ +static int secp256k1_bulletproofs_eq65_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + const secp256k1_bulletproofs_eq65_data* ctx = (const secp256k1_bulletproofs_eq65_data*) data; + switch(idx) { + case 0: /* (delta - t - min*z^2) H */ + *pt = *ctx->asset_genp; + *sc = ctx->delta_y_z__minus_t__minus_mv_z_sq; + break; + case 1: /* z^2 V */ + *pt = *ctx->commitp; + *sc = ctx->z_sq; + break; + case 2: /* x T1 */ + *pt = *ctx->t1p; + *sc = ctx->x; + break; + case 3: /* x^2 T2 */ + *pt = *ctx->t2p; + secp256k1_scalar_sqr(sc, &ctx->x); + break; + default: + VERIFY_CHECK(!"should only call indexes 0-3"); + break; + } + return 1; +} + +/** Equation (67) is the sum of these 2 scalar-point multiplications, minus mu*G, plus a + * somewhat complicatied inner product computation.. */ +typedef struct { + const secp256k1_bulletproofs_generators* gens; + const unsigned char* proof; + const secp256k1_ge* ap; + const secp256k1_ge* sp; + secp256k1_scalar x; + secp256k1_scalar z; + secp256k1_scalar z_sq_2i; + secp256k1_scalar y_inv; + secp256k1_scalar y_neg_i; +} secp256k1_bulletproofs_eq67_data; + +static int secp256k1_bulletproofs_eq67_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_bulletproofs_eq67_data* ctx = (secp256k1_bulletproofs_eq67_data*) data; + secp256k1_scalar lr_i; + int overflow; + + switch(idx) { + case 0: /* A */ + *pt = *ctx->ap; + secp256k1_scalar_set_int(sc, 1); + return 1; + case 1: /* xS */ + *pt = *ctx->sp; + *sc = ctx->x; + return 1; + } + + *pt = ctx->gens->gens[idx - 2]; + secp256k1_scalar_set_b32(&lr_i, &ctx->proof[194 + 32 * (idx - 2)], &overflow); + VERIFY_CHECK(overflow == 0); /* checked in main_impl.h */ + /* (-z - l_i) G_i */ + if (idx % 2 == 0) { + secp256k1_scalar_add(sc, &ctx->z, &lr_i); + secp256k1_scalar_negate(sc, sc); + /* (z + (2^i*z^2 - r)/y^i) H_i */ + } else { + secp256k1_scalar_negate(sc, &lr_i); + secp256k1_scalar_add(sc, sc, &ctx->z_sq_2i); + secp256k1_scalar_mul(sc, sc, &ctx->y_neg_i); + secp256k1_scalar_add(sc, sc, &ctx->z); + + secp256k1_scalar_mul(&ctx->y_neg_i, &ctx->y_neg_i, &ctx->y_inv); + secp256k1_scalar_add(&ctx->z_sq_2i, &ctx->z_sq_2i, &ctx->z_sq_2i); + } + return 1; +} + +static int secp256k1_bulletproofs_rangeproof_uncompressed_verify_impl( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const unsigned char* proof, + const secp256k1_scalar* l_dot_r, + const uint64_t n_bits, + const uint64_t min_value, + const secp256k1_ge* commitp, + const secp256k1_ge* asset_genp, + const secp256k1_ge* ap, + const secp256k1_ge* sp, + const secp256k1_ge* t1p, + const secp256k1_ge* t2p, + const secp256k1_bulletproofs_generators* gens, + const unsigned char* extra_commit, + const size_t extra_commit_len +) { + secp256k1_sha256 sha256; + secp256k1_scalar x, y, z; + secp256k1_scalar neg_tau_x, neg_mu; + unsigned char commit[32]; + int overflow; + + /* Sanity checks */ + if (n_bits > 64) { + return 0; + } + if (gens->n < n_bits * 2) { + return 0; + } + if (extra_commit_len > 0 && extra_commit == NULL) { + return 0; + } + + /* Unpack tau_x and mu */ + secp256k1_scalar_set_b32(&neg_tau_x, &proof[130], &overflow); + if (overflow || secp256k1_scalar_is_zero(&neg_tau_x)) { + return 0; + } + secp256k1_scalar_set_b32(&neg_mu, &proof[162], &overflow); + if (overflow || secp256k1_scalar_is_zero(&neg_mu)) { + return 0; + } + + /* Commit to all input data: min value, pedersen commit, asset generator, extra_commit */ + secp256k1_bulletproofs_commit_initial_data(commit, n_bits, min_value, commitp, asset_genp, extra_commit, extra_commit_len); + /* Then y, z will be the hash of the first 65 bytes of the proof */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &proof[0], 65); + secp256k1_sha256_finalize(&sha256, commit); + secp256k1_scalar_set_b32(&y, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&y)) { + return 0; + } + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_finalize(&sha256, commit); + secp256k1_scalar_set_b32(&z, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&z)) { + return 0; + } + /* x additionally covers the next 65 bytes */ + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, commit, 32); + secp256k1_sha256_write(&sha256, &proof[65], 65); + secp256k1_sha256_finalize(&sha256, commit); + secp256k1_scalar_set_b32(&x, commit, &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + return 0; + } + + /* There are two equations to verify: the one in eq (65) and the one in + * eq (66-67). To ease review we'll implement both of them separately + * and then combine them in a followup commit. First (65). + */ + { + secp256k1_gej res_j; + secp256k1_bulletproofs_eq65_data eq65; + eq65.t1p = t1p; + eq65.t2p = t2p; + eq65.asset_genp = asset_genp; + eq65.commitp = commitp; + secp256k1_scalar_sqr(&eq65.z_sq, &z); + /**/ + secp256k1_scalar_set_u64(&eq65.delta_y_z__minus_t__minus_mv_z_sq, min_value); + secp256k1_scalar_mul(&eq65.delta_y_z__minus_t__minus_mv_z_sq, &eq65.delta_y_z__minus_t__minus_mv_z_sq, &eq65.z_sq); + secp256k1_scalar_add(&eq65.delta_y_z__minus_t__minus_mv_z_sq, &eq65.delta_y_z__minus_t__minus_mv_z_sq, l_dot_r); + secp256k1_scalar_negate(&eq65.delta_y_z__minus_t__minus_mv_z_sq, &eq65.delta_y_z__minus_t__minus_mv_z_sq); + secp256k1_bulletproofs_add_delta(&eq65.delta_y_z__minus_t__minus_mv_z_sq, &y, &z, &eq65.z_sq, n_bits); + /**/ + eq65.x = x; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res_j, &neg_tau_x, secp256k1_bulletproofs_eq65_cb, (void*) &eq65, 4)) { + return 0; + } + if (!secp256k1_gej_is_infinity(&res_j)) { + return 0; + } + } + /* Then (66-67) */ + { + secp256k1_gej res_j; + secp256k1_bulletproofs_eq67_data eq67; + eq67.gens = gens; + eq67.proof = proof; + eq67.ap = ap; + eq67.sp = sp; + eq67.x = x; + eq67.z = z; + secp256k1_scalar_sqr(&eq67.z_sq_2i, &z); + secp256k1_scalar_inverse_var(&eq67.y_inv, &y); + secp256k1_scalar_set_int(&eq67.y_neg_i, 1); + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res_j, &neg_mu, secp256k1_bulletproofs_eq67_cb, (void*) &eq67, 2 + 2 * n_bits)) { + return 0; + } + if (!secp256k1_gej_is_infinity(&res_j)) { + return 0; + } + } + return 1; +} + +#endif diff --git a/src/modules/bulletproofs/tests_impl.h b/src/modules/bulletproofs/tests_impl.h new file mode 100644 index 000000000..d2048330d --- /dev/null +++ b/src/modules/bulletproofs/tests_impl.h @@ -0,0 +1,231 @@ +/********************************************************************** + * Copyright (c) 2020 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_BULLETPROOFS_TEST_ +#define _SECP256K1_MODULE_BULLETPROOFS_TEST_ + +#include + +#include "include/secp256k1_bulletproofs.h" + +static void test_bulletproofs_generators_api(void) { + /* The BP generator API requires no precomp */ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + secp256k1_bulletproofs_generators *gens; + secp256k1_bulletproofs_generators *gens_orig; + unsigned char gens_ser[330]; + size_t len = sizeof(gens_ser); + + int32_t ecount = 0; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + + /* Create */ + gens = secp256k1_bulletproofs_generators_create(none, 10); + CHECK(gens != NULL && ecount == 0); + gens_orig = gens; /* Preserve for round-trip test */ + + /* Serialize */ + ecount = 0; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, NULL, gens_ser, &len)); + CHECK(ecount == 1); + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, NULL, &len)); + CHECK(ecount == 2); + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, NULL)); + CHECK(ecount == 3); + len = 0; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + len = sizeof(gens_ser) - 1; + CHECK(!secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + len = sizeof(gens_ser) + 1; /* len can be greater than minimum needed */ + CHECK(secp256k1_bulletproofs_generators_serialize(none, gens, gens_ser, &len)); + CHECK(len == sizeof(gens_ser)); + CHECK(ecount == 3); + + /* Parse */ + ecount = 0; + gens = secp256k1_bulletproofs_generators_parse(none, NULL, sizeof(gens_ser)); + CHECK(gens == NULL && ecount == 1); + /* Not a multiple of 33 */ + gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser) - 1); + CHECK(gens == NULL && ecount == 1); + gens = secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)); + CHECK(gens != NULL && ecount == 1); + /* Not valid generators */ + memset(gens_ser, 1, sizeof(gens_ser)); + CHECK(secp256k1_bulletproofs_generators_parse(none, gens_ser, sizeof(gens_ser)) == NULL); + CHECK(ecount == 1); + + /* Check that round-trip succeeded */ + CHECK(gens->n == gens_orig->n); + for (len = 0; len < gens->n; len++) { + ge_equals_ge(&gens->gens[len], &gens_orig->gens[len]); + } + + /* Destroy (we allow destroying a NULL context, it's just a noop. like free().) */ + ecount = 0; + secp256k1_bulletproofs_generators_destroy(none, NULL); + secp256k1_bulletproofs_generators_destroy(none, gens); + secp256k1_bulletproofs_generators_destroy(none, gens_orig); + CHECK(ecount == 0); + + secp256k1_context_destroy(none); +} + +static void test_bulletproofs_generators_fixed(void) { + secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, 3); + unsigned char gens_ser[330]; + const unsigned char fixed_first_3[99] = { + 0x0b, + 0xb3, 0x4d, 0x5f, 0xa6, 0xb8, 0xf3, 0xd1, 0x38, + 0x49, 0xce, 0x51, 0x91, 0xb7, 0xf6, 0x76, 0x18, + 0xfe, 0x5b, 0xd1, 0x2a, 0x88, 0xb2, 0x0e, 0xac, + 0x33, 0x89, 0x45, 0x66, 0x7f, 0xb3, 0x30, 0x56, + 0x0a, + 0x62, 0x86, 0x15, 0x16, 0x92, 0x42, 0x10, 0x9e, + 0x9e, 0x64, 0xd4, 0xcb, 0x28, 0x81, 0x60, 0x9c, + 0x24, 0xb9, 0x89, 0x51, 0x2a, 0xd9, 0x01, 0xae, + 0xff, 0x75, 0x64, 0x9c, 0x37, 0x5d, 0xbd, 0x79, + 0x0a, + 0xed, 0xe0, 0x6e, 0x07, 0x5e, 0x79, 0xd0, 0xf7, + 0x7b, 0x03, 0x3e, 0xb9, 0xa9, 0x21, 0xa4, 0x5b, + 0x99, 0xf3, 0x9b, 0xee, 0xfe, 0xa0, 0x37, 0xa2, + 0x1f, 0xe9, 0xd7, 0x4f, 0x95, 0x8b, 0x10, 0xe2, + }; + size_t len; + + len = 99; + CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + len = sizeof(gens_ser); + CHECK(secp256k1_bulletproofs_generators_serialize(ctx, gens, gens_ser, &len)); + CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); + + secp256k1_bulletproofs_generators_destroy(ctx, gens); +} + +static void test_bulletproofs_rangeproof_uncompressed_api(void) { + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_bulletproofs_generators *gens = secp256k1_bulletproofs_generators_create(ctx, 32); + secp256k1_scratch *scratch = secp256k1_scratch_space_create(ctx, 250); /* shouldn't need much */ + unsigned char proof[SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH_] = {0}; + size_t plen = sizeof(proof); + const uint64_t min_value = 99; + const uint64_t value = 100; + secp256k1_pedersen_commitment commit; + const unsigned char blind[32] = "help me! i'm bliiiiiiiiiiiiiiind"; + const unsigned char nonce[32] = "nonce? non ce n'est vrai amirite"; + const unsigned char enc_data[32] = "this data is encrypted: ********"; + /* Extra commit is a Joan Shelley lyric */ + const unsigned char extra_commit[] = "Shock of teal blue beneath clouds gathering, and the light of empty black on the waves at the horizon"; + const size_t extra_commit_len = sizeof(extra_commit); + + int ecount; + + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, value, secp256k1_generator_h)); + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + + /* size estimate */ + ecount = 0; + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_proof_length(none, 0) == 194); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_proof_length(none, 1) == 258); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_proof_length(none, 64) + == SECP256K1_BULLETPROOFS_RANGEPROOF_UNCOMPRESSED_MAX_LENGTH); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_proof_length(none, 65) == 0); + CHECK(ecount == 0); + + /* proving */ + ecount = 0; + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, NULL, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, NULL, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, NULL, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, NULL, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 0, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + plen = sizeof(proof); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 17, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + plen = sizeof(proof); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 1000, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + plen = sizeof(proof); + CHECK(ecount == 4); /* bad n_bits is not an API error */ + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 1); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, value + 1, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 4); /* bad value vs min_value is not an API error */ + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, NULL, blind, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, NULL, nonce, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, NULL, enc_data, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, NULL, extra_commit, extra_commit_len) == 1); + CHECK(ecount == 7); /* enc_data can be NULL */ + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, NULL, extra_commit_len) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, NULL, 0) == 1); + CHECK(ecount == 8); /* extra_commit can be NULL as long as its length is 0 */ + + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(none, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 1); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(vrfy, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 1); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_prove(sign, gens, secp256k1_generator_h, proof, &plen, 16, value, min_value, &commit, blind, nonce, enc_data, extra_commit, extra_commit_len) == 1); + CHECK(ecount == 8); + + /* verifying */ + ecount = 0; + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, NULL, gens, secp256k1_generator_h, proof, plen, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, NULL, secp256k1_generator_h, proof, plen, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, NULL, proof, plen, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, NULL, plen, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, 0, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen + 1, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen + 64, min_value, &commit, extra_commit, extra_commit_len) == 0); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen, min_value + 1, &commit, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 4); /* bad plen, min_value are not API errors */ + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen, min_value, NULL, extra_commit, extra_commit_len) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen, min_value, &commit, NULL, extra_commit_len) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen, min_value, &commit, NULL, 0) == 0); + CHECK(ecount == 6); /* zeroed out extra_commitment is not an API error */ + + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(none, scratch, gens, secp256k1_generator_h, proof, plen, min_value, &commit, NULL, 0) == 0); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(sign, scratch, gens, secp256k1_generator_h, proof, plen, min_value, &commit, NULL, 0) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_bulletproofs_rangeproof_uncompressed_verify(vrfy, scratch, gens, secp256k1_generator_h, proof, plen, min_value, &commit, extra_commit, extra_commit_len) == 1); + CHECK(ecount == 6); + + secp256k1_bulletproofs_generators_destroy(ctx, gens); + secp256k1_scratch_space_destroy(ctx, scratch); + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + +void run_bulletproofs_tests(void) { + test_bulletproofs_generators_api(); + test_bulletproofs_generators_fixed(); + test_bulletproofs_rangeproof_uncompressed_api(); +} + +#endif diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include index 69933e999..4119966cc 100644 --- a/src/modules/generator/Makefile.am.include +++ b/src/modules/generator/Makefile.am.include @@ -1,4 +1,6 @@ include_HEADERS += include/secp256k1_generator.h +noinst_HEADERS += src/modules/generator/pedersen.h +noinst_HEADERS += src/modules/generator/pedersen_impl.h noinst_HEADERS += src/modules/generator/main_impl.h noinst_HEADERS += src/modules/generator/tests_impl.h if USE_BENCHMARK diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h index c915c791f..25db19e74 100644 --- a/src/modules/generator/main_impl.h +++ b/src/modules/generator/main_impl.h @@ -14,6 +14,29 @@ #include "../../hash.h" #include "../../scalar.h" +#include "modules/generator/pedersen_impl.h" + +/** Alternative generator for secp256k1. + * This is the sha256 of 'g' after standard encoding (without compression), + * which happens to be a point on the curve. More precisely, the generator is + * derived by running the following script with the sage mathematics software. + + import hashlib + F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' + H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) + print('%x %x' % H.xy()) + */ +static const secp256k1_generator secp256k1_generator_h_internal = {{ + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, + 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, + 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 +}}; + +const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; + + static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { int succeed; succeed = secp256k1_fe_set_b32(&ge->x, &gen->data[0]); @@ -219,4 +242,202 @@ int secp256k1_generator_generate_blinded(const secp256k1_context* ctx, secp256k1 return secp256k1_generator_generate_internal(ctx, gen, key32, blind32); } +static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &commit->data[1]); + secp256k1_ge_set_xquad(ge, &fe); + if (commit->data[0] & 1) { + secp256k1_ge_neg(ge, ge); + } + secp256k1_fe_normalize(&ge->y); +} + +static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { + secp256k1_fe_normalize(&ge->x); + secp256k1_fe_get_b32(&commit->data[1], &ge->x); + commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y); +} + +int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(input != NULL); + (void) ctx; + + if ((input[0] & 0xFE) != 8 || + !secp256k1_fe_set_b32(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { + return 0; + } + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_pedersen_commitment_save(commit, &ge); + return 1; +} + +int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(commit != NULL); + + secp256k1_pedersen_commitment_load(&ge, commit); + + output[0] = 9 ^ secp256k1_fe_is_quad_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); + return 1; +} + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { + secp256k1_ge genp; + secp256k1_gej rj; + secp256k1_ge r; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(gen != NULL); + secp256k1_generator_load(&genp, gen); + secp256k1_scalar_set_b32(&sec, blind, &overflow); + if (!overflow) { + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); + if (!secp256k1_gej_is_infinity(&rj)) { + secp256k1_ge_set_gej(&r, &rj); + secp256k1_pedersen_commitment_save(commit, &r); + ret = 1; + } + secp256k1_gej_clear(&rj); + secp256k1_ge_clear(&r); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest + * negative, then calculates an additional blinding value that adds to zero. + */ +int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { + secp256k1_scalar acc; + secp256k1_scalar x; + size_t i; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(blind_out != NULL); + ARG_CHECK(blinds != NULL); + ARG_CHECK(npositive <= n); + (void) ctx; + secp256k1_scalar_set_int(&acc, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&x, blinds[i], &overflow); + if (overflow) { + return 0; + } + if (i >= npositive) { + secp256k1_scalar_negate(&x, &x); + } + secp256k1_scalar_add(&acc, &acc, &x); + } + secp256k1_scalar_get_b32(blind_out, &acc); + secp256k1_scalar_clear(&acc); + secp256k1_scalar_clear(&x); + return 1; +} + +/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { + secp256k1_gej accj; + secp256k1_ge add; + size_t i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(!pcnt || (commits != NULL)); + ARG_CHECK(!ncnt || (ncommits != NULL)); + (void) ctx; + secp256k1_gej_set_infinity(&accj); + for (i = 0; i < ncnt; i++) { + secp256k1_pedersen_commitment_load(&add, ncommits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + secp256k1_gej_neg(&accj, &accj); + for (i = 0; i < pcnt; i++) { + secp256k1_pedersen_commitment_load(&add, commits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + return secp256k1_gej_is_infinity(&accj); +} + +int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { + secp256k1_scalar sum; + secp256k1_scalar tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(n_total == 0 || value != NULL); + ARG_CHECK(n_total == 0 || generator_blind != NULL); + ARG_CHECK(n_total == 0 || blinding_factor != NULL); + ARG_CHECK(n_total > n_inputs); + (void) ctx; + + if (n_total == 0) { + return 1; + } + + secp256k1_scalar_set_int(&sum, 0); + secp256k1_scalar_set_int(&tmp, 0); + + /* Here, n_total > 0. Thus the loop runs at least once. + Thus we may use a do-while loop, which checks the loop + condition only at the end. + + The do-while loop helps GCC prove that the loop runs at least + once and suppresses a -Wmaybe-uninitialized warning. */ + i = 0; + do { + int overflow = 0; + secp256k1_scalar addend; + secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ + + secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ + + secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); + if (overflow == 1) { + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&addend); + secp256k1_scalar_clear(&sum); + return 0; + } + secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ + secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ + secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ + secp256k1_scalar_clear(&addend); + + i++; + } while (i < n_total); + + /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ + secp256k1_scalar_negate(&sum, &sum); + secp256k1_scalar_add(&tmp, &tmp, &sum); + secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); + + secp256k1_scalar_clear(&tmp); + secp256k1_scalar_clear(&sum); + return 1; +} + #endif diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/generator/pedersen.h similarity index 100% rename from src/modules/rangeproof/pedersen.h rename to src/modules/generator/pedersen.h diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/generator/pedersen_impl.h similarity index 100% rename from src/modules/rangeproof/pedersen_impl.h rename to src/modules/generator/pedersen_impl.h diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index 9f36f83de..b49ecae9a 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -223,11 +223,174 @@ void test_generator_fixed_vector(void) { CHECK(!secp256k1_generator_parse(ctx, &parse, result)); } +static void test_pedersen_api(void) { + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *sttc = secp256k1_context_clone(secp256k1_context_no_precomp); + secp256k1_pedersen_commitment commit; + const secp256k1_pedersen_commitment *commit_ptr = &commit; + unsigned char blind[32]; + unsigned char blind_out[32]; + const unsigned char *blind_ptr = blind; + unsigned char *blind_out_ptr = blind_out; + uint64_t val = secp256k1_testrand32(); + int32_t ecount = 0; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sttc, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); + + secp256k1_testrand256(blind); + CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(ecount == 0); + CHECK(secp256k1_pedersen_commit(sttc, &commit, blind, val, secp256k1_generator_h) == 0); + CHECK(ecount == 1); + + CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); + CHECK(ecount == 4); + + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0); + CHECK(ecount == 4); + CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); + CHECK(ecount == 7); + + CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0); + CHECK(ecount == 7); + CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); + CHECK(ecount == 9); + + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); + CHECK(ecount == 9); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); + CHECK(ecount == 14); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(sttc); +} + +static void test_pedersen(void) { + secp256k1_pedersen_commitment commits[19]; + const secp256k1_pedersen_commitment *cptr[19]; + unsigned char blinds[32*19]; + const unsigned char *bptr[19]; + secp256k1_scalar s; + uint64_t values[19]; + int64_t totalv; + int i; + int inputs; + int outputs; + int total; + inputs = (secp256k1_testrand32() & 7) + 1; + outputs = (secp256k1_testrand32() & 7) + 2; + total = inputs + outputs; + for (i = 0; i < 19; i++) { + cptr[i] = &commits[i]; + bptr[i] = &blinds[i * 32]; + } + totalv = 0; + for (i = 0; i < inputs; i++) { + values[i] = secp256k1_testrandi64(0, INT64_MAX - totalv); + totalv += values[i]; + } + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_testrandi64(0, totalv); + totalv -= values[i + inputs]; + } + values[total - 1] = totalv; + + for (i = 0; i < total - 1; i++) { + random_scalar_order(&s); + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); + for (i = 0; i < total; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); + if (inputs > 0 && values[0] > 0) { + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs)); + } + random_scalar_order(&s); + for (i = 0; i < 4; i++) { + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + values[0] = INT64_MAX; + values[1] = 0; + values[2] = 1; + for (i = 0; i < 3; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); +} + +void test_pedersen_commitment_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x09, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); + CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); + CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); + + result[0] = 0x08; + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); + result[0] = 0x0c; + CHECK(!secp256k1_pedersen_commitment_parse(ctx, &parse, result)); +} + + void run_generator_tests(void) { + int i; + test_shallue_van_de_woestijne(); test_generator_fixed_vector(); test_generator_api(); test_generator_generate(); + test_pedersen_api(); + test_pedersen_commitment_fixed_vector(); + for (i = 0; i < count / 2 + 1; i++) { + test_pedersen(); + } } #endif diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include index ff8b8d380..5272f2293 100644 --- a/src/modules/rangeproof/Makefile.am.include +++ b/src/modules/rangeproof/Makefile.am.include @@ -1,7 +1,5 @@ include_HEADERS += include/secp256k1_rangeproof.h noinst_HEADERS += src/modules/rangeproof/main_impl.h -noinst_HEADERS += src/modules/rangeproof/pedersen.h -noinst_HEADERS += src/modules/rangeproof/pedersen_impl.h noinst_HEADERS += src/modules/rangeproof/borromean.h noinst_HEADERS += src/modules/rangeproof/borromean_impl.h noinst_HEADERS += src/modules/rangeproof/rangeproof.h diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 432f4b959..b1af2a5ec 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -9,225 +9,9 @@ #include "../../group.h" -#include "pedersen_impl.h" -#include "borromean_impl.h" -#include "rangeproof_impl.h" - -/** Alternative generator for secp256k1. - * This is the sha256 of 'g' after standard encoding (without compression), - * which happens to be a point on the curve. More precisely, the generator is - * derived by running the following script with the sage mathematics software. - - import hashlib - F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) - G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' - H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) - print('%x %x' % H.xy()) - */ -static const secp256k1_generator secp256k1_generator_h_internal = {{ - 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, - 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, - 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, - 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 -}}; - -const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; - -static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { - secp256k1_fe fe; - secp256k1_fe_set_b32(&fe, &commit->data[1]); - secp256k1_ge_set_xquad(ge, &fe); - if (commit->data[0] & 1) { - secp256k1_ge_neg(ge, ge); - } -} - -static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { - secp256k1_fe_normalize(&ge->x); - secp256k1_fe_get_b32(&commit->data[1], &ge->x); - commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y); -} - -int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { - secp256k1_fe x; - secp256k1_ge ge; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(commit != NULL); - ARG_CHECK(input != NULL); - (void) ctx; - - if ((input[0] & 0xFE) != 8 || - !secp256k1_fe_set_b32(&x, &input[1]) || - !secp256k1_ge_set_xquad(&ge, &x)) { - return 0; - } - if (input[0] & 1) { - secp256k1_ge_neg(&ge, &ge); - } - secp256k1_pedersen_commitment_save(commit, &ge); - return 1; -} - -int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { - secp256k1_ge ge; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output != NULL); - ARG_CHECK(commit != NULL); - - secp256k1_pedersen_commitment_load(&ge, commit); - - output[0] = 9 ^ secp256k1_fe_is_quad_var(&ge.y); - secp256k1_fe_normalize_var(&ge.x); - secp256k1_fe_get_b32(&output[1], &ge.x); - return 1; -} - -/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ -int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { - secp256k1_ge genp; - secp256k1_gej rj; - secp256k1_ge r; - secp256k1_scalar sec; - int overflow; - int ret = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(commit != NULL); - ARG_CHECK(blind != NULL); - ARG_CHECK(gen != NULL); - secp256k1_generator_load(&genp, gen); - secp256k1_scalar_set_b32(&sec, blind, &overflow); - if (!overflow) { - secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); - if (!secp256k1_gej_is_infinity(&rj)) { - secp256k1_ge_set_gej(&r, &rj); - secp256k1_pedersen_commitment_save(commit, &r); - ret = 1; - } - secp256k1_gej_clear(&rj); - secp256k1_ge_clear(&r); - } - secp256k1_scalar_clear(&sec); - return ret; -} - -/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest - * negative, then calculates an additional blinding value that adds to zero. - */ -int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { - secp256k1_scalar acc; - secp256k1_scalar x; - size_t i; - int overflow; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(blind_out != NULL); - ARG_CHECK(blinds != NULL); - ARG_CHECK(npositive <= n); - (void) ctx; - secp256k1_scalar_set_int(&acc, 0); - for (i = 0; i < n; i++) { - secp256k1_scalar_set_b32(&x, blinds[i], &overflow); - if (overflow) { - return 0; - } - if (i >= npositive) { - secp256k1_scalar_negate(&x, &x); - } - secp256k1_scalar_add(&acc, &acc, &x); - } - secp256k1_scalar_get_b32(blind_out, &acc); - secp256k1_scalar_clear(&acc); - secp256k1_scalar_clear(&x); - return 1; -} - -/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ -int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { - secp256k1_gej accj; - secp256k1_ge add; - size_t i; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(!pcnt || (commits != NULL)); - ARG_CHECK(!ncnt || (ncommits != NULL)); - (void) ctx; - secp256k1_gej_set_infinity(&accj); - for (i = 0; i < ncnt; i++) { - secp256k1_pedersen_commitment_load(&add, ncommits[i]); - secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); - } - secp256k1_gej_neg(&accj, &accj); - for (i = 0; i < pcnt; i++) { - secp256k1_pedersen_commitment_load(&add, commits[i]); - secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); - } - return secp256k1_gej_is_infinity(&accj); -} - -int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { - secp256k1_scalar sum; - secp256k1_scalar tmp; - size_t i; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(n_total == 0 || value != NULL); - ARG_CHECK(n_total == 0 || generator_blind != NULL); - ARG_CHECK(n_total == 0 || blinding_factor != NULL); - ARG_CHECK(n_total > n_inputs); - (void) ctx; - - if (n_total == 0) { - return 1; - } - - secp256k1_scalar_set_int(&sum, 0); - - /* Here, n_total > 0. Thus the loop runs at least once. - Thus we may use a do-while loop, which checks the loop - condition only at the end. - - The do-while loop helps GCC prove that the loop runs at least - once and suppresses a -Wmaybe-uninitialized warning. */ - i = 0; - do { - int overflow = 0; - secp256k1_scalar addend; - secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */ - - secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow); - if (overflow == 1) { - secp256k1_scalar_clear(&tmp); - secp256k1_scalar_clear(&addend); - secp256k1_scalar_clear(&sum); - return 0; - } - secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */ - - secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow); - if (overflow == 1) { - secp256k1_scalar_clear(&tmp); - secp256k1_scalar_clear(&addend); - secp256k1_scalar_clear(&sum); - return 0; - } - secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */ - secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */ - secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */ - secp256k1_scalar_clear(&addend); - - i++; - } while (i < n_total); - - /* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */ - secp256k1_scalar_negate(&sum, &sum); - secp256k1_scalar_add(&tmp, &tmp, &sum); - secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp); - - secp256k1_scalar_clear(&tmp); - secp256k1_scalar_clear(&sum); - return 1; -} +#include "modules/generator/main_impl.h" +#include "modules/rangeproof/borromean_impl.h" +#include "modules/rangeproof/rangeproof_impl.h" int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa, uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) { diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index fbf32b291..dd79b6adb 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -13,9 +13,9 @@ #include "../../hash_impl.h" #include "../../util.h" -#include "pedersen.h" -#include "rangeproof.h" -#include "borromean.h" +#include "modules/generator/pedersen.h" +#include "modules/rangeproof/borromean.h" +#include "modules/rangeproof/rangeproof.h" SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs, int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) { diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 9c9207343..61d0492ea 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -16,66 +16,6 @@ #include "../../../include/secp256k1_rangeproof.h" -static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *sttc, const int32_t *ecount) { - secp256k1_pedersen_commitment commit; - const secp256k1_pedersen_commitment *commit_ptr = &commit; - unsigned char blind[32]; - unsigned char blind_out[32]; - const unsigned char *blind_ptr = blind; - unsigned char *blind_out_ptr = blind_out; - uint64_t val = secp256k1_testrand32(); - - secp256k1_testrand256(blind); - CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(*ecount == 0); - CHECK(secp256k1_pedersen_commit(sttc, &commit, blind, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 1); - - CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 2); - CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0); - CHECK(*ecount == 3); - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0); - CHECK(*ecount == 4); - - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0); - CHECK(*ecount == 4); - CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0); - CHECK(*ecount == 5); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0); - CHECK(*ecount == 6); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0); - CHECK(*ecount == 7); - CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0); - CHECK(*ecount == 7); - - CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0); - CHECK(*ecount == 7); - CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0); - CHECK(*ecount == 8); - CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0); - CHECK(*ecount == 9); - - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0); - CHECK(*ecount == 9); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0); - CHECK(*ecount == 10); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0); - CHECK(*ecount == 11); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0); - CHECK(*ecount == 12); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0); - CHECK(*ecount == 13); - CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0); - CHECK(*ecount == 14); -} - static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, const int32_t *ecount) { unsigned char proof[5134]; unsigned char blind[32]; @@ -253,8 +193,6 @@ static void test_api(void) { secp256k1_context_set_illegal_callback(sttc, counting_illegal_callback_fn, &ecount); for (i = 0; i < count; i++) { - ecount = 0; - test_pedersen_api(none, sign, vrfy, sttc, &ecount); ecount = 0; test_rangeproof_api(none, sign, vrfy, both, sttc, &ecount); } @@ -266,63 +204,6 @@ static void test_api(void) { secp256k1_context_destroy(sttc); } -static void test_pedersen(void) { - secp256k1_pedersen_commitment commits[19]; - const secp256k1_pedersen_commitment *cptr[19]; - unsigned char blinds[32*19]; - const unsigned char *bptr[19]; - secp256k1_scalar s; - uint64_t values[19]; - int64_t totalv; - int i; - int inputs; - int outputs; - int total; - inputs = (secp256k1_testrand32() & 7) + 1; - outputs = (secp256k1_testrand32() & 7) + 2; - total = inputs + outputs; - for (i = 0; i < 19; i++) { - cptr[i] = &commits[i]; - bptr[i] = &blinds[i * 32]; - } - totalv = 0; - for (i = 0; i < inputs; i++) { - values[i] = secp256k1_testrandi64(0, INT64_MAX - totalv); - totalv += values[i]; - } - for (i = 0; i < outputs - 1; i++) { - values[i + inputs] = secp256k1_testrandi64(0, totalv); - totalv -= values[i + inputs]; - } - values[total - 1] = totalv; - - for (i = 0; i < total - 1; i++) { - random_scalar_order(&s); - secp256k1_scalar_get_b32(&blinds[i * 32], &s); - } - CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); - for (i = 0; i < total; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); - } - CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs)); - if (inputs > 0 && values[0] > 0) { - CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs)); - } - random_scalar_order(&s); - for (i = 0; i < 4; i++) { - secp256k1_scalar_get_b32(&blinds[i * 32], &s); - } - values[0] = INT64_MAX; - values[1] = 0; - values[2] = 1; - for (i = 0; i < 3; i++) { - CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h)); - } - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1)); - CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1)); -} - static void test_borromean(void) { unsigned char e0[32]; secp256k1_scalar s[64]; @@ -1523,25 +1404,6 @@ void test_rangeproof_fixed_vectors_reproducible(void) { } } -void test_pedersen_commitment_fixed_vector(void) { - const unsigned char two_g[33] = { - 0x09, - 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, - 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 - }; - unsigned char result[33]; - secp256k1_pedersen_commitment parse; - - CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); - CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); - CHECK(secp256k1_memcmp_var(two_g, result, 33) == 0); - - result[0] = 0x08; - CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); - result[0] = 0x0c; - CHECK(!secp256k1_pedersen_commitment_parse(ctx, &parse, result)); -} - void run_rangeproof_tests(void) { int i; test_api(); @@ -1552,10 +1414,6 @@ void run_rangeproof_tests(void) { test_rangeproof_fixed_vectors(); test_rangeproof_fixed_vectors_reproducible(); - test_pedersen_commitment_fixed_vector(); - for (i = 0; i < count / 2 + 1; i++) { - test_pedersen(); - } for (i = 0; i < count / 2 + 1; i++) { test_borromean(); } diff --git a/src/scalar.h b/src/scalar.h index 36eb0db8d..227913cb5 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -65,6 +65,9 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, * the low bits that were shifted off */ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + /** Compute the inverse of a scalar (modulo the group order). */ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 6b0b44ed9..585a4b630 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -224,6 +224,28 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { VERIFY_CHECK(c1 >= th); \ } +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint64_t tl, th, th2, tl2; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ + c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ + th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2); /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ @@ -733,10 +755,148 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c #endif } +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast +#undef muladd2 #undef extract #undef extract_fast @@ -758,6 +918,12 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { return ret; } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { r1->d[0] = k->d[0]; r1->d[1] = k->d[1]; diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index acd5ef7de..6086f1ec2 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -306,6 +306,28 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { VERIFY_CHECK(c1 >= th); \ } +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint32_t tl, th, th2, tl2; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ + c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ + th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2); /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ @@ -569,10 +591,71 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, con l[15] = c0; } +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast +#undef muladd2 #undef extract #undef extract_fast @@ -598,6 +681,12 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { return ret; } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { r1->d[0] = k->d[0]; r1->d[1] = k->d[1]; diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index 47001fccb..aa75f8b0f 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -105,6 +105,10 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { return ret; } +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { *r1 = *a; *r2 = 0; diff --git a/src/secp256k1.c b/src/secp256k1.c index 6c686e0b6..857e9a76e 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -38,8 +38,6 @@ #ifdef ENABLE_MODULE_RANGEPROOF # include "include/secp256k1_rangeproof.h" -# include "modules/rangeproof/pedersen.h" -# include "modules/rangeproof/rangeproof.h" #endif #ifdef ENABLE_MODULE_ECDSA_S2C @@ -802,6 +800,10 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, return 1; } +#ifdef ENABLE_MODULE_BULLETPROOFS +# include "modules/bulletproofs/main_impl.h" +#endif + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index ca1ded47b..fc84b3cb1 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1898,6 +1898,14 @@ void scalar_test(void) { CHECK(secp256k1_scalar_eq(&r1, &r2)); } + { + /* Test square. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_sqr(&r1, &s1); + secp256k1_scalar_mul(&r2, &s1, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + { /* Test multiplicative identity. */ secp256k1_scalar r1, v1; @@ -2653,6 +2661,12 @@ void run_scalar_tests(void) { CHECK(!secp256k1_scalar_check_overflow(&zz)); CHECK(secp256k1_scalar_eq(&one, &zz)); } + secp256k1_scalar_mul(&z, &x, &x); + CHECK(!secp256k1_scalar_check_overflow(&z)); + secp256k1_scalar_sqr(&zz, &x); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&zz, &z)); + CHECK(secp256k1_scalar_eq(&r2, &zz)); } } } @@ -7118,6 +7132,10 @@ void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } +#ifdef ENABLE_MODULE_BULLETPROOFS +# include "modules/bulletproofs/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/tests_impl.h" #endif @@ -7438,6 +7456,10 @@ int main(int argc, char **argv) { /* EC key arithmetic test */ run_eckey_negate_test(); +#ifdef ENABLE_MODULE_BULLETPROOFS + run_bulletproofs_tests(); +#endif + #ifdef ENABLE_MODULE_ECDH /* ecdh tests */ run_ecdh_tests();