diff --git a/include/secp256k1_bppp.h b/include/secp256k1_bppp.h index d34c2e4d6..2a0d8b2b0 100644 --- a/include/secp256k1_bppp.h +++ b/include/secp256k1_bppp.h @@ -9,17 +9,18 @@ extern "C" { #include +#include "secp256k1_generator.h" + /** Opaque structure representing a large number of NUMS generators */ typedef struct secp256k1_bppp_generators secp256k1_bppp_generators; +/** Opaque structure representing a prover context used in bulletproofs++ prover */ +typedef struct secp256k1_bppp_rangeproof_prover_context secp256k1_bppp_rangeproof_prover_context; + /** Allocates and initializes a list of NUMS generators. * Returns a list of generators, or calls the error callback if the allocation fails. * Args: ctx: pointer to a context object * n: number of NUMS generators to produce. - * - * TODO: In a followup range-proof PR, this is would still require 16 + 8 = 24 NUMS - * points. We will later use G = H0(required for compatibility with pedersen_commitment DS) - * in a separate commit to make review easier. */ SECP256K1_API secp256k1_bppp_generators *secp256k1_bppp_generators_create( const secp256k1_context *ctx, @@ -43,11 +44,9 @@ SECP256K1_API secp256k1_bppp_generators *secp256k1_bppp_generators_parse( * 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 at least - * k = 33 * num_gens. Will be set to k on successful return - * - * TODO: For ease of review, this setting G = H0 is not included in this commit. We will - * add it in the follow-up rangeproof PR. + * In/Out: data_len: the length of the `data` buffer. Should be initially set to at + * least 33 times the number of generators plus one(33 * (num_gens - 1)). + * Upon success, data_len will be set to the (33 * (num_gens - 1)). */ SECP256K1_API int secp256k1_bppp_generators_serialize( const secp256k1_context *ctx, @@ -66,6 +65,97 @@ SECP256K1_API void secp256k1_bppp_generators_destroy( secp256k1_bppp_generators *gen ) SECP256K1_ARG_NONNULL(1); +/** Returns the serialized size of an bulletproofs++ proof of a given number + * of bits and the base. Both base and n_bits must be a power of two. The number + * of digits required to represent number of bits in the given base must also be + * a power of two. Specifically, all of n_bits, base and num_digits = (n_bits / log2(base)) + * must all be a power of two. + * Args: ctx: pointer to a context object + * Out: len: 0 if the parameters and num_digits (n_bits/log2(base)) are not a power of two + * length of the serialized proof otherwise + * In: n_bits: number of bits to prove (max 64, should usually be 64) + * base: base representation to be used in proof construction (max 256, recommended 16) + */ +SECP256K1_API size_t secp256k1_bppp_rangeproof_proof_length( + const secp256k1_context* ctx, + size_t n_bits, + size_t base +) SECP256K1_ARG_NONNULL(1); + +/** Produces a Bulletproofs++ rangeproof. Returns 1 on success, 0 on failure. + * Proof creation can only fail if the arguments are invalid. The documentation + * below specifies the constraints on inputs and arguments under which this API + * can fail. + * Args: ctx: pointer to a context object + * scratch: pointer to a scratch space + * gens: pointer to the generator set to use, which must have exactly + * `n = max(num_digits, base) + 7` generators, where num_digits is the number. + * 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_bppp_rangeproof_proof_length` + * In: n_bits: size of range being proven, in bits. Must be a power of two, + * and at most 64. + * base: base representation to be used in proof construction. Must be a power of two, + * value: value committed in the Pedersen commitment. Must be less + * than 2^n_bits. + * min_value: minimum value of the range being proven. Must be less than value + * commit: the Pedersen commitment being proven + * blind: blinding factor for the Pedersen commitment. Must be a 32 byte + * valid scalar within secp curve order. + * nonce: seed for the RNG used to generate random data during proving + * 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_bppp_rangeproof_prove( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_bppp_generators* gens, + const secp256k1_generator* asset_gen, + unsigned char* proof, + size_t* plen, + const size_t n_bits, + const size_t base, + 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* 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(11) SECP256K1_ARG_NONNULL(12) SECP256K1_ARG_NONNULL(13); + +/** Verifies an Bulletproofs++ 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 + * n_bits: size of range being proven, in bits. Must be a power of two, + * and at most 64. + * base: base representation to be used in proof construction. Must be a power of two, + * 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_bppp_rangeproof_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_bppp_generators* gens, + const secp256k1_generator* asset_gen, + const unsigned char* proof, + const size_t plen, + const uint64_t n_bits, + const uint64_t base, + 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(10); + # ifdef __cplusplus } # endif diff --git a/src/bench_bppp.c b/src/bench_bppp.c index 82f50a583..52464b0ce 100644 --- a/src/bench_bppp.c +++ b/src/bench_bppp.c @@ -7,32 +7,85 @@ #include #include "../include/secp256k1_bppp.h" +#include "../include/secp256k1.h" #include "util.h" #include "bench.h" +#define MAX_PROOF_SIZE 500 + typedef struct { secp256k1_context* ctx; + secp256k1_bppp_generators* gens; + secp256k1_scratch_space *scratch; + secp256k1_pedersen_commitment commit; + unsigned char *proofs; + unsigned char blind[32]; + unsigned char nonce[32]; + size_t proof_len; + size_t n_bits; + size_t base; + uint64_t min_value; + uint64_t value; } bench_bppp_data; static void bench_bppp_setup(void* arg) { - (void) arg; + bench_bppp_data *data = (bench_bppp_data*)arg; + + data->min_value = 0; + data->value = 100; + data->proof_len = MAX_PROOF_SIZE; + 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_bppp_rangeproof_prove(data->ctx, data->scratch, data->gens, secp256k1_generator_h, data->proofs, &data->proof_len, data->n_bits, data->base, data->value, 0, &data->commit, data->blind, data->nonce, NULL, 0)); + CHECK(secp256k1_bppp_rangeproof_verify(data->ctx, data->scratch, data->gens, secp256k1_generator_h, data->proofs, data->proof_len, data->n_bits, data->base, data->min_value, &data->commit, NULL, 0)); +} + +static void bench_bppp_prove(void* arg, int iters) { + bench_bppp_data *data = (bench_bppp_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 = MAX_PROOF_SIZE; + CHECK(secp256k1_bppp_rangeproof_prove(data->ctx, data->scratch, data->gens, secp256k1_generator_h, &data->proofs[i*MAX_PROOF_SIZE], &data->proof_len, data->n_bits, data->base, data->value, 0, &data->commit, data->blind, data->nonce, NULL, 0)); + } } -static void bench_bppp(void* arg, int iters) { +static void bench_bppp_verify(void* arg, int iters) { bench_bppp_data *data = (bench_bppp_data*)arg; + int i; - (void) data; - (void) iters; + for (i = 0; i < iters; i++) { + CHECK(secp256k1_bppp_rangeproof_verify(data->ctx, data->scratch, data->gens, secp256k1_generator_h, &data->proofs[i*MAX_PROOF_SIZE], data->proof_len, data->n_bits, data->base, data->min_value, &data->commit, NULL, 0)); + } } int main(void) { bench_bppp_data data; - int iters = get_iters(32); + int iters = get_iters(64); + char test_name[64]; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + data.gens = secp256k1_bppp_generators_create(data.ctx, 24); + data.scratch = secp256k1_scratch_space_create(data.ctx, 80 * 1024); + data.proofs = (unsigned char *)malloc(iters * MAX_PROOF_SIZE); - run_benchmark("bppp_verify_bit", bench_bppp, bench_bppp_setup, NULL, &data, 10, iters); + data.n_bits = 1ul << 6; + data.base = 16; + sprintf(test_name, "bppp_prove_64bits_16base"); + run_benchmark(test_name, bench_bppp_prove, bench_bppp_setup, NULL, &data, 4, iters); + sprintf(test_name, "bppp_verify_64bits_16base"); + run_benchmark(test_name, bench_bppp_verify, bench_bppp_setup, NULL, &data, 20, iters); + + secp256k1_scratch_space_destroy(data.ctx, data.scratch); + free(data.proofs); + secp256k1_bppp_generators_destroy(data.ctx, data.gens); secp256k1_context_destroy(data.ctx); + return 0; } diff --git a/src/modules/bppp/Makefile.am.include b/src/modules/bppp/Makefile.am.include index de90e06e9..4c4f2f2c5 100644 --- a/src/modules/bppp/Makefile.am.include +++ b/src/modules/bppp/Makefile.am.include @@ -3,6 +3,7 @@ noinst_HEADERS += src/modules/bppp/bppp_util.h noinst_HEADERS += src/modules/bppp/main_impl.h noinst_HEADERS += src/modules/bppp/bppp_transcript_impl.h noinst_HEADERS += src/modules/bppp/bppp_norm_product_impl.h +noinst_HEADERS += src/modules/bppp/bppp_rangeproof_impl.h noinst_HEADERS += src/modules/bppp/tests_impl.h if USE_BENCHMARK diff --git a/src/modules/bppp/bppp_norm_product_impl.h b/src/modules/bppp/bppp_norm_product_impl.h index 848e8cde3..4e7fe0ea3 100644 --- a/src/modules/bppp/bppp_norm_product_impl.h +++ b/src/modules/bppp/bppp_norm_product_impl.h @@ -84,11 +84,19 @@ typedef struct ecmult_bp_commit_cb_data { const secp256k1_scalar *n; const secp256k1_ge *g; const secp256k1_scalar *l; + const secp256k1_ge *asset_genp; + secp256k1_scalar v; size_t g_len; } ecmult_bp_commit_cb_data; static int ecmult_bp_commit_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { ecmult_bp_commit_cb_data *data = (ecmult_bp_commit_cb_data*) cbdata; + if (idx == 0) { + *pt = *data->asset_genp; + *sc = data->v; + return 1; + } + idx -= 1; *pt = data->g[idx]; if (idx < data->g_len) { *sc = data->n[idx]; @@ -107,6 +115,7 @@ static int secp256k1_bppp_commit( secp256k1_scratch_space* scratch, secp256k1_ge* commit, const secp256k1_bppp_generators* g_vec, + const secp256k1_ge* asset_genp, const secp256k1_scalar* n_vec, size_t n_vec_len, const secp256k1_scalar* l_vec, @@ -117,7 +126,7 @@ static int secp256k1_bppp_commit( ) { secp256k1_scalar v, l_c; /* First n_vec_len generators are Gs, rest are Hs*/ - VERIFY_CHECK(g_vec->n == (n_vec_len + l_vec_len)); + VERIFY_CHECK(g_vec->n >= (n_vec_len + l_vec_len)); VERIFY_CHECK(l_vec_len == c_vec_len); /* It is possible to extend to support n_vec and c_vec to not be power of @@ -137,8 +146,10 @@ static int secp256k1_bppp_commit( data.n = n_vec; data.l = l_vec; data.g_len = n_vec_len; + data.asset_genp = asset_genp; + data.v = v; - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &commitj, &v, ecmult_bp_commit_cb, (void*) &data, n_vec_len + l_vec_len)) { + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &commitj, NULL, ecmult_bp_commit_cb, (void*) &data, n_vec_len + l_vec_len + 1)) { return 0; } secp256k1_ge_set_gej_var(commit, &commitj); @@ -148,16 +159,25 @@ static int secp256k1_bppp_commit( typedef struct ecmult_x_cb_data { const secp256k1_scalar *n; + const secp256k1_ge *asset_genp; const secp256k1_ge *g; const secp256k1_scalar *l; const secp256k1_scalar *rho; const secp256k1_scalar *rho_inv; + const secp256k1_scalar *v; size_t G_GENS_LEN; /* Figure out initialization syntax so that this can also be const */ size_t n_len; } ecmult_x_cb_data; static int ecmult_x_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { ecmult_x_cb_data *data = (ecmult_x_cb_data*) cbdata; + /* idx = 0 with asset_genp */ + if (idx == 0) { + *pt = *data->asset_genp; + *sc = *data->v; + return 1; + } + idx -= 1; if (idx < data->n_len) { if (idx % 2 == 0) { secp256k1_scalar_mul(sc, &data->n[idx + 1], data->rho); @@ -182,13 +202,22 @@ static int ecmult_x_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void typedef struct ecmult_r_cb_data { const secp256k1_scalar *n1; const secp256k1_ge *g1; + const secp256k1_ge *asset_genp; const secp256k1_scalar *l1; + secp256k1_scalar *v; size_t G_GENS_LEN; size_t n_len; } ecmult_r_cb_data; static int ecmult_r_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { ecmult_r_cb_data *data = (ecmult_r_cb_data*) cbdata; + /* idx = 0 with asset_genp */ + if (idx == 0) { + *pt = *data->asset_genp; + *sc = *data->v; + return 1; + } + idx -= 1; if (idx < data->n_len) { *sc = data->n1[2*idx + 1]; *pt = data->g1[2*idx + 1]; @@ -224,7 +253,8 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( secp256k1_sha256* transcript, /* Transcript hash of the parent protocol */ const secp256k1_scalar* rho, secp256k1_ge* g_vec, - size_t g_vec_len, + size_t gens_vec_len, + const secp256k1_ge* asset_genp, secp256k1_scalar* n_vec, size_t n_vec_len, secp256k1_scalar* l_vec, @@ -247,21 +277,22 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( num_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; /* Check proof sizes.*/ VERIFY_CHECK(*proof_len >= 65 * num_rounds + 64); - VERIFY_CHECK(g_vec_len == (n_vec_len + l_vec_len) && l_vec_len == c_vec_len); + VERIFY_CHECK(gens_vec_len >= (n_vec_len + l_vec_len) && l_vec_len == c_vec_len); VERIFY_CHECK(secp256k1_is_power_of_two(n_vec_len) && secp256k1_is_power_of_two(c_vec_len)); x_cb_data.n = n_vec; x_cb_data.g = g_vec; x_cb_data.l = l_vec; x_cb_data.G_GENS_LEN = G_GENS_LEN; + x_cb_data.asset_genp = asset_genp; r_cb_data.n1 = n_vec; r_cb_data.g1 = g_vec; r_cb_data.l1 = l_vec; r_cb_data.G_GENS_LEN = G_GENS_LEN; + r_cb_data.asset_genp = asset_genp; secp256k1_scalar_sqr(&mu_f, &rho_f); - while (g_len > 1 || h_len > 1) { size_t i, num_points; secp256k1_scalar mu_sq, rho_inv, c0_l1, c1_l0, x_v, c1_l1, r_v; @@ -284,9 +315,10 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( x_cb_data.rho = &rho_f; x_cb_data.rho_inv = &rho_inv; x_cb_data.n_len = g_len >= 2 ? g_len : 0; + x_cb_data.v = &x_v; num_points = x_cb_data.n_len + (h_len >= 2 ? h_len : 0); - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &xj, &x_v, ecmult_x_cb, (void*)&x_cb_data, num_points)) { + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &xj, NULL, ecmult_x_cb, (void*)&x_cb_data, num_points + 1)) { return 0; } @@ -295,8 +327,9 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( secp256k1_scalar_add(&r_v, &r_v, &c1_l1); r_cb_data.n_len = g_len/2; + r_cb_data.v = &r_v; num_points = r_cb_data.n_len + h_len/2; - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &rj, &r_v, ecmult_r_cb, (void*)&r_cb_data, num_points)) { + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &rj, NULL, ecmult_r_cb, (void*)&r_cb_data, num_points + 1)) { return 0; } @@ -355,14 +388,14 @@ static int secp256k1_bppp_rangeproof_norm_product_prove( return 1; } -typedef struct ec_mult_verify_cb_data1 { +typedef struct ecmult_verify_cb_data1 { const unsigned char *proof; const secp256k1_ge *commit; const secp256k1_scalar *gammas; -} ec_mult_verify_cb_data1; +} ecmult_verify_cb_data1; -static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { - ec_mult_verify_cb_data1 *data = (ec_mult_verify_cb_data1*) cbdata; +static int ecmult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_verify_cb_data1 *data = (ecmult_verify_cb_data1*) cbdata; if (idx == 0) { *pt = *data->commit; secp256k1_scalar_set_int(sc, 1); @@ -390,24 +423,55 @@ static int ec_mult_verify_cb1(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx return 1; } -typedef struct ec_mult_verify_cb_data2 { +typedef struct ecmult_verify_cb_data2 { const secp256k1_scalar *s_g; const secp256k1_scalar *s_h; const secp256k1_ge *g_vec; - size_t g_vec_len; -} ec_mult_verify_cb_data2; + const secp256k1_ge *asset_genp; + const secp256k1_scalar *v; + size_t n_vec_len; +} ecmult_verify_cb_data2; -static int ec_mult_verify_cb2(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { - ec_mult_verify_cb_data2 *data = (ec_mult_verify_cb_data2*) cbdata; - if (idx < data->g_vec_len) { +static int ecmult_verify_cb2(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_verify_cb_data2 *data = (ecmult_verify_cb_data2*) cbdata; + if (idx == 0) { + *pt = *data->asset_genp; + *sc = *data->v; + secp256k1_scalar_negate(sc, sc); + return 1; + } + idx -= 1; + if (idx < data->n_vec_len) { *sc = data->s_g[idx]; } else { - *sc = data->s_h[idx - data->g_vec_len]; + *sc = data->s_h[idx - data->n_vec_len]; } *pt = data->g_vec[idx]; + secp256k1_scalar_negate(sc, sc); return 1; } +typedef struct ecmult_verify_cb_data3 { + const ecmult_verify_cb_data1 *cb_data1; + const ecmult_verify_cb_data2 *cb_data2; + size_t idx2; +} ecmult_verify_cb_data3; + +static int ecmult_verify_cb3(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_verify_cb_data3 *data = (ecmult_verify_cb_data3*) cbdata; + if (idx < data->idx2) { + if(!ecmult_verify_cb1(sc, pt, idx, (void*)data->cb_data1)) { + return 0; + } + } else { + if(!ecmult_verify_cb2(sc, pt, idx - data->idx2, (void*)data->cb_data2)) { + return 0; + } + } + return 1; +} + + /* Verify the proof. This function modifies the generators, c_vec and the challenge r. The caller should make sure to back them up if they need to be reused. */ @@ -419,6 +483,7 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( secp256k1_sha256* transcript, const secp256k1_scalar* rho, const secp256k1_bppp_generators* g_vec, + const secp256k1_ge *asset_genp, size_t g_len, const secp256k1_scalar* c_vec, size_t c_vec_len, @@ -426,7 +491,7 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( ) { secp256k1_scalar rho_f, mu_f, v, n, l, rho_inv, h_c; secp256k1_scalar *gammas, *s_g, *s_h, *rho_inv_pows; - secp256k1_gej res1, res2; + secp256k1_gej res; size_t i = 0, scratch_checkpoint; int overflow; size_t log_g_len, log_h_len; @@ -440,7 +505,7 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( log_h_len = secp256k1_bppp_log2(c_vec_len); n_rounds = log_g_len > log_h_len ? log_g_len : log_h_len; - if (g_vec->n != (h_len + g_len) || (proof_len != 65 * n_rounds + 64)) { + if (g_vec->n < (h_len + g_len) || (proof_len != 65 * n_rounds + 64)) { return 0; } @@ -511,24 +576,23 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( secp256k1_scalar_add(&v, &v, &h_c); { - ec_mult_verify_cb_data1 data; - data.proof = proof; - data.commit = commit; - data.gammas = gammas; - - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res1, NULL, ec_mult_verify_cb1, &data, 2*n_rounds + 1)) { - secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); - return 0; - } - } - { - ec_mult_verify_cb_data2 data; - data.g_vec = g_vec->gens; - data.g_vec_len = g_len; - data.s_g = s_g; - data.s_h = s_h; - - if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res2, &v, ec_mult_verify_cb2, &data, g_len + h_len)) { + ecmult_verify_cb_data1 data1; + ecmult_verify_cb_data2 data2; + ecmult_verify_cb_data3 data3; + data1.proof = proof; + data1.commit = commit; + data1.gammas = gammas; + data2.g_vec = g_vec->gens; + data2.n_vec_len = g_len; + data2.s_g = s_g; + data2.s_h = s_h; + data2.v = &v; + data2.asset_genp = asset_genp; + data3.cb_data1 = &data1; + data3.cb_data2 = &data2; + data3.idx2 = 2*n_rounds + 1; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &res, NULL, ecmult_verify_cb3, &data3, 2*n_rounds + 1 + g_len + h_len + 1)) { secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); return 0; } @@ -536,9 +600,6 @@ static int secp256k1_bppp_rangeproof_norm_product_verify( secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); - /* res1 and res2 should be equal. Could not find a simpler way to compare them */ - secp256k1_gej_neg(&res1, &res1); - secp256k1_gej_add_var(&res1, &res1, &res2, NULL); - return secp256k1_gej_is_infinity(&res1); + return secp256k1_gej_is_infinity(&res); } #endif diff --git a/src/modules/bppp/bppp_rangeproof_impl.h b/src/modules/bppp/bppp_rangeproof_impl.h new file mode 100644 index 000000000..d4605144d --- /dev/null +++ b/src/modules/bppp/bppp_rangeproof_impl.h @@ -0,0 +1,1025 @@ +/********************************************************************** + * Copyright (c) 2022 Sanket Kanjalkar * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_BPP_RANGEPROOF_IMPL_ +#define SECP256K1_MODULE_BPP_RANGEPROOF_IMPL_ + + +#include "../../group.h" +#include "../../scalar.h" +#include "../../../include/secp256k1.h" +#include "../../ecmult_const.h" +#include "../../field.h" +#include "../../../include/secp256k1_bppp.h" + +#include "../bppp/bppp_util.h" +#include "../bppp/bppp_transcript_impl.h" +#include "../bppp/bppp_norm_product_impl.h" + +struct secp256k1_bppp_rangeproof_prover_context { + + /* Components committed along G_vec + d = digits(array of size num_digits) + m = multiplicities of digits (array of size base) + r = reciprocals of each digit (array of size num_digits) + s = random blinding factors (array of size G_vec_len = max(num_digits, base)) + */ + secp256k1_scalar *d, *m, *r, *s; + /* The blinding value associated with b_i along G */ + secp256k1_scalar r_d_0, r_m_0, r_r_0, r_s_0; + /* The blinding values associated with m/s along H_vec(array of size 6) */ + secp256k1_scalar *r_m_1_vec, *r_s_1_vec; + /* Blinding values for r_d[2] and r_d[5] */ + secp256k1_scalar r_d_1_vec_2, r_d_1_vec_5; + /* The challenges during prover computation + x here is actually separating the linear constraints, but we don't use lambda here + as we will use lambda later for aggregation. */ + secp256k1_scalar alpha, mu, x, beta, t, rho, delta; + /* Pre-computed powers of mu len = max(num_digits, base) */ + secp256k1_scalar *mu_pows, *mu_inv_pows; + /* The cached values of c_m = x/(alpha+i)*mu^(-i-1) */ + secp256k1_scalar *c_m; +}; + +/* Compute the powers of mu as mu, mu^2, mu^3, ... */ +static void secp256k1_bppp_rangeproof_powers_of_mu(secp256k1_scalar *mu_pows, const secp256k1_scalar *mu, size_t len) { + size_t i; + mu_pows[0] = *mu; + for (i = 1; i < len; i++) { + secp256k1_scalar_mul(&mu_pows[i], &mu_pows[i - 1], mu); + } +} + +/* Round 1 of the proof. Computes the digits and multiplicities of the values. + * Uses the value but not the blinding factor. Takes the complete commitment, + * but only to put it into the hash. + * Outputs points M and D serialized as 33-byte compressed points. + * Always succeeds. + */ +static void secp256k1_bppp_rangeproof_prove_round1_impl( + secp256k1_bppp_rangeproof_prover_context* prover_ctx, + const secp256k1_bppp_generators* gens, + const secp256k1_ge* asset_genp, + unsigned char* output, + secp256k1_sha256* transcript, + const size_t num_digits, + const size_t digit_base, + uint64_t value, + const unsigned char* nonce +) { + size_t log_base = secp256k1_bppp_log2(digit_base); + size_t i, j; + size_t g_offset = digit_base > num_digits ? digit_base : num_digits; + secp256k1_gej d_commj, m_commj; + uint16_t multiplicities[64]; /* SECP256K1_BPP_MAX_BASE = 64. TODO: Check this in high level API */ + /* Obtain random values for r_m_1_vec. Note the values for indices at 3, 6, 7 should be zero.*/ + secp256k1_scalar_chacha20(&prover_ctx->r_m_1_vec[0], &prover_ctx->r_m_1_vec[1], nonce, 0); + secp256k1_scalar_chacha20(&prover_ctx->r_m_1_vec[2], &prover_ctx->r_m_1_vec[4], nonce, 1); + secp256k1_scalar_chacha20(&prover_ctx->r_m_1_vec[5], &prover_ctx->r_m_1_vec[5], nonce, 2); + /* r_d1_vec needs values at indices of 2 and 5. */ + secp256k1_scalar_chacha20(&prover_ctx->r_d_1_vec_2, &prover_ctx->r_d_1_vec_5, nonce, 3); + secp256k1_scalar_clear(&prover_ctx->r_m_1_vec[7]); + /* Obtain the values for r_m_0 and r_d_0 */ + secp256k1_scalar_chacha20(&prover_ctx->r_m_0, &prover_ctx->r_d_0, nonce, 4); + + for (i = 0; i < digit_base; i++) { + multiplicities[i] = 0; + } + + /* Commit to the vector d in gens */ + secp256k1_ecmult_const(&d_commj, asset_genp, &prover_ctx->r_d_0); + + for (i = 0; i < num_digits; i++) { + secp256k1_gej resj; + secp256k1_ge d_comm; + unsigned int digit = value & (digit_base - 1); + value = value >> log_base; + /* Constant time way to hide conditional access to multiplicities[digit] */ + for (j = 0; j < digit_base; j++) { + multiplicities[j] += (j == digit); + } + secp256k1_scalar_set_int(&prover_ctx->d[i], digit); + secp256k1_ecmult_const(&resj, &gens->gens[i], &prover_ctx->d[i]); /* should be = log_base + 1 bits here */ + secp256k1_ge_set_gej(&d_comm, &d_commj); + secp256k1_gej_add_ge(&d_commj, &resj, &d_comm); /* d_comm cannot be zero */ + } + + /* Additional t**7 and t**3 term which cannot be cancelled out: + delta*lm_v[0, 6] + ld_v[0, 5] + lr_v[0, 4] => lm_v[6] = 0 && ld_v[5] = -lr_v[4]. + t^3 term: delta*lm_v[0, 3] + ld_v[0, 2] + lr_v[0, 1] => lm_v[3] = 0 && ld_v[2] = -lr_v[1] */ + /* There are multiple choices for these values, we choose the simplest one + where all other blinding values along H for r, d are zero. */ + { + secp256k1_gej resj; + secp256k1_ge d_comm; + + secp256k1_scalar_clear(&prover_ctx->r_m_1_vec[3]); /* r_m_1_vec[3] = 0 */ + secp256k1_scalar_clear(&prover_ctx->r_m_1_vec[6]); /* r_m_1_vec[6] = 0 */ + secp256k1_ecmult_const(&resj, &gens->gens[g_offset + 2], &prover_ctx->r_d_1_vec_2); + secp256k1_ge_set_gej(&d_comm, &d_commj); + secp256k1_gej_add_ge(&d_commj, &resj, &d_comm); + secp256k1_ecmult_const(&resj, &gens->gens[g_offset + 5], &prover_ctx->r_d_1_vec_5); + secp256k1_ge_set_gej(&d_comm, &d_commj); + secp256k1_gej_add_ge(&d_commj, &resj, &d_comm); + } + + /* Compute the m vector as multiplicity of each digit */ + secp256k1_ecmult_const(&m_commj, asset_genp, &prover_ctx->r_m_0); + for (i = 0; i < digit_base; i++) { + secp256k1_gej resj; + secp256k1_ge m_comm; + secp256k1_scalar_set_int(&prover_ctx->m[i], multiplicities[i]); + secp256k1_ecmult_const(&resj, &gens->gens[i], &prover_ctx->m[i]); /* , log_num_digits + 1 (I think ) there should there be +1 here? */ + secp256k1_ge_set_gej(&m_comm, &m_commj); + secp256k1_gej_add_ge(&m_commj, &resj, &m_comm); /* m_comm cannot be zero*/ + } + + for (i = 0; i < 8; i++) { + secp256k1_gej resj; + secp256k1_ge m_comm; + secp256k1_ecmult_const(&resj, &gens->gens[g_offset + i], &prover_ctx->r_m_1_vec[i]); + secp256k1_ge_set_gej(&m_comm, &m_commj); + secp256k1_gej_add_ge(&m_commj, &resj, &m_comm); /* m_comm cannot be zero */ + } + + { + secp256k1_ge m_comm, d_comm; + /* r_m_1_vec are sampled randomly and two components of l_d are sampled randomly. + * Improbable to be zero commitment. Safe to serialize. */ + VERIFY_CHECK(!secp256k1_gej_is_infinity(&m_commj)); + VERIFY_CHECK(!secp256k1_gej_is_infinity(&d_commj)); + + secp256k1_ge_set_gej_var(&m_comm, &m_commj); + secp256k1_ge_set_gej_var(&d_comm, &d_commj); + secp256k1_fe_normalize_var(&m_comm.x); + secp256k1_fe_normalize_var(&m_comm.y); + secp256k1_fe_normalize_var(&d_comm.x); + secp256k1_fe_normalize_var(&d_comm.y); + secp256k1_bppp_serialize_pt(&output[0], &m_comm); + secp256k1_bppp_serialize_pt(&output[33], &d_comm); + + secp256k1_sha256_write(transcript, output, 66); + secp256k1_bppp_challenge_scalar(&prover_ctx->alpha, transcript, 0); + } +} + + +/* Round 2 of the proof. Computes the reciprocals of the digits. + * Serialized as 33 byte compressed point. + * Always succeeds. + */ +static void secp256k1_bppp_rangeproof_prove_round2_impl( + secp256k1_bppp_rangeproof_prover_context* prover_ctx, + const secp256k1_bppp_generators* gens, + const secp256k1_ge* asset_genp, + unsigned char* output, + secp256k1_sha256* transcript, + const size_t num_digits, + const size_t digit_base, + const unsigned char* nonce +) { + size_t i; + size_t g_offset = digit_base > num_digits ? digit_base : num_digits; + secp256k1_gej r_commj; + secp256k1_scalar mu_inv; + + /* We need only one value in this round, ignore the second value. */ + secp256k1_scalar_chacha20(&prover_ctx->r_r_0, &prover_ctx->r_r_0, nonce, 5); + + /* Commit to the vector d in gens */ + secp256k1_ecmult_const(&r_commj, asset_genp, &prover_ctx->r_r_0); + for (i = 0; i < num_digits; i++) { + secp256k1_gej resj; + secp256k1_ge r_comm; + secp256k1_scalar_add(&prover_ctx->r[i], &prover_ctx->d[i], &prover_ctx->alpha); + secp256k1_scalar_inverse(&prover_ctx->r[i], &prover_ctx->r[i]); /* r_i cannot be zero as it added by random value `alpha`*/ + secp256k1_ecmult_const(&resj, &gens->gens[i], &prover_ctx->r[i]); + secp256k1_ge_set_gej(&r_comm, &r_commj); + secp256k1_gej_add_ge(&r_commj, &resj, &r_comm); /* r_comm cannot be zero */ + } + + /* Additional t**7 term which cannot be cancelled out: + delta*lm_v[0, 6] + ld_v[0, 5] + lr_v[0, 4] => lm_v[6] = 0 && ld_v[5] = -lr_v[4]. + t^3 term: delta*lm_v[0, 3] + ld_v[0, 2] + lr_v[0, 1] => lm_v[3] = 0 && ld_v[2] = -lr_v[1] */ + { + secp256k1_gej resj; + secp256k1_ge r_comm; + secp256k1_scalar tmp; + + secp256k1_scalar_negate(&tmp, &prover_ctx->r_d_1_vec_2); + secp256k1_ecmult_const(&resj, &gens->gens[g_offset + 1], &tmp); + secp256k1_ge_set_gej(&r_comm, &r_commj); + secp256k1_gej_add_ge(&r_commj, &resj, &r_comm); + + secp256k1_scalar_negate(&tmp, &prover_ctx->r_d_1_vec_5); + secp256k1_ecmult_const(&resj, &gens->gens[g_offset + 4], &tmp); + secp256k1_ge_set_gej(&r_comm, &r_commj); + secp256k1_gej_add_ge(&r_commj, &resj, &r_comm); + } + + { + secp256k1_ge r_comm; + /* All r values are non-zero(computed by inverse), rcommj must be non-zero */ + VERIFY_CHECK(secp256k1_gej_is_infinity(&r_commj) == 0); + secp256k1_ge_set_gej_var(&r_comm, &r_commj); + secp256k1_fe_normalize_var(&r_comm.x); + secp256k1_fe_normalize_var(&r_comm.y); + secp256k1_bppp_serialize_pt(&output[0], &r_comm); + + secp256k1_sha256_write(transcript, output, 33); + secp256k1_bppp_challenge_scalar(&prover_ctx->rho, transcript, 0); + secp256k1_bppp_challenge_scalar(&prover_ctx->x, transcript, 1); + secp256k1_bppp_challenge_scalar(&prover_ctx->beta, transcript, 2); + secp256k1_bppp_challenge_scalar(&prover_ctx->delta, transcript, 3); + secp256k1_scalar_sqr(&prover_ctx->mu, &prover_ctx->rho); + } + /* Pre-compute powers of mu and mu_inv. We will need them in future rounds. */ + secp256k1_bppp_rangeproof_powers_of_mu(prover_ctx->mu_pows, &prover_ctx->mu, g_offset); + secp256k1_scalar_inverse_var(&mu_inv, &prover_ctx->mu); /* mu cannot be zero */ + secp256k1_bppp_rangeproof_powers_of_mu(prover_ctx->mu_inv_pows, &mu_inv, g_offset); + /* Compute the values of c_m = (x/(alpha+i)*mu_inv[i]) */ + for (i = 0; i < digit_base; i++) { + secp256k1_scalar_set_int(&prover_ctx->c_m[i], i); /* digit base is less than 2^32, can directly set*/ + secp256k1_scalar_add(&prover_ctx->c_m[i], &prover_ctx->c_m[i], &prover_ctx->alpha); + secp256k1_scalar_inverse_var(&prover_ctx->c_m[i], &prover_ctx->c_m[i]); + secp256k1_scalar_mul(&prover_ctx->c_m[i], &prover_ctx->c_m[i], &prover_ctx->x); + secp256k1_scalar_mul(&prover_ctx->c_m[i], &prover_ctx->c_m[i], &prover_ctx->mu_inv_pows[i]); + } +} + +/* Choose of the co-effs of poly and len of co-effs w = s + mT + dT^2 + rT^3 */ +static const secp256k1_scalar* secp256k1_bppp_w_coeff( + unsigned int *len, + size_t idx, + const secp256k1_scalar *s, + const secp256k1_scalar *m, + const secp256k1_scalar *d, + const secp256k1_scalar *r, + const secp256k1_scalar *c_m, + size_t digit_base, + size_t num_digits +) { + switch (idx) { + case 0: + *len = num_digits > digit_base ? num_digits : digit_base; + return s; + case 1: + *len = digit_base; + return m; + case 2: + *len = num_digits; + return d; + case 3: + *len = num_digits; + return r; + case 4: + *len = digit_base; + return c_m; + default: + VERIFY_CHECK(0); + } + return NULL; +} + +/* Compute the mu-norm square of w = s/T + m + dT + rT^2 + c_mT^3. + Note that m here is already scaled by delta. + Since this is a degree 4 polynomial, we can hard code the expansion + to get improve performance. + |w|^2 = + c_m**2*t**6 + + 2*c_m*r*t**5 + + t**4*(2*c_m*d + r**2) + + t**3*(2*c_m*m + 2*d*r) + + t**2*(2*c_m*s + d**2 + 2*m*r) + + t**1*(2*d*m + 2*r*s) + + t**0*(2*d*s + m**2) + + 2*m*s/t + + s**2/t**2. + Doing this is left as a future optimization. + The output vector w[0] represents degree t^-2. w[1] represents degree t^-1... +*/ +static void secp256k1_bppp_rangeproof_w_w_q( + secp256k1_scalar *w, /* size G_len = max(digits_base, num_digits)*/ + const secp256k1_scalar *s, /* size G_len */ + const secp256k1_scalar *m, /* size digits_base */ + const secp256k1_scalar *d, /* size num_digits */ + const secp256k1_scalar *r, /* size num_digits */ + const secp256k1_scalar *c_m, /* size digits_base */ + const secp256k1_scalar* mu_pows, /* size G_len */ + size_t digit_base, + size_t num_digits +) { + size_t i, j, k; + for (i = 0; i < 9; i++) { + secp256k1_scalar_clear(&w[i]); + } + + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + /* Can add an optimization to skip the last term if i + j >= 8 */ + unsigned int a_len, b_len; + const secp256k1_scalar* a_coeffs = + secp256k1_bppp_w_coeff(&a_len, i, s, m, d, r, c_m, digit_base, num_digits); + const secp256k1_scalar* b_coeffs = + secp256k1_bppp_w_coeff(&b_len, j, s, m, d, r, c_m, digit_base, num_digits); + /* Compute w[i + j] += Sum(a[k] * b[k] * mu_pows[k]) */ + unsigned int len = a_len < b_len ? a_len : b_len; + for (k = 0; k < len; k++) { + secp256k1_scalar tmp; + secp256k1_scalar_mul(&tmp, &a_coeffs[k], &b_coeffs[k]); + secp256k1_scalar_mul(&tmp, &tmp, &mu_pows[k]); + secp256k1_scalar_add(&w[i + j], &w[i + j], &tmp); + } + } + } +} + +/* Round 3 of the proof. Computes the S value. + * Serialized as 33 byte compressed point. + * Always succeeds. + */ +static void secp256k1_bppp_rangeproof_prove_round3_impl( + secp256k1_bppp_rangeproof_prover_context* prover_ctx, + const secp256k1_bppp_generators* gens, + const secp256k1_ge* asset_genp, + unsigned char* output, + secp256k1_sha256* transcript, + const size_t num_digits, + const size_t digit_base, + const secp256k1_scalar* gamma, + const unsigned char* nonce +) { + size_t i; + size_t g_offset = digit_base > num_digits ? digit_base : num_digits; + secp256k1_gej s_commj; + + /* We don't need only one value in this round, ignore the second value. */ + for (i = 0; i < g_offset/2; i++) { + secp256k1_scalar_chacha20(&prover_ctx->s[2*i], &prover_ctx->s[2*i + 1], nonce, i + 6); + } + + /* The r_s values must be computed adaptively in order to satisfy the following relation for all Ts. */ + { + /* Add public values p_r=(-x*delta*mu^-(i + 1) + alpha) to to d */ + secp256k1_scalar tmp, b_pow_i, base; + for (i = 0; i < num_digits; i++) { + secp256k1_scalar_negate(&tmp, &prover_ctx->x); /* tmp = -x */ + secp256k1_scalar_mul(&tmp, &tmp, &prover_ctx->delta); /* tmp = -x*delta */ + secp256k1_scalar_mul(&tmp, &tmp, &prover_ctx->mu_inv_pows[i]); /* tmp = -x*delta*mu_inv^(i+1) */ + secp256k1_scalar_add(&tmp, &tmp, &prover_ctx->alpha); /* tmp = -x*delta*mu_inv^(i+1) + alpha */ + secp256k1_scalar_add(&prover_ctx->d[i], &prover_ctx->d[i], &tmp); /* d[i] = d[i] + tmp */ + } + + /* Add public values p_d=(b^i*mu_inv^(i+1)) to to r */ + secp256k1_scalar_set_int(&b_pow_i, 1); + secp256k1_scalar_set_int(&base, digit_base); + for (i = 0; i < num_digits; i++) { + secp256k1_scalar_mul(&tmp, &b_pow_i, &prover_ctx->mu_inv_pows[i]); /* tmp = b^i*mu_inv^(i+1) */ + secp256k1_scalar_add(&prover_ctx->r[i], &prover_ctx->r[i], &tmp); /* r[i] = r[i] + tmp */ + secp256k1_scalar_mul(&b_pow_i, &b_pow_i, &base); /* b_pow_i = b_pow_i * base */ + } + } + + /* Scale the m values by delta. Scales all of m, r_m_1_vec, r_m_0. */ + { + for (i = 0; i < digit_base; i++) { + secp256k1_scalar_mul(&prover_ctx->m[i], &prover_ctx->m[i], &prover_ctx->delta); + } + for (i = 0; i < 8; i++) { + secp256k1_scalar_mul(&prover_ctx->r_m_1_vec[i], &prover_ctx->r_m_1_vec[i], &prover_ctx->delta); + } + secp256k1_scalar_mul(&prover_ctx->r_m_0, &prover_ctx->r_m_0, &prover_ctx->delta); + } + + /* Adaptively compute r_s_1_vec to balance out the w_w_q + is zero is + in all powers of T where c = beta*(T^-1, T, T^2, T^3, T^5, T^6, T^7, 0) + Note the absence of T**3 in this equation. T^-1 is balanced by r_s_0. + T^3 is balanced only if all the constraints in the rangeproof are correctly satisfied. + l = r_s_1_vec/T + r_m_1_vec + r_d*T^1 + r_r*T^2 + 2*gamma*T^3. + Only the values r_m_vec[i] are sampled randomly. + */ + { + secp256k1_scalar w_w_q[9]; + secp256k1_scalar beta_inv, two_gamma; + secp256k1_bppp_rangeproof_w_w_q( + w_w_q, + prover_ctx->s, + prover_ctx->m, + prover_ctx->d, + prover_ctx->r, + prover_ctx->c_m, + prover_ctx->mu_pows, + digit_base, + num_digits + ); + + secp256k1_scalar_negate(&prover_ctx->r_m_0, &prover_ctx->r_m_0); + secp256k1_scalar_negate(&prover_ctx->r_d_0, &prover_ctx->r_d_0); + secp256k1_scalar_negate(&prover_ctx->r_r_0, &prover_ctx->r_r_0); + + #define TPOW(x) (x + 2) + + /* Add b_i values to w_w_q. -r_m_0 - r_d_0*T^1 - r_r_0*T^2 */ + secp256k1_scalar_add(&w_w_q[TPOW(0)], &w_w_q[TPOW(0)], &prover_ctx->r_m_0); + secp256k1_scalar_add(&w_w_q[TPOW(1)], &w_w_q[TPOW(1)], &prover_ctx->r_d_0); + secp256k1_scalar_add(&w_w_q[TPOW(2)], &w_w_q[TPOW(2)], &prover_ctx->r_r_0); + + secp256k1_scalar_inverse_var(&beta_inv, &prover_ctx->beta); + + secp256k1_scalar_mul(&prover_ctx->r_s_0, &prover_ctx->r_m_1_vec[0], &prover_ctx->beta); + secp256k1_scalar_add(&prover_ctx->r_s_0, &prover_ctx->r_s_0, &w_w_q[TPOW(-1)]); + /* Note the limits on i from 0 to 9.*/ + for (i = 0; i < 9; i++) { + secp256k1_scalar_mul(&w_w_q[i], &w_w_q[i], &beta_inv); + } + secp256k1_scalar_add(&w_w_q[TPOW(6)], &w_w_q[TPOW(6)], &prover_ctx->r_m_1_vec[5]); + secp256k1_scalar_add(&w_w_q[TPOW(5)], &w_w_q[TPOW(5)], &prover_ctx->r_m_1_vec[4]); + /* r_m_1_vec[3] maps to T^4 */ + /* secp256k1_scalar_add(&w_w_q[TPOW(3)], &w_w_q[TPOW(3)], &prover_ctx->r_m_1_vec[3]); */ + secp256k1_scalar_add(&w_w_q[TPOW(2)], &w_w_q[TPOW(2)], &prover_ctx->r_m_1_vec[2]); + secp256k1_scalar_add(&w_w_q[TPOW(1)], &w_w_q[TPOW(1)], &prover_ctx->r_m_1_vec[1]); + + secp256k1_scalar_set_int(&two_gamma, 2); + secp256k1_scalar_mul(&two_gamma, &two_gamma, gamma); + secp256k1_scalar_add(&w_w_q[TPOW(2)], &w_w_q[TPOW(2)], &two_gamma); + + /* Set all r_s_1_vec values as negation of w_w_q */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[0], &w_w_q[TPOW(-2)]); /* T^-1 */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[1], &w_w_q[TPOW(0)]); /* T^1 */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[2], &w_w_q[TPOW(1)]); /* T^2 */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[3], &w_w_q[TPOW(2)]); /* T^3 */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[4], &w_w_q[TPOW(4)]); /* T^5 */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[5], &w_w_q[TPOW(5)]); /* T^6 */ + secp256k1_scalar_negate(&prover_ctx->r_s_1_vec[6], &w_w_q[TPOW(6)]); /* T^7 */ + } + /* Commit to the vector s in gens, with r_s_0 along asset and l in H_vec */ + secp256k1_ecmult_const(&s_commj, asset_genp, &prover_ctx->r_s_0); + for (i = 0; i < g_offset; i++) { + secp256k1_gej resj; + secp256k1_ge s_comm; + secp256k1_ecmult_const(&resj, &gens->gens[i], &prover_ctx->s[i]); + secp256k1_ge_set_gej(&s_comm, &s_commj); + secp256k1_gej_add_ge(&s_commj, &resj, &s_comm); /* s_comm cannot be 0 */ + } + + for (i = 0; i < 7; i++) { + secp256k1_gej resj; + secp256k1_ge s_comm; + secp256k1_ecmult_const(&resj, &gens->gens[g_offset + i], &prover_ctx->r_s_1_vec[i]); + secp256k1_ge_set_gej(&s_comm, &s_commj); + secp256k1_gej_add_ge(&s_commj, &resj, &s_comm); /* s_comm cannot be 0 */ + } + + { + secp256k1_ge s_comm; + /* All s values are non-zero(computed by inverse), scommj must be non-zero */ + VERIFY_CHECK(secp256k1_gej_is_infinity(&s_commj) == 0); + secp256k1_ge_set_gej_var(&s_comm, &s_commj); + secp256k1_fe_normalize_var(&s_comm.x); + secp256k1_fe_normalize_var(&s_comm.y); + secp256k1_bppp_serialize_pt(&output[0], &s_comm); + + secp256k1_sha256_write(transcript, output, 33); + secp256k1_bppp_challenge_scalar(&prover_ctx->t, transcript, 0); + } +} + +/* Round 4 of the proof. Computes the norm proof on the w and l values. + * Can fail only when the norm proof fails. + * This should not happen in our setting because w_vec and l_vec and uniformly + * distributed and thus norm argument can only fail when the lengths are not a + * power of two or if the allocated proof size is not enough. + * + * We check for both of these conditions beforehand, therefore in practice this + * function should never fail because it returns point at infinity during some + * interim calculations. However, since the overall API can fail, we also fail + * if the norm proofs fails for any reason. + */ +static int secp256k1_bppp_rangeproof_prove_round4_impl( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const secp256k1_bppp_generators* gens, + const secp256k1_ge* asset_genp, + secp256k1_bppp_rangeproof_prover_context* prover_ctx, + unsigned char* output, + size_t *output_len, + secp256k1_sha256* transcript, + const secp256k1_scalar* gamma, + const size_t num_digits, + const size_t digit_base +) { + size_t i, scratch_checkpoint, ret; + size_t g_offset = digit_base > num_digits ? digit_base : num_digits; + /* Compute w = s/t + m + t*d + t^2*r + t^3*c_m. Store w in s*/ + secp256k1_scalar t_pows[8], c_poly[8], t_inv; + secp256k1_ge *gs; + + secp256k1_scalar_inverse(&t_inv, &prover_ctx->t); + secp256k1_bppp_rangeproof_powers_of_mu(&t_pows[0], &prover_ctx->t, 7); /* Computes from t^1 to t^7 */ + + for (i = 0; i < g_offset; i++) { + secp256k1_scalar_mul(&prover_ctx->s[i], &prover_ctx->s[i], &t_inv); + if (i < num_digits) { + secp256k1_scalar_mul(&prover_ctx->r[i], &prover_ctx->r[i], &t_pows[1]); + secp256k1_scalar_add(&prover_ctx->s[i], &prover_ctx->s[i], &prover_ctx->r[i]); + + secp256k1_scalar_mul(&prover_ctx->d[i], &prover_ctx->d[i], &t_pows[0]); + secp256k1_scalar_add(&prover_ctx->s[i], &prover_ctx->s[i], &prover_ctx->d[i]); + } + if (i < digit_base) { + secp256k1_scalar_add(&prover_ctx->s[i], &prover_ctx->s[i], &prover_ctx->m[i]); + + secp256k1_scalar_mul(&prover_ctx->c_m[i], &prover_ctx->c_m[i], &t_pows[2]); + secp256k1_scalar_add(&prover_ctx->s[i], &prover_ctx->s[i], &prover_ctx->c_m[i]); + } + } + /* Compute l = l_s/t + r_m_1_vec + t^1*l_d + t^2*l_r. Store l in l_s*/ + for (i = 0; i < 7; i++) { + secp256k1_scalar_mul(&prover_ctx->r_s_1_vec[i], &prover_ctx->r_s_1_vec[i], &t_inv); + secp256k1_scalar_add(&prover_ctx->r_s_1_vec[i], &prover_ctx->r_s_1_vec[i], &prover_ctx->r_m_1_vec[i]); + } + /* Manually add r_d2, r_d5, r_r1 and r_r4 */ + { + secp256k1_scalar tmp; + secp256k1_scalar_mul(&tmp, &prover_ctx->r_d_1_vec_2, &t_pows[0]); + secp256k1_scalar_add(&prover_ctx->r_s_1_vec[2], &prover_ctx->r_s_1_vec[2], &tmp); + + secp256k1_scalar_mul(&tmp, &prover_ctx->r_d_1_vec_2, &t_pows[1]); + secp256k1_scalar_negate(&tmp, &tmp);/* r_r[1] = -r_d_vec[2] */ + secp256k1_scalar_add(&prover_ctx->r_s_1_vec[1], &prover_ctx->r_s_1_vec[1], &tmp); + + secp256k1_scalar_mul(&tmp, &prover_ctx->r_d_1_vec_5, &t_pows[0]); + secp256k1_scalar_add(&prover_ctx->r_s_1_vec[5], &prover_ctx->r_s_1_vec[5], &tmp); + + secp256k1_scalar_mul(&tmp, &prover_ctx->r_d_1_vec_5, &t_pows[1]); + secp256k1_scalar_negate(&tmp, &tmp);/* r_r[4] = -r_m_vec[5] */ + secp256k1_scalar_add(&prover_ctx->r_s_1_vec[4], &prover_ctx->r_s_1_vec[4], &tmp); + + /* Add two_gamma * t3 to l_s[0] */ + secp256k1_scalar_add(&tmp, &t_pows[2], &t_pows[2]); + secp256k1_scalar_mul(&tmp, &tmp, gamma); + secp256k1_scalar_add(&prover_ctx->r_s_1_vec[0], &prover_ctx->r_s_1_vec[0], &tmp); + } + /* Set non used 7th and 8th l_s to 0 */ + secp256k1_scalar_set_int(&prover_ctx->r_s_1_vec[7], 0); + + /* Make c = beta*(T^-1, T, T^2, T^3, T^5, T^6, T^7, 0) */ + c_poly[0] = t_inv; + c_poly[1] = t_pows[0]; + c_poly[2] = t_pows[1]; + c_poly[3] = t_pows[2]; + c_poly[4] = t_pows[4]; + c_poly[5] = t_pows[5]; + c_poly[6] = t_pows[6]; + secp256k1_scalar_set_int(&c_poly[7], 0); + for (i = 0; i < 7; i++) { + secp256k1_scalar_mul(&c_poly[i], &c_poly[i], &prover_ctx->beta); + } + /* Call the norm argument on w, l */ + /* We have completed the blinding, none of part that comes from this point on + needs to constant time. We can safely early return + */ + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + gs = (secp256k1_ge*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, (gens->n) * sizeof(secp256k1_ge)); + if (gs == NULL) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + memcpy(gs, gens->gens, (gens->n) * sizeof(secp256k1_ge)); + + ret = secp256k1_bppp_rangeproof_norm_product_prove( + ctx, + scratch, + output, + output_len, + transcript, + &prover_ctx->rho, + gs, + gens->n, + asset_genp, + prover_ctx->s, + g_offset, + prover_ctx->r_s_1_vec, + 8, + c_poly, + 8 + ); + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return ret; +} + +static int secp256k1_bppp_rangeproof_prove_impl( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const secp256k1_bppp_generators* gens, + secp256k1_ge* asset_genp, + unsigned char* proof, + size_t* proof_len, + const size_t n_bits, + const size_t digit_base, + const uint64_t value, + const uint64_t min_value, + secp256k1_ge* commitp, + const secp256k1_scalar* gamma, + const unsigned char* nonce, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + size_t scratch_checkpoint, n_proof_bytes_written, norm_proof_len; + secp256k1_sha256 transcript; + size_t num_digits = n_bits / secp256k1_bppp_log2(digit_base); + size_t h_len = 8; + size_t g_offset = num_digits > digit_base ? num_digits : digit_base; + size_t log_n = secp256k1_bppp_log2(g_offset), log_m = secp256k1_bppp_log2(h_len); + size_t n_rounds = log_n > log_m ? log_n : log_m; + int res; + secp256k1_bppp_rangeproof_prover_context prover_ctx; + /* Check proof sizes*/ + if (*proof_len < 33 * 4 + (65 * n_rounds) + 64) { + return 0; + } + if (gens->n != (g_offset + h_len)) { + return 0; + } + if (!secp256k1_is_power_of_two(digit_base) || !secp256k1_is_power_of_two(num_digits)) { + return 0; + } + if (n_bits > 64) { + 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; + } + + /* Compute the base digits representation of the value */ + /* Alloc for prover->ctx */ + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + prover_ctx.s = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_offset * sizeof(secp256k1_scalar)); + prover_ctx.d = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, num_digits * sizeof(secp256k1_scalar)); + prover_ctx.m = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, digit_base * sizeof(secp256k1_scalar)); + prover_ctx.r = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, num_digits * sizeof(secp256k1_scalar)); + prover_ctx.c_m = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, digit_base * sizeof(secp256k1_scalar)); + + prover_ctx.r_m_1_vec = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, 8 * sizeof(secp256k1_scalar)); + prover_ctx.r_s_1_vec = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, h_len * sizeof(secp256k1_scalar)); + + prover_ctx.mu_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_offset * sizeof(secp256k1_scalar)); + prover_ctx.mu_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_offset * sizeof(secp256k1_scalar)); + + if ( prover_ctx.s == NULL || prover_ctx.d == NULL || prover_ctx.m == NULL || prover_ctx.r == NULL + || prover_ctx.c_m == NULL || prover_ctx.r_m_1_vec == NULL || prover_ctx.r_s_1_vec == NULL + || prover_ctx.mu_pows == NULL || prover_ctx.mu_inv_pows == NULL ) + { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + /* Initialze the transcript by committing to all the public data */ + secp256k1_bppp_commit_initial_data( + &transcript, + num_digits, + digit_base, + min_value, + commitp, + asset_genp, + extra_commit, + extra_commit_len + ); + + n_proof_bytes_written = 0; + secp256k1_bppp_rangeproof_prove_round1_impl( + &prover_ctx, + gens, + asset_genp, + &proof[n_proof_bytes_written], + &transcript, + num_digits, + digit_base, + value - min_value, + nonce + ); + n_proof_bytes_written += 33 *2; + + secp256k1_bppp_rangeproof_prove_round2_impl( + &prover_ctx, + gens, + asset_genp, + &proof[n_proof_bytes_written], + &transcript, + num_digits, + digit_base, + nonce + ); + n_proof_bytes_written += 33; + + secp256k1_bppp_rangeproof_prove_round3_impl( + &prover_ctx, + gens, + asset_genp, + &proof[n_proof_bytes_written], + &transcript, + num_digits, + digit_base, + gamma, + nonce + ); + n_proof_bytes_written += 33; + + /* Calculate the remaining buffer size. We have already checked that buffer is of correct size */ + norm_proof_len = *proof_len - n_proof_bytes_written; + res = secp256k1_bppp_rangeproof_prove_round4_impl( + ctx, + scratch, + gens, + asset_genp, + &prover_ctx, + &proof[n_proof_bytes_written], + &norm_proof_len, + &transcript, + gamma, + num_digits, + digit_base + ); + /* No need to worry about constant time-ness from this point. All data is public */ + if (res) { + *proof_len = n_proof_bytes_written + norm_proof_len; + } + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return res; +} + +typedef struct secp256k1_bppp_verify_cb_data { + const unsigned char *proof; + const secp256k1_scalar *g_vec_pub_deltas; + const secp256k1_scalar *t_pows; + const secp256k1_scalar *t_inv; + const secp256k1_scalar *v; + const secp256k1_scalar *delta; + const secp256k1_ge *asset_genp; + const secp256k1_ge *commit; + const secp256k1_ge *g_gens; +} secp256k1_bppp_verify_cb_data; + +static int secp256k1_bppp_verify_cb(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + secp256k1_bppp_verify_cb_data *data = (secp256k1_bppp_verify_cb_data*) cbdata; + switch(idx) { + case 0: /* v * asset_genp */ + *pt = *data->asset_genp; + *sc = *data->v; + break; + case 1: /* delta * M */ + if (!secp256k1_eckey_pubkey_parse(pt, data->proof, 33)) { + return 0; + } + *sc = *data->delta; + break; + case 2: /* t * D */ + if (!secp256k1_eckey_pubkey_parse(pt, &data->proof[33], 33)) { + return 0; + } + *sc = data->t_pows[0]; + break; + case 3: /* t^2 * R */ + if (!secp256k1_eckey_pubkey_parse(pt, &data->proof[33 * 2], 33)) { + return 0; + } + *sc = data->t_pows[1]; + break; + case 4: /* t^-1 * S */ + if (!secp256k1_eckey_pubkey_parse(pt, &data->proof[33 * 3], 33)) { + return 0; + } + *sc = *data->t_inv; + break; + case 5: /* 2t^3 * V(commit) */ + *pt = *data->commit; + *sc = data->t_pows[2]; + secp256k1_scalar_add(sc, sc, sc); + break; + default: + idx -= 6; + *pt = data->g_gens[idx]; + *sc = data->g_vec_pub_deltas[idx]; + break; + } + return 1; +} + +static int secp256k1_bppp_rangeproof_verify_impl( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const secp256k1_bppp_generators* gens, + secp256k1_ge* asset_genp, + const unsigned char* proof, + const size_t proof_len, + const size_t n_bits, + const size_t digit_base, + const uint64_t min_value, + secp256k1_ge* commitp, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + int res; + size_t scratch_checkpoint; + secp256k1_sha256 transcript; + size_t num_digits = n_bits / secp256k1_bppp_log2(digit_base); + size_t h_len = 8, i; + size_t g_offset = num_digits > digit_base ? num_digits : digit_base; + size_t log_n = secp256k1_bppp_log2(g_offset), log_m = secp256k1_bppp_log2(h_len); + size_t n_rounds = log_n > log_m ? log_n : log_m; + secp256k1_scalar v_g; + secp256k1_scalar *mu_pows, *mu_inv_pows, *g_vec_pub_deltas; + secp256k1_scalar alpha, rho, mu, mu_inv, x, beta, delta, t; + /* To be re-used as c_vec later */ + secp256k1_scalar t_pows[8]; + + /* Check proof sizes*/ + if (proof_len != 33 * 4 + (65 * n_rounds) + 64) { + return 0; + } + if (gens->n != (g_offset + h_len)) { + return 0; + } + if (!secp256k1_is_power_of_two(digit_base) || !secp256k1_is_power_of_two(num_digits) || !secp256k1_is_power_of_two(n_bits)) { + return 0; + } + if (n_bits > 64) { + return 0; + } + if (extra_commit_len > 0 && extra_commit == NULL) { + return 0; + } + + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + mu_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_offset * sizeof(secp256k1_scalar)); + mu_inv_pows = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_offset * sizeof(secp256k1_scalar)); + g_vec_pub_deltas = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, g_offset * sizeof(secp256k1_scalar)); + + if ( mu_pows == NULL || mu_inv_pows == NULL ) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + /* Obtain the challenges */ + secp256k1_bppp_commit_initial_data( + &transcript, + num_digits, + digit_base, + min_value, + commitp, + asset_genp, + extra_commit, + extra_commit_len + ); + + /* Verify round 1 */ + secp256k1_sha256_write(&transcript, proof, 66); + secp256k1_bppp_challenge_scalar(&alpha, &transcript, 0); + + /* Verify round 2 */ + secp256k1_sha256_write(&transcript, &proof[33*2], 33); + secp256k1_bppp_challenge_scalar(&rho, &transcript, 0); + secp256k1_bppp_challenge_scalar(&x, &transcript, 1); + secp256k1_bppp_challenge_scalar(&beta, &transcript, 2); + secp256k1_bppp_challenge_scalar(&delta, &transcript, 3); + secp256k1_scalar_sqr(&mu, &rho); + secp256k1_scalar_inverse_var(&mu_inv, &mu); + + /* Verify round 3 */ + secp256k1_sha256_write(&transcript, &proof[33*3], 33); + secp256k1_bppp_challenge_scalar(&t, &transcript, 0); + + secp256k1_bppp_rangeproof_powers_of_mu(mu_pows, &mu, g_offset); + secp256k1_bppp_rangeproof_powers_of_mu(mu_inv_pows, &mu_inv, g_offset); + + /* Computes from t^1 to t^7, Others unneeded, will be set to 0 later */ + secp256k1_bppp_rangeproof_powers_of_mu(&t_pows[0], &t, 7); + { + /* g_vec_pub_delta[i] = (b^i*t^2 + (-x*delta)*t^1 + (x/alpha+i)*t^3)*mu_inv^i + alpha*t^1 */ + secp256k1_scalar b_pow_i_t2, neg_x_delta_t1, x_t3, alpha_t1, base; + b_pow_i_t2 = t_pows[1]; + secp256k1_scalar_negate(&neg_x_delta_t1, &x); + secp256k1_scalar_mul(&neg_x_delta_t1, &neg_x_delta_t1, &delta); /* -x */ + secp256k1_scalar_mul(&neg_x_delta_t1, &neg_x_delta_t1, &t_pows[0]); /* -x * delta *t */ + x_t3 = t_pows[2]; + secp256k1_scalar_mul(&x_t3, &x_t3, &x); + alpha_t1 = t_pows[0]; + secp256k1_scalar_mul(&alpha_t1, &alpha_t1, &alpha); + secp256k1_scalar_set_int(&base, digit_base); + + for (i = 0; i < g_offset; i++) { + secp256k1_scalar_clear(&g_vec_pub_deltas[i]); + if (i < num_digits) { + secp256k1_scalar_add(&g_vec_pub_deltas[i], &b_pow_i_t2, &neg_x_delta_t1); /* g_vec_pub_delta[i] = b^i*t^2 + (-x * delta)*t^1 */ + secp256k1_scalar_mul(&b_pow_i_t2, &b_pow_i_t2, &base); /* b^i */ + } + + if (i < digit_base) { + secp256k1_scalar e_plus_i_inv; + secp256k1_scalar_set_int(&e_plus_i_inv, i); /* digit base is less than 2^32, can directly set*/ + secp256k1_scalar_add(&e_plus_i_inv, &e_plus_i_inv, &alpha); /* (alpha + i)*/ + secp256k1_scalar_inverse_var(&e_plus_i_inv, &e_plus_i_inv); /* 1/(alpha +i)*/ + secp256k1_scalar_mul(&e_plus_i_inv, &e_plus_i_inv, &x_t3); /* xt^3/(alpha+i) */ + + secp256k1_scalar_add(&g_vec_pub_deltas[i], &g_vec_pub_deltas[i], &e_plus_i_inv); /* g_vec_pub_delta[i] = b^i*t^2 + (-x * delta)*t^1 + xt^3/(alpha+i) */ + } + + secp256k1_scalar_mul(&g_vec_pub_deltas[i], &g_vec_pub_deltas[i], &mu_inv_pows[i]); /* g_vec_pub_delta[i] = (b^i*t^2 + (-x*delta)*t^1 + (x/alpha+i)*t^3)*mu_inv^i */ + if (i < num_digits) { + secp256k1_scalar_add(&g_vec_pub_deltas[i], &g_vec_pub_deltas[i], &alpha_t1); /* g_vec_pub_delta[i] = (b^i*t^2 + (-x*delta)*t^1 + (x/alpha+i)*t^3)*mu_inv^i + alpha*t^1 */ + } + } + } + + { + /* v = 2*t3( + + ) */ + secp256k1_scalar two_t3, b_pow_i, neg_x_mu_inv_pow_plus_alpha, base, sc_min_v; + secp256k1_scalar_set_int(&two_t3, 2); /* 2 */ + secp256k1_scalar_mul(&two_t3, &two_t3, &t_pows[2]); /* 2*t^3 */ + secp256k1_scalar_set_int(&b_pow_i, 1); /* b^0 */ + secp256k1_scalar_clear(&v_g); /* v_g = 0 */ + secp256k1_scalar_set_int(&base, digit_base); /* base = digit_base */ + + /* Compute v_g = 2*t5( + + ) */ + for (i = 0; i < num_digits; i++) { + secp256k1_scalar_add(&v_g, &v_g, &mu_pows[i]); /* v_g = */ + secp256k1_scalar_mul(&neg_x_mu_inv_pow_plus_alpha, &x, &delta); /* x * delta */ + secp256k1_scalar_negate(&neg_x_mu_inv_pow_plus_alpha, &neg_x_mu_inv_pow_plus_alpha); /* -x *delta*/ + secp256k1_scalar_mul(&neg_x_mu_inv_pow_plus_alpha, &mu_inv_pows[i], &neg_x_mu_inv_pow_plus_alpha); /* -x*mu^-(i+1) */ + secp256k1_scalar_add(&neg_x_mu_inv_pow_plus_alpha, &neg_x_mu_inv_pow_plus_alpha, &alpha); /* -x*mu^-(i+1) + alpha */ + secp256k1_scalar_mul(&neg_x_mu_inv_pow_plus_alpha, &neg_x_mu_inv_pow_plus_alpha, &b_pow_i); /* */ + secp256k1_scalar_add(&v_g, &v_g, &neg_x_mu_inv_pow_plus_alpha); /* v_g = + */ + secp256k1_scalar_mul(&b_pow_i, &b_pow_i, &base); + } + secp256k1_scalar_set_int(&sc_min_v, min_value); + secp256k1_scalar_negate(&sc_min_v, &sc_min_v); + secp256k1_scalar_add(&v_g, &v_g, &sc_min_v); /* v_g = + - min_value */ + secp256k1_scalar_mul(&v_g, &v_g, &two_t3); /* v_g = 2*t5( + + - min_value) */ + } + /* Ecmult to compute C = S/T + M*delta + t*D + t^2R + 2t^3V + + v_g*A(asset_genP) */ + { + secp256k1_bppp_verify_cb_data cb_data; + secp256k1_gej c_commj; + secp256k1_ge c_comm; + secp256k1_scalar c_poly[8], t_inv; + size_t num_points; + cb_data.g_vec_pub_deltas = g_vec_pub_deltas; + cb_data.v = &v_g; + cb_data.asset_genp = asset_genp; + cb_data.commit = commitp; + cb_data.g_gens = gens->gens; + cb_data.proof = proof; + cb_data.t_pows = t_pows; + cb_data.delta = δ + secp256k1_scalar_inverse(&t_inv, &t); + cb_data.t_inv = &t_inv; + num_points = 6 + g_offset; + + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &c_commj, NULL, secp256k1_bppp_verify_cb, (void*) &cb_data, num_points)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + secp256k1_ge_set_gej_var(&c_comm, &c_commj); + /* Make c = beta*(1/T, T, T^2, T^3, T^5, T^6, T^7, 0) */ + c_poly[0] = t_inv; + c_poly[1] = t_pows[0]; + c_poly[2] = t_pows[1]; + c_poly[3] = t_pows[2]; + c_poly[4] = t_pows[4]; + c_poly[5] = t_pows[5]; + c_poly[6] = t_pows[6]; + for (i = 0; i < 7; i++) { + secp256k1_scalar_mul(&c_poly[i], &c_poly[i], &beta); + } + secp256k1_scalar_clear(&c_poly[7]); + + res = secp256k1_bppp_rangeproof_norm_product_verify( + ctx, + scratch, + &proof[33*4], + proof_len - 33*4, + &transcript, + &rho, + gens, + asset_genp, + g_offset, + c_poly, + 8, + &c_comm + ); + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return res; + } +} + +#endif diff --git a/src/modules/bppp/bppp_transcript_impl.h b/src/modules/bppp/bppp_transcript_impl.h index 5fe9b96c9..93ffb4793 100644 --- a/src/modules/bppp/bppp_transcript_impl.h +++ b/src/modules/bppp/bppp_transcript_impl.h @@ -27,6 +27,33 @@ static void secp256k1_bppp_sha256_tagged_commitment_init(secp256k1_sha256 *sha) sha->bytes = 64; } +static void secp256k1_bppp_commit_initial_data( + secp256k1_sha256* transcript, + const uint64_t num_digits, + const uint64_t digits_base, + const uint64_t min_value, + secp256k1_ge* commitp, + secp256k1_ge* asset_genp, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + unsigned char scratch[65]; + secp256k1_bppp_sha256_tagged_commitment_init(transcript); + secp256k1_bppp_le64(scratch, num_digits); + secp256k1_sha256_write(transcript, scratch, 8); + secp256k1_bppp_le64(scratch, digits_base); + secp256k1_sha256_write(transcript, scratch, 8); + secp256k1_bppp_le64(scratch, min_value); + secp256k1_sha256_write(transcript, scratch, 8); + secp256k1_bppp_serialize_points(scratch, commitp, asset_genp); + secp256k1_sha256_write(transcript, scratch, 65); + if (extra_commit != NULL) { + secp256k1_bppp_le64(scratch, (uint64_t) extra_commit_len); + secp256k1_sha256_write(transcript, scratch, 8); + secp256k1_sha256_write(transcript, extra_commit, extra_commit_len); + } +} + /* Obtain a challenge scalar from the current transcript.*/ static void secp256k1_bppp_challenge_scalar(secp256k1_scalar* ch, const secp256k1_sha256 *transcript, uint64_t idx) { unsigned char buf[32]; diff --git a/src/modules/bppp/main_impl.h b/src/modules/bppp/main_impl.h index 49a09447d..3589e494d 100644 --- a/src/modules/bppp/main_impl.h +++ b/src/modules/bppp/main_impl.h @@ -14,6 +14,7 @@ #include "../../util.h" #include "../bppp/main.h" #include "../bppp/bppp_norm_product_impl.h" +#include "../bppp/bppp_rangeproof_impl.h" secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_context *ctx, size_t n) { secp256k1_bppp_generators *ret; @@ -23,6 +24,11 @@ secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_cont VERIFY_CHECK(ctx != NULL); + /* Must have atleast 8 generators */ + if (n <= 8) { + return NULL; + } + ret = (secp256k1_bppp_generators *)checked_malloc(&ctx->error_callback, sizeof(*ret)); if (ret == NULL) { return NULL; @@ -41,6 +47,11 @@ secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_cont for (i = 0; i < n; i++) { secp256k1_generator gen; unsigned char tmp[32] = { 0 }; + if (i == n - 8) { + /* The first generator in H is secp G */ + ret->gens[i] = secp256k1_ge_const_g; + continue; + } secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); CHECK(secp256k1_generator_generate(ctx, &gen, tmp)); secp256k1_generator_load(&ret->gens[i], &gen); @@ -49,67 +60,147 @@ secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_cont return ret; } -secp256k1_bppp_generators* secp256k1_bppp_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) { - size_t n = data_len / 33; - secp256k1_bppp_generators* ret; - +void secp256k1_bppp_generators_destroy(const secp256k1_context* ctx, secp256k1_bppp_generators *gens) { VERIFY_CHECK(ctx != NULL); - ARG_CHECK(data != NULL); - - if (data_len % 33 != 0) { - return NULL; + (void) ctx; + if (gens != NULL) { + free(gens->gens); + free(gens); } +} - ret = (secp256k1_bppp_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; +size_t secp256k1_bppp_rangeproof_proof_length( + const secp256k1_context* ctx, + size_t n_bits, + size_t base +) { + size_t num_digits, n_rounds, g_len, h_len; + VERIFY_CHECK(ctx != NULL); + if (n_bits > 64 || base < 2 || base > 64) { + return 0; } - 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); + if (!secp256k1_is_power_of_two(base) || !secp256k1_is_power_of_two(n_bits)) { + return 0; } - return ret; + num_digits = n_bits / secp256k1_bppp_log2(base); + if (!secp256k1_is_power_of_two(num_digits)) { + return 0; + } + g_len = num_digits > base ? num_digits : base; + h_len = 8; + n_rounds = secp256k1_bppp_log2(g_len > h_len ? g_len : h_len); + return 33 * 4 + 65*n_rounds + 64; } -int secp256k1_bppp_generators_serialize(const secp256k1_context* ctx, const secp256k1_bppp_generators* gens, unsigned char* data, size_t *data_len) { - size_t i; +int secp256k1_bppp_rangeproof_prove( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_bppp_generators* gens, + const secp256k1_generator* asset_gen, + unsigned char* proof, + size_t* plen, + const size_t n_bits, + const size_t base, + 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* extra_commit, + size_t extra_commit_len +) { + secp256k1_ge commitp, asset_genp; + secp256k1_scalar blinds; + int overflow; VERIFY_CHECK(ctx != NULL); + VERIFY_CHECK(scratch != NULL); ARG_CHECK(gens != NULL); - ARG_CHECK(data != NULL); - ARG_CHECK(data_len != NULL); - ARG_CHECK(*data_len >= 33 * gens->n); - - memset(data, 0, *data_len); - 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); + 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); + + secp256k1_scalar_set_b32(&blinds, blind, &overflow); + if (overflow) { + return 0; } - *data_len = 33 * gens->n; - return 1; + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&asset_genp, asset_gen); + secp256k1_fe_normalize_var(&commitp.x); + secp256k1_fe_normalize_var(&commitp.y); + secp256k1_fe_normalize_var(&asset_genp.x); + secp256k1_fe_normalize_var(&asset_genp.y); + + return secp256k1_bppp_rangeproof_prove_impl( + ctx, + scratch, + gens, + &asset_genp, + proof, + plen, + n_bits, + base, + value, + min_value, + &commitp, + &blinds, + nonce, + extra_commit, + extra_commit_len + ); } -void secp256k1_bppp_generators_destroy(const secp256k1_context* ctx, secp256k1_bppp_generators *gens) { +int secp256k1_bppp_rangeproof_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_bppp_generators* gens, + const secp256k1_generator* asset_gen, + const unsigned char* proof, + const size_t plen, + const uint64_t n_bits, + const uint64_t base, + const uint64_t min_value, + const secp256k1_pedersen_commitment* commit, + const unsigned char* extra_commit, + size_t extra_commit_len +) { + secp256k1_ge commitp, asset_genp; + VERIFY_CHECK(ctx != NULL); - (void) ctx; - if (gens != NULL) { - free(gens->gens); - free(gens); - } + VERIFY_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); + + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&asset_genp, asset_gen); + secp256k1_fe_normalize_var(&commitp.x); + secp256k1_fe_normalize_var(&commitp.y); + secp256k1_fe_normalize_var(&asset_genp.x); + secp256k1_fe_normalize_var(&asset_genp.y); + + return secp256k1_bppp_rangeproof_verify_impl( + ctx, + scratch, + gens, + &asset_genp, + proof, + plen, + n_bits, + base, + min_value, + &commitp, + extra_commit, + extra_commit_len + ); } #endif diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index ce6cc3186..0bf64b2b7 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -10,116 +10,27 @@ #include #include "../../../include/secp256k1_bppp.h" -#include "bppp_norm_product_impl.h" -#include "bppp_util.h" -#include "bppp_transcript_impl.h" +#include "../bppp/bppp_norm_product_impl.h" +#include "../bppp/bppp_util.h" +#include "../bppp/bppp_transcript_impl.h" #include "test_vectors/verify.h" #include "test_vectors/prove.h" static void test_bppp_generators_api(void) { secp256k1_bppp_generators *gens; - secp256k1_bppp_generators *gens_orig; - unsigned char gens_ser[330]; - size_t len = sizeof(gens_ser); int32_t ecount = 0; - /* The BP generator API requires no precomp */ secp256k1_context_set_error_callback(CTX, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount); /* Create */ gens = secp256k1_bppp_generators_create(CTX, 10); CHECK(gens != NULL && ecount == 0); - gens_orig = gens; /* Preserve for round-trip test */ - - /* Serialize */ - ecount = 0; - CHECK(!secp256k1_bppp_generators_serialize(CTX, NULL, gens_ser, &len)); - CHECK(ecount == 1); - CHECK(!secp256k1_bppp_generators_serialize(CTX, gens, NULL, &len)); - CHECK(ecount == 2); - CHECK(!secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, NULL)); - CHECK(ecount == 3); - len = 0; - CHECK(!secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); - CHECK(ecount == 4); - len = sizeof(gens_ser) - 1; - CHECK(!secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); - CHECK(ecount == 5); - len = sizeof(gens_ser); - { - /* Output buffer can be greater than minimum needed */ - unsigned char gens_ser_tmp[331]; - size_t len_tmp = sizeof(gens_ser_tmp); - CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser_tmp, &len_tmp)); - CHECK(len_tmp == sizeof(gens_ser_tmp) - 1); - CHECK(ecount == 5); - } - /* Parse */ - CHECK(secp256k1_bppp_generators_serialize(CTX, gens, gens_ser, &len)); - ecount = 0; - gens = secp256k1_bppp_generators_parse(CTX, NULL, sizeof(gens_ser)); - CHECK(gens == NULL && ecount == 1); - /* Not a multiple of 33 */ - gens = secp256k1_bppp_generators_parse(CTX, gens_ser, sizeof(gens_ser) - 1); - CHECK(gens == NULL && ecount == 1); - gens = secp256k1_bppp_generators_parse(CTX, gens_ser, sizeof(gens_ser)); - CHECK(gens != NULL && ecount == 1); - /* Not valid generators */ - memset(gens_ser, 1, sizeof(gens_ser)); - CHECK(secp256k1_bppp_generators_parse(CTX, 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_bppp_generators_destroy(CTX, NULL); secp256k1_bppp_generators_destroy(CTX, gens); - secp256k1_bppp_generators_destroy(CTX, gens_orig); CHECK(ecount == 0); - - secp256k1_context_set_error_callback(CTX, NULL, NULL); - secp256k1_context_set_illegal_callback(CTX, NULL, NULL); -} - -static void test_bppp_generators_fixed(void) { - secp256k1_bppp_generators *gens = secp256k1_bppp_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_bppp_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_bppp_generators_serialize(CTX, gens, gens_ser, &len)); - CHECK(memcmp(gens_ser, fixed_first_3, sizeof(fixed_first_3)) == 0); - - secp256k1_bppp_generators_destroy(CTX, gens); } static void test_bppp_tagged_hash(void) { @@ -257,12 +168,12 @@ static void test_serialize_two_points(void) { random_group_element_test(&R); secp256k1_bppp_serialize_points(buf, &X, &R); - buf[0] = 4 + (unsigned char)secp256k1_testrandi64(0, 253); + buf[0] = 4 + (unsigned char)secp256k1_testrandi64(0, 251); /* min,max inclusive*/ /* Assert that buf[0] is actually invalid. */ - CHECK(buf[0] != 0x02 && buf[0] != 0x03); + CHECK(buf[0] > 0x03); CHECK(!secp256k1_bppp_parse_one_of_points(&X_tmp, buf, 0)); - CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 0)); + CHECK(!secp256k1_bppp_parse_one_of_points(&R_tmp, buf, 1)); } /* Check that sign bit is 0 for point at infinity */ for (i = 0; i < COUNT; i++) { @@ -360,6 +271,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove_const( const secp256k1_scalar* rho, const secp256k1_ge* g_vec, size_t g_vec_len, + const secp256k1_ge* asset_genp, const secp256k1_scalar* n_vec, size_t n_vec_len, const secp256k1_scalar* l_vec, @@ -384,6 +296,7 @@ static int secp256k1_bppp_rangeproof_norm_product_prove_const( rho, gs, g_vec_len, + asset_genp, ns, n_vec_len, ls, @@ -407,6 +320,7 @@ static int secp256k1_norm_arg_prove( size_t *proof_len, const secp256k1_scalar* rho, const secp256k1_bppp_generators* gens_vec, + const secp256k1_ge* asset_genp, const secp256k1_scalar* n_vec, size_t n_vec_len, const secp256k1_scalar* l_vec, @@ -418,7 +332,7 @@ static int secp256k1_norm_arg_prove( secp256k1_sha256 transcript; secp256k1_norm_arg_commit_initial_data(&transcript, rho, gens_vec, n_vec_len, c_vec, c_vec_len, commit); - return secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, proof, proof_len, &transcript, rho, gens_vec->gens, gens_vec->n, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len); + return secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, proof, proof_len, &transcript, rho, gens_vec->gens, gens_vec->n, asset_genp, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len); } /* Verify the proof */ @@ -428,6 +342,7 @@ static int secp256k1_norm_arg_verify( size_t proof_len, const secp256k1_scalar* rho, const secp256k1_bppp_generators* gens_vec, + const secp256k1_ge* asset_genp, size_t g_len, const secp256k1_scalar* c_vec, size_t c_vec_len, @@ -448,6 +363,7 @@ static int secp256k1_norm_arg_verify( &transcript, rho, gens_vec, + asset_genp, g_len, c_vec, c_vec_len, @@ -460,24 +376,25 @@ static int secp256k1_norm_arg_verify( static void norm_arg_verify_zero_len(void) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; secp256k1_scalar rho, mu; - secp256k1_ge commit; + secp256k1_ge commit, asset_genp; secp256k1_scratch *scratch = secp256k1_scratch_space_create(CTX, 1000*10); /* shouldn't need much */ unsigned char proof[1000]; unsigned int n_vec_len = 1; unsigned int c_vec_len = 1; - secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(CTX, n_vec_len + c_vec_len); + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(CTX, 9); /* requires e generators, but the API needs 8.*/ size_t plen = sizeof(proof); + secp256k1_generator_load(&asset_genp, secp256k1_generator_h); random_scalar_order(&rho); secp256k1_scalar_sqr(&mu, &rho); random_scalar_order(&n_vec[0]); random_scalar_order(&c_vec[0]); random_scalar_order(&l_vec[0]); - CHECK(secp256k1_bppp_commit(CTX, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); - CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); - CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); - CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n_vec_len, c_vec, 0, &commit)); + CHECK(secp256k1_bppp_commit(CTX, scratch, &commit, gs, &asset_genp, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + CHECK(secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, &asset_genp, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, &asset_genp, n_vec_len, c_vec, c_vec_len, &commit)); + CHECK(!secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, &asset_genp, n_vec_len, c_vec, 0, &commit)); secp256k1_bppp_generators_destroy(CTX, gs); @@ -487,7 +404,7 @@ static void norm_arg_verify_zero_len(void) { static void norm_arg_test(unsigned int n, unsigned int m) { secp256k1_scalar n_vec[64], l_vec[64], c_vec[64]; secp256k1_scalar rho, mu; - secp256k1_ge commit; + secp256k1_ge commit, asset_genp; size_t i, plen; int res; secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(CTX, n + m); @@ -496,6 +413,7 @@ static void norm_arg_test(unsigned int n, unsigned int m) { plen = 1000; random_scalar_order(&rho); secp256k1_scalar_sqr(&mu, &rho); + secp256k1_generator_load(&asset_genp, secp256k1_generator_h); for (i = 0; i < n; i++) { random_scalar_order(&n_vec[i]); @@ -506,20 +424,20 @@ static void norm_arg_test(unsigned int n, unsigned int m) { random_scalar_order(&c_vec[i]); } - res = secp256k1_bppp_commit(CTX, scratch, &commit, gs, n_vec, n, l_vec, m, c_vec, m, &mu); + res = secp256k1_bppp_commit(CTX, scratch, &commit, gs, &asset_genp, n_vec, n, l_vec, m, c_vec, m, &mu); CHECK(res == 1); - res = secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, n_vec, n, l_vec, m, c_vec, m, &commit); + res = secp256k1_norm_arg_prove(scratch, proof, &plen, &rho, gs, &asset_genp, n_vec, n, l_vec, m, c_vec, m, &commit); CHECK(res == 1); - res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, &asset_genp, n, c_vec, m, &commit); CHECK(res == 1); /* Changing any of last two scalars should break the proof */ proof[plen - 1] ^= 1; - res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, &asset_genp, n, c_vec, m, &commit); CHECK(res == 0); proof[plen - 1 - 32] ^= 1; - res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, n, c_vec, m, &commit); + res = secp256k1_norm_arg_verify(scratch, proof, plen, &rho, gs, &asset_genp, n, c_vec, m, &commit); CHECK(res == 0); secp256k1_scratch_space_destroy(CTX, scratch); @@ -561,6 +479,7 @@ int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned ch secp256k1_bppp_generators *gs = bppp_generators_parse_regular(gens, 33*(n_vec_len + c_vec_len)); secp256k1_scalar rho; secp256k1_ge commit; + secp256k1_ge g_asset_gen = secp256k1_ge_const_g; /* For fixed tests in norm-arg, we generate them using asset-gen as G */ int overflow; int i; int ret; @@ -576,7 +495,7 @@ int norm_arg_verify_vectors_helper(secp256k1_scratch *scratch, const unsigned ch CHECK(!overflow); } CHECK(secp256k1_ge_parse_ext(&commit, commit33)); - ret = secp256k1_bppp_rangeproof_norm_product_verify(CTX, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit); + ret = secp256k1_bppp_rangeproof_norm_product_verify(CTX, scratch, proof, plen, &transcript, &rho, gs, &g_asset_gen, n_vec_len, c_vec, c_vec_len, &commit); secp256k1_bppp_generators_destroy(CTX, gs); return ret; @@ -616,6 +535,7 @@ static void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsi size_t myplen = sizeof(myproof); int overflow; int i; + secp256k1_ge g_asset_gen = secp256k1_ge_const_g; /* For fixed tests in norm-arg, we generate them using asset-gen as G */ CHECK(gs != NULL); secp256k1_sha256_initialize(&transcript); @@ -634,7 +554,7 @@ static void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsi CHECK(!overflow); } - CHECK(secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, myproof, &myplen, &transcript, &rho, gs->gens, gs->n, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len) == result); + CHECK(secp256k1_bppp_rangeproof_norm_product_prove_const(scratch, myproof, &myplen, &transcript, &rho, gs->gens, gs->n, &g_asset_gen, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len) == result); if (!result) { secp256k1_bppp_generators_destroy(CTX, gs); return; @@ -642,9 +562,9 @@ static void norm_arg_prove_vectors_helper(secp256k1_scratch *scratch, const unsi CHECK(plen == myplen); CHECK(secp256k1_memcmp_var(proof, myproof, plen) == 0); - CHECK(secp256k1_bppp_commit(CTX, scratch, &commit, gs, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); + CHECK(secp256k1_bppp_commit(CTX, scratch, &commit, gs, &g_asset_gen, n_vec, n_vec_len, l_vec, c_vec_len, c_vec, c_vec_len, &mu)); secp256k1_sha256_initialize(&transcript); - CHECK(secp256k1_bppp_rangeproof_norm_product_verify(CTX, scratch, proof, plen, &transcript, &rho, gs, n_vec_len, c_vec, c_vec_len, &commit)); + CHECK(secp256k1_bppp_rangeproof_norm_product_verify(CTX, scratch, proof, plen, &transcript, &rho, gs, &g_asset_gen, n_vec_len, c_vec, c_vec_len, &commit)); secp256k1_bppp_generators_destroy(CTX, gs); } @@ -671,18 +591,54 @@ static void norm_arg_prove_vectors(void) { #undef IDX_TO_TEST +static void rangeproof_test(size_t digit_base, size_t num_bits, uint64_t value, uint64_t min_value) { + secp256k1_generator asset_genp; + size_t plen; + size_t num_digits = num_bits/secp256k1_bppp_log2(digit_base); + size_t n = num_digits > digit_base ? num_digits : digit_base; + size_t res; + 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"; + /* 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); + secp256k1_sha256 transcript; + secp256k1_bppp_generators *gs = secp256k1_bppp_generators_create(CTX, n + 8); + secp256k1_scratch *scratch = secp256k1_scratch_space_create(CTX, 1000*1000); /* shouldn't need much */ + unsigned char proof[1000]; + plen = 1000; + asset_genp = *secp256k1_generator_h; + CHECK(secp256k1_pedersen_commit(CTX, &commit, blind, value, &asset_genp)); + secp256k1_sha256_initialize(&transcript); + + + res = secp256k1_bppp_rangeproof_prove(CTX, scratch, gs, &asset_genp, proof, &plen, num_bits, digit_base, value, min_value, &commit, blind, nonce, extra_commit, extra_commit_len); + CHECK(res == 1); + + res = secp256k1_bppp_rangeproof_verify(CTX, scratch, gs, &asset_genp, proof, plen, num_bits, digit_base, min_value, &commit, extra_commit, extra_commit_len); + CHECK(res == 1); + + proof[plen - 1] ^= 1; + res = secp256k1_bppp_rangeproof_verify(CTX, scratch, gs, &asset_genp, proof, plen, num_bits, digit_base, min_value, &commit, extra_commit, extra_commit_len); + CHECK(res == 0); + secp256k1_bppp_generators_destroy(CTX, gs); + secp256k1_scratch_space_destroy(CTX, scratch); +} + static void run_bppp_tests(void) { + /* Update the global context for all bppp tests*/ + size_t i; test_log_exp(); test_norm_util_helpers(); test_serialize_two_points(); test_bppp_generators_api(); - test_bppp_generators_fixed(); test_bppp_tagged_hash(); - norm_arg_verify_zero_len(); - norm_arg_test(1, 1); + /* norm_arg_verify_zero_len(); */ + norm_arg_test(1, 8); norm_arg_test(1, 64); - norm_arg_test(64, 1); + norm_arg_test(64, 8); norm_arg_test(32, 32); norm_arg_test(32, 64); norm_arg_test(64, 32); @@ -690,6 +646,19 @@ static void run_bppp_tests(void) { norm_arg_verify_vectors(); norm_arg_prove_vectors(); + + for (i = 0; i < 16; i++) { + rangeproof_test(2, 4, i, i/2); + } + + rangeproof_test(16, 4, 7, 3); + rangeproof_test(16, 8, 243, 129); + rangeproof_test(16, 16, 12431, 6332); + rangeproof_test(16, 32, 134132, 57251); + for (i = 0; i < 100; i++) { + uint64_t v = secp256k1_testrand64(); + rangeproof_test(16, 64, v, 0); + } } #endif diff --git a/src/scalar.h b/src/scalar.h index c9193ffaf..ce480362b 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -105,4 +105,7 @@ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_ /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); +/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 7cd334765..9d02e70f6 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -999,6 +999,93 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#if defined(SECP256K1_BIG_ENDIAN) +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#elif defined(SECP256K1_LITTLE_ENDIAN) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[3] = (((uint64_t) x0) << 32) | x1; + r1->d[2] = (((uint64_t) x2) << 32) | x3; + r1->d[1] = (((uint64_t) x4) << 32) | x5; + r1->d[0] = (((uint64_t) x6) << 32) | x7; + r2->d[3] = (((uint64_t) x8) << 32) | x9; + r2->d[2] = (((uint64_t) x10) << 32) | x11; + r2->d[1] = (((uint64_t) x12) << 32) | x13; + r2->d[0] = (((uint64_t) x14) << 32) | x15; + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef LE32 + static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) { const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index e7091857f..448ef0c8b 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -751,6 +751,101 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#if defined(SECP256K1_BIG_ENDIAN) +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#elif defined(SECP256K1_LITTLE_ENDIAN) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[7] = x0; + r1->d[6] = x1; + r1->d[5] = x2; + r1->d[4] = x3; + r1->d[3] = x4; + r1->d[2] = x5; + r1->d[1] = x6; + r1->d[0] = x7; + r2->d[7] = x8; + r2->d[6] = x9; + r2->d[5] = x10; + r2->d[4] = x11; + r2->d[3] = x12; + r2->d[2] = x13; + r2->d[1] = x14; + r2->d[0] = x15; + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef LE32 + static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) { const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index f78075567..17ebc53ae 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -129,6 +129,11 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se *r = (*r & mask0) | (*a & mask1); } +SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { + *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; + *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; +} + static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { int i; *r = 0; diff --git a/src/tests.c b/src/tests.c index 300a0324f..1b4f64ba2 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2368,6 +2368,114 @@ static void run_scalar_set_b32_seckey_tests(void) { CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } +static void scalar_chacha_tests(void) { + /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A + * Note that scalar_set_b32 and scalar_get_b32 represent integers + * underlying the scalar in big-endian format. */ + unsigned char expected1[64] = { + 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, + 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, + 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, + 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, + 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, + 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, + 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, + 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 + }; + unsigned char expected2[64] = { + 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, + 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, + 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, + 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, + 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, + 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, + 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, + 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b + }; + unsigned char seed3[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + unsigned char expected3[64] = { + 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, + 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, + 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, + 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, + 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, + 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, + 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, + 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 + }; + unsigned char seed4[32] = { + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char expected4[64] = { + 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, + 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, + 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, + 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, + 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, + 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, + 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, + 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e + }; + unsigned char seed5[32] = { + 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, + 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, + 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, + 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 + }; + unsigned char expected5[64] = { + 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, + 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, + 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, + 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, + 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, + 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, + 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, + 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 + }; + + secp256k1_scalar exp_r1, exp_r2; + secp256k1_scalar r1, r2; + unsigned char seed0[32] = { 0 }; + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); + secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); + secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); + secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); +} + static void run_scalar_tests(void) { int i; for (i = 0; i < 128 * COUNT; i++) { @@ -2377,6 +2485,8 @@ static void run_scalar_tests(void) { run_scalar_set_b32_seckey_tests(); } + scalar_chacha_tests(); + { /* Check that the scalar constants secp256k1_scalar_zero and secp256k1_scalar_one contain the expected values. */ @@ -7917,6 +8027,11 @@ int main(int argc, char **argv) { memcpy(STATIC_CTX, secp256k1_context_static, sizeof(secp256k1_context)); CHECK(!secp256k1_context_is_proper(STATIC_CTX)); +#ifdef ENABLE_MODULE_BPPP + run_bppp_tests(); + return 0; +#endif + /*** Run actual tests ***/ /* selftest tests */ diff --git a/src/util.h b/src/util.h index 9c0eb0fdd..cc36bc584 100644 --- a/src/util.h +++ b/src/util.h @@ -220,6 +220,31 @@ SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { # define SECP256K1_GNUC_EXT #endif +/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */ +#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN) +/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */ +# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \ + defined(__i486__) || defined(__i586__) || defined(__i686__) || \ + defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \ + defined(__ARMEL__) || defined(__AARCH64EL__) || \ + (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \ + (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \ + defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */ +# define SECP256K1_LITTLE_ENDIAN +# endif +# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ + defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \ + defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \ + (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \ + (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1) +# define SECP256K1_BIG_ENDIAN +# endif +#endif +#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN) +# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h. +#endif + /* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { unsigned char *p = (unsigned char *)s;