From bd067945ead3b514fba884abd0de95fc4b5db9ae Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sun, 17 May 2015 21:29:02 +0000 Subject: [PATCH] Pedersen commitments, borromean ring signatures, and ZK range proofs. This commit adds three new cryptosystems to libsecp256k1: Pedersen commitments are a system for making blinded commitments to a value. Functionally they work like: commit_b,v = H(blind_b || value_v), except they are additively homorphic, e.g. C(b1, v1) - C(b2, v2) = C(b1 - b2, v1 - v2) and C(b1, v1) - C(b1, v1) = 0, etc. The commitments themselves are EC points, serialized as 33 bytes. In addition to the commit function this implementation includes utility functions for verifying that a set of commitments sums to zero, and for picking blinding factors that sum to zero. If the blinding factors are uniformly random, pedersen commitments have information theoretic privacy. Borromean ring signatures are a novel efficient ring signature construction for AND/OR admissions policies (the code here implements an AND of ORs, each of any size). This construction requires 32 bytes of signature per pubkey used plus 32 bytes of constant overhead. With these you can construct signatures like "Given pubkeys A B C D E F G, the signer knows the discrete logs satisifying (A || B) & (C || D || E) & (F || G)". ZK range proofs allow someone to prove a pedersen commitment is in a particular range (e.g. [0..2^64)) without revealing the specific value. The construction here is based on the above borromean ring signature and uses a radix-4 encoding and other optimizations to maximize efficiency. It also supports encoding proofs with a non-private base-10 exponent and minimum-value to allow trading off secrecy for size and speed (or just avoiding wasting space keeping data private that was already public due to external constraints). A proof for a 32-bit mantissa takes 2564 bytes, but 2048 bytes of this can be used to communicate a private message to a receiver who shares a secret random seed with the prover. --- Makefile.am | 9 +- configure.ac | 6 + include/secp256k1.h | 168 ++++++++++ src/bench_rangeproof.c | 63 ++++ src/borromean.h | 24 ++ src/borromean_impl.h | 200 +++++++++++ src/ecmult_gen.h | 19 ++ src/ecmult_gen_impl.h | 116 +++++++ src/group_impl.h | 13 + src/rangeproof.h | 31 ++ src/rangeproof_impl.h | 728 +++++++++++++++++++++++++++++++++++++++++ src/secp256k1.c | 171 +++++++++- src/testrand_impl.h | 17 + src/tests.c | 334 ++++++++++++++++++- src/util.h | 31 +- 15 files changed, 1924 insertions(+), 6 deletions(-) create mode 100644 src/bench_rangeproof.c create mode 100644 src/borromean.h create mode 100644 src/borromean_impl.h create mode 100644 src/rangeproof.h create mode 100644 src/rangeproof_impl.h diff --git a/Makefile.am b/Makefile.am index ea9770e01..fc3eb9e61 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,6 +40,10 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.h +noinst_HEADERS += src/borromean.h +noinst_HEADERS += src/borromean_impl.h +noinst_HEADERS += src/rangeproof.h +noinst_HEADERS += src/rangeproof_impl.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libsecp256k1.pc @@ -51,7 +55,7 @@ libsecp256k1_la_LIBADD = $(SECP_LIBS) noinst_PROGRAMS = if USE_BENCHMARK -noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal bench_ecdh +noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_rangeproof bench_internal bench_ecdh bench_verify_SOURCES = src/bench_verify.c bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) bench_verify_LDFLAGS = -static @@ -61,6 +65,9 @@ bench_recover_LDFLAGS = -static bench_sign_SOURCES = src/bench_sign.c bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) bench_sign_LDFLAGS = -static +bench_rangeproof_SOURCES = src/bench_rangeproof.c +bench_rangeproof_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_rangeproof_LDFLAGS = -static bench_internal_SOURCES = src/bench_internal.c bench_internal_LDADD = $(SECP_LIBS) bench_internal_LDFLAGS = -static diff --git a/configure.ac b/configure.ac index 3dc182951..7f415e7f5 100644 --- a/configure.ac +++ b/configure.ac @@ -116,6 +116,12 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], [ AC_MSG_RESULT([no]) ]) +AC_MSG_CHECKING([for __builtin_clzll]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() { __builtin_clzll(1);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_CLZLL,1,[Define this symbol if __builtin_clzll is available]) ], + [ AC_MSG_RESULT([no]) + ]) + if test x"$req_asm" = x"auto"; then SECP_64BIT_ASM_CHECK if test x"$has_64bit_asm" = x"yes"; then diff --git a/include/secp256k1.h b/include/secp256k1.h index 722957948..fef28cd9e 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -5,6 +5,8 @@ extern "C" { # endif +#include + # if !defined(SECP256K1_GNUC_PREREQ) # if defined(__GNUC__)&&defined(__GNUC_MINOR__) # define SECP256K1_GNUC_PREREQ(_maj,_min) \ @@ -50,6 +52,8 @@ typedef struct secp256k1_context_struct secp256k1_context_t; /** Flags to pass to secp256k1_context_create. */ # define SECP256K1_CONTEXT_VERIFY (1 << 0) # define SECP256K1_CONTEXT_SIGN (1 << 1) +# define SECP256K1_CONTEXT_COMMIT (1 << 7) +# define SECP256K1_CONTEXT_RANGEPROOF (1 << 8) /** Create a secp256k1 context object. * Returns: a newly created context object. @@ -358,6 +362,170 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( ) SECP256K1_ARG_NONNULL(1); +/** Generate a pedersen commitment. + * Returns 1: commitment successfully created. + * 0: error + * In: ctx: pointer to a context object, initialized for signing and commitment (cannot be NULL) + * blind: pointer to a 32-byte blinding factor (cannot be NULL) + * value: unsigned 64-bit integer value to commit to. + * Out: commit: pointer to a 33-byte array for the commitment (cannot be NULL) + * + * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( + const secp256k1_context_t* ctx, + unsigned char *commit, + unsigned char *blind, + uint64_t value +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Computes the sum of multiple positive and negative blinding factors. + * Returns 1: sum successfully computed. + * 0: error + * In: ctx: pointer to a context object (cannot be NULL) + * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) + * n: number of factors pointed to by blinds. + * nneg: how many of the initial factors should be treated with a positive sign. + * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( + const secp256k1_context_t* ctx, + unsigned char *blind_out, + const unsigned char * const *blinds, + int n, + int npositive +)SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a tally of pedersen commitments + * Returns 1: commitments successfully sum to zero. + * 0: Commitments do not sum to zero or other error. + * In: ctx: pointer to a context object, initialized for commitment (cannot be NULL) + * commits: pointer to pointers to 33-byte character arrays for the commitments. (cannot be NULL if pcnt is non-zero) + * pcnt: number of commitments pointed to by commits. + * ncommits: pointer to pointers to 33-byte character arrays for negative commitments. (cannot be NULL if ncnt is non-zero) + * ncnt: number of commitments pointed to by ncommits. + * excess: signed 64bit amount to add to the total to bring it to zero, can be negative. + * + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) - excess*H == 0. + * + * A pedersen commitment is xG + vH where G and H are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero both their blinding factors and + * values must sum to zero. + * + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( + const secp256k1_context_t* ctx, + const unsigned char * const *commits, + int pcnt, + const unsigned char * const *ncommits, + int ncnt, + int64_t excess +)SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Verify a proof that a committed value is within a range. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. + * 0: Proof failed or other error. + * In: ctx: pointer to a context object, initialized for range-proof and commitment (cannot be NULL) + * commit: the 33-byte commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( + const secp256k1_context_t* ctx, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *commit, + const unsigned char *proof, + int plen +)SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a range proof proof and rewind the proof to recover information sent by its author. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. + * 0: Proof failed, rewind failed, or other error. + * In: ctx: pointer to a context object, initialized for range-proof and commitment (cannot be NULL) + * commit: the 33-byte commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * nonce: 32-byte secret nonce used by the prover (cannot be NULL) + * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment + * value_out: pointer to an unsigned int64 which has the exact value of the commitment. + * message_out: pointer to a 4096 byte character array to receive message data from the proof author. + * outlen: length of message data written to message_out. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( + const secp256k1_context_t* ctx, + unsigned char *blind_out, + uint64_t *value_out, + unsigned char *message_out, + int *outlen, + const unsigned char *nonce, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *commit, + const unsigned char *proof, + int plen +)SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10); + +/** Author a proof that a committed value is within a range. + * Returns 1: Proof successfully created. + * 0: Error + * In: ctx: pointer to a context object, initialized for range-proof, signing, and commitment (cannot be NULL) + * proof: pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL) + * min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount. + * commit: 33-byte array with the commitment being proved. + * blind: 32-byte blinding factor used by commit. + * nonce: 32-byte secret nonce used to initialize the proof (value can be reverse-engineered out of the proof if this secret is known.) + * exp: Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18. + * (-1 is a special case that makes the value public. 0 is the most private.) + * min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64). + * value: Actual value of the commitment. + * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. + * + * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. + * + * If exp is -1 the value is revealed by the proof (e.g. it proves that the proof is a blinding of a specific value, without revealing the blinding key.) + * + * This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding. + * + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( + const secp256k1_context_t* ctx, + unsigned char *proof, + int *plen, + uint64_t min_value, + const unsigned char *commit, + const unsigned char *blind, + const unsigned char *nonce, + int exp, + int min_bits, + uint64_t value +)SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Extract some basic information from a range-proof. + * Returns 1: Information successfully extracted. + * 0: Decode failed. + * In: ctx: pointer to a context object + * proof: pointer to character array with the proof. + * plen: length of proof in bytes. + * Out: exp: Exponent used in the proof (-1 means the value isn't private). + * mantissa: Number of bits covered by the proof. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( + const secp256k1_context_t* ctx, + int *exp, + int *mantissa, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *proof, + int plen +)SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + # ifdef __cplusplus } # endif diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c new file mode 100644 index 000000000..22f29b9cd --- /dev/null +++ b/src/bench_rangeproof.c @@ -0,0 +1,63 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context_t* ctx; + unsigned char commit[33]; + unsigned char proof[5134]; + unsigned char blind[32]; + int len; + int min_bits; + uint64_t v; +} bench_rangeproof_t; + +static void bench_rangeproof_setup(void* arg) { + int i; + uint64_t minv; + uint64_t maxv; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + data->v = 0; + for (i = 0; i < 32; i++) data->blind[i] = i + 1; + CHECK(secp256k1_pedersen_commit(data->ctx, data->commit, data->blind, data->v)); + data->len = 5134; + CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, data->commit, data->blind, data->commit, 0, data->min_bits, data->v)); + CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, data->commit, data->proof, data->len)); +} + +static void bench_rangeproof(void* arg) { + int i; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + for (i = 0; i < 1000; i++) { + int j; + uint64_t minv; + uint64_t maxv; + j = secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, data->commit, data->proof, data->len); + for (j = 0; j < 4; j++) { + data->proof[j + 2 + 32 *((data->min_bits + 1) >> 1) - 4] = (i >> 8)&255; + } + } +} + +int main(void) { + bench_rangeproof_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_COMMIT | SECP256K1_CONTEXT_RANGEPROOF); + + data.min_bits = 32; + + run_benchmark("rangeproof_verif_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, 1000 * data.min_bits); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/borromean.h b/src/borromean.h new file mode 100644 index 000000000..a28dfebdb --- /dev/null +++ b/src/borromean.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_BORROMEAN_H_ +#define _SECP256K1_BORROMEAN_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +int secp256k1_borromean_verify(const secp256k1_ecmult_context_t* ecmult_ctx, secp256k1_scalar_t *evalues, const unsigned char *e0, const secp256k1_scalar_t *s, + const secp256k1_gej_t *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen); + +int secp256k1_borromean_sign(const secp256k1_ecmult_context_t* ecmult_ctx, const secp256k1_ecmult_gen_context_t *ecmult_gen_ctx, + unsigned char *e0, secp256k1_scalar_t *s, const secp256k1_gej_t *pubs, const secp256k1_scalar_t *k, const secp256k1_scalar_t *sec, + const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen); + +#endif diff --git a/src/borromean_impl.h b/src/borromean_impl.h new file mode 100644 index 000000000..eaff6103e --- /dev/null +++ b/src/borromean_impl.h @@ -0,0 +1,200 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_BORROMEAN_IMPL_H_ +#define _SECP256K1_BORROMEAN_IMPL_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#include "borromean.h" + + +#ifdef WORDS_BIGENDIAN +#define BE32(x) (x) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#endif + +SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, int mlen, const unsigned char *e, int elen, + int ridx, int eidx) { + uint32_t ring; + uint32_t epos; + secp256k1_sha256_t sha256_en; + secp256k1_sha256_initialize(&sha256_en); + ring = BE32((uint32_t)ridx); + epos = BE32((uint32_t)eidx); + secp256k1_sha256_write(&sha256_en, e, elen); + secp256k1_sha256_write(&sha256_en, m, mlen); + secp256k1_sha256_write(&sha256_en, (unsigned char*)&ring, 4); + secp256k1_sha256_write(&sha256_en, (unsigned char*)&epos, 4); + secp256k1_sha256_finalize(&sha256_en, hash); +} + +/** "Borromean" ring signature. + * Verifies nrings concurrent ring signatures all sharing a challenge value. + * Signature is one s value per pubkey and a hash. + * Verification equation: + * | m = H(P_{0..}||message) (Message must contain pubkeys or a pubkey commitment) + * | For each ring i: + * | | en = to_scalar(H(e0||m||i||0)) + * | | For each pubkey j: + * | | | r = s_i_j G + en * P_i_j + * | | | e = H(r||m||i||j) + * | | | en = to_scalar(e) + * | | r_i = r + * | return e_0 ==== H(r_{0..i}||m) + */ +int secp256k1_borromean_verify(const secp256k1_ecmult_context_t* ecmult_ctx, secp256k1_scalar_t *evalues, const unsigned char *e0, + const secp256k1_scalar_t *s, const secp256k1_gej_t *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen) { + secp256k1_gej_t rgej; + secp256k1_ge_t rge; + secp256k1_scalar_t ens; + secp256k1_sha256_t sha256_e0; + unsigned char tmp[33]; + int i; + int j; + int count; + int size; + int overflow; + VERIFY_CHECK(ecmult_ctx != NULL); + VERIFY_CHECK(e0 != NULL); + VERIFY_CHECK(s != NULL); + VERIFY_CHECK(pubs != NULL); + VERIFY_CHECK(rsizes != NULL); + VERIFY_CHECK(nrings > 0); + VERIFY_CHECK(m != NULL); + count = 0; + secp256k1_sha256_initialize(&sha256_e0); + for (i = 0; i < nrings; i++) { + DEBUG_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + for (j = 0; j < rsizes[i]; j++) { + if (overflow || secp256k1_scalar_is_zero(&s[count]) || secp256k1_scalar_is_zero(&ens) || secp256k1_gej_is_infinity(&pubs[count])) { + return 0; + } + if (evalues) { + /*If requested, save the challenges for proof rewind.*/ + evalues[count] = ens; + } + secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count], &ens, &s[count]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + /* OPT: loop can be hoisted and split to use batch inversion across all the rings; this would make it much faster. */ + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + if (j != rsizes[i] - 1) { + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + } else { + secp256k1_sha256_write(&sha256_e0, tmp, size); + } + count++; + } + } + secp256k1_sha256_write(&sha256_e0, m, mlen); + secp256k1_sha256_finalize(&sha256_e0, tmp); + return memcmp(e0, tmp, 32) == 0; +} + +int secp256k1_borromean_sign(const secp256k1_ecmult_context_t* ecmult_ctx, const secp256k1_ecmult_gen_context_t *ecmult_gen_ctx, + unsigned char *e0, secp256k1_scalar_t *s, const secp256k1_gej_t *pubs, const secp256k1_scalar_t *k, const secp256k1_scalar_t *sec, + const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen) { + secp256k1_gej_t rgej; + secp256k1_ge_t rge; + secp256k1_scalar_t ens; + secp256k1_sha256_t sha256_e0; + unsigned char tmp[33]; + int i; + int j; + int count; + int size; + int overflow; + VERIFY_CHECK(ecmult_ctx != NULL); + VERIFY_CHECK(ecmult_gen_ctx != NULL); + VERIFY_CHECK(e0 != NULL); + VERIFY_CHECK(s != NULL); + VERIFY_CHECK(pubs != NULL); + VERIFY_CHECK(k != NULL); + VERIFY_CHECK(sec != NULL); + VERIFY_CHECK(rsizes != NULL); + VERIFY_CHECK(secidx != NULL); + VERIFY_CHECK(nrings > 0); + VERIFY_CHECK(m != NULL); + secp256k1_sha256_initialize(&sha256_e0); + count = 0; + for (i = 0; i < nrings; i++) { + DEBUG_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &k[i]); + secp256k1_ge_set_gej(&rge, &rgej); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + for (j = secidx[i] + 1; j < rsizes[i]; j++) { + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + /** The signing algorithm as a whole is not memory uniform so there is likely a cache sidechannel that + * leaks which members are non-forgeries. That the forgeries themselves are variable time may leave + * an additional privacy impacting timing side-channel, but not a key loss one. + */ + secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count + j], &ens, &s[count + j]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + } + secp256k1_sha256_write(&sha256_e0, tmp, size); + count += rsizes[i]; + } + secp256k1_sha256_write(&sha256_e0, m, mlen); + secp256k1_sha256_finalize(&sha256_e0, e0); + count = 0; + for (i = 0; i < nrings; i++) { + DEBUG_CHECK(INT_MAX - count > rsizes[i]); + secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + for (j = 0; j < secidx[i]; j++) { + secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count + j], &ens, &s[count + j]); + if (secp256k1_gej_is_infinity(&rgej)) { + return 0; + } + secp256k1_ge_set_gej_var(&rge, &rgej); + secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); + secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); + secp256k1_scalar_set_b32(&ens, tmp, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ens)) { + return 0; + } + } + secp256k1_scalar_mul(&s[count + j], &ens, &sec[i]); + secp256k1_scalar_negate(&s[count + j], &s[count + j]); + secp256k1_scalar_add(&s[count + j], &s[count + j], &k[i]); + if (secp256k1_scalar_is_zero(&s[count + j])) { + return 0; + } + count += rsizes[i]; + } + secp256k1_scalar_clear(&ens); + secp256k1_ge_clear(&rge); + secp256k1_gej_clear(&rgej); + memset(tmp, 0, 33); + return 1; +} + +#endif diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index 3745633c4..01358a219 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -28,6 +28,10 @@ typedef struct { secp256k1_gej_t initial; } secp256k1_ecmult_gen_context_t; +typedef struct { + secp256k1_ge_storage_t (*prec)[16][16]; /* prec[j][i] = 16^j * i * G + U_i */ +} secp256k1_ecmult_gen2_context_t; + static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t* ctx); static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t* ctx); static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context_t *dst, @@ -40,4 +44,19 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t* ctx, secp static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32); +static void secp256k1_ecmult_gen2_context_init(secp256k1_ecmult_gen2_context_t* ctx); +static void secp256k1_ecmult_gen2_context_build(secp256k1_ecmult_gen2_context_t* ctx); +static void secp256k1_ecmult_gen2_context_clone(secp256k1_ecmult_gen2_context_t *dst, + const secp256k1_ecmult_gen2_context_t* src); +static void secp256k1_ecmult_gen2_context_clear(secp256k1_ecmult_gen2_context_t* ctx); + +static int secp256k1_ecmult_gen2_context_is_built(const secp256k1_ecmult_gen2_context_t* ctx); + +/** Multiply a small number with the generator: r = gn*G2 */ +static void secp256k1_ecmult_gen2_small(const secp256k1_ecmult_gen2_context_t *ctx, secp256k1_gej_t *r, uint64_t gn); + +/* sec * G + value * G2. */ +static void secp256k1_ecmult_gen_gen2(const secp256k1_ecmult_gen_context_t *ecmult_gen_ctx, + const secp256k1_ecmult_gen2_context_t *cmult_gen2_ctx, secp256k1_gej_t *rj, const secp256k1_scalar_t *sec, uint64_t value); + #endif diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index a5447de88..3cd17be30 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -16,6 +16,10 @@ static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t *ct ctx->prec = NULL; } +static void secp256k1_ecmult_gen2_context_init(secp256k1_ecmult_gen2_context_t *ctx) { + ctx->prec = NULL; +} + static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t *ctx) { secp256k1_ge_t prec[1024]; secp256k1_gej_t gj; @@ -78,10 +82,75 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t *c secp256k1_ecmult_gen_blind(ctx, NULL); } +static void secp256k1_ecmult_gen2_context_build(secp256k1_ecmult_gen2_context_t *ctx) { + secp256k1_ge_t prec[256]; + secp256k1_gej_t gj; + secp256k1_gej_t nums_gej; + int i, j; + + if (ctx->prec != NULL) { + return; + } + + ctx->prec = (secp256k1_ge_storage_t (*)[16][16])checked_malloc(sizeof(*ctx->prec)); + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g2); + + /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ + { + static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; + secp256k1_fe_t nums_x; + secp256k1_ge_t nums_ge; + VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32)); + VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0)); + secp256k1_gej_set_ge(&nums_gej, &nums_ge); + /* Add G to make the bits in x uniformly distributed. */ + secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g2, NULL); + } + + /* compute prec. */ + { + secp256k1_gej_t precj[256]; /* Jacobian versions of prec. */ + secp256k1_gej_t gbase; + secp256k1_gej_t numsbase; + gbase = gj; /* 16^j * G */ + numsbase = nums_gej; /* 2^j * nums. */ + for (j = 0; j < 16; j++) { + /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ + precj[j*16] = numsbase; + for (i = 1; i < 16; i++) { + secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); + } + /* Multiply gbase by 16. */ + for (i = 0; i < 4; i++) { + secp256k1_gej_double_var(&gbase, &gbase, NULL); + } + /* Multiply numbase by 2. */ + secp256k1_gej_double_var(&numsbase, &numsbase, NULL); + if (j == 14) { + /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ + secp256k1_gej_neg(&numsbase, &numsbase); + secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); + } + } + secp256k1_ge_set_all_gej_var(256, prec, precj); + } + for (j = 0; j < 16; j++) { + for (i = 0; i < 16; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); + } + } +} + static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context_t* ctx) { return ctx->prec != NULL; } +static int secp256k1_ecmult_gen2_context_is_built(const secp256k1_ecmult_gen2_context_t* ctx) { + return ctx->prec != NULL; +} + static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context_t *dst, const secp256k1_ecmult_gen_context_t *src) { if (src->prec == NULL) { @@ -94,6 +163,16 @@ static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context_t *d } } +static void secp256k1_ecmult_gen2_context_clone(secp256k1_ecmult_gen2_context_t *dst, + const secp256k1_ecmult_gen2_context_t *src) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { + dst->prec = (secp256k1_ge_storage_t (*)[16][16])checked_malloc(sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + } +} + static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context_t *ctx) { free(ctx->prec); secp256k1_scalar_clear(&ctx->blind); @@ -101,6 +180,11 @@ static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context_t *c ctx->prec = NULL; } +static void secp256k1_ecmult_gen2_context_clear(secp256k1_ecmult_gen2_context_t *ctx) { + free(ctx->prec); + ctx->prec = NULL; +} + static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t *ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *gn) { secp256k1_ge_t add; secp256k1_ge_storage_t adds; @@ -181,4 +265,36 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, cons secp256k1_gej_clear(&gb); } +/* Version of secp256k1_ecmult_gen using the second generator and working only on numbers in the range [0 .. 2^64). */ +static void secp256k1_ecmult_gen2_small(const secp256k1_ecmult_gen2_context_t *ctx, secp256k1_gej_t *r, uint64_t gn) { + secp256k1_ge_t add; + secp256k1_ge_storage_t adds; + int bits; + int i, j; + memset(&adds, 0, sizeof(adds)); + secp256k1_gej_set_infinity(r); + add.infinity = 0; + for (j = 0; j < 16; j++) { + bits = (gn >> (j * 4)) & 15; + for (i = 0; i < 16; i++) { + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + } + secp256k1_ge_from_storage(&add, &adds); + secp256k1_gej_add_ge(r, r, &add); + } + bits = 0; + secp256k1_ge_clear(&add); +} + +/* sec * G + value * G2. */ +SECP256K1_INLINE static void secp256k1_ecmult_gen_gen2(const secp256k1_ecmult_gen_context_t *ecmult_gen_ctx, + const secp256k1_ecmult_gen2_context_t *cmult_gen2_ctx, secp256k1_gej_t *rj, const secp256k1_scalar_t *sec, uint64_t value) { + secp256k1_gej_t vj; + secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec); + secp256k1_ecmult_gen2_small(cmult_gen2_ctx, &vj, value); + /* FIXME: constant time. */ + secp256k1_gej_add_var(rj, rj, &vj, NULL); + secp256k1_gej_clear(&vj); +} + #endif diff --git a/src/group_impl.h b/src/group_impl.h index 45bafa662..c98037349 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -23,6 +23,19 @@ static const secp256k1_ge_t secp256k1_ge_const_g = SECP256K1_GE_CONST( 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL ); +/** Alternative generator for secp256k1. + * This is the sha256 of 'g' after DER encoding (without compression), + * which happens to be a point on the curve. + * sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16)) + * sage: '%x %x'%G2.xy() + */ +static const secp256k1_ge_t secp256k1_ge_const_g2 = SECP256K1_GE_CONST( + 0x50929b74UL, 0xc1a04954UL, 0xb78b4b60UL, 0x35e97a5eUL, + 0x078a5a0fUL, 0x28ec96d5UL, 0x47bfee9aUL, 0xce803ac0UL, + 0x31d3c686UL, 0x3973926eUL, 0x049e637cUL, 0xb1b5f40aUL, + 0x36dac28aUL, 0xf1766968UL, 0xc30c2313UL, 0xf3a38904UL +); + static void secp256k1_ge_set_gej_zinv(secp256k1_ge_t *r, const secp256k1_gej_t *a, const secp256k1_fe_t *zi) { secp256k1_fe_t zi2; secp256k1_fe_t zi3; diff --git a/src/rangeproof.h b/src/rangeproof.h new file mode 100644 index 000000000..6f34e9443 --- /dev/null +++ b/src/rangeproof.h @@ -0,0 +1,31 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_RANGEPROOF__ +#define _SECP256K1_RANGEPROOF__ + +#include "scalar.h" +#include "group.h" + +typedef struct { + secp256k1_ge_storage_t (*prec)[1005]; +} secp256k1_rangeproof_context_t; + + +static void secp256k1_rangeproof_context_init(secp256k1_rangeproof_context_t* ctx); +static void secp256k1_rangeproof_context_build(secp256k1_rangeproof_context_t* ctx); +static void secp256k1_rangeproof_context_clone(secp256k1_rangeproof_context_t *dst, + const secp256k1_rangeproof_context_t* src); +static void secp256k1_rangeproof_context_clear(secp256k1_rangeproof_context_t* ctx); +static int secp256k1_rangeproof_context_is_built(const secp256k1_rangeproof_context_t* ctx); + +static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context_t* ecmult_ctx, + const secp256k1_ecmult_gen_context_t* ecmult_gen_ctx, + const secp256k1_ecmult_gen2_context_t* ecmult_gen2_ctx, const secp256k1_rangeproof_context_t* rangeproof_ctx, + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const unsigned char *commit, const unsigned char *proof, int plen); + +#endif diff --git a/src/rangeproof_impl.h b/src/rangeproof_impl.h new file mode 100644 index 000000000..6a3b60932 --- /dev/null +++ b/src/rangeproof_impl.h @@ -0,0 +1,728 @@ +/********************************************************************** + * Copyright (c) 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_RANGEPROOF_IMPL_H_ +#define _SECP256K1_RANGEPROOF_IMPL_H_ + +#include "scalar.h" +#include "group.h" +#include "rangeproof.h" +#include "hash_impl.h" + +static const int secp256k1_rangeproof_offsets[20] = { + 0, 96, 189, 276, 360, 438, 510, 579, 642, + 699, 753, 801, 843, 882, 915, 942, 966, 984, + 996, 1005, +}; + +static void secp256k1_rangeproof_context_init(secp256k1_rangeproof_context_t *ctx) { + ctx->prec = NULL; +} + +static void secp256k1_rangeproof_context_build(secp256k1_rangeproof_context_t *ctx) { + secp256k1_ge_t *prec; + secp256k1_gej_t *precj; + secp256k1_gej_t gj; + secp256k1_gej_t one; + int i, pos; + + if (ctx->prec != NULL) { + return; + } + + precj = (secp256k1_gej_t (*))checked_malloc(sizeof(*precj) * 1005); + if (precj == NULL) { + return; + } + prec = (secp256k1_ge_t (*))checked_malloc(sizeof(*prec) * 1005); + if (prec == NULL) { + free(precj); + return; + } + + /* get the generator */ + secp256k1_gej_set_ge(&one, &secp256k1_ge_const_g2); + secp256k1_gej_neg(&one, &one); + + /* compute prec. */ + pos = 0; + for (i = 0; i < 19; i++) { + int pmax; + pmax = secp256k1_rangeproof_offsets[i + 1]; + gj = one; + while (pos < pmax) { + precj[pos] = gj; + pos++; + secp256k1_gej_double_var(&precj[pos], &gj, NULL); + pos++; + secp256k1_gej_add_var(&precj[pos], &precj[pos - 1], &gj, NULL); + pos++; + if (pos < pmax - 1) { + secp256k1_gej_double_var(&gj, &precj[pos - 2], NULL); + } + } + if (i < 18) { + secp256k1_gej_double_var(&gj, &one, NULL); + one = gj; + secp256k1_gej_double_var(&gj, &gj, NULL); + secp256k1_gej_double_var(&gj, &gj, NULL); + secp256k1_gej_add_var(&one, &one, &gj, NULL); + } + } + VERIFY_CHECK(pos == 1005); + secp256k1_ge_set_all_gej_var(1005, prec, precj); + + free(precj); + + ctx->prec = (secp256k1_ge_storage_t (*)[1005])checked_malloc(sizeof(*ctx->prec)); + if (ctx->prec == NULL) { + free(prec); + return; + } + + for (i = 0; i < 1005; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[i], &prec[i]); + } + free(prec); +} + + +static int secp256k1_rangeproof_context_is_built(const secp256k1_rangeproof_context_t* ctx) { + return ctx->prec != NULL; +} + +static void secp256k1_rangeproof_context_clone(secp256k1_rangeproof_context_t *dst, + const secp256k1_rangeproof_context_t *src) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { + dst->prec = (secp256k1_ge_storage_t (*)[1005])checked_malloc(sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + } +} + +static void secp256k1_rangeproof_context_clear(secp256k1_rangeproof_context_t *ctx) { + free(ctx->prec); + ctx->prec = NULL; +} + +SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(const secp256k1_rangeproof_context_t *ctx, secp256k1_gej_t *pubs, + int exp, int *rsizes, int rings) { + secp256k1_ge_t ge; + secp256k1_ge_storage_t *basis; + int i; + int j; + int npub; + VERIFY_CHECK(exp < 19); + if (exp < 0) { + exp = 0; + } + basis = &(*ctx->prec)[secp256k1_rangeproof_offsets[exp]]; + npub = 0; + for (i = 0; i < rings; i++) { + for (j = 1; j < rsizes[i]; j++) { + secp256k1_ge_from_storage(&ge, &basis[i * 3 + j - 1]); + secp256k1_gej_add_ge_var(&pubs[npub + j], &pubs[npub], &ge, NULL); + } + npub += rsizes[i]; + } +} + +SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar_t *sec, secp256k1_scalar_t *s, unsigned char *message, + int *rsizes, int rings, const unsigned char *nonce, const unsigned char *commit, const unsigned char *proof, int len) { + unsigned char tmp[32]; + secp256k1_rfc6979_hmac_sha256_t rng; + secp256k1_scalar_t acc; + int overflow; + int ret; + int i; + int j; + int b; + int npub; + secp256k1_rfc6979_hmac_sha256_initialize(&rng, nonce, 32, commit, 33, proof, len); + secp256k1_scalar_clear(&acc); + npub = 0; + ret = 1; + for (i = 0; i < rings; i++) { + if (i < rings - 1) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + secp256k1_scalar_set_b32(&sec[i], tmp, &overflow); + } while (overflow || secp256k1_scalar_is_zero(&sec[i])); + secp256k1_scalar_add(&acc, &acc, &sec[i]); + } else { + secp256k1_scalar_negate(&acc, &acc); + sec[i] = acc; + } + for (j = 0; j < rsizes[i]; j++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32); + if (message) { + for (b = 0; b < 32; b++) { + tmp[b] ^= message[(i * 4 + j) * 32 + b]; + message[(i * 4 + j) * 32 + b] = tmp[b]; + } + } + secp256k1_scalar_set_b32(&s[npub], tmp, &overflow); + ret &= !(overflow || secp256k1_scalar_is_zero(&s[npub])); + npub++; + } + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + secp256k1_scalar_clear(&acc); + memset(tmp, 0, 32); + return ret; +} + +SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, int *rings, int *rsizes, int *npub, int *secidx, uint64_t *min_value, + int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) { + int i; + *rings = 1; + rsizes[0] = 1; + secidx[0] = 0; + *scale = 1; + *mantissa = 0; + *npub = 0; + if (*min_value == UINT64_MAX) { + /* If the minimum value is the maximal representable value, then we cannot code a range. */ + *exp = -1; + } + if (*exp >= 0) { + int max_bits; + uint64_t v2; + if ((*min_value && value > INT64_MAX) || (value && *min_value >= INT64_MAX)) { + /* If either value or min_value is >= 2^63-1 then the other must by zero to avoid overflowing the proven range. */ + return 0; + } + max_bits = *min_value ? secp256k1_clz64_var(*min_value) : 64; + if (*min_bits > max_bits) { + *min_bits = max_bits; + } + if (*min_bits > 61 || value > INT64_MAX) { + /** Ten is not a power of two, so dividing by ten and then representing in base-2 times ten + * expands the representable range. The verifier requires the proven range is within 0..2**64. + * For very large numbers (all over 2**63) we must change our exponent to compensate. + * Rather than handling it precisely, this just disables use of the exponent for big values. + */ + *exp = 0; + } + /* Mask off the least significant digits, as requested. */ + *v = value - *min_value; + /* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */ + v2 = *min_bits ? (UINT64_MAX>>(64-*min_bits)) : 0; + for (i = 0; i < *exp && (v2 <= UINT64_MAX / 10); i++) { + *v /= 10; + v2 *= 10; + } + *exp = i; + v2 = *v; + for (i = 0; i < *exp; i++) { + v2 *= 10; + *scale *= 10; + } + /* If the masked number isn't precise, compute the public offset. */ + *min_value = value - v2; + /* How many bits do we need to represent our value? */ + *mantissa = *v ? 64 - secp256k1_clz64_var(*v) : 1; + if (*min_bits > *mantissa) { + /* If the user asked for more precision, give it to them. */ + *mantissa = *min_bits; + } + /* Digits in radix-4, except for the last digit if our mantissa length is odd. */ + *rings = (*mantissa + 1) >> 1; + for (i = 0; i < *rings; i++) { + rsizes[i] = ((i < *rings - 1) | (!(*mantissa&1))) ? 4 : 2; + *npub += rsizes[i]; + secidx[i] = (*v >> (i*2)) & 3; + } + VERIFY_CHECK(*mantissa>0); + VERIFY_CHECK((*v & ~(UINT64_MAX>>(64-*mantissa))) == 0); /* Did this get all the bits? */ + } else { + /* A proof for an exact value. */ + *exp = 0; + *min_value = value; + *v = 0; + *npub = 2; + } + VERIFY_CHECK(*v * *scale + *min_value == value); + VERIFY_CHECK(*rings > 0); + VERIFY_CHECK(*rings <= 32); + VERIFY_CHECK(*npub <= 128); + return 1; +} + +/* strawman interface, writes proof in proof, a buffer of plen, proves with respect to min_value the range for commit which has the provided blinding factor and value. */ +SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmult_context_t* ecmult_ctx, + const secp256k1_ecmult_gen_context_t* ecmult_gen_ctx, const secp256k1_ecmult_gen2_context_t* ecmult_gen2_ctx, + const secp256k1_rangeproof_context_t* rangeproof_ctx, unsigned char *proof, int *plen, uint64_t min_value, + const unsigned char *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + secp256k1_gej_t pubs[128]; /* Candidate digits for our proof, most inferred. */ + secp256k1_scalar_t s[128]; /* Signatures in our proof, most forged. */ + secp256k1_scalar_t sec[32]; /* Blinding factors for the correct digits. */ + secp256k1_scalar_t k[32]; /* Nonces for our non-forged signatures. */ + secp256k1_scalar_t stmp; + secp256k1_sha256_t sha256_m; + unsigned char prep[4096]; + unsigned char tmp[33]; + unsigned char *signs; /* Location of sign flags in the proof. */ + uint64_t v; + uint64_t scale; /* scale = 10^exp. */ + int mantissa; /* Number of bits proven in the blinded value. */ + int rings; /* How many digits will our proof cover. */ + int rsizes[32]; /* How many possible values there are for each place. */ + int secidx[32]; /* Which digit is the correct one. */ + int len; /* Number of bytes used so far. */ + int i; + int overflow; + int npub; + len = 0; + if (*plen < 65 || min_value > value || min_bits > 64 || min_bits < 0 || exp < -1 || exp > 18) { + return 0; + } + if (!secp256k1_range_proveparams(&v, &rings, rsizes, &npub, secidx, &min_value, &mantissa, &scale, &exp, &min_bits, value)) { + return 0; + } + proof[len] = (rsizes[0] > 1 ? (64 | exp) : 0) | (min_value ? 32 : 0); + len++; + if (rsizes[0] > 1) { + VERIFY_CHECK(mantissa > 0 && mantissa <= 64); + proof[len] = mantissa - 1; + len++; + } + if (min_value) { + for (i = 0; i < 8; i++) { + proof[len + i] = (min_value >> ((7-i) * 8)) & 255; + } + len += 8; + } + /* Do we have enough room for the proof? */ + if (*plen - len < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + return 0; + } + secp256k1_sha256_initialize(&sha256_m); + secp256k1_sha256_write(&sha256_m, commit, 33); + secp256k1_sha256_write(&sha256_m, proof, len); + + memset(prep, 0, 4096); + /* Note, the data corresponding to the blinding factors must be zero. */ + if (rsizes[rings - 1] > 1) { + int idx; + /* Value encoding sidechannel. */ + idx = rsizes[rings - 1] - 1; + idx -= secidx[rings - 1] == idx; + idx = ((rings - 1) * 4 + idx) * 32; + for (i = 0; i < 8; i++) { + prep[8 + i + idx] = prep[16 + i + idx] = prep[24 + i + idx] = (v >> (56 - i * 8)) & 255; + prep[i + idx] = 0; + } + prep[idx] = 128; + } + if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len)) { + return 0; + } + memset(prep, 0, 4096); + for (i = 0; i < rings; i++) { + /* Sign will overwrite the non-forged signature, move that random value into the nonce. */ + k[i] = s[i * 4 + secidx[i]]; + secp256k1_scalar_clear(&s[i * 4 + secidx[i]]); + } + /** Genrand returns the last blinding factor as -sum(rest), + * adding in the blinding factor for our commitment, results in the blinding factor for + * the commitment to the last digit that the verifier can compute for itself by subtracting + * all the digits in the proof from the commitment. This lets the prover skip sending the + * blinded value for one digit. + */ + secp256k1_scalar_set_b32(&stmp, blind, &overflow); + secp256k1_scalar_add(&sec[rings - 1], &sec[rings - 1], &stmp); + if (overflow || secp256k1_scalar_is_zero(&sec[rings - 1])) { + return 0; + } + signs = &proof[len]; + /* We need one sign bit for each blinded value we send. */ + for (i = 0; i < (rings + 6) >> 3; i++) { + signs[i] = 0; + len++; + } + npub = 0; + for (i = 0; i < rings; i++) { + /*OPT: Use the precomputed gen2 basis?*/ + secp256k1_ecmult_gen_gen2(ecmult_gen_ctx, ecmult_gen2_ctx, &pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2)); + if (secp256k1_gej_is_infinity(&pubs[npub])) { + return 0; + } + if (i < rings - 1) { + int size = 33; + secp256k1_ge_t c; + /*OPT: split loop and batch invert.*/ + secp256k1_ge_set_gej_var(&c, &pubs[npub]); + if(!secp256k1_eckey_pubkey_serialize(&c, tmp, &size, 1)) { + return 0; + } + secp256k1_sha256_write(&sha256_m, tmp, 33); + signs[i>>3] |= (tmp[0] == 3) << (i&7); + memcpy(&proof[len], &tmp[1], 32); + len += 32; + } + npub += rsizes[i]; + } + secp256k1_rangeproof_pub_expand(rangeproof_ctx, pubs, exp, rsizes, rings); + secp256k1_sha256_finalize(&sha256_m, tmp); + if (!secp256k1_borromean_sign(ecmult_ctx, ecmult_gen_ctx, &proof[len], s, pubs, k, sec, rsizes, secidx, rings, tmp, 32)) { + return 0; + } + len += 32; + for (i = 0; i < npub; i++) { + secp256k1_scalar_get_b32(&proof[len],&s[i]); + len += 32; + } + VERIFY_CHECK(len <= *plen); + *plen = len; + memset(prep, 0, 4096); + return 1; +} + +/* Computes blinding factor x given k, s, and the challenge e. */ +SECP256K1_INLINE static void secp256k1_rangeproof_recover_x(secp256k1_scalar_t *x, const secp256k1_scalar_t *k, const secp256k1_scalar_t *e, + const secp256k1_scalar_t *s) { + secp256k1_scalar_t stmp; + secp256k1_scalar_negate(x, s); + secp256k1_scalar_add(x, x, k); + secp256k1_scalar_inverse(&stmp, e); + secp256k1_scalar_mul(x, x, &stmp); +} + +/* Computes ring's nonce given the blinding factor x, the challenge e, and the signature s. */ +SECP256K1_INLINE static void secp256k1_rangeproof_recover_k(secp256k1_scalar_t *k, const secp256k1_scalar_t *x, const secp256k1_scalar_t *e, + const secp256k1_scalar_t *s) { + secp256k1_scalar_t stmp; + secp256k1_scalar_mul(&stmp, x, e); + secp256k1_scalar_add(k, s, &stmp); +} + +SECP256K1_INLINE static void secp256k1_rangeproof_ch32xor(unsigned char *x, const unsigned char *y) { + int i; + for (i = 0; i < 32; i++) { + x[i] ^= y[i]; + } +} + +SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar_t *blind, uint64_t *v, + unsigned char *m, int *mlen, secp256k1_scalar_t *ev, secp256k1_scalar_t *s, + int *rsizes, int rings, const unsigned char *nonce, const unsigned char *commit, const unsigned char *proof, int len) { + secp256k1_scalar_t s_orig[128]; + secp256k1_scalar_t sec[32]; + secp256k1_scalar_t stmp; + unsigned char prep[4096]; + unsigned char tmp[32]; + uint64_t value; + int offset; + int i; + int j; + int b; + int skip1; + int skip2; + int npub; + npub = ((rings - 1) << 2) + rsizes[rings-1]; + VERIFY_CHECK(npub <= 128); + VERIFY_CHECK(npub >= 1); + memset(prep, 0, 4096); + /* Reconstruct the provers random values. */ + secp256k1_rangeproof_genrand(sec, s_orig, prep, rsizes, rings, nonce, commit, proof, len); + *v = UINT64_MAX; + secp256k1_scalar_clear(blind); + if (rings == 1 && rsizes[0] == 1) { + /* With only a single proof, we can only recover the blinding factor. */ + secp256k1_rangeproof_recover_x(blind, &s_orig[0], &ev[0], &s[0]); + if (v) { + *v = 0; + } + if (mlen) { + *mlen = 0; + } + return 1; + } + npub = (rings - 1) << 2; + for (j = 0; j < 2; j++) { + int idx; + /* Look for a value encoding in the last ring. */ + idx = npub + rsizes[rings - 1] - 1 - j; + secp256k1_scalar_get_b32(tmp, &s[idx]); + secp256k1_rangeproof_ch32xor(tmp, &prep[idx * 32]); + if ((tmp[0] & 128) && (memcmp(&tmp[16], &tmp[24], 8) == 0) && (memcmp(&tmp[8], &tmp[16], 8) == 0)) { + value = 0; + for (i = 0; i < 8; i++) { + value = (value << 8) + tmp[24 + i]; + } + if (v) { + *v = value; + } + memcpy(&prep[idx * 32], tmp, 32); + break; + } + } + if (j > 1) { + /* Couldn't extract a value. */ + if (mlen) { + *mlen = 0; + } + return 0; + } + skip1 = rsizes[rings - 1] - 1 - j; + skip2 = ((value >> ((rings - 1) << 1)) & 3); + if (skip1 == skip2) { + /*Value is in wrong position.*/ + if (mlen) { + *mlen = 0; + } + return 0; + } + skip1 += (rings - 1) << 2; + skip2 += (rings - 1) << 2; + /* Like in the rsize[] == 1 case, Having figured out which s is the one which was not forged, we can recover the blinding factor. */ + secp256k1_rangeproof_recover_x(&stmp, &s_orig[skip2], &ev[skip2], &s[skip2]); + secp256k1_scalar_negate(&sec[rings - 1], &sec[rings - 1]); + secp256k1_scalar_add(blind, &stmp, &sec[rings - 1]); + if (!m || !mlen || *mlen == 0) { + if (mlen) { + *mlen = 0; + } + /* FIXME: cleanup in early out/failure cases. */ + return 1; + } + offset = 0; + npub = 0; + for (i = 0; i < rings; i++) { + int idx; + idx = (value >> (i << 1)) & 3; + for (j = 0; j < rsizes[i]; j++) { + if (npub == skip1 || npub == skip2) { + npub++; + continue; + } + if (idx == j) { + /** For the non-forged signatures the signature is calculated instead of random, instead we recover the prover's nonces. + * this could just as well recover the blinding factors and messages could be put there as is done for recovering the + * blinding factor in the last ring, but it takes an inversion to recover x so it's faster to put the message data in k. + */ + secp256k1_rangeproof_recover_k(&stmp, &sec[i], &ev[npub], &s[npub]); + } else { + stmp = s[npub]; + } + secp256k1_scalar_get_b32(tmp, &stmp); + secp256k1_rangeproof_ch32xor(tmp, &prep[npub * 32]); + for (b = 0; b < 32 && offset < *mlen; b++) { + m[offset] = tmp[b]; + offset++; + } + npub++; + } + } + *mlen = offset; + memset(prep, 0, 4096); + for (i = 0; i < 128; i++) { + secp256k1_scalar_clear(&s_orig[i]); + } + for (i = 0; i < 32; i++) { + secp256k1_scalar_clear(&sec[i]); + } + secp256k1_scalar_clear(&stmp); + return 1; +} + +SECP256K1_INLINE static int secp256k1_rangeproof_getheader_impl(int *offset, int *exp, int *mantissa, uint64_t *scale, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, int plen) { + int i; + int has_nz_range; + int has_min; + if (plen < 65 || ((proof[*offset] & 128) != 0)) { + return 0; + } + has_nz_range = proof[*offset] & 64; + has_min = proof[*offset] & 32; + *exp = -1; + *mantissa = 0; + if (has_nz_range) { + *exp = proof[*offset] & 31; + *offset += 1; + if (*exp > 18) { + return 0; + } + *mantissa = proof[*offset] + 1; + if (*mantissa > 64) { + return 0; + } + *max_value = UINT64_MAX>>(64-*mantissa); + } else { + *max_value = 0; + } + *offset += 1; + *scale = 1; + for (i = 0; i < *exp; i++) { + if (*max_value > UINT64_MAX / 10) { + return 0; + } + *max_value *= 10; + *scale *= 10; + } + *min_value = 0; + if (has_min) { + if(plen - *offset < 8) { + return 0; + } + /*FIXME: Compact minvalue encoding?*/ + for (i = 0; i < 8; i++) { + *min_value = (*min_value << 8) | proof[*offset + i]; + } + *offset += 8; + } + if (*max_value > UINT64_MAX - *min_value) { + return 0; + } + *max_value += *min_value; + return 1; +} + +/* Verifies range proof (len plen) for 33-byte commit, the min/max values proven are put in the min/max arguments; returns 0 on failure 1 on success.*/ +SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context_t* ecmult_ctx, + const secp256k1_ecmult_gen_context_t* ecmult_gen_ctx, + const secp256k1_ecmult_gen2_context_t* ecmult_gen2_ctx, const secp256k1_rangeproof_context_t* rangeproof_ctx, + unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, const unsigned char *commit, const unsigned char *proof, int plen) { + secp256k1_gej_t accj; + secp256k1_gej_t pubs[128]; + secp256k1_ge_t c; + secp256k1_scalar_t s[128]; + secp256k1_scalar_t evalues[128]; /* Challenges, only used during proof rewind. */ + secp256k1_sha256_t sha256_m; + int rsizes[32]; + int ret; + int i; + int exp; + int mantissa; + int offset; + int rings; + int overflow; + int npub; + int offset_post_header; + uint64_t scale; + unsigned char signs[31]; + unsigned char m[33]; + const unsigned char *e0; + offset = 0; + if (!secp256k1_rangeproof_getheader_impl(&offset, &exp, &mantissa, &scale, min_value, max_value, proof, plen)) { + return 0; + } + offset_post_header = offset; + rings = 1; + rsizes[0] = 1; + npub = 1; + if (mantissa != 0) { + rings = (mantissa >> 1); + for (i = 0; i < rings; i++) { + rsizes[i] = 4; + } + npub = (mantissa >> 1) << 2; + if (mantissa & 1) { + rsizes[rings] = 2; + npub += rsizes[rings]; + rings++; + } + } + VERIFY_CHECK(rings <= 32); + if (plen - offset < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) { + return 0; + } + secp256k1_sha256_initialize(&sha256_m); + secp256k1_sha256_write(&sha256_m, commit, 33); + secp256k1_sha256_write(&sha256_m, proof, offset); + for(i = 0; i < rings - 1; i++) { + signs[i] = (proof[offset + ( i>> 3)] & (1 << (i & 7))) != 0; + } + offset += (rings + 6) >> 3; + if ((rings - 1) & 7) { + /* Number of coded blinded points is not a multiple of 8, force extra sign bits to 0 to reject mutation. */ + if ((proof[offset - 1] >> ((rings - 1) & 7)) != 0) { + return 0; + } + } + npub = 0; + secp256k1_gej_set_infinity(&accj); + if (*min_value) { + secp256k1_ecmult_gen2_small(ecmult_gen2_ctx, &accj, *min_value); + } + for(i = 0; i < rings - 1; i++) { + memcpy(&m[1], &proof[offset], 32); + m[0] = 2 + signs[i]; + if (!secp256k1_eckey_pubkey_parse(&c, m, 33)) { + return 0; + } + secp256k1_sha256_write(&sha256_m, m, 33); + secp256k1_gej_set_ge(&pubs[npub], &c); + secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL); + offset += 32; + npub += rsizes[i]; + } + secp256k1_gej_neg(&accj, &accj); + if (!secp256k1_eckey_pubkey_parse(&c, commit, 33)) { + return 0; + } + secp256k1_gej_add_ge_var(&pubs[npub], &accj, &c, NULL); + if (secp256k1_gej_is_infinity(&pubs[npub])) { + return 0; + } + secp256k1_rangeproof_pub_expand(rangeproof_ctx, pubs, exp, rsizes, rings); + npub += rsizes[rings - 1]; + e0 = &proof[offset]; + offset += 32; + for (i = 0; i < npub; i++) { + secp256k1_scalar_set_b32(&s[i], &proof[offset], &overflow); + if (overflow) { + return 0; + } + offset += 32; + } + if (offset != plen) { + /*Extra data found, reject.*/ + return 0; + } + secp256k1_sha256_finalize(&sha256_m, m); + ret = secp256k1_borromean_verify(ecmult_ctx, nonce ? evalues : NULL, e0, s, pubs, rsizes, rings, m, 32); + if (ret && nonce) { + /* Given the nonce, try rewinding the witness to recover its initial state. */ + secp256k1_scalar_t blind; + unsigned char commitrec[33]; + uint64_t vv; + if (!ecmult_gen_ctx) { + return 0; + } + if (!secp256k1_rangeproof_rewind_inner(&blind, &vv, message_out, outlen, evalues, s, rsizes, rings, nonce, commit, proof, offset_post_header)) { + return 0; + } + /* Unwind apparently successful, see if the commitment can be reconstructed. */ + /* FIXME: should check vv is in the mantissa's range. */ + vv = (vv * scale) + *min_value; + secp256k1_ecmult_gen_gen2(ecmult_gen_ctx, ecmult_gen2_ctx, &accj, &blind, vv); + if (secp256k1_gej_is_infinity(&accj)) { + return 0; + } + secp256k1_ge_set_gej(&c, &accj); + i = 33; + secp256k1_eckey_pubkey_serialize(&c, commitrec, &i, 1); + if (memcmp(commitrec, commit, 33) != 0) { + return 0; + } + if (blindout) { + secp256k1_scalar_get_b32(blindout, &blind); + } + if (value_out) { + *value_out = vv; + } + } + return ret; +} + + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 568eb3b76..4e3c6cc97 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013-2015 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -20,10 +20,14 @@ #include "ecdsa_impl.h" #include "eckey_impl.h" #include "hash_impl.h" +#include "borromean_impl.h" +#include "rangeproof_impl.h" struct secp256k1_context_struct { secp256k1_ecmult_context_t ecmult_ctx; secp256k1_ecmult_gen_context_t ecmult_gen_ctx; + secp256k1_ecmult_gen2_context_t ecmult_gen2_ctx; + secp256k1_rangeproof_context_t rangeproof_ctx; }; secp256k1_context_t* secp256k1_context_create(int flags) { @@ -31,6 +35,8 @@ secp256k1_context_t* secp256k1_context_create(int flags) { secp256k1_ecmult_context_init(&ret->ecmult_ctx); secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + secp256k1_ecmult_gen2_context_init(&ret->ecmult_gen2_ctx); + secp256k1_rangeproof_context_init(&ret->rangeproof_ctx); if (flags & SECP256K1_CONTEXT_SIGN) { secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx); @@ -38,6 +44,12 @@ secp256k1_context_t* secp256k1_context_create(int flags) { if (flags & SECP256K1_CONTEXT_VERIFY) { secp256k1_ecmult_context_build(&ret->ecmult_ctx); } + if (flags & SECP256K1_CONTEXT_COMMIT) { + secp256k1_ecmult_gen2_context_build(&ret->ecmult_gen2_ctx); + } + if (flags & SECP256K1_CONTEXT_RANGEPROOF) { + secp256k1_rangeproof_context_build(&ret->rangeproof_ctx); + } return ret; } @@ -46,12 +58,16 @@ secp256k1_context_t* secp256k1_context_clone(const secp256k1_context_t* ctx) { secp256k1_context_t* ret = (secp256k1_context_t*)checked_malloc(sizeof(secp256k1_context_t)); secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx); secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx); + secp256k1_ecmult_gen2_context_clone(&ret->ecmult_gen2_ctx, &ctx->ecmult_gen2_ctx); + secp256k1_rangeproof_context_clone(&ret->rangeproof_ctx, &ctx->rangeproof_ctx); return ret; } void secp256k1_context_destroy(secp256k1_context_t* ctx) { secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + secp256k1_ecmult_gen2_context_clear(&ctx->ecmult_gen2_ctx); + secp256k1_rangeproof_context_clear(&ctx->rangeproof_ctx); free(ctx); } @@ -445,3 +461,156 @@ int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *s secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); return 1; } + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The commitment is 33 bytes, the blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context_t* ctx, unsigned char *commit, unsigned char *blind, uint64_t value) { + secp256k1_gej_t rj; + secp256k1_ge_t r; + secp256k1_scalar_t sec; + int sz; + int overflow; + int ret = 0; + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + DEBUG_CHECK(secp256k1_ecmult_gen2_context_is_built(&ctx->ecmult_gen2_ctx)); + DEBUG_CHECK(commit != NULL); + DEBUG_CHECK(blind != NULL); + secp256k1_scalar_set_b32(&sec, blind, &overflow); + if (!overflow) { + secp256k1_ecmult_gen_gen2(&ctx->ecmult_gen_ctx, &ctx->ecmult_gen2_ctx, &rj, &sec, value); + if (!secp256k1_gej_is_infinity(&rj)) { + secp256k1_ge_set_gej(&r, &rj); + sz = 33; + ret = secp256k1_eckey_pubkey_serialize(&r, commit, &sz, 1); + } + secp256k1_gej_clear(&rj); + secp256k1_ge_clear(&r); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest + * negative, then calculates an additional blinding value that adds to zero. + */ +int secp256k1_pedersen_blind_sum(const secp256k1_context_t* ctx, unsigned char *blind_out, const unsigned char * const *blinds, int n, int npositive) { + secp256k1_scalar_t acc; + secp256k1_scalar_t x; + int i; + int overflow; + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(blind_out != NULL); + DEBUG_CHECK(blinds != NULL); + secp256k1_scalar_set_int(&acc, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&x, blinds[i], &overflow); + if (overflow) { + return 0; + } + if (i >= npositive) { + secp256k1_scalar_negate(&x, &x); + } + secp256k1_scalar_add(&acc, &acc, &x); + } + secp256k1_scalar_get_b32(blind_out, &acc); + secp256k1_scalar_clear(&acc); + secp256k1_scalar_clear(&x); + return 1; +} +/* Takes two list of 33-byte commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context_t* ctx, const unsigned char * const *commits, int pcnt, + const unsigned char * const *ncommits, int ncnt, int64_t excess) { + secp256k1_gej_t accj; + secp256k1_ge_t add; + int i; + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(!pcnt || (commits != NULL)); + DEBUG_CHECK(!ncnt || (ncommits != NULL)); + DEBUG_CHECK(secp256k1_ecmult_gen2_context_is_built(&ctx->ecmult_gen2_ctx)); + secp256k1_gej_set_infinity(&accj); + if (excess) { + uint64_t ex; + int neg; + /* Take the absolute value, and negate the result if the input was negative. */ + neg = secp256k1_sign_and_abs64(&ex, excess); + secp256k1_ecmult_gen2_small(&ctx->ecmult_gen2_ctx, &accj, ex); + if (neg) { + secp256k1_gej_neg(&accj, &accj); + } + } + for (i = 0; i < ncnt; i++) { + if (!secp256k1_eckey_pubkey_parse(&add, ncommits[i], 33)) { + return 0; + } + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + secp256k1_gej_neg(&accj, &accj); + for (i = 0; i < pcnt; i++) { + if (!secp256k1_eckey_pubkey_parse(&add, commits[i], 33)) { + return 0; + } + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + return secp256k1_gej_is_infinity(&accj); +} + +int secp256k1_rangeproof_info(const secp256k1_context_t* ctx, int *exp, int *mantissa, + uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, int plen) { + int offset; + uint64_t scale; + DEBUG_CHECK(exp != NULL); + DEBUG_CHECK(mantissa != NULL); + DEBUG_CHECK(min_value != NULL); + DEBUG_CHECK(max_value != NULL); + offset = 0; + scale = 1; + (void)ctx; + return secp256k1_rangeproof_getheader_impl(&offset, exp, mantissa, &scale, min_value, max_value, proof, plen); +} + +int secp256k1_rangeproof_rewind(const secp256k1_context_t* ctx, + unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, int *outlen, const unsigned char *nonce, + uint64_t *min_value, uint64_t *max_value, + const unsigned char *commit, const unsigned char *proof, int plen) { + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(commit != NULL); + DEBUG_CHECK(proof != NULL); + DEBUG_CHECK(min_value != NULL); + DEBUG_CHECK(max_value != NULL); + DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + DEBUG_CHECK(secp256k1_ecmult_gen2_context_is_built(&ctx->ecmult_gen2_ctx)); + DEBUG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &ctx->ecmult_gen2_ctx, &ctx->rangeproof_ctx, + blind_out, value_out, message_out, outlen, nonce, min_value, max_value, commit, proof, plen); +} + +int secp256k1_rangeproof_verify(const secp256k1_context_t* ctx, uint64_t *min_value, uint64_t *max_value, + const unsigned char *commit, const unsigned char *proof, int plen) { + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(commit != NULL); + DEBUG_CHECK(proof != NULL); + DEBUG_CHECK(min_value != NULL); + DEBUG_CHECK(max_value != NULL); + DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + DEBUG_CHECK(secp256k1_ecmult_gen2_context_is_built(&ctx->ecmult_gen2_ctx)); + DEBUG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); + return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL, &ctx->ecmult_gen2_ctx, &ctx->rangeproof_ctx, + NULL, NULL, NULL, NULL, NULL, min_value, max_value, commit, proof, plen); +} + +int secp256k1_rangeproof_sign(const secp256k1_context_t* ctx, unsigned char *proof, int *plen, uint64_t min_value, + const unsigned char *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value){ + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(proof != NULL); + DEBUG_CHECK(plen != NULL); + DEBUG_CHECK(commit != NULL); + DEBUG_CHECK(blind != NULL); + DEBUG_CHECK(nonce != NULL); + DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + DEBUG_CHECK(secp256k1_ecmult_gen2_context_is_built(&ctx->ecmult_gen2_ctx)); + DEBUG_CHECK(secp256k1_rangeproof_context_is_built(&ctx->rangeproof_ctx)); + return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &ctx->ecmult_gen2_ctx, &ctx->rangeproof_ctx, + proof, plen, min_value, commit, blind, nonce, exp, min_bits, value); +} diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 21c69f1c5..8d9930657 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -29,6 +29,23 @@ SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++]; } +SECP256K1_INLINE static int64_t secp256k1_rands64(uint64_t min, uint64_t max) { + uint64_t range; + uint64_t r; + uint64_t clz; + DEBUG_CHECK(max >= min); + if (max == min) { + return min; + } + range = max - min; + clz = secp256k1_clz64_var(range); + do { + r = ((uint64_t)secp256k1_rand32() << 32) | secp256k1_rand32(); + r >>= clz; + } while (r > range); + return min + (int64_t)r; +} + static void secp256k1_rand256(unsigned char *b32) { secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); } diff --git a/src/tests.c b/src/tests.c index c646e2a08..38012738f 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -103,11 +103,57 @@ void random_scalar_order(secp256k1_scalar_t *num) { } while(1); } +void run_util_tests(void) { + int i; + uint64_t r; + uint64_t r2; + uint64_t r3; + int64_t s; + CHECK(secp256k1_clz64_var(0) == 64); + CHECK(secp256k1_clz64_var(1) == 63); + CHECK(secp256k1_clz64_var(2) == 62); + CHECK(secp256k1_clz64_var(3) == 62); + CHECK(secp256k1_clz64_var(~0ULL) == 0); + CHECK(secp256k1_clz64_var((~0ULL) - 1) == 0); + CHECK(secp256k1_clz64_var((~0ULL) >> 1) == 1); + CHECK(secp256k1_clz64_var((~0ULL) >> 2) == 2); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MAX) == 0); + CHECK(r == INT64_MAX); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MAX - 1) == 0); + CHECK(r == INT64_MAX - 1); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MIN) == 1); + CHECK(r == (uint64_t)INT64_MAX + 1); + CHECK(secp256k1_sign_and_abs64(&r, INT64_MIN + 1) == 1); + CHECK(r == (uint64_t)INT64_MAX); + CHECK(secp256k1_sign_and_abs64(&r, 0) == 0); + CHECK(r == 0); + CHECK(secp256k1_sign_and_abs64(&r, 1) == 0); + CHECK(r == 1); + CHECK(secp256k1_sign_and_abs64(&r, -1) == 1); + CHECK(r == 1); + CHECK(secp256k1_sign_and_abs64(&r, 2) == 0); + CHECK(r == 2); + CHECK(secp256k1_sign_and_abs64(&r, -2) == 1); + CHECK(r == 2); + for (i = 0; i < 10; i++) { + CHECK(secp256k1_clz64_var((~0ULL) - secp256k1_rand32()) == 0); + r = ((uint64_t)secp256k1_rand32() << 32) | secp256k1_rand32(); + r2 = secp256k1_rands64(0, r); + CHECK(r2 <= r); + r3 = secp256k1_rands64(r2, r); + CHECK((r3 >= r2) && (r3 <= r)); + r = secp256k1_rands64(0, INT64_MAX); + s = (int64_t)r * (secp256k1_rand32()&1?-1:1); + CHECK(secp256k1_sign_and_abs64(&r2, s) == (s < 0)); + CHECK(r2 == r); + } +} + void run_context_tests(void) { secp256k1_context_t *none = secp256k1_context_create(0); secp256k1_context_t *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context_t *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context_t *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context_t *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_COMMIT | SECP256K1_CONTEXT_RANGEPROOF); secp256k1_gej_t pubj; secp256k1_ge_t pub; @@ -2260,9 +2306,285 @@ void run_ecdsa_openssl(void) { } #endif +void test_pedersen(void) { + unsigned char commits[33*19]; + const unsigned char *cptr[19]; + unsigned char blinds[32*19]; + const unsigned char *bptr[19]; + secp256k1_scalar_t s; + uint64_t values[19]; + int64_t totalv; + int i; + int inputs; + int outputs; + int total; + inputs = (secp256k1_rand32() & 7) + 1; + outputs = (secp256k1_rand32() & 7) + 2; + total = inputs + outputs; + for (i = 0; i < 19; i++) { + cptr[i] = &commits[i * 33]; + bptr[i] = &blinds[i * 32]; + } + totalv = 0; + for (i = 0; i < inputs; i++) { + values[i] = secp256k1_rands64(0, INT64_MAX - totalv); + totalv += values[i]; + } + if (secp256k1_rand32() & 1) { + for (i = 0; i < outputs; i++) { + int64_t max = INT64_MAX; + if (totalv < 0) { + max += totalv; + } + values[i + inputs] = secp256k1_rands64(0, max); + totalv -= values[i + inputs]; + } + } else { + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_rands64(0, totalv); + totalv -= values[i + inputs]; + } + values[total - 1] = totalv >> (secp256k1_rand32() & 1); + totalv -= values[total - 1]; + } + for (i = 0; i < total - 1; i++) { + random_scalar_order(&s); + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs)); + for (i = 0; i < total; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i * 33], &blinds[i * 32], values[i])); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv)); + CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs, totalv + 1)); + random_scalar_order(&s); + for (i = 0; i < 4; i++) { + secp256k1_scalar_get_b32(&blinds[i * 32], &s); + } + values[0] = INT64_MAX; + values[1] = 0; + values[2] = 1; + for (i = 0; i < 3; i++) { + CHECK(secp256k1_pedersen_commit(ctx, &commits[i * 33], &blinds[i * 32], values[i])); + } + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[2], 1, -1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[2], 1, &cptr[1], 1, 1)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1, 0)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[1], 1, INT64_MAX)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1, 0)); + CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[0], 1, -INT64_MAX)); +} + +void run_pedersen(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_pedersen(); + } +} + +void test_borromean(void) { + unsigned char e0[32]; + secp256k1_scalar_t s[64]; + secp256k1_gej_t pubs[64]; + secp256k1_scalar_t k[8]; + secp256k1_scalar_t sec[8]; + secp256k1_ge_t ge; + secp256k1_scalar_t one; + unsigned char m[32]; + int rsizes[8]; + int secidx[8]; + int nrings; + int i; + int j; + int c; + secp256k1_rand256_test(m); + nrings = 1 + (secp256k1_rand32()&7); + c = 0; + secp256k1_scalar_set_int(&one, 1); + if (secp256k1_rand32()&1) { + secp256k1_scalar_negate(&one, &one); + } + for (i = 0; i < nrings; i++) { + rsizes[i] = 1 + (secp256k1_rand32()&7); + secidx[i] = secp256k1_rand32() % rsizes[i]; + random_scalar_order(&sec[i]); + random_scalar_order(&k[i]); + if(secp256k1_rand32()&7) { + sec[i] = one; + } + if(secp256k1_rand32()&7) { + k[i] = one; + } + for (j = 0; j < rsizes[i]; j++) { + random_scalar_order(&s[c + j]); + if(secp256k1_rand32()&7) { + s[i] = one; + } + if (j == secidx[i]) { + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubs[c + j], &sec[i]); + } else { + random_group_element_test(&ge); + random_group_element_jacobian_test(&pubs[c + j],&ge); + } + } + c += rsizes[i]; + } + CHECK(secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, secidx, nrings, m, 32)); + CHECK(secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32)); + i = secp256k1_rand32() % c; + secp256k1_scalar_negate(&s[i],&s[i]); + CHECK(!secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32)); + secp256k1_scalar_negate(&s[i],&s[i]); + secp256k1_scalar_set_int(&one, 1); + for(j = 0; j < 4; j++) { + i = secp256k1_rand32() % c; + if (secp256k1_rand32() & 1) { + secp256k1_gej_double_var(&pubs[i],&pubs[i], NULL); + } else { + secp256k1_scalar_add(&s[i],&s[i],&one); + } + CHECK(!secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32)); + } +} + +void test_rangeproof(void) { + const uint64_t testvs[11] = {0, 1, 5, 11, 65535, 65537, INT32_MAX, UINT32_MAX, INT64_MAX - 1, INT64_MAX, UINT64_MAX}; + unsigned char commit[33]; + unsigned char commit2[33]; + unsigned char proof[5134]; + unsigned char blind[32]; + unsigned char blindout[32]; + unsigned char message[4096]; + int mlen; + uint64_t v; + uint64_t vout; + uint64_t vmin; + uint64_t minv; + uint64_t maxv; + int len; + int i; + int j; + int k; + secp256k1_rand256(blind); + for (i = 0; i < 11; i++) { + v = testvs[i]; + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) { + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, commit, blind, commit, 0, 0, v)); + CHECK(len <= 5134); + mlen = 4096; + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit, &minv, &maxv, commit, proof, len)); + for (j = 0; j < mlen; j++) { + CHECK(message[j] == 0); + } + CHECK(mlen <= 4096); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv <= v); + CHECK(maxv >= v); + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, commit, blind, commit, -1, 64, v)); + CHECK(len <= 73); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit, &minv, &maxv, commit, proof, len)); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv == v); + CHECK(maxv == v); + } + } + secp256k1_rand256(blind); + v = INT64_MAX - 1; + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + for (i = 0; i < 19; i++) { + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, commit, blind, commit, i, 0, v)); + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(len <= 5134); + CHECK(minv <= v); + CHECK(maxv >= v); + } + secp256k1_rand256(blind); + { + /*Malleability test.*/ + v = secp256k1_rands64(0, 255); + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + len = 5134; + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, commit, blind, commit, 0, 3, v)); + CHECK(len <= 5134); + for (i = 0; i < len*8; i++) { + proof[i >> 3] ^= 1 << (i & 7); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + proof[i >> 3] ^= 1 << (i & 7); + } + CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit, proof, len)); + CHECK(minv <= v); + CHECK(maxv >= v); + } + memcpy(commit2, commit, 33); + for (i = 0; i < 10 * count; i++) { + int exp; + int min_bits; + v = secp256k1_rands64(0, UINT64_MAX >> (secp256k1_rand32()&63)); + vmin = 0; + if ((v < INT64_MAX) && (secp256k1_rand32()&1)) { + vmin = secp256k1_rands64(0, v); + } + secp256k1_rand256(blind); + CHECK(secp256k1_pedersen_commit(ctx, commit, blind, v)); + len = 5134; + exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18); + if (exp < 0) { + exp = -exp; + } + min_bits = (int)secp256k1_rands64(0,64)-(int)secp256k1_rands64(0,64); + if (min_bits < 0) { + min_bits = -min_bits; + } + CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, commit, blind, commit, exp, min_bits, v)); + CHECK(len <= 5134); + mlen = 4096; + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit, &minv, &maxv, commit, proof, len)); + for (j = 0; j < mlen; j++) { + CHECK(message[j] == 0); + } + CHECK(mlen <= 4096); + CHECK(memcmp(blindout, blind, 32) == 0); + CHECK(vout == v); + CHECK(minv <= v); + CHECK(maxv >= v); + CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit, &minv, &maxv, commit, proof, len)); + memcpy(commit2, commit, 33); + } + for (j = 0; j < 10; j++) { + for (i = 0; i < 96; i++) { + secp256k1_rand256(&proof[i * 32]); + } + for (k = 0; k < 128; k++) { + len = k; + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit2, proof, len)); + } + len = secp256k1_rands64(0, 3072); + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, commit2, proof, len)); + } +} + +void run_borromean(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_borromean(); + } +} + +void run_rangeproof(void) { + test_rangeproof(); +} + + int main(int argc, char **argv) { unsigned char seed16[16] = {0}; unsigned char run32[32] = {0}; + memset(seed16, 0, sizeof(seed16)); /* find iteration count */ if (argc > 1) { count = strtol(argv[1], NULL, 0); @@ -2304,13 +2626,19 @@ int main(int argc, char **argv) { /* initialize */ run_context_tests(); - ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_COMMIT | SECP256K1_CONTEXT_RANGEPROOF); if (secp256k1_rand32() & 1) { secp256k1_rand256(run32); CHECK(secp256k1_context_randomize(ctx, secp256k1_rand32() & 1 ? run32 : NULL)); } + run_util_tests(); + + run_pedersen(); + run_borromean(); + run_rangeproof(); + run_sha256_tests(); run_hmac_sha256_tests(); run_rfc6979_hmac_sha256_tests(); diff --git a/src/util.h b/src/util.h index ae98639f7..abe3a00c4 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -13,6 +13,7 @@ #include #include +#include #include #ifdef DETERMINISTIC @@ -101,4 +102,32 @@ static SECP256K1_INLINE void *checked_malloc(size_t size) { SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; #endif +/* Extract the sign of an int64, take the abs and return a uint64, constant time. */ +SECP256K1_INLINE static int secp256k1_sign_and_abs64(uint64_t *out, int64_t in) { + uint64_t mask0, mask1; + int ret; + ret = in < 0; + mask0 = ret + ~((uint64_t)0); + mask1 = ~mask0; + *out = (uint64_t)in; + *out = (*out & mask0) | ((~*out + 1) & mask1); + return ret; +} + +SECP256K1_INLINE static int secp256k1_clz64_var(uint64_t x) { + int ret; + if (!x) { + return 64; + } +# if defined(HAVE_BUILTIN_CLZLL) + ret = __builtin_clzll(x); +# else + /*FIXME: debruijn fallback. */ + for (ret = 0; ((x & (1ULL << 63)) == 0); x <<= 1, ret++); +# endif + return ret; + +} + + #endif