Compare commits

...

33 Commits

Author SHA1 Message Date
Andrew Poelstra
84faa7eb03
Merge pull request #17 from instagibbs/fixwhitelistdist
add whitelist_impl.h to include for dist
2018-02-13 21:37:38 +00:00
Gregory Sanders
450e64a427 add whitelist_impl.h to include for dist 2018-02-13 16:28:30 -05:00
Andrew Poelstra
b2df03da55 Merge pull request #13 from apoelstra/argcheck-removal
generator: remove unnecessary ARG_CHECK from generate()
2017-09-06 17:28:37 +00:00
Andrew Poelstra
911a08f245
generator: add API tests 2017-09-03 05:24:16 +00:00
Andrew Poelstra
9da79c3782
generator: remove unnecessary ARG_CHECK from generate() 2017-08-30 17:59:26 +00:00
Jonas Nick
f73e50653a Merge pull request #12 from ElementsProject/gen-header
Fix generator module makefile
2017-08-19 19:24:12 -07:00
Gregory Sanders
1e2fda1480 Fix generator makefile
Include test_impl.h
2017-08-15 22:39:26 -04:00
Andrew Poelstra
4564081e26 Merge pull request #11 from jonasnick/blind_sum_doc_fix
Fix pedersen_blind_generator_blind_sum return value documentation
2017-07-18 14:04:25 +00:00
Jonas Nick
619b8f16dc Fix pedersen_blind_generator_blind_sum return value documentation 2017-07-11 16:35:39 +02:00
Andrew Poelstra
8584c066c7 Merge pull request #10 from jonasnick/whitelist-verify-nkeys
Add n_keys argument to whitelist_verify
2017-07-10 17:08:23 +00:00
Jonas Nick
d5b3440c3b Add n_keys argument to whitelist_verify 2017-07-10 18:51:16 +02:00
Andrew Poelstra
12bd9fd8e5 Merge pull request #8 from jonasnick/whitelist_api_fix
Fix checks of whitelist serialize/parse arguments
2017-06-27 13:59:43 +00:00
Jonas Nick
a809ac484f Fix checks of whitelist serialize/parse arguments 2017-06-27 15:02:40 +02:00
Andrew Poelstra
cb8a394102 Merge pull request #7 from apoelstra/whitelist-api-fix
whitelist: fix serialize/parse API to take serialized length
2017-06-26 17:57:03 +00:00
Andrew Poelstra
0f21e05dfa
whitelist: fix serialize/parse API to take serialized length 2017-06-26 17:08:47 +00:00
Andrew Poelstra
4f7a50e173 Merge pull request #6 from jonasnick/doc-fixes
Fix include/secp256k1_rangeproof.h function argument documentation.
2017-05-09 10:26:47 +00:00
Jonas Nick
5b72133255 Fix include/secp256k1_rangeproof.h function argument documentation. 2017-05-09 02:00:14 +02:00
Andrew Poelstra
7dd7883de6 Merge pull request #5 from apoelstra/test-cleanup
Additional rangeproof and surjection proof tests
2017-05-08 09:45:04 +00:00
Andrew Poelstra
0cfa29f87a rangeproof: add API tests 2017-05-08 09:44:11 +00:00
Andrew Poelstra
d8295970d2 surjectionproof: rename unit test functions to be more consistent with other modules 2017-05-08 09:44:11 +00:00
Andrew Poelstra
5eae1b9793 surjectionproof: add API unit tests 2017-05-08 09:44:11 +00:00
Andrew Poelstra
8454a98521 surjectionproof: tests_impl.h s/assert/CHECK/g 2017-05-08 09:44:08 +00:00
Andrew Poelstra
d702d210b8 rangeproof: fix memory leak in unit tests 2017-05-02 16:55:48 +00:00
Andrew Poelstra
d78f12b04e add surjection proof module
Includes fix and tests by Jonas Nick.
2017-04-03 08:18:52 -07:00
Andrew Poelstra
da035050f8 Implement ring-signature based whitelist delegation scheme 2017-04-03 08:18:52 -07:00
Andrew Poelstra
6eebf82d8a rangeproof: add summing function for blinded generators; drop excess and gen from verify_tally 2017-04-03 08:18:52 -07:00
Pieter Wuille
4577c24234 Expose generator in pedersen/rangeproof API 2017-04-03 07:53:53 -07:00
Pieter Wuille
e7f7b3c941 Constant-time generator module 2017-04-03 07:53:52 -07:00
Andrew Poelstra
9722b11506 rangeproof: expose sidechannel message field in the signing API
Including a fix by Jonas Nick.
2017-04-03 07:46:53 -07:00
Andrew Poelstra
9f21e1b518 [RANGEPROOF BREAK] Use quadratic residue for tie break and modularity cleanup
Switch to secp256k1_pedersen_commitment by Andrew Poelstra.
Switch to quadratic residue based disambiguation by Pieter Wuille.
2017-04-03 07:46:53 -07:00
Pieter Wuille
dc05520096 Get rid of precomputed H tables 2017-04-03 07:46:52 -07:00
Gregory Maxwell
8de58308d8 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.
2017-04-03 07:03:48 -07:00
Greg Maxwell
f7b48c577d Add 64-bit integer utilities 2017-04-03 07:03:18 -07:00
46 changed files with 5236 additions and 72 deletions

View File

@ -175,3 +175,19 @@ endif
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
if ENABLE_MODULE_GENERATOR
include src/modules/generator/Makefile.am.include
endif
if ENABLE_MODULE_RANGEPROOF
include src/modules/rangeproof/Makefile.am.include
endif
if ENABLE_MODULE_WHITELIST
include src/modules/whitelist/Makefile.am.include
endif
if ENABLE_MODULE_SURJECTIONPROOF
include src/modules/surjection/Makefile.am.include
endif

View File

@ -134,11 +134,31 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
AC_ARG_ENABLE(module_generator,
AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module (default is no)]),
[enable_module_generator=$enableval],
[enable_module_generator=no])
AC_ARG_ENABLE(module_rangeproof,
AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module (default is no)]),
[enable_module_rangeproof=$enableval],
[enable_module_rangeproof=no])
AC_ARG_ENABLE(module_whitelist,
AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module (default is no)]),
[enable_module_whitelist=$enableval],
[enable_module_whitelist=no])
AC_ARG_ENABLE(jni,
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
[use_jni=$enableval],
[use_jni=auto])
AC_ARG_ENABLE(module_surjectionproof,
AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module (default is no)]),
[enable_module_surjectionproof=$enableval],
[enable_module_surjectionproof=no])
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
@ -167,6 +187,12 @@ else
CFLAGS="$CFLAGS -O3"
fi
AC_MSG_CHECKING([for __builtin_popcount])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])],
[ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_POPCOUNT,1,[Define this symbol if __builtin_popcount is available]) ],
[ AC_MSG_RESULT([no])
])
if test x"$use_ecmult_static_precomputation" != x"no"; then
save_cross_compiling=$cross_compiling
cross_compiling=no
@ -195,6 +221,12 @@ else
set_precomp=no
fi
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
@ -435,6 +467,22 @@ if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
if test x"$enable_module_generator" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_GENERATOR, 1, [Define this symbol to enable the NUMS generator module])
fi
if test x"$enable_module_rangeproof" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RANGEPROOF, 1, [Define this symbol to enable the Pedersen / zero knowledge range proof module])
fi
if test x"$enable_module_whitelist" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_WHITELIST, 1, [Define this symbol to enable the key whitelisting module])
fi
if test x"$enable_module_surjectionproof" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_SURJECTIONPROOF, 1, [Define this symbol to enable the surjection proof module])
fi
AC_C_BIGENDIAN()
if test x"$use_external_asm" = x"yes"; then
@ -457,7 +505,26 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator])
AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof])
AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist])
AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof])
AC_MSG_NOTICE([******])
if test x"$enable_module_generator" != x"yes"; then
if test x"$enable_module_rangeproof" = x"yes"; then
AC_MSG_ERROR([Rangeproof module requires the generator module. Use --enable-module-generator to allow.])
fi
fi
if test x"$enable_module_rangeproof" != x"yes"; then
if test x"$enable_module_whitelist" = x"yes"; then
AC_MSG_ERROR([Whitelist module requires the rangeproof module. Use --enable-module-rangeproof to allow.])
fi
if test x"$enable_module_surjectionproof" = x"yes"; then
AC_MSG_ERROR([Surjection proof module requires the rangeproof module. Use --enable-module-rangeproof to allow.])
fi
fi
else
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
@ -465,6 +532,18 @@ else
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_generator" = x"yes"; then
AC_MSG_ERROR([NUMS generator module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_rangeproof" = x"yes"; then
AC_MSG_ERROR([Range proof module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_whitelist" = x"yes"; then
AC_MSG_ERROR([Key whitelisting module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_surjectionproof" = x"yes"; then
AC_MSG_ERROR([Surjection proof module is experimental. Use --enable-experimental to allow.])
fi
fi
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
@ -481,9 +560,13 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"])
dnl make sure nothing new is exported so that we don't break the cache
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"

View File

@ -0,0 +1,96 @@
#ifndef _SECP256K1_GENERATOR_
# define _SECP256K1_GENERATOR_
# include "secp256k1.h"
# ifdef __cplusplus
extern "C" {
# endif
#include <stdint.h>
/** Opaque data structure that stores a base point
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 33 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage or transmission, use
* the secp256k1_generator_serialize_*.
*
* Furthermore, it is guaranteed to identical points will have identical
* representation, so they can be memcmp'ed.
*/
typedef struct {
unsigned char data[33];
} secp256k1_generator;
/** Parse a 33-byte generator byte sequence into a generator object.
*
* Returns: 1 if input contains a valid generator.
* Args: ctx: a secp256k1 context object.
* Out: commit: pointer to the output generator object
* In: input: pointer to a 33-byte serialized generator
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse(
const secp256k1_context* ctx,
secp256k1_generator* commit,
const unsigned char *input
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a 33-byte generator into a serialized byte sequence.
*
* Returns: 1 always.
* Args: ctx: a secp256k1 context object.
* Out: output: a pointer to a 33-byte byte array
* In: commit: a pointer to a generator
*/
SECP256K1_API int secp256k1_generator_serialize(
const secp256k1_context* ctx,
unsigned char *output,
const secp256k1_generator* commit
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Generate a generator for the curve.
*
* Returns: 0 in the highly unlikely case the seed is not acceptable,
* 1 otherwise.
* Args: ctx: a secp256k1 context object
* Out: gen: a generator object
* In: seed32: a 32-byte seed
*
* If succesful, a valid generator will be placed in gen. The produced
* generators are distributed uniformly over the curve, and will not have a
* known dicrete logarithm with respect to any other generator produced,
* or to the base generator G.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate(
const secp256k1_context* ctx,
secp256k1_generator* gen,
const unsigned char *seed32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Generate a blinded generator for the curve.
*
* Returns: 0 in the highly unlikely case the seed is not acceptable or when
* blind is out of range. 1 otherwise.
* Args: ctx: a secp256k1 context object, initialized for signing
* Out: gen: a generator object
* In: seed32: a 32-byte seed
* blind32: a 32-byte secret value to blind the generator with.
*
* The result is equivalent to first calling secp256k1_generator_generate,
* converting the result to a public key, calling secp256k1_ec_pubkey_tweak_add,
* and then converting back to generator form.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_generate_blinded(
const secp256k1_context* ctx,
secp256k1_generator* gen,
const unsigned char *key32,
const unsigned char *blind32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
# ifdef __cplusplus
}
# endif
#endif

View File

@ -0,0 +1,299 @@
#ifndef _SECP256K1_RANGEPROOF_
# define _SECP256K1_RANGEPROOF_
# include "secp256k1.h"
# include "secp256k1_generator.h"
# ifdef __cplusplus
extern "C" {
# endif
#include <stdint.h>
/** Opaque data structure that stores a Pedersen commitment
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 33 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage or transmission, use
* secp256k1_pedersen_commitment_serialize and secp256k1_pedersen_commitment_parse.
*
* Furthermore, it is guaranteed to identical signatures will have identical
* representation, so they can be memcmp'ed.
*/
typedef struct {
unsigned char data[33];
} secp256k1_pedersen_commitment;
/**
* Static constant generator 'h' maintained for historical reasons.
*/
extern const secp256k1_generator *secp256k1_generator_h;
/** Parse a 33-byte commitment into a commitment object.
*
* Returns: 1 if input contains a valid commitment.
* Args: ctx: a secp256k1 context object.
* Out: commit: pointer to the output commitment object
* In: input: pointer to a 33-byte serialized commitment key
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse(
const secp256k1_context* ctx,
secp256k1_pedersen_commitment* commit,
const unsigned char *input
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a commitment object into a serialized byte sequence.
*
* Returns: 1 always.
* Args: ctx: a secp256k1 context object.
* Out: output: a pointer to a 33-byte byte array
* In: commit: a pointer to a secp256k1_pedersen_commitment containing an
* initialized commitment
*/
SECP256K1_API int secp256k1_pedersen_commitment_serialize(
const secp256k1_context* ctx,
unsigned char *output,
const secp256k1_pedersen_commitment* commit
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Initialize a context for usage with Pedersen commitments. */
void secp256k1_pedersen_context_initialize(secp256k1_context* ctx);
/** Generate a pedersen commitment.
* Returns 1: Commitment successfully created.
* 0: Error. The blinding factor is larger than the group order
* (probability for random 32 byte number < 2^-127) or results in the
* point at infinity. Retry with a different factor.
* In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL)
* blind: pointer to a 32-byte blinding factor (cannot be NULL)
* value: unsigned 64-bit integer value to commit to.
* gen: additional generator 'h'
* Out: commit: pointer to the commitment (cannot be NULL)
*
* Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit(
const secp256k1_context* ctx,
secp256k1_pedersen_commitment *commit,
const unsigned char *blind,
uint64_t value,
const secp256k1_generator *gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
/** Computes the sum of multiple positive and negative blinding factors.
* Returns 1: Sum successfully computed.
* 0: Error. A blinding factor is larger than the group order
* (probability for random 32 byte number < 2^-127). Retry with
* different factors.
* In: ctx: pointer to a context object (cannot be NULL)
* blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL)
* n: number of factors pointed to by blinds.
* npositive: how many of the initial factors should be treated with a positive sign.
* Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum(
const secp256k1_context* ctx,
unsigned char *blind_out,
const unsigned char * const *blinds,
size_t n,
size_t npositive
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Verify a tally of pedersen commitments
* Returns 1: commitments successfully sum to zero.
* 0: Commitments do not sum to zero or other error.
* In: ctx: pointer to a context object (cannot be NULL)
* commits: pointer to array of pointers to the commitments. (cannot be NULL if pcnt is non-zero)
* pcnt: number of commitments pointed to by commits.
* ncommits: pointer to array of pointers to the negative commitments. (cannot be NULL if ncnt is non-zero)
* ncnt: number of commitments pointed to by ncommits.
*
* This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) == 0.
*
* A pedersen commitment is xG + vA where G and A are generators for the secp256k1 group and x is a blinding factor,
* while v is the committed value. For a collection of commitments to sum to zero, for each distinct generator
* A all blinding factors and all values must sum to zero.
*
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally(
const secp256k1_context* ctx,
const secp256k1_pedersen_commitment * const* commits,
size_t pcnt,
const secp256k1_pedersen_commitment * const* ncommits,
size_t ncnt
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
/** Sets the final Pedersen blinding factor correctly when the generators themselves
* have blinding factors.
*
* Consider a generator of the form A' = A + rG, where A is the "real" generator
* but A' is the generator provided to verifiers. Then a Pedersen commitment
* P = vA' + r'G really has the form vA + (vr + r')G. To get all these (vr + r')
* to sum to zero for multiple commitments, we take three arrays consisting of
* the `v`s, `r`s, and `r'`s, respectively called `value`s, `generator_blind`s
* and `blinding_factor`s, and sum them.
*
* The function then subtracts the sum of all (vr + r') from the last element
* of the `blinding_factor` array, setting the total sum to zero.
*
* Returns 1: Blinding factor successfully computed.
* 0: Error. A blinding_factor or generator_blind are larger than the group
* order (probability for random 32 byte number < 2^-127). Retry with
* different values.
*
* In: ctx: pointer to a context object
* value: array of asset values, `v` in the above paragraph.
* May not be NULL unless `n_total` is 0.
* generator_blind: array of asset blinding factors, `r` in the above paragraph
* May not be NULL unless `n_total` is 0.
* n_total: Total size of the above arrays
* n_inputs: How many of the initial array elements represent commitments that
* will be negated in the final sum
* In/Out: blinding_factor: array of commitment blinding factors, `r'` in the above paragraph
* May not be NULL unless `n_total` is 0.
* the last value will be modified to get the total sum to zero.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generator_blind_sum(
const secp256k1_context* ctx,
const uint64_t *value,
const unsigned char* const* generator_blind,
unsigned char* const* blinding_factor,
size_t n_total,
size_t n_inputs
);
/** Initialize a context for usage with Pedersen commitments. */
void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx);
/** 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 commitment being proved. (cannot be NULL)
* proof: pointer to character array with the proof. (cannot be NULL)
* plen: length of proof in bytes.
* extra_commit: additional data covered in rangeproof signature
* extra_commit_len: length of extra_commit byte array (0 if NULL)
* gen: additional generator 'h'
* 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_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify(
const secp256k1_context* ctx,
uint64_t *min_value,
uint64_t *max_value,
const secp256k1_pedersen_commitment *commit,
const unsigned char *proof,
size_t plen,
const unsigned char *extra_commit,
size_t extra_commit_len,
const secp256k1_generator* gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(9);
/** 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 Pedersen commitment (cannot be NULL)
* commit: the 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)
* extra_commit: additional data covered in rangeproof signature
* extra_commit_len: length of extra_commit byte array (0 if NULL)
* gen: additional generator 'h'
* 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_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind(
const secp256k1_context* ctx,
unsigned char *blind_out,
uint64_t *value_out,
unsigned char *message_out,
size_t *outlen,
const unsigned char *nonce,
uint64_t *min_value,
uint64_t *max_value,
const secp256k1_pedersen_commitment *commit,
const unsigned char *proof,
size_t plen,
const unsigned char *extra_commit,
size_t extra_commit_len,
const secp256k1_generator *gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(14);
/** 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 Pedersen 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: 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.
* message: pointer to a byte array of data to be embedded in the rangeproof that can be recovered by rewinding the proof
* msg_len: size of the message to be embedded in the rangeproof
* extra_commit: additional data to be covered in rangeproof signature
* extra_commit_len: length of extra_commit byte array (0 if NULL)
* gen: additional generator 'h'
* 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_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign(
const secp256k1_context* ctx,
unsigned char *proof,
size_t *plen,
uint64_t min_value,
const secp256k1_pedersen_commitment *commit,
const unsigned char *blind,
const unsigned char *nonce,
int exp,
int min_bits,
uint64_t value,
const unsigned char *message,
size_t msg_len,
const unsigned char *extra_commit,
size_t extra_commit_len,
const secp256k1_generator *gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(15);
/** 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_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info(
const secp256k1_context* ctx,
int *exp,
int *mantissa,
uint64_t *min_value,
uint64_t *max_value,
const unsigned char *proof,
size_t plen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
# ifdef __cplusplus
}
# endif
#endif

View File

@ -0,0 +1,212 @@
#ifndef _SECP256K1_SURJECTIONPROOF_
#define _SECP256K1_SURJECTIONPROOF_
#include "secp256k1.h"
#include "secp256k1_rangeproof.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Maximum number of inputs that may be given in a surjection proof */
#define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256
/** Number of bytes a serialized surjection proof requires given the
* number of inputs and the number of used inputs.
*/
#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used_inputs) \
(2 + (n_inputs + 7)/8 + 32 * (1 + (n_used_inputs)))
/** Maximum number of bytes a serialized surjection proof requires. */
#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX \
SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)
/** Opaque data structure that holds a parsed surjection proof
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. Nor is
* it guaranteed to have any particular size, nor that identical proofs
* will have identical representation. (That is, memcmp may return nonzero
* even for identical proofs.)
*
* To obtain these properties, instead use secp256k1_surjectionproof_parse
* and secp256k1_surjectionproof_serialize to encode/decode proofs into a
* well-defined format.
*
* The representation is exposed to allow creation of these objects on the
* stack; please *do not* use these internals directly.
*/
typedef struct {
#ifdef VERIFY
/** Mark whether this proof has gone through `secp256k1_surjectionproof_initialize` */
int initialized;
#endif
/** Total number of input asset tags */
size_t n_inputs;
/** Bitmap of which input tags are used in the surjection proof */
unsigned char used_inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8];
/** Borromean signature: e0, scalars */
unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)];
} secp256k1_surjectionproof;
/** Parse a surjection proof
*
* Returns: 1 when the proof could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: proof: a pointer to a proof object
* In: input: a pointer to the array to parse
* inputlen: length of the array pointed to by input
*
* The proof must consist of:
* - A 2-byte little-endian total input count `n`
* - A ceil(n/8)-byte bitmap indicating which inputs are used.
* - A big-endian 32-byte borromean signature e0 value
* - `m` big-endian 32-byte borromean signature s values, where `m`
* is the number of set bits in the bitmap
*/
SECP256K1_API int secp256k1_surjectionproof_parse(
const secp256k1_context* ctx,
secp256k1_surjectionproof *proof,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a surjection proof
*
* Returns: 1 if enough space was available to serialize, 0 otherwise
* Args: ctx: a secp256k1 context object
* Out: output: a pointer to an array to store the serialization
* In/Out: outputlen: a pointer to an integer which is initially set to the
* size of output, and is overwritten with the written
* size.
* In: proof: a pointer to an initialized proof object
*
* See secp256k1_surjectionproof_parse for details about the encoding.
*/
SECP256K1_API int secp256k1_surjectionproof_serialize(
const secp256k1_context* ctx,
unsigned char *output,
size_t *outputlen,
const secp256k1_surjectionproof *proof
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Data structure that holds a fixed asset tag.
*
* This data type is *not* opaque. It will always be 32 bytes of whatever
* data the API user wants to use as an asset tag. Its contents have no
* semantic meaning to libsecp whatsoever.
*/
typedef struct {
unsigned char data[32];
} secp256k1_fixed_asset_tag;
/** Returns the total number of inputs a proof expects to be over.
*
* Returns: the number of inputs for the given proof
* In: ctx: pointer to a context object
* proof: a pointer to a proof object
*/
SECP256K1_API size_t secp256k1_surjectionproof_n_total_inputs(
const secp256k1_context* ctx,
const secp256k1_surjectionproof* proof
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Returns the actual number of inputs that a proof uses
*
* Returns: the number of inputs for the given proof
* In: ctx: pointer to a context object
* proof: a pointer to a proof object
*/
SECP256K1_API size_t secp256k1_surjectionproof_n_used_inputs(
const secp256k1_context* ctx,
const secp256k1_surjectionproof* proof
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Returns the total size this proof would take, in bytes, when serialized
*
* Returns: the total size
* In: ctx: pointer to a context object
* proof: a pointer to a proof object
*/
SECP256K1_API size_t secp256k1_surjectionproof_serialized_size(
const secp256k1_context* ctx,
const secp256k1_surjectionproof* proof
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Surjection proof initialization function; decides on inputs to use
* Returns 0: inputs could not be selected
* n: inputs were selected after n iterations of random selection
*
* In: ctx: pointer to a context object
* fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known,
* e.g. in a coinjoin with others' inputs, an ephemeral tag can be given;
* this won't match the output tag but might be used in the anonymity set.)
* n_input_tags: the number of entries in the fixed_input_tags array
* n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set
* fixed_output_tag: fixed output tag
* max_n_iterations: the maximum number of iterations to do before giving up
* random_seed32: a random seed to be used for input selection
* Out: proof: The proof whose bitvector will be initialized. In case of failure,
* the state of the proof is undefined.
* input_index: The index of the actual input that is secretly mapped to the output
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initialize(
const secp256k1_context* ctx,
secp256k1_surjectionproof* proof,
size_t *input_index,
const secp256k1_fixed_asset_tag* fixed_input_tags,
const size_t n_input_tags,
const size_t n_input_tags_to_use,
const secp256k1_fixed_asset_tag* fixed_output_tag,
const size_t n_max_iterations,
const unsigned char *random_seed32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7);
/** Surjection proof generation function
* Returns 0: proof could not be created
* 1: proof was successfully created
*
* In: ctx: pointer to a context object, initialized for signing and verification
* ephemeral_input_tags: the ephemeral asset tag of all inputs
* n_ephemeral_input_tags: the number of entries in the ephemeral_input_tags array
* ephemeral_output_tag: the ephemeral asset tag of the output
* input_index: the index of the input that actually maps to the output
* input_blinding_key: the blinding key of the input
* output_blinding_key: the blinding key of the output
* In/Out: proof: The produced surjection proof. Must have already gone through `secp256k1_surjectionproof_initialize`
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_generate(
const secp256k1_context* ctx,
secp256k1_surjectionproof* proof,
const secp256k1_generator* ephemeral_input_tags,
size_t n_ephemeral_input_tags,
const secp256k1_generator* ephemeral_output_tag,
size_t input_index,
const unsigned char *input_blinding_key,
const unsigned char *output_blinding_key
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
/** Surjection proof verification function
* Returns 0: proof was invalid
* 1: proof was valid
*
* In: ctx: pointer to a context object, initialized for signing and verification
* proof: proof to be verified
* ephemeral_input_tags: the ephemeral asset tag of all inputs
* n_ephemeral_input_tags: the number of entries in the ephemeral_input_tags array
* ephemeral_output_tag: the ephemeral asset tag of the output
*/
SECP256K1_API int secp256k1_surjectionproof_verify(
const secp256k1_context* ctx,
const secp256k1_surjectionproof* proof,
const secp256k1_generator* ephemeral_input_tags,
size_t n_ephemeral_input_tags,
const secp256k1_generator* ephemeral_output_tag
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,152 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_WHITELIST_
#define _SECP256K1_WHITELIST_
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SECP256K1_WHITELIST_MAX_N_KEYS 256
/** Opaque data structure that holds a parsed whitelist proof
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. Nor is
* it guaranteed to have any particular size, nor that identical signatures
* will have identical representation. (That is, memcmp may return nonzero
* even for identical signatures.)
*
* To obtain these properties, instead use secp256k1_whitelist_signature_parse
* and secp256k1_whitelist_signature_serialize to encode/decode signatures
* into a well-defined format.
*
* The representation is exposed to allow creation of these objects on the
* stack; please *do not* use these internals directly. To learn the number
* of keys for a signature, use `secp256k1_whitelist_signature_n_keys`.
*/
typedef struct {
size_t n_keys;
/* e0, scalars */
unsigned char data[32 * (1 + SECP256K1_WHITELIST_MAX_N_KEYS)];
} secp256k1_whitelist_signature;
/** Parse a whitelist signature
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input: a pointer to the array to parse
* input_len: the length of the above array
*
* The signature must consist of a 1-byte n_keys value, followed by a 32-byte
* big endian e0 value, followed by n_keys many 32-byte big endian s values.
* If n_keys falls outside of [0..SECP256K1_WHITELIST_MAX_N_KEYS] the encoding
* is invalid.
*
* The total length of the input array must therefore be 33 + 32 * n_keys.
* If the length `input_len` does not match this value, parsing will fail.
*
* After the call, sig will always be initialized. If parsing failed or any
* scalar values overflow or are zero, the resulting sig value is guaranteed
* to fail validation for any set of keys.
*/
SECP256K1_API int secp256k1_whitelist_signature_parse(
const secp256k1_context* ctx,
secp256k1_whitelist_signature *sig,
const unsigned char *input,
size_t input_len
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Returns the number of keys a signature expects to have.
*
* Returns: the number of keys for the given signature
* In: sig: a pointer to a signature object
*/
SECP256K1_API size_t secp256k1_whitelist_signature_n_keys(
const secp256k1_whitelist_signature *sig
) SECP256K1_ARG_NONNULL(1);
/** Serialize a whitelist signature
*
* Returns: 1
* Args: ctx: a secp256k1 context object
* Out: output64: a pointer to an array to store the serialization
* In/Out: output_len: length of the above array, updated with the actual serialized length
* In: sig: a pointer to an initialized signature object
*
* See secp256k1_whitelist_signature_parse for details about the encoding.
*/
SECP256K1_API int secp256k1_whitelist_signature_serialize(
const secp256k1_context* ctx,
unsigned char *output,
size_t *output_len,
const secp256k1_whitelist_signature *sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Compute a whitelist signature
* Returns 1: signature was successfully created
* 0: signature was not successfully created
* In: ctx: pointer to a context object, initialized for signing and verification
* online_pubkeys: list of all online pubkeys
* offline_pubkeys: list of all offline pubkeys
* n_keys: the number of entries in each of the above two arrays
* sub_pubkey: the key to be whitelisted
* online_seckey: the secret key to the signer's online pubkey
* summed_seckey: the secret key to the sum of (whitelisted key, signer's offline pubkey)
* index: the signer's index in the lists of keys
* noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
* Out: sig: The produced signature.
*
* The signatures are of the list of all passed pubkeys in the order
* ( whitelist, online_1, offline_1, online_2, offline_2, ... )
* The verification key list consists of
* online_i + H(offline_i + whitelist)(offline_i + whitelist)
* for each public key pair (offline_i, offline_i). Here H means sha256 of the
* compressed serialization of the key.
*/
SECP256K1_API int secp256k1_whitelist_sign(
const secp256k1_context* ctx,
secp256k1_whitelist_signature *sig,
const secp256k1_pubkey *online_pubkeys,
const secp256k1_pubkey *offline_pubkeys,
const size_t n_keys,
const secp256k1_pubkey *sub_pubkey,
const unsigned char *online_seckey,
const unsigned char *summed_seckey,
const size_t index,
secp256k1_nonce_function noncefp,
const void *noncedata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
/** Verify a whitelist signature
* Returns 1: signature is valid
* 0: signature is not valid
* In: ctx: pointer to a context object, initialized for signing and verification
* sig: the signature to be verified
* online_pubkeys: list of all online pubkeys
* offline_pubkeys: list of all offline pubkeys
* n_keys: the number of entries in each of the above two arrays
* sub_pubkey: the key to be whitelisted
*/
SECP256K1_API int secp256k1_whitelist_verify(
const secp256k1_context* ctx,
const secp256k1_whitelist_signature *sig,
const secp256k1_pubkey *online_pubkeys,
const secp256k1_pubkey *offline_pubkeys,
const size_t n_keys,
const secp256k1_pubkey *sub_pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,51 @@
### http://www.di.ens.fr/~fouque/pub/latincrypt12.pdf
# Parameters for secp256k1
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
a = 0
b = 7
F = FiniteField (p)
C = EllipticCurve ([F(a), F(b)])
def svdw(t):
sqrt_neg_3 = F(-3).nth_root(2)
## Compute candidate x values
w = sqrt_neg_3 * t / (1 + b + t^2)
x = [ F(0), F(0), F(0) ]
x[0] = (-1 + sqrt_neg_3) / 2 - t * w
x[1] = -1 - x[0]
x[2] = 1 + 1 / w^2
print
print "On %2d" % t
print " x1 %064x" % x[0]
print " x2 %064x" % x[1]
print " x3 %064x" % x[2]
## Select which to use
alph = jacobi_symbol(x[0]^3 + b, p)
beta = jacobi_symbol(x[1]^3 + b, p)
if alph == 1 and beta == 1:
i = 0
elif alph == 1 and beta == -1:
i = 0
elif alph == -1 and beta == 1:
i = 1
elif alph == -1 and beta == -1:
i = 2
else:
print "Help! I don't understand Python!"
## Expand to full point
sign = 1 - 2 * (int(F(t)) % 2)
ret_x = x[i]
ret_y = sign * F(x[i]^3 + b).nth_root(2)
return C.point((ret_x, ret_y))
## main
for i in range(1, 11):
res = svdw(i)
print "Result: %064x %064x" % res.xy()

59
src/bench_generator.c Normal file
View File

@ -0,0 +1,59 @@
/**********************************************************************
* Copyright (c) 2016 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#include <stdint.h>
#include <string.h>
#include "include/secp256k1_generator.h"
#include "util.h"
#include "bench.h"
typedef struct {
secp256k1_context* ctx;
unsigned char key[32];
unsigned char blind[32];
} bench_generator_t;
static void bench_generator_setup(void* arg) {
bench_generator_t *data = (bench_generator_t*)arg;
memset(data->key, 0x31, 32);
memset(data->blind, 0x13, 32);
}
static void bench_generator_generate(void* arg) {
int i;
bench_generator_t *data = (bench_generator_t*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_generator gen;
CHECK(secp256k1_generator_generate(data->ctx, &gen, data->key));
data->key[i & 31]++;
}
}
static void bench_generator_generate_blinded(void* arg) {
int i;
bench_generator_t *data = (bench_generator_t*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_generator gen;
CHECK(secp256k1_generator_generate_blinded(data->ctx, &gen, data->key, data->blind));
data->key[1 + (i & 30)]++;
data->blind[1 + (i & 30)]++;
}
}
int main(void) {
bench_generator_t data;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, 20000);
run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, 20000);
secp256k1_context_destroy(data.ctx);
return 0;
}

View File

@ -251,7 +251,7 @@ void bench_wnaf_const(void* arg) {
bench_inv_t *data = (bench_inv_t*)arg;
for (i = 0; i < 20000; i++) {
secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A);
secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256, 1);
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
}
}

63
src/bench_rangeproof.c Normal file
View File

@ -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 <stdint.h>
#include "include/secp256k1_rangeproof.h"
#include "util.h"
#include "bench.h"
typedef struct {
secp256k1_context* ctx;
secp256k1_pedersen_commitment commit;
unsigned char proof[5134];
unsigned char blind[32];
size_t 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, secp256k1_generator_h));
data->len = 5134;
CHECK(secp256k1_rangeproof_sign(data->ctx, data->proof, &data->len, 0, &data->commit, data->blind, (const unsigned char*)&data->commit, 0, data->min_bits, data->v, NULL, 0, NULL, 0, secp256k1_generator_h));
CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h));
}
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, NULL, 0, secp256k1_generator_h);
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);
data.min_bits = 32;
run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, 1000 * data.min_bits);
secp256k1_context_destroy(data.ctx);
return 0;
}

View File

@ -10,6 +10,6 @@
#include "scalar.h"
#include "group.h"
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q);
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits);
#endif

View File

@ -12,12 +12,7 @@
#include "ecmult_const.h"
#include "ecmult_impl.h"
#ifdef USE_ENDOMORPHISM
#define WNAF_BITS 128
#else
#define WNAF_BITS 256
#endif
#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w))
#define WNAF_SIZE(bits, w) (((bits) + (w) - 1) / (w))
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
@ -54,7 +49,7 @@
*
* Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335
*/
static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size, int maybe_negative) {
int global_sign;
int skew = 0;
int word = 0;
@ -75,26 +70,34 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
* or 2 (for odd) to the number we are encoding, returning a skew value indicating
* this, and having the caller compensate after doing the multiplication. */
/* Negative numbers will be negated to keep their bit representation below the maximum width */
flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ !secp256k1_scalar_is_even(&s);
/* We check for negative one, since adding 2 to it will cause an overflow */
secp256k1_scalar_negate(&neg_s, &s);
not_neg_one = !secp256k1_scalar_is_one(&neg_s);
secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
/* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
* that we added two to it and flipped it. In fact for -1 these operations are
* identical. We only flipped, but since skewing is required (in the sense that
* the skew must be 1 or 2, never zero) and flipping is not, we need to change
* our flags to claim that we only skewed. */
global_sign = secp256k1_scalar_cond_negate(&s, flip);
global_sign *= not_neg_one * 2 - 1;
skew = 1 << bit;
if (maybe_negative) {
/* Negative numbers will be negated to keep their bit representation below the maximum width */
flip = maybe_negative ? secp256k1_scalar_is_high(&s) : 0;
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ !secp256k1_scalar_is_even(&s);
/* We check for negative one, since adding 2 to it will cause an overflow */
secp256k1_scalar_negate(&neg_s, &s);
not_neg_one = !secp256k1_scalar_is_one(&neg_s);
secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
/* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
* that we added two to it and flipped it. In fact for -1 these operations are
* identical. We only flipped, but since skewing is required (in the sense that
* the skew must be 1 or 2, never zero) and flipping is not, we need to change
* our flags to claim that we only skewed. */
global_sign = secp256k1_scalar_cond_negate(&s, flip);
global_sign *= not_neg_one * 2 - 1;
skew = 1 << bit;
} else {
VERIFY_CHECK(!secp256k1_scalar_is_high(&s));
bit = !secp256k1_scalar_is_even(&s);
skew = 1 << bit;
secp256k1_scalar_cadd_bit(&s, bit, 1);
global_sign = 1;
}
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
while (word * w < WNAF_BITS) {
do {
int sign;
int even;
@ -110,41 +113,50 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
wnaf[word++] = u_last * global_sign;
u_last = u;
}
} while(word * w < size);
wnaf[word] = u * global_sign;
VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
VERIFY_CHECK(word == WNAF_SIZE(w));
VERIFY_CHECK(word == WNAF_SIZE(size, w));
return skew;
}
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) {
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar, int size) {
secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_ge tmpa;
secp256k1_fe Z;
int skew_1;
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
#ifdef USE_ENDOMORPHISM
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
int wnaf_1[1 + WNAF_SIZE(128, WINDOW_A - 1)];
int wnaf_lam[1 + WNAF_SIZE(128, WINDOW_A - 1)];
int skew_lam;
secp256k1_scalar q_1, q_lam;
#else
int wnaf_1[1 + WNAF_SIZE(256, WINDOW_A - 1)];
#endif
int i;
secp256k1_scalar sc = *scalar;
/* build wnaf representation for q. */
int rsize = size;
#ifdef USE_ENDOMORPHISM
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1);
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1);
#else
skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1);
if (size > 128) {
rsize = 128;
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128, 1);
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128, 1);
} else
#endif
{
skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size, size == 256);
#ifdef USE_ENDOMORPHISM
skew_lam = 0;
#endif
}
/* Calculate odd multiples of a.
* All multiples are brought to the same Z 'denominator', which is stored
@ -158,26 +170,30 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_fe_normalize_weak(&pre_a[i].y);
}
#ifdef USE_ENDOMORPHISM
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
if (size > 128) {
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
}
}
#endif
/* first loop iteration (separated out so we can directly set r, rather
* than having it start at infinity, get doubled several times, then have
* its new value added to it) */
i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)];
i = wnaf_1[WNAF_SIZE(rsize, WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
secp256k1_gej_set_ge(r, &tmpa);
#ifdef USE_ENDOMORPHISM
i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
secp256k1_gej_add_ge(r, r, &tmpa);
if (size > 128) {
i = wnaf_lam[WNAF_SIZE(rsize, WINDOW_A - 1)];
VERIFY_CHECK(i != 0);
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
secp256k1_gej_add_ge(r, r, &tmpa);
}
#endif
/* remaining loop iterations */
for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) {
for (i = WNAF_SIZE(rsize, WINDOW_A - 1) - 1; i >= 0; i--) {
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
@ -189,10 +205,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
#ifdef USE_ENDOMORPHISM
n = wnaf_lam[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
if (size > 128) {
n = wnaf_lam[i];
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
VERIFY_CHECK(n != 0);
secp256k1_gej_add_ge(r, r, &tmpa);
}
#endif
}
@ -212,14 +230,18 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_ge_set_gej(&correction, &tmpj);
secp256k1_ge_to_storage(&correction_1_stor, a);
#ifdef USE_ENDOMORPHISM
secp256k1_ge_to_storage(&correction_lam_stor, a);
if (size > 128) {
secp256k1_ge_to_storage(&correction_lam_stor, a);
}
#endif
secp256k1_ge_to_storage(&a2_stor, &correction);
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
#ifdef USE_ENDOMORPHISM
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
if (size > 128) {
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
}
#endif
/* Apply the correction */
@ -228,10 +250,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
secp256k1_gej_add_ge(r, r, &correction);
#ifdef USE_ENDOMORPHISM
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_ge_mul_lambda(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
if (size > 128) {
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
secp256k1_ge_neg(&correction, &correction);
secp256k1_ge_mul_lambda(&correction, &correction);
secp256k1_gej_add_ge(r, r, &correction);
}
#endif
}
}

View File

@ -30,7 +30,7 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se
unsigned char y[1];
secp256k1_sha256_t sha;
secp256k1_ecmult_const(&res, &pt, &s);
secp256k1_ecmult_const(&res, &pt, &s, 256);
secp256k1_ge_set_gej(&pt, &res);
/* Compute a hash of the point in compressed form
* Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not

View File

@ -0,0 +1,9 @@
include_HEADERS += include/secp256k1_generator.h
noinst_HEADERS += src/modules/generator/main_impl.h
noinst_HEADERS += src/modules/generator/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_generator
bench_generator_SOURCES = src/bench_generator.c
bench_generator_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_generator_LDFLAGS = -static
endif

View File

@ -0,0 +1,206 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra & Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_GENERATOR_MAIN
#define SECP256K1_MODULE_GENERATOR_MAIN
#include <stdio.h>
#include "field.h"
#include "group.h"
#include "hash.h"
#include "scalar.h"
static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) {
secp256k1_fe fe;
secp256k1_fe_set_b32(&fe, &gen->data[1]);
secp256k1_ge_set_xquad(ge, &fe);
if (gen->data[0] & 1) {
secp256k1_ge_neg(ge, ge);
}
}
static void secp256k1_generator_save(secp256k1_generator* commit, secp256k1_ge* ge) {
secp256k1_fe_normalize(&ge->x);
secp256k1_fe_get_b32(&commit->data[1], &ge->x);
commit->data[0] = 11 ^ secp256k1_fe_is_quad_var(&ge->y);
}
int secp256k1_generator_parse(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *input) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(gen != NULL);
ARG_CHECK(input != NULL);
if ((input[0] & 0xFE) != 10) {
return 0;
}
memcpy(gen->data, input, sizeof(gen->data));
return 1;
}
int secp256k1_generator_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_generator* gen) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(gen != NULL);
memcpy(output, gen->data, sizeof(gen->data));
return 1;
}
static void shallue_van_de_woestijne(secp256k1_ge* ge, const secp256k1_fe* t) {
/* Implements the algorithm from:
* Indifferentiable Hashing to Barreto-Naehrig Curves
* Pierre-Alain Fouque and Mehdi Tibouchi
* Latincrypt 2012
*/
/* Basic algorithm:
c = sqrt(-3)
d = (c - 1)/2
w = c * t / (1 + b + t^2) [with b = 7]
x1 = d - t*w
x2 = -(x1 + 1)
x3 = 1 + 1/w^2
To avoid the 2 divisions, compute the above in numerator/denominator form:
wn = c * t
wd = 1 + 7 + t^2
x1n = d*wd - t*wn
x1d = wd
x2n = -(x1n + wd)
x2d = wd
x3n = wd^2 + c^2 + t^2
x3d = (c * t)^2
The joint denominator j = wd * c^2 * t^2, and
1 / x1d = 1/j * c^2 * t^2
1 / x2d = x3d = 1/j * wd
*/
static const secp256k1_fe c = SECP256K1_FE_CONST(0x0a2d2ba9, 0x3507f1df, 0x233770c2, 0xa797962c, 0xc61f6d15, 0xda14ecd4, 0x7d8d27ae, 0x1cd5f852);
static const secp256k1_fe d = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40);
static const secp256k1_fe b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7);
static const secp256k1_fe b_plus_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 8);
secp256k1_fe wn, wd, x1n, x2n, x3n, x3d, jinv, tmp, x1, x2, x3, alphain, betain, gammain, y1, y2, y3;
int alphaquad, betaquad;
secp256k1_fe_mul(&wn, &c, t); /* mag 1 */
secp256k1_fe_sqr(&wd, t); /* mag 1 */
secp256k1_fe_add(&wd, &b_plus_one); /* mag 2 */
secp256k1_fe_mul(&tmp, t, &wn); /* mag 1 */
secp256k1_fe_negate(&tmp, &tmp, 1); /* mag 2 */
secp256k1_fe_mul(&x1n, &d, &wd); /* mag 1 */
secp256k1_fe_add(&x1n, &tmp); /* mag 3 */
x2n = x1n; /* mag 3 */
secp256k1_fe_add(&x2n, &wd); /* mag 5 */
secp256k1_fe_negate(&x2n, &x2n, 5); /* mag 6 */
secp256k1_fe_mul(&x3d, &c, t); /* mag 1 */
secp256k1_fe_sqr(&x3d, &x3d); /* mag 1 */
secp256k1_fe_sqr(&x3n, &wd); /* mag 1 */
secp256k1_fe_add(&x3n, &x3d); /* mag 2 */
secp256k1_fe_mul(&jinv, &x3d, &wd); /* mag 1 */
secp256k1_fe_inv(&jinv, &jinv); /* mag 1 */
secp256k1_fe_mul(&x1, &x1n, &x3d); /* mag 1 */
secp256k1_fe_mul(&x1, &x1, &jinv); /* mag 1 */
secp256k1_fe_mul(&x2, &x2n, &x3d); /* mag 1 */
secp256k1_fe_mul(&x2, &x2, &jinv); /* mag 1 */
secp256k1_fe_mul(&x3, &x3n, &wd); /* mag 1 */
secp256k1_fe_mul(&x3, &x3, &jinv); /* mag 1 */
secp256k1_fe_sqr(&alphain, &x1); /* mag 1 */
secp256k1_fe_mul(&alphain, &alphain, &x1); /* mag 1 */
secp256k1_fe_add(&alphain, &b); /* mag 2 */
secp256k1_fe_sqr(&betain, &x2); /* mag 1 */
secp256k1_fe_mul(&betain, &betain, &x2); /* mag 1 */
secp256k1_fe_add(&betain, &b); /* mag 2 */
secp256k1_fe_sqr(&gammain, &x3); /* mag 1 */
secp256k1_fe_mul(&gammain, &gammain, &x3); /* mag 1 */
secp256k1_fe_add(&gammain, &b); /* mag 2 */
alphaquad = secp256k1_fe_sqrt(&y1, &alphain);
betaquad = secp256k1_fe_sqrt(&y2, &betain);
secp256k1_fe_sqrt(&y3, &gammain);
secp256k1_fe_cmov(&x1, &x2, (!alphaquad) & betaquad);
secp256k1_fe_cmov(&y1, &y2, (!alphaquad) & betaquad);
secp256k1_fe_cmov(&x1, &x3, (!alphaquad) & !betaquad);
secp256k1_fe_cmov(&y1, &y3, (!alphaquad) & !betaquad);
secp256k1_ge_set_xy(ge, &x1, &y1);
/* The linked algorithm from the paper uses the Jacobi symbol of t to
* determine the Jacobi symbol of the produced y coordinate. Since the
* rest of the algorithm only uses t^2, we can safely use another criterion
* as long as negation of t results in negation of the y coordinate. Here
* we choose to use t's oddness, as it is faster to determine. */
secp256k1_fe_negate(&tmp, &ge->y, 1);
secp256k1_fe_cmov(&ge->y, &tmp, secp256k1_fe_is_odd(t));
}
static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) {
static const unsigned char prefix1[16] = "1st generation: ";
static const unsigned char prefix2[16] = "2nd generation: ";
secp256k1_fe t = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 4);
secp256k1_ge add;
secp256k1_gej accum;
int overflow;
secp256k1_sha256_t sha256;
unsigned char b32[32];
int ret = 1;
if (blind32) {
secp256k1_scalar blind;
secp256k1_scalar_set_b32(&blind, blind32, &overflow);
ret = !overflow;
CHECK(ret);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &accum, &blind);
}
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, prefix1, 16);
secp256k1_sha256_write(&sha256, key32, 32);
secp256k1_sha256_finalize(&sha256, b32);
ret &= secp256k1_fe_set_b32(&t, b32);
CHECK(ret);
shallue_van_de_woestijne(&add, &t);
if (blind32) {
secp256k1_gej_add_ge(&accum, &accum, &add);
} else {
secp256k1_gej_set_ge(&accum, &add);
}
secp256k1_sha256_initialize(&sha256);
secp256k1_sha256_write(&sha256, prefix2, 16);
secp256k1_sha256_write(&sha256, key32, 32);
secp256k1_sha256_finalize(&sha256, b32);
ret &= secp256k1_fe_set_b32(&t, b32);
CHECK(ret);
shallue_van_de_woestijne(&add, &t);
secp256k1_gej_add_ge(&accum, &accum, &add);
secp256k1_ge_set_gej(&add, &accum);
secp256k1_generator_save(gen, &add);
return ret;
}
int secp256k1_generator_generate(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(gen != NULL);
ARG_CHECK(key32 != NULL);
return secp256k1_generator_generate_internal(ctx, gen, key32, NULL);
}
int secp256k1_generator_generate_blinded(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *key32, const unsigned char *blind32) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(gen != NULL);
ARG_CHECK(key32 != NULL);
ARG_CHECK(blind32 != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
return secp256k1_generator_generate_internal(ctx, gen, key32, blind32);
}
#endif

View File

@ -0,0 +1,199 @@
/**********************************************************************
* Copyright (c) 2016 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_GENERATOR_TESTS
#define SECP256K1_MODULE_GENERATOR_TESTS
#include <string.h>
#include <stdio.h>
#include "group.h"
#include "scalar.h"
#include "testrand.h"
#include "util.h"
#include "include/secp256k1_generator.h"
void test_generator_api(void) {
unsigned char key[32];
unsigned char blind[32];
unsigned char sergen[33];
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_generator gen;
int32_t ecount = 0;
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_rand256(key);
secp256k1_rand256(blind);
CHECK(secp256k1_generator_generate(none, &gen, key) == 1);
CHECK(ecount == 0);
CHECK(secp256k1_generator_generate(none, NULL, key) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_generator_generate(none, &gen, NULL) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_generator_generate_blinded(sign, &gen, key, blind) == 1);
CHECK(ecount == 2);
CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, key, blind) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_generator_generate_blinded(none, &gen, key, blind) == 0);
CHECK(ecount == 4);
CHECK(secp256k1_generator_generate_blinded(vrfy, NULL, key, blind) == 0);
CHECK(ecount == 5);
CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, NULL, blind) == 0);
CHECK(ecount == 6);
CHECK(secp256k1_generator_generate_blinded(vrfy, &gen, key, NULL) == 0);
CHECK(ecount == 7);
CHECK(secp256k1_generator_serialize(none, sergen, &gen) == 1);
CHECK(ecount == 7);
CHECK(secp256k1_generator_serialize(none, NULL, &gen) == 0);
CHECK(ecount == 8);
CHECK(secp256k1_generator_serialize(none, sergen, NULL) == 0);
CHECK(ecount == 9);
CHECK(secp256k1_generator_serialize(none, sergen, &gen) == 1);
CHECK(secp256k1_generator_parse(none, &gen, sergen) == 1);
CHECK(ecount == 9);
CHECK(secp256k1_generator_parse(none, NULL, sergen) == 0);
CHECK(ecount == 10);
CHECK(secp256k1_generator_parse(none, &gen, NULL) == 0);
CHECK(ecount == 11);
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
}
void test_shallue_van_de_woestijne(void) {
/* Matches with the output of the shallue_van_de_woestijne.sage SAGE program */
static const secp256k1_ge_storage results[32] = {
SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a),
SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705),
SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97),
SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0xa98e9f96, 0x97e7d794, 0x8d0fe5c1, 0xa1735358, 0xc9db6e9f, 0x38212962, 0x2ae6ec3b, 0xfc5d0198),
SECP256K1_GE_STORAGE_CONST(0x531f7239, 0xaebc780e, 0x179fbf8d, 0x412a1b01, 0x511f0abc, 0xe0c46151, 0x8b38db84, 0xcc2467f3, 0x82387d45, 0xec7bd5cc, 0x61fcb9df, 0x41cddd7b, 0x217d8114, 0x3577dc8f, 0x23de356a, 0x7e97704e),
SECP256K1_GE_STORAGE_CONST(0x531f7239, 0xaebc780e, 0x179fbf8d, 0x412a1b01, 0x511f0abc, 0xe0c46151, 0x8b38db84, 0xcc2467f3, 0x7dc782ba, 0x13842a33, 0x9e034620, 0xbe322284, 0xde827eeb, 0xca882370, 0xdc21ca94, 0x81688be1),
SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0x56716069, 0x6818286b, 0x72f01a3e, 0x5e8caca7, 0x36249160, 0xc7ded69d, 0xd51913c3, 0x03a2fa97),
SECP256K1_GE_STORAGE_CONST(0x2c5cdc9c, 0x338152fa, 0x85de92cb, 0x1bee9907, 0x765a922e, 0x4f037cce, 0x14ecdbf2, 0x2f78fe15, 0xa98e9f96, 0x97e7d794, 0x8d0fe5c1, 0xa1735358, 0xc9db6e9f, 0x38212962, 0x2ae6ec3b, 0xfc5d0198),
SECP256K1_GE_STORAGE_CONST(0x5e5936b1, 0x81db0b65, 0x8e33a8c6, 0x1aa687dd, 0x31d11e15, 0x85e35664, 0x6b4c2071, 0xcde7e942, 0x88bb5332, 0xa8e05654, 0x78d4f60c, 0x0cd979ec, 0x938558f2, 0xcac11216, 0x7c387a56, 0xe3a6d5f3),
SECP256K1_GE_STORAGE_CONST(0x5e5936b1, 0x81db0b65, 0x8e33a8c6, 0x1aa687dd, 0x31d11e15, 0x85e35664, 0x6b4c2071, 0xcde7e942, 0x7744accd, 0x571fa9ab, 0x872b09f3, 0xf3268613, 0x6c7aa70d, 0x353eede9, 0x83c785a8, 0x1c59263c),
SECP256K1_GE_STORAGE_CONST(0x657d438f, 0xfac34a50, 0x463fd07c, 0x3f09f320, 0x4c98e8ed, 0x6927e330, 0xc0c7735f, 0x76d32f6d, 0x577c2b11, 0xcaca2f6f, 0xd60bcaf0, 0x3e7cebe9, 0x5da6e1f4, 0xbb557f12, 0x2a397331, 0x81df897f),
SECP256K1_GE_STORAGE_CONST(0x657d438f, 0xfac34a50, 0x463fd07c, 0x3f09f320, 0x4c98e8ed, 0x6927e330, 0xc0c7735f, 0x76d32f6d, 0xa883d4ee, 0x3535d090, 0x29f4350f, 0xc1831416, 0xa2591e0b, 0x44aa80ed, 0xd5c68ccd, 0x7e2072b0),
SECP256K1_GE_STORAGE_CONST(0xbe0bc11b, 0x2bc639cb, 0xc28f72a8, 0xd07c21cc, 0xbc06cfa7, 0x4c2ff25e, 0x630c9740, 0x23128eab, 0x6f062fc8, 0x75148197, 0xd10375c3, 0xcc3fadb6, 0x20277e9c, 0x00579c55, 0xeddd7f95, 0xe95604db),
SECP256K1_GE_STORAGE_CONST(0xbe0bc11b, 0x2bc639cb, 0xc28f72a8, 0xd07c21cc, 0xbc06cfa7, 0x4c2ff25e, 0x630c9740, 0x23128eab, 0x90f9d037, 0x8aeb7e68, 0x2efc8a3c, 0x33c05249, 0xdfd88163, 0xffa863aa, 0x12228069, 0x16a9f754),
SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0xfdda0ad6, 0x118a5350, 0x3303ba9f, 0xd93a1b94, 0x07fdc85c, 0xc6db9aa5, 0xe906f176, 0xf7a12705),
SECP256K1_GE_STORAGE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c, 0x0225f529, 0xee75acaf, 0xccfc4560, 0x26c5e46b, 0xf80237a3, 0x3924655a, 0x16f90e88, 0x085ed52a),
SECP256K1_GE_STORAGE_CONST(0xaee172d4, 0xce7c5010, 0xdb20a88f, 0x469598c1, 0xd7f7926f, 0xabb85cb5, 0x339f1403, 0x87e6b494, 0x38065980, 0x4de81b35, 0x098c7190, 0xe3380f9d, 0x95b2ed6c, 0x6c869e85, 0xc772bc5a, 0x7bc3d9d5),
SECP256K1_GE_STORAGE_CONST(0xaee172d4, 0xce7c5010, 0xdb20a88f, 0x469598c1, 0xd7f7926f, 0xabb85cb5, 0x339f1403, 0x87e6b494, 0xc7f9a67f, 0xb217e4ca, 0xf6738e6f, 0x1cc7f062, 0x6a4d1293, 0x9379617a, 0x388d43a4, 0x843c225a),
SECP256K1_GE_STORAGE_CONST(0xc28f5c28, 0xf5c28f5c, 0x28f5c28f, 0x5c28f5c2, 0x8f5c28f5, 0xc28f5c28, 0xf5c28f5b, 0x6666635a, 0x0c4da840, 0x1b2cf5be, 0x4604e6ec, 0xf92b2780, 0x063a5351, 0xe294bf65, 0xbb2f8b61, 0x00902db7),
SECP256K1_GE_STORAGE_CONST(0xc28f5c28, 0xf5c28f5c, 0x28f5c28f, 0x5c28f5c2, 0x8f5c28f5, 0xc28f5c28, 0xf5c28f5b, 0x6666635a, 0xf3b257bf, 0xe4d30a41, 0xb9fb1913, 0x06d4d87f, 0xf9c5acae, 0x1d6b409a, 0x44d0749d, 0xff6fce78),
SECP256K1_GE_STORAGE_CONST(0xecf56be6, 0x9c8fde26, 0x152832c6, 0xe043b3d5, 0xaf9a723f, 0x789854a0, 0xcb1b810d, 0xe2614ece, 0x66127ae4, 0xe4c17a75, 0x60a727e6, 0xffd2ea7f, 0xaed99088, 0xbec465c6, 0xbde56791, 0x37ed5572),
SECP256K1_GE_STORAGE_CONST(0xecf56be6, 0x9c8fde26, 0x152832c6, 0xe043b3d5, 0xaf9a723f, 0x789854a0, 0xcb1b810d, 0xe2614ece, 0x99ed851b, 0x1b3e858a, 0x9f58d819, 0x002d1580, 0x51266f77, 0x413b9a39, 0x421a986d, 0xc812a6bd),
SECP256K1_GE_STORAGE_CONST(0xba72860f, 0x10fcd142, 0x23f71e3c, 0x228deb9a, 0xc46c5ff5, 0x90b884e5, 0xcc60d51e, 0x0629d16e, 0x67999f31, 0x5a74ada3, 0x526832cf, 0x76b9fec3, 0xa348cc97, 0x33c3aa67, 0x02bd2516, 0x7814f635),
SECP256K1_GE_STORAGE_CONST(0xba72860f, 0x10fcd142, 0x23f71e3c, 0x228deb9a, 0xc46c5ff5, 0x90b884e5, 0xcc60d51e, 0x0629d16e, 0x986660ce, 0xa58b525c, 0xad97cd30, 0x8946013c, 0x5cb73368, 0xcc3c5598, 0xfd42dae8, 0x87eb05fa),
SECP256K1_GE_STORAGE_CONST(0x92ef5657, 0xdba51cc7, 0xf3e1b442, 0xa6a0916b, 0x8ce03079, 0x2ef5657d, 0xba51cc7e, 0xab2beb65, 0x782c65d2, 0x3f1e0eb2, 0x9179a994, 0xe5e8ff80, 0x5a0d50d9, 0xdeeaed90, 0xcec96ca5, 0x973e2ad3),
SECP256K1_GE_STORAGE_CONST(0x92ef5657, 0xdba51cc7, 0xf3e1b442, 0xa6a0916b, 0x8ce03079, 0x2ef5657d, 0xba51cc7e, 0xab2beb65, 0x87d39a2d, 0xc0e1f14d, 0x6e86566b, 0x1a17007f, 0xa5f2af26, 0x2115126f, 0x31369359, 0x68c1d15c),
SECP256K1_GE_STORAGE_CONST(0x9468ad22, 0xf921fc78, 0x8de3f1b0, 0x586c58eb, 0x5e6f0270, 0xe950b602, 0x7ada90d9, 0xd71ae323, 0x922a0c6a, 0x9ccc31d9, 0xc3bf87fd, 0x88381739, 0x35fe393f, 0xa64dfdec, 0x29f2846d, 0x12918d86),
SECP256K1_GE_STORAGE_CONST(0x9468ad22, 0xf921fc78, 0x8de3f1b0, 0x586c58eb, 0x5e6f0270, 0xe950b602, 0x7ada90d9, 0xd71ae323, 0x6dd5f395, 0x6333ce26, 0x3c407802, 0x77c7e8c6, 0xca01c6c0, 0x59b20213, 0xd60d7b91, 0xed6e6ea9),
SECP256K1_GE_STORAGE_CONST(0x76ddc7f5, 0xe029e59e, 0x22b0e54f, 0xa811db94, 0x5a209c4f, 0x5e912ca2, 0x8b4da6a7, 0x4c1e00a2, 0x1e8f516c, 0x91c20437, 0x50f6e24e, 0x8c2cf202, 0xacf68291, 0xbf8b66eb, 0xf7335b62, 0xec2c88fe),
SECP256K1_GE_STORAGE_CONST(0x76ddc7f5, 0xe029e59e, 0x22b0e54f, 0xa811db94, 0x5a209c4f, 0x5e912ca2, 0x8b4da6a7, 0x4c1e00a2, 0xe170ae93, 0x6e3dfbc8, 0xaf091db1, 0x73d30dfd, 0x53097d6e, 0x40749914, 0x08cca49c, 0x13d37331),
SECP256K1_GE_STORAGE_CONST(0xf75763bc, 0x2907e79b, 0x125e33c3, 0x9a027f48, 0x0f8c6409, 0x2153432f, 0x967bc2b1, 0x1d1f5cf0, 0xb4a8edc6, 0x36391b39, 0x9bc219c0, 0x3d033128, 0xdbcd463e, 0xd2506394, 0x061b87a5, 0x9e510235),
SECP256K1_GE_STORAGE_CONST(0xf75763bc, 0x2907e79b, 0x125e33c3, 0x9a027f48, 0x0f8c6409, 0x2153432f, 0x967bc2b1, 0x1d1f5cf0, 0x4b571239, 0xc9c6e4c6, 0x643de63f, 0xc2fcced7, 0x2432b9c1, 0x2daf9c6b, 0xf9e47859, 0x61aef9fa),
};
secp256k1_ge ge;
secp256k1_fe fe;
secp256k1_ge_storage ges;
int i, s;
for (i = 1; i <= 16; i++) {
secp256k1_fe_set_int(&fe, i);
for (s = 0; s < 2; s++) {
if (s) {
secp256k1_fe_negate(&fe, &fe, 1);
secp256k1_fe_normalize(&fe);
}
shallue_van_de_woestijne(&ge, &fe);
secp256k1_ge_to_storage(&ges, &ge);
CHECK(memcmp(&ges, &results[i * 2 + s - 2], sizeof(secp256k1_ge_storage)) == 0);
}
}
}
void test_generator_generate(void) {
static const secp256k1_ge_storage results[32] = {
SECP256K1_GE_STORAGE_CONST(0x806cd8ed, 0xd6c153e3, 0x4aa9b9a0, 0x8755c4be, 0x4718b1ef, 0xb26cb93f, 0xfdd99e1b, 0x21f2af8e, 0xc7062208, 0xcc649a03, 0x1bdc1a33, 0x9d01f115, 0x4bcd0dca, 0xfe0b875d, 0x62f35f73, 0x28673006),
SECP256K1_GE_STORAGE_CONST(0xd91b15ec, 0x47a811f4, 0xaa189561, 0xd13f5c4d, 0x4e81f10d, 0xc7dc551f, 0x4fea9b84, 0x610314c4, 0x9b0ada1e, 0xb38efd67, 0x8bff0b6c, 0x7d7315f7, 0xb49b8cc5, 0xa679fad4, 0xc94f9dc6, 0x9da66382),
SECP256K1_GE_STORAGE_CONST(0x11c00de6, 0xf885035e, 0x76051430, 0xa3c38b2a, 0x5f86ab8c, 0xf66dae58, 0x04ea7307, 0x348b19bf, 0xe0858ae7, 0x61dcb1ba, 0xff247e37, 0xd38fcd88, 0xf3bd7911, 0xaa4ed6e0, 0x28d792dd, 0x3ee1ac09),
SECP256K1_GE_STORAGE_CONST(0x986b99eb, 0x3130e7f0, 0xe779f674, 0xb85cb514, 0x46a676bf, 0xb1dfb603, 0x4c4bb639, 0x7c406210, 0xdf900609, 0x8b3ef1e0, 0x30e32fb0, 0xd97a4329, 0xff98aed0, 0xcd278c3f, 0xe6078467, 0xfbd12f35),
SECP256K1_GE_STORAGE_CONST(0xae528146, 0x03fdf91e, 0xc592977e, 0x12461dc7, 0xb9e038f8, 0x048dcb62, 0xea264756, 0xd459ae42, 0x80ef658d, 0x92becb84, 0xdba8e4f9, 0x560d7a72, 0xbaf4c393, 0xfbcf6007, 0x11039f1c, 0x224faaad),
SECP256K1_GE_STORAGE_CONST(0x00df3d91, 0x35975eee, 0x91fab903, 0xe3128e4a, 0xca071dde, 0x270814e5, 0xcbda69ec, 0xcad58f46, 0x11b590aa, 0x92d89969, 0x2dbd932f, 0x08013b8b, 0x45afabc6, 0x43677db2, 0x143e0c0f, 0x5865fb03),
SECP256K1_GE_STORAGE_CONST(0x1168155b, 0x987e9bc8, 0x84c5f3f4, 0x92ebf784, 0xcc8c6735, 0x39d8e5e8, 0xa967115a, 0x2949da9b, 0x0858a470, 0xf403ca97, 0xb1827f6f, 0x544c2c67, 0x08f6cb83, 0xc510c317, 0x96c981ed, 0xb9f61780),
SECP256K1_GE_STORAGE_CONST(0xe8d7c0cf, 0x2bb4194c, 0x97bf2a36, 0xbd115ba0, 0x81a9afe8, 0x7663fa3c, 0x9c3cd253, 0x79fe2571, 0x2028ad04, 0xefa00119, 0x5a25d598, 0x67e79502, 0x49de7c61, 0x4751cd9d, 0x4fb317f6, 0xf76f1110),
SECP256K1_GE_STORAGE_CONST(0x9532c491, 0xa64851dd, 0xcd0d3e5a, 0x93e17267, 0xa10aca95, 0xa23781aa, 0x5087f340, 0xc45fecc3, 0xb691ddc2, 0x3143a7b6, 0x09969302, 0x258affb8, 0x5bbf8666, 0xe1192319, 0xeb174d88, 0x308bd57a),
SECP256K1_GE_STORAGE_CONST(0x6b20b6e2, 0x1ba6cc44, 0x3f2c3a0c, 0x5283ba44, 0xbee43a0a, 0x2799a6cf, 0xbecc0f8a, 0xf8c583ac, 0xf7021e76, 0xd51291a6, 0xf9396215, 0x686f25aa, 0xbec36282, 0x5e11eeea, 0x6e51a6e6, 0xd7d7c006),
SECP256K1_GE_STORAGE_CONST(0xde27e6ff, 0x219b3ab1, 0x2b0a9e4e, 0x51fc6092, 0x96e55af6, 0xc6f717d6, 0x12cd6cce, 0x65d6c8f2, 0x48166884, 0x4dc13fd2, 0xed7a7d81, 0x66a0839a, 0x8a960863, 0xfe0001c1, 0x35d206fd, 0x63b87c09),
SECP256K1_GE_STORAGE_CONST(0x79a96fb8, 0xd88a08d3, 0x055d38d1, 0x3346b0d4, 0x47d838ca, 0xfcc8fa40, 0x6d3a7157, 0xef84e7e3, 0x6bab9c45, 0x2871b51d, 0xb0df2369, 0xe7860e01, 0x2e37ffea, 0x6689fd1a, 0x9c6fe9cf, 0xb940acea),
SECP256K1_GE_STORAGE_CONST(0x06c4d4cb, 0xd32c0ddb, 0x67e988c6, 0x2bdbe6ad, 0xa39b80cc, 0x61afb347, 0x234abe27, 0xa689618c, 0x5b355949, 0xf904fe08, 0x569b2313, 0xe8f19f8d, 0xc5b79e27, 0x70da0832, 0x5fb7a229, 0x238ca6b6),
SECP256K1_GE_STORAGE_CONST(0x7027e566, 0x3e727c28, 0x42aa14e5, 0x52c2d2ec, 0x1d8beaa9, 0x8a22ceab, 0x15ccafc3, 0xb4f06249, 0x9b3dffbc, 0xdbd5e045, 0x6931fd03, 0x8b1c6a9b, 0x4c168c6d, 0xa6553897, 0xfe11ce49, 0xac728139),
SECP256K1_GE_STORAGE_CONST(0xee3520c3, 0x9f2b954d, 0xf8e15547, 0xdaeb6cc8, 0x04c8f3b0, 0x9301f53e, 0xe0c11ea1, 0xeace539d, 0x244ff873, 0x7e060c98, 0xe843c353, 0xcd35d2e4, 0x3cd8b082, 0xcffbc9ae, 0x81eafa70, 0x332f9748),
SECP256K1_GE_STORAGE_CONST(0xdaecd756, 0xf5b706a4, 0xc14e1095, 0x3e2f70df, 0xa81276e7, 0x71806b89, 0x4d8a5502, 0xa0ef4998, 0xbac906c0, 0x948b1d48, 0xe023f439, 0xfd3770b8, 0x837f60cc, 0x40552a51, 0x433d0b79, 0x6610da27),
SECP256K1_GE_STORAGE_CONST(0x55e1ca28, 0x750fe2d0, 0x57f7449b, 0x3f49d999, 0x3b9616dd, 0x5387bc2e, 0x6e6698f8, 0xc4ea49f4, 0xe339e0e9, 0xa4c7fa99, 0xd063e062, 0x6582bce2, 0x33c6b1ee, 0x17a5b47f, 0x6d43ecf8, 0x98b40120),
SECP256K1_GE_STORAGE_CONST(0xdd82cac2, 0x9e0e0135, 0x4964d3bc, 0x27469233, 0xf13bbd5e, 0xd7aff24b, 0x4902fca8, 0x17294b12, 0x561ab1d6, 0xcd9bcb6e, 0x805585cf, 0x3df8714c, 0x1bfa6304, 0x5efbf122, 0x1a3d8fd9, 0x3827764a),
SECP256K1_GE_STORAGE_CONST(0xda5cbfb7, 0x3522e9c7, 0xcb594436, 0x83677038, 0x0eaa64a9, 0x2eca3888, 0x0fe4c9d6, 0xdeb22dbf, 0x4f46de68, 0x0447c780, 0xc54a314b, 0x5389a926, 0xbba8910b, 0x869fc6cd, 0x42ee82e8, 0x5895e42a),
SECP256K1_GE_STORAGE_CONST(0x4e09830e, 0xc8894c58, 0x4e6278de, 0x167a96b0, 0x20d60463, 0xee48f788, 0x4974d66e, 0x871e35e9, 0x21259c4d, 0x332ca932, 0x2e187df9, 0xe7afbc23, 0x9d171ebc, 0x7d9e2560, 0x503f50b1, 0x9fe45834),
SECP256K1_GE_STORAGE_CONST(0xabfff6ca, 0x41dcfd17, 0x03cae629, 0x9d127971, 0xf19ee000, 0x2db332e6, 0x5cc209a3, 0xc21b8f54, 0x65991d60, 0xee54f5cc, 0xddf7a732, 0xa76b0303, 0xb9f519a6, 0x22ea0390, 0x8af23ffa, 0x35ae6632),
SECP256K1_GE_STORAGE_CONST(0xc6c9b92c, 0x91e045a5, 0xa1913277, 0x44d6fce2, 0x11b12c7c, 0x9b3112d6, 0xc61e14a6, 0xd6b1ae12, 0x04ab0396, 0xebdc4c6a, 0xc213cc3e, 0x077a2e80, 0xb4ba7b2b, 0x33907d56, 0x2c98ccf7, 0xb82a2e9f),
SECP256K1_GE_STORAGE_CONST(0x66f6e6d9, 0xc4bb9a5f, 0x99085781, 0x83cb9362, 0x2ea437d8, 0xccd31969, 0xffadca3a, 0xff1d3935, 0x50a5b06e, 0x39e039d7, 0x1dfb2723, 0x18db74e5, 0x5af64da1, 0xdfc34586, 0x6aac3bd0, 0x5792a890),
SECP256K1_GE_STORAGE_CONST(0x58ded03c, 0x98e1a890, 0x63fc7793, 0xe3ecd896, 0x235e75c9, 0x82e7008f, 0xddbf3ca8, 0x5b7e9ecb, 0x34594776, 0x58ab6821, 0xaf43a453, 0xa946fda9, 0x13d24999, 0xccf22df8, 0xd291ef59, 0xb08975c0),
SECP256K1_GE_STORAGE_CONST(0x74557864, 0x4f2b0486, 0xd5beea7c, 0x2d258ccb, 0x78a870e1, 0x848982d8, 0xed3f91a4, 0x9db83a36, 0xd84e940e, 0x1d33c28a, 0x62398ec8, 0xc493aee7, 0x7c2ba722, 0x42dee7ae, 0x3c35c256, 0xad00cf42),
SECP256K1_GE_STORAGE_CONST(0x7fc7963a, 0x16abc8fb, 0x5d61eb61, 0x0fc50a68, 0x754470d2, 0xf43df3be, 0x52228f66, 0x522fe61b, 0x499f9e7f, 0x462c6545, 0x29687af4, 0x9f7c732d, 0x48801ce5, 0x21acd546, 0xc6fb903c, 0x7c265032),
SECP256K1_GE_STORAGE_CONST(0xb2f6257c, 0xc58df82f, 0xb9ba4f36, 0x7ededf03, 0xf8ea10f3, 0x104d7ae6, 0x233b7ac4, 0x725e11de, 0x9c7a32df, 0x4842f33d, 0xaad84f0b, 0x62e88b40, 0x46ddcbde, 0xbbeec6f8, 0x93bfde27, 0x0561dc73),
SECP256K1_GE_STORAGE_CONST(0xe2cdfd27, 0x8a8e22be, 0xabf08b79, 0x1bc6ae38, 0x41d22a9a, 0x9472e266, 0x1a7c6e83, 0xa2f74725, 0x0e26c103, 0xe0dd93b2, 0x3724f3b7, 0x8bb7366e, 0x2c245768, 0xd64f3283, 0xd8316e8a, 0x1383b977),
SECP256K1_GE_STORAGE_CONST(0x757c13e7, 0xe866017e, 0xe6af61d7, 0x161d208a, 0xc438f712, 0x242fcd23, 0x63a10e59, 0xd67e41fb, 0xb550c6a9, 0x4ddb15f3, 0xfeea4bfe, 0xd2faa19f, 0x2aa2fbd3, 0x0c6ae785, 0xe357f365, 0xb30d12e0),
SECP256K1_GE_STORAGE_CONST(0x528d525e, 0xac30095b, 0x5e5f83ca, 0x4d3dea63, 0xeb608f2d, 0x18dd25a7, 0x2529c8e5, 0x1ae5f9f1, 0xfde2860b, 0x492a4106, 0x9f356c05, 0x3ebc045e, 0x4ad08b79, 0x3e264935, 0xf25785a9, 0x8690b5ee),
SECP256K1_GE_STORAGE_CONST(0x150df593, 0x5b6956a0, 0x0cfed843, 0xb9d6ffce, 0x4f790022, 0xea18730f, 0xc495111d, 0x91568e55, 0x6700a2ca, 0x9ff4ed32, 0xc1697312, 0x4eb51ce3, 0x5656344b, 0x65a1e3d5, 0xd6c1f7ce, 0x29233f82),
SECP256K1_GE_STORAGE_CONST(0x38e02eaf, 0x2c8774fd, 0x58b8b373, 0x732457f1, 0x16dbe53b, 0xea5683d9, 0xada20dd7, 0x14ce20a6, 0x6ac5362e, 0xbb425416, 0x8250f43f, 0xa4ee2b63, 0x0406324f, 0x1c876d60, 0xebe5be2c, 0x6eb1515b),
};
secp256k1_generator gen;
secp256k1_ge ge;
secp256k1_ge_storage ges;
int i;
unsigned char v[32];
static const unsigned char s[32] = {0};
secp256k1_scalar sc;
secp256k1_scalar_set_b32(&sc, s, NULL);
for (i = 1; i <= 32; i++) {
memset(v, 0, 31);
v[31] = i;
CHECK(secp256k1_generator_generate_blinded(ctx, &gen, v, s));
secp256k1_generator_load(&ge, &gen);
secp256k1_ge_to_storage(&ges, &ge);
CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0);
CHECK(secp256k1_generator_generate(ctx, &gen, v));
secp256k1_generator_load(&ge, &gen);
secp256k1_ge_to_storage(&ges, &ge);
CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0);
}
}
void run_generator_tests(void) {
test_shallue_van_de_woestijne();
test_generator_api();
test_generator_generate();
}
#endif

View File

@ -0,0 +1,15 @@
include_HEADERS += include/secp256k1_rangeproof.h
noinst_HEADERS += src/modules/rangeproof/main_impl.h
noinst_HEADERS += src/modules/rangeproof/pedersen.h
noinst_HEADERS += src/modules/rangeproof/pedersen_impl.h
noinst_HEADERS += src/modules/rangeproof/borromean.h
noinst_HEADERS += src/modules/rangeproof/borromean_impl.h
noinst_HEADERS += src/modules/rangeproof/rangeproof.h
noinst_HEADERS += src/modules/rangeproof/rangeproof_impl.h
noinst_HEADERS += src/modules/rangeproof/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_rangeproof
bench_rangeproof_SOURCES = src/bench_rangeproof.c
bench_rangeproof_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_rangeproof_LDFLAGS = -static
endif

View File

@ -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* ecmult_ctx, secp256k1_scalar *evalues, const unsigned char *e0, const secp256k1_scalar *s,
const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen);
int secp256k1_borromean_sign(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ecmult_gen_context *ecmult_gen_ctx,
unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec,
const size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen);
#endif

View File

@ -0,0 +1,204 @@
/**********************************************************************
* 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 "hash.h"
#include "eckey.h"
#include "ecmult.h"
#include "ecmult_gen.h"
#include "borromean.h"
#include <limits.h>
#include <string.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, size_t mlen, const unsigned char *e, size_t elen,
size_t ridx, size_t 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* ecmult_ctx, secp256k1_scalar *evalues, const unsigned char *e0,
const secp256k1_scalar *s, const secp256k1_gej *pubs, const size_t *rsizes, size_t nrings, const unsigned char *m, size_t mlen) {
secp256k1_gej rgej;
secp256k1_ge rge;
secp256k1_scalar ens;
secp256k1_sha256_t sha256_e0;
unsigned char tmp[33];
size_t i;
size_t j;
size_t count;
size_t 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++) {
VERIFY_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* ecmult_ctx, const secp256k1_ecmult_gen_context *ecmult_gen_ctx,
unsigned char *e0, secp256k1_scalar *s, const secp256k1_gej *pubs, const secp256k1_scalar *k, const secp256k1_scalar *sec,
const size_t *rsizes, const size_t *secidx, size_t nrings, const unsigned char *m, size_t mlen) {
secp256k1_gej rgej;
secp256k1_ge rge;
secp256k1_scalar ens;
secp256k1_sha256_t sha256_e0;
unsigned char tmp[33];
size_t i;
size_t j;
size_t count;
size_t 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++) {
VERIFY_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++) {
VERIFY_CHECK(INT_MAX - count > rsizes[i]);
secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0);
secp256k1_scalar_set_b32(&ens, tmp, &overflow);
if (overflow || secp256k1_scalar_is_zero(&ens)) {
return 0;
}
for (j = 0; j < secidx[i]; j++) {
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

View File

@ -0,0 +1,278 @@
/**********************************************************************
* 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_MODULE_RANGEPROOF_MAIN
#define SECP256K1_MODULE_RANGEPROOF_MAIN
#include "group.h"
#include "modules/rangeproof/pedersen_impl.h"
#include "modules/rangeproof/borromean_impl.h"
#include "modules/rangeproof/rangeproof_impl.h"
/** 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' % (11 - G2.xy()[1].is_square(), G2.xy()[0])
*/
static const secp256k1_generator secp256k1_generator_h_internal = {{
0x11,
0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e,
0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0
}};
const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal;
static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) {
secp256k1_fe fe;
secp256k1_fe_set_b32(&fe, &commit->data[1]);
secp256k1_ge_set_xquad(ge, &fe);
if (commit->data[0] & 1) {
secp256k1_ge_neg(ge, ge);
}
}
static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) {
secp256k1_fe_normalize(&ge->x);
secp256k1_fe_get_b32(&commit->data[1], &ge->x);
commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y);
}
int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(commit != NULL);
ARG_CHECK(input != NULL);
(void) ctx;
if ((input[0] & 0xFE) != 8) {
return 0;
}
memcpy(commit->data, input, sizeof(commit->data));
return 1;
}
int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(commit != NULL);
memcpy(output, commit->data, sizeof(commit->data));
return 1;
}
/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/
int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) {
secp256k1_ge genp;
secp256k1_gej rj;
secp256k1_ge r;
secp256k1_scalar sec;
int overflow;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(commit != NULL);
ARG_CHECK(blind != NULL);
ARG_CHECK(gen != NULL);
secp256k1_generator_load(&genp, gen);
secp256k1_scalar_set_b32(&sec, blind, &overflow);
if (!overflow) {
secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp);
if (!secp256k1_gej_is_infinity(&rj)) {
secp256k1_ge_set_gej(&r, &rj);
secp256k1_pedersen_commitment_save(commit, &r);
ret = 1;
}
secp256k1_gej_clear(&rj);
secp256k1_ge_clear(&r);
}
secp256k1_scalar_clear(&sec);
return ret;
}
/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest
* negative, then calculates an additional blinding value that adds to zero.
*/
int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) {
secp256k1_scalar acc;
secp256k1_scalar x;
size_t i;
int overflow;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(blind_out != NULL);
ARG_CHECK(blinds != NULL);
ARG_CHECK(npositive <= n);
(void) ctx;
secp256k1_scalar_set_int(&acc, 0);
for (i = 0; i < n; i++) {
secp256k1_scalar_set_b32(&x, blinds[i], &overflow);
if (overflow) {
return 0;
}
if (i >= npositive) {
secp256k1_scalar_negate(&x, &x);
}
secp256k1_scalar_add(&acc, &acc, &x);
}
secp256k1_scalar_get_b32(blind_out, &acc);
secp256k1_scalar_clear(&acc);
secp256k1_scalar_clear(&x);
return 1;
}
/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */
int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) {
secp256k1_gej accj;
secp256k1_ge add;
size_t i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(!pcnt || (commits != NULL));
ARG_CHECK(!ncnt || (ncommits != NULL));
(void) ctx;
secp256k1_gej_set_infinity(&accj);
for (i = 0; i < ncnt; i++) {
secp256k1_pedersen_commitment_load(&add, ncommits[i]);
secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL);
}
secp256k1_gej_neg(&accj, &accj);
for (i = 0; i < pcnt; i++) {
secp256k1_pedersen_commitment_load(&add, commits[i]);
secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL);
}
return secp256k1_gej_is_infinity(&accj);
}
int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) {
secp256k1_scalar sum;
secp256k1_scalar tmp;
size_t i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(n_total == 0 || value != NULL);
ARG_CHECK(n_total == 0 || generator_blind != NULL);
ARG_CHECK(n_total == 0 || blinding_factor != NULL);
ARG_CHECK(n_total > n_inputs);
(void) ctx;
if (n_total == 0) {
return 1;
}
secp256k1_scalar_set_int(&sum, 0);
for (i = 0; i < n_total; i++) {
int overflow = 0;
secp256k1_scalar addend;
secp256k1_scalar_set_u64(&addend, value[i]); /* s = v */
secp256k1_scalar_set_b32(&tmp, generator_blind[i], &overflow);
if (overflow == 1) {
secp256k1_scalar_clear(&tmp);
secp256k1_scalar_clear(&addend);
secp256k1_scalar_clear(&sum);
return 0;
}
secp256k1_scalar_mul(&addend, &addend, &tmp); /* s = vr */
secp256k1_scalar_set_b32(&tmp, blinding_factor[i], &overflow);
if (overflow == 1) {
secp256k1_scalar_clear(&tmp);
secp256k1_scalar_clear(&addend);
secp256k1_scalar_clear(&sum);
return 0;
}
secp256k1_scalar_add(&addend, &addend, &tmp); /* s = vr + r' */
secp256k1_scalar_cond_negate(&addend, i < n_inputs); /* s is negated if it's an input */
secp256k1_scalar_add(&sum, &sum, &addend); /* sum += s */
secp256k1_scalar_clear(&addend);
}
/* Right now tmp has the last pedersen blinding factor. Subtract the sum from it. */
secp256k1_scalar_negate(&sum, &sum);
secp256k1_scalar_add(&tmp, &tmp, &sum);
secp256k1_scalar_get_b32(blinding_factor[n_total - 1], &tmp);
secp256k1_scalar_clear(&tmp);
secp256k1_scalar_clear(&sum);
return 1;
}
int secp256k1_rangeproof_info(const secp256k1_context* ctx, int *exp, int *mantissa,
uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) {
size_t offset;
uint64_t scale;
ARG_CHECK(exp != NULL);
ARG_CHECK(mantissa != NULL);
ARG_CHECK(min_value != NULL);
ARG_CHECK(max_value != NULL);
ARG_CHECK(proof != NULL);
offset = 0;
scale = 1;
(void)ctx;
return secp256k1_rangeproof_getheader_impl(&offset, exp, mantissa, &scale, min_value, max_value, proof, plen);
}
int secp256k1_rangeproof_rewind(const secp256k1_context* ctx,
unsigned char *blind_out, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce,
uint64_t *min_value, uint64_t *max_value,
const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) {
secp256k1_ge commitp;
secp256k1_ge genp;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(commit != NULL);
ARG_CHECK(proof != NULL);
ARG_CHECK(min_value != NULL);
ARG_CHECK(max_value != NULL);
ARG_CHECK(message_out != NULL || outlen == NULL);
ARG_CHECK(nonce != NULL);
ARG_CHECK(extra_commit != NULL || extra_commit_len == 0);
ARG_CHECK(gen != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
secp256k1_pedersen_commitment_load(&commitp, commit);
secp256k1_generator_load(&genp, gen);
return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx,
blind_out, value_out, message_out, outlen, nonce, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp);
}
int secp256k1_rangeproof_verify(const secp256k1_context* ctx, uint64_t *min_value, uint64_t *max_value,
const secp256k1_pedersen_commitment *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen) {
secp256k1_ge commitp;
secp256k1_ge genp;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(commit != NULL);
ARG_CHECK(proof != NULL);
ARG_CHECK(min_value != NULL);
ARG_CHECK(max_value != NULL);
ARG_CHECK(extra_commit != NULL || extra_commit_len == 0);
ARG_CHECK(gen != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
secp256k1_pedersen_commitment_load(&commitp, commit);
secp256k1_generator_load(&genp, gen);
return secp256k1_rangeproof_verify_impl(&ctx->ecmult_ctx, NULL,
NULL, NULL, NULL, NULL, NULL, min_value, max_value, &commitp, proof, plen, extra_commit, extra_commit_len, &genp);
}
int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof, size_t *plen, uint64_t min_value,
const secp256k1_pedersen_commitment *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value,
const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_generator* gen){
secp256k1_ge commitp;
secp256k1_ge genp;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
ARG_CHECK(plen != NULL);
ARG_CHECK(commit != NULL);
ARG_CHECK(blind != NULL);
ARG_CHECK(nonce != NULL);
ARG_CHECK(message != NULL || msg_len == 0);
ARG_CHECK(extra_commit != NULL || extra_commit_len == 0);
ARG_CHECK(gen != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
secp256k1_pedersen_commitment_load(&commitp, commit);
secp256k1_generator_load(&genp, gen);
return secp256k1_rangeproof_sign_impl(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx,
proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp);
}
#endif

View File

@ -0,0 +1,22 @@
/**********************************************************************
* 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_PEDERSEN_H_
#define _SECP256K1_PEDERSEN_H_
#include "ecmult_gen.h"
#include "group.h"
#include "scalar.h"
#include <stdint.h>
/** Multiply a small number with the generator: r = gn*G2 */
static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp);
/* sec * G + value * G2. */
static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp);
#endif

View File

@ -0,0 +1,51 @@
/***********************************************************************
* 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_PEDERSEN_IMPL_H_
#define _SECP256K1_PEDERSEN_IMPL_H_
#include <string.h>
#include "eckey.h"
#include "ecmult_const.h"
#include "ecmult_gen.h"
#include "group.h"
#include "field.h"
#include "scalar.h"
#include "util.h"
static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t value) {
unsigned char data[32];
int i;
for (i = 0; i < 24; i++) {
data[i] = 0;
}
for (; i < 32; i++) {
data[i] = value >> 56;
value <<= 8;
}
secp256k1_scalar_set_b32(sec, data, NULL);
memset(data, 0, 32);
}
static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) {
secp256k1_scalar s;
secp256k1_pedersen_scalar_set_u64(&s, gn);
secp256k1_ecmult_const(r, genp, &s, 64);
secp256k1_scalar_clear(&s);
}
/* sec * G + value * G2. */
SECP256K1_INLINE static void secp256k1_pedersen_ecmult(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_gej *rj, const secp256k1_scalar *sec, uint64_t value, const secp256k1_ge* genp) {
secp256k1_gej vj;
secp256k1_ecmult_gen(ecmult_gen_ctx, rj, sec);
secp256k1_pedersen_ecmult_small(&vj, value, genp);
/* FIXME: constant time. */
secp256k1_gej_add_var(rj, rj, &vj, NULL);
secp256k1_gej_clear(&vj);
}
#endif

View File

@ -0,0 +1,21 @@
/**********************************************************************
* 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_H_
#define _SECP256K1_RANGEPROOF_H_
#include "scalar.h"
#include "group.h"
#include "ecmult.h"
#include "ecmult_gen.h"
static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx,
const secp256k1_ecmult_gen_context* ecmult_gen_ctx,
unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce,
uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen,
const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp);
#endif

View File

@ -0,0 +1,683 @@
/**********************************************************************
* 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 "eckey.h"
#include "scalar.h"
#include "group.h"
#include "rangeproof.h"
#include "hash_impl.h"
#include "pedersen_impl.h"
#include "util.h"
#include "modules/rangeproof/pedersen.h"
#include "modules/rangeproof/borromean.h"
SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs,
int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) {
secp256k1_gej base;
size_t i;
size_t j;
size_t npub;
VERIFY_CHECK(exp < 19);
if (exp < 0) {
exp = 0;
}
secp256k1_gej_set_ge(&base, genp);
secp256k1_gej_neg(&base, &base);
while (exp--) {
/* Multiplication by 10 */
secp256k1_gej tmp;
secp256k1_gej_double_var(&tmp, &base, NULL);
secp256k1_gej_double_var(&base, &tmp, NULL);
secp256k1_gej_double_var(&base, &base, NULL);
secp256k1_gej_add_var(&base, &base, &tmp, NULL);
}
npub = 0;
for (i = 0; i < rings; i++) {
for (j = 1; j < rsizes[i]; j++) {
secp256k1_gej_add_var(&pubs[npub + j], &pubs[npub + j - 1], &base, NULL);
}
if (i < rings - 1) {
secp256k1_gej_double_var(&base, &base, NULL);
secp256k1_gej_double_var(&base, &base, NULL);
}
npub += rsizes[i];
}
}
SECP256K1_INLINE static void secp256k1_rangeproof_serialize_point(unsigned char* data, const secp256k1_ge *point) {
secp256k1_fe pointx;
pointx = point->x;
secp256k1_fe_normalize(&pointx);
data[0] = !secp256k1_fe_is_quad_var(&point->y);
secp256k1_fe_get_b32(data + 1, &pointx);
}
SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_scalar *s, unsigned char *message,
size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge* genp) {
unsigned char tmp[32];
unsigned char rngseed[32 + 33 + 33 + 10];
secp256k1_rfc6979_hmac_sha256_t rng;
secp256k1_scalar acc;
int overflow;
int ret;
size_t i;
size_t j;
int b;
size_t npub;
VERIFY_CHECK(len <= 10);
memcpy(rngseed, nonce, 32);
secp256k1_rangeproof_serialize_point(rngseed + 32, commit);
secp256k1_rangeproof_serialize_point(rngseed + 32 + 33, genp);
memcpy(rngseed + 33 + 33 + 32, proof, len);
secp256k1_rfc6979_hmac_sha256_initialize(&rng, rngseed, 32 + 33 + 33 + len);
secp256k1_scalar_clear(&acc);
npub = 0;
ret = 1;
for (i = 0; i < rings; i++) {
if (i < rings - 1) {
secp256k1_rfc6979_hmac_sha256_generate(&rng, tmp, 32);
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, size_t *rings, size_t *rsizes, size_t *npub, size_t *secidx, uint64_t *min_value,
int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) {
size_t i;
*rings = 1;
rsizes[0] = 1;
secidx[0] = 0;
*scale = 1;
*mantissa = 0;
*npub = 0;
if (*min_value == UINT64_MAX) {
/* If the minimum value is the maximal representable value, then we cannot code a range. */
*exp = -1;
}
if (*exp >= 0) {
int max_bits;
uint64_t v2;
if ((*min_value && value > INT64_MAX) || (value && *min_value >= INT64_MAX)) {
/* If either value or min_value is >= 2^63-1 then the other must by zero to avoid overflowing the proven range. */
return 0;
}
max_bits = *min_value ? secp256k1_clz64_var(*min_value) : 64;
if (*min_bits > max_bits) {
*min_bits = max_bits;
}
if (*min_bits > 61 || value > INT64_MAX) {
/** Ten is not a power of two, so dividing by ten and then representing in base-2 times ten
* expands the representable range. The verifier requires the proven range is within 0..2**64.
* For very large numbers (all over 2**63) we must change our exponent to compensate.
* Rather than handling it precisely, this just disables use of the exponent for big values.
*/
*exp = 0;
}
/* Mask off the least significant digits, as requested. */
*v = value - *min_value;
/* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */
v2 = *min_bits ? (UINT64_MAX>>(64-*min_bits)) : 0;
for (i = 0; (int) i < *exp && (v2 <= UINT64_MAX / 10); i++) {
*v /= 10;
v2 *= 10;
}
*exp = i;
v2 = *v;
for (i = 0; (int) i < *exp; i++) {
v2 *= 10;
*scale *= 10;
}
/* If the masked number isn't precise, compute the public offset. */
*min_value = value - v2;
/* How many bits do we need to represent our value? */
*mantissa = *v ? 64 - secp256k1_clz64_var(*v) : 1;
if (*min_bits > *mantissa) {
/* If the user asked for more precision, give it to them. */
*mantissa = *min_bits;
}
/* Digits in radix-4, except for the last digit if our mantissa length is odd. */
*rings = (*mantissa + 1) >> 1;
for (i = 0; i < *rings; i++) {
rsizes[i] = ((i < *rings - 1) | (!(*mantissa&1))) ? 4 : 2;
*npub += rsizes[i];
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* ecmult_ctx,
const secp256k1_ecmult_gen_context* ecmult_gen_ctx,
unsigned char *proof, size_t *plen, uint64_t min_value,
const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value,
const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp){
secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */
secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */
secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */
secp256k1_scalar k[32]; /* Nonces for our non-forged signatures. */
secp256k1_scalar stmp;
secp256k1_sha256_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. */
size_t rings; /* How many digits will our proof cover. */
size_t rsizes[32]; /* How many possible values there are for each place. */
size_t secidx[32]; /* Which digit is the correct one. */
size_t len; /* Number of bytes used so far. */
size_t i;
int overflow;
size_t 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 in the proof for the message? Each ring gives us 128 bytes, but the
* final ring is used to encode the blinding factor and the value, so we can't use that. (Well,
* technically there are 64 bytes available if we avoided the other data, but this is difficult
* because it's not always in the same place. */
if (msg_len > 0 && msg_len > 128 * (rings - 1)) {
return 0;
}
/* 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_rangeproof_serialize_point(tmp, commit);
secp256k1_sha256_write(&sha256_m, tmp, 33);
secp256k1_rangeproof_serialize_point(tmp, genp);
secp256k1_sha256_write(&sha256_m, tmp, 33);
secp256k1_sha256_write(&sha256_m, proof, len);
memset(prep, 0, 4096);
if (message != NULL) {
memcpy(prep, message, msg_len);
}
/* Note, the data corresponding to the blinding factors must be zero. */
if (rsizes[rings - 1] > 1) {
size_t 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, genp)) {
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_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], ((uint64_t)secidx[i] * scale) << (i*2), genp);
if (secp256k1_gej_is_infinity(&pubs[npub])) {
return 0;
}
if (i < rings - 1) {
unsigned char tmpc[33];
secp256k1_ge c;
unsigned char quadness;
/*OPT: split loop and batch invert.*/
/*OPT: do not compute full pubs[npub] in ge form; we only need x */
secp256k1_ge_set_gej_var(&c, &pubs[npub]);
secp256k1_rangeproof_serialize_point(tmpc, &c);
quadness = tmpc[0];
secp256k1_sha256_write(&sha256_m, tmpc, 33);
signs[i>>3] |= quadness << (i&7);
memcpy(&proof[len], tmpc + 1, 32);
len += 32;
}
npub += rsizes[i];
}
secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp);
if (extra_commit != NULL) {
secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len);
}
secp256k1_sha256_finalize(&sha256_m, tmp);
if (!secp256k1_borromean_sign(ecmult_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 *x, const secp256k1_scalar *k, const secp256k1_scalar *e,
const secp256k1_scalar *s) {
secp256k1_scalar 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 *k, const secp256k1_scalar *x, const secp256k1_scalar *e,
const secp256k1_scalar *s) {
secp256k1_scalar 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 *blind, uint64_t *v,
unsigned char *m, size_t *mlen, secp256k1_scalar *ev, secp256k1_scalar *s,
size_t *rsizes, size_t rings, const unsigned char *nonce, const secp256k1_ge *commit, const unsigned char *proof, size_t len, const secp256k1_ge *genp) {
secp256k1_scalar s_orig[128];
secp256k1_scalar sec[32];
secp256k1_scalar stmp;
unsigned char prep[4096];
unsigned char tmp[32];
uint64_t value;
size_t offset;
size_t i;
size_t j;
int b;
size_t skip1;
size_t skip2;
size_t npub;
npub = ((rings - 1) << 2) + rsizes[rings-1];
VERIFY_CHECK(npub <= 128);
VERIFY_CHECK(npub >= 1);
memset(prep, 0, 4096);
/* Reconstruct the provers random values. */
secp256k1_rangeproof_genrand(sec, s_orig, prep, rsizes, rings, nonce, commit, proof, len, genp);
*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++) {
size_t 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++) {
size_t 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(size_t *offset, int *exp, int *mantissa, uint64_t *scale,
uint64_t *min_value, uint64_t *max_value, const unsigned char *proof, size_t plen) {
int i;
int has_nz_range;
int has_min;
if (plen < 65 || ((proof[*offset] & 128) != 0)) {
return 0;
}
has_nz_range = proof[*offset] & 64;
has_min = proof[*offset] & 32;
*exp = -1;
*mantissa = 0;
if (has_nz_range) {
*exp = proof[*offset] & 31;
*offset += 1;
if (*exp > 18) {
return 0;
}
*mantissa = proof[*offset] + 1;
if (*mantissa > 64) {
return 0;
}
*max_value = UINT64_MAX>>(64-*mantissa);
} else {
*max_value = 0;
}
*offset += 1;
*scale = 1;
for (i = 0; i < *exp; i++) {
if (*max_value > UINT64_MAX / 10) {
return 0;
}
*max_value *= 10;
*scale *= 10;
}
*min_value = 0;
if (has_min) {
if(plen - *offset < 8) {
return 0;
}
/*FIXME: Compact minvalue encoding?*/
for (i = 0; i < 8; i++) {
*min_value = (*min_value << 8) | proof[*offset + i];
}
*offset += 8;
}
if (*max_value > UINT64_MAX - *min_value) {
return 0;
}
*max_value += *min_value;
return 1;
}
/* Verifies range proof (len plen) for commit, the min/max values proven are put in the min/max arguments; returns 0 on failure 1 on success.*/
SECP256K1_INLINE static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_context* ecmult_ctx,
const secp256k1_ecmult_gen_context* ecmult_gen_ctx,
unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce,
uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp) {
secp256k1_gej accj;
secp256k1_gej pubs[128];
secp256k1_ge c;
secp256k1_scalar s[128];
secp256k1_scalar evalues[128]; /* Challenges, only used during proof rewind. */
secp256k1_sha256_t sha256_m;
size_t rsizes[32];
int ret;
size_t i;
int exp;
int mantissa;
size_t offset;
size_t rings;
int overflow;
size_t 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_rangeproof_serialize_point(m, commit);
secp256k1_sha256_write(&sha256_m, m, 33);
secp256k1_rangeproof_serialize_point(m, genp);
secp256k1_sha256_write(&sha256_m, m, 33);
secp256k1_sha256_write(&sha256_m, proof, offset);
for(i = 0; i < rings - 1; i++) {
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_pedersen_ecmult_small(&accj, *min_value, genp);
}
for(i = 0; i < rings - 1; i++) {
secp256k1_fe fe;
secp256k1_fe_set_b32(&fe, &proof[offset]);
secp256k1_ge_set_xquad(&c, &fe);
if (signs[i]) {
secp256k1_ge_neg(&c, &c);
}
/* Not using secp256k1_rangeproof_serialize_point as we almost have it
* serialized form already. */
secp256k1_sha256_write(&sha256_m, &signs[i], 1);
secp256k1_sha256_write(&sha256_m, &proof[offset], 32);
secp256k1_gej_set_ge(&pubs[npub], &c);
secp256k1_gej_add_ge_var(&accj, &accj, &c, NULL);
offset += 32;
npub += rsizes[i];
}
secp256k1_gej_neg(&accj, &accj);
secp256k1_gej_add_ge_var(&pubs[npub], &accj, commit, NULL);
if (secp256k1_gej_is_infinity(&pubs[npub])) {
return 0;
}
secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp);
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;
}
if (extra_commit != NULL) {
secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len);
}
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 blind;
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, genp)) {
return 0;
}
/* Unwind apparently successful, see if the commitment can be reconstructed. */
/* FIXME: should check vv is in the mantissa's range. */
vv = (vv * scale) + *min_value;
secp256k1_pedersen_ecmult(ecmult_gen_ctx, &accj, &blind, vv, genp);
if (secp256k1_gej_is_infinity(&accj)) {
return 0;
}
secp256k1_gej_neg(&accj, &accj);
secp256k1_gej_add_ge_var(&accj, &accj, commit, NULL);
if (!secp256k1_gej_is_infinity(&accj)) {
return 0;
}
if (blindout) {
secp256k1_scalar_get_b32(blindout, &blind);
}
if (value_out) {
*value_out = vv;
}
}
return ret;
}
#endif

View File

@ -0,0 +1,620 @@
/**********************************************************************
* 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_MODULE_RANGEPROOF_TESTS
#define SECP256K1_MODULE_RANGEPROOF_TESTS
#include <string.h>
#include "group.h"
#include "scalar.h"
#include "testrand.h"
#include "util.h"
#include "include/secp256k1_rangeproof.h"
static void test_pedersen_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const int32_t *ecount) {
secp256k1_pedersen_commitment commit;
const secp256k1_pedersen_commitment *commit_ptr = &commit;
unsigned char blind[32];
unsigned char blind_out[32];
const unsigned char *blind_ptr = blind;
unsigned char *blind_out_ptr = blind_out;
uint64_t val = secp256k1_rand32();
secp256k1_rand256(blind);
CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) == 0);
CHECK(*ecount == 1);
CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) == 0);
CHECK(*ecount == 2);
CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0);
CHECK(*ecount == 2);
CHECK(secp256k1_pedersen_commit(sign, NULL, blind, val, secp256k1_generator_h) == 0);
CHECK(*ecount == 3);
CHECK(secp256k1_pedersen_commit(sign, &commit, NULL, val, secp256k1_generator_h) == 0);
CHECK(*ecount == 4);
CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, NULL) == 0);
CHECK(*ecount == 5);
CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 1, 1) != 0);
CHECK(*ecount == 5);
CHECK(secp256k1_pedersen_blind_sum(none, NULL, &blind_ptr, 1, 1) == 0);
CHECK(*ecount == 6);
CHECK(secp256k1_pedersen_blind_sum(none, blind_out, NULL, 1, 1) == 0);
CHECK(*ecount == 7);
CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 1) == 0);
CHECK(*ecount == 8);
CHECK(secp256k1_pedersen_blind_sum(none, blind_out, &blind_ptr, 0, 0) != 0);
CHECK(*ecount == 8);
CHECK(secp256k1_pedersen_commit(sign, &commit, blind, val, secp256k1_generator_h) != 0);
CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, &commit_ptr, 1) != 0);
CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, &commit_ptr, 1) == 0);
CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 0) == 0);
CHECK(secp256k1_pedersen_verify_tally(none, NULL, 0, NULL, 0) != 0);
CHECK(*ecount == 8);
CHECK(secp256k1_pedersen_verify_tally(none, NULL, 1, &commit_ptr, 1) == 0);
CHECK(*ecount == 9);
CHECK(secp256k1_pedersen_verify_tally(none, &commit_ptr, 1, NULL, 1) == 0);
CHECK(*ecount == 10);
CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 0) != 0);
CHECK(*ecount == 10);
CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 1, 1) == 0);
CHECK(*ecount == 11);
CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, &blind_out_ptr, 0, 0) == 0);
CHECK(*ecount == 12);
CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, NULL, &blind_ptr, &blind_out_ptr, 1, 0) == 0);
CHECK(*ecount == 13);
CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, NULL, &blind_out_ptr, 1, 0) == 0);
CHECK(*ecount == 14);
CHECK(secp256k1_pedersen_blind_generator_blind_sum(none, &val, &blind_ptr, NULL, 1, 0) == 0);
CHECK(*ecount == 15);
}
static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const int32_t *ecount) {
unsigned char proof[5134];
unsigned char blind[32];
secp256k1_pedersen_commitment commit;
uint64_t vmin = secp256k1_rand32();
uint64_t val = vmin + secp256k1_rand32();
size_t len = sizeof(proof);
/* we'll switch to dylan thomas for this one */
const unsigned char message[68] = "My tears are like the quiet drift / Of petals from some magic rose;";
size_t mlen = sizeof(message);
const unsigned char ext_commit[72] = "And all my grief flows from the rift / Of unremembered skies and snows.";
size_t ext_commit_len = sizeof(ext_commit);
secp256k1_rand256(blind);
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, secp256k1_generator_h));
CHECK(secp256k1_rangeproof_sign(none, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 1);
CHECK(secp256k1_rangeproof_sign(sign, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 2);
CHECK(secp256k1_rangeproof_sign(vrfy, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 3);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 3);
CHECK(secp256k1_rangeproof_sign(both, NULL, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 4);
CHECK(secp256k1_rangeproof_sign(both, proof, NULL, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 5);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, NULL, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 6);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, NULL, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 7);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, NULL, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 8);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, vmin - 1, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 8);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 9);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 9);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 10);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, secp256k1_generator_h) != 0);
CHECK(*ecount == 10);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, NULL, 0, NULL, 0, NULL) == 0);
CHECK(*ecount == 11);
CHECK(secp256k1_rangeproof_sign(both, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
{
int exp;
int mantissa;
uint64_t min_value;
uint64_t max_value;
CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, proof, len) != 0);
CHECK(exp == 0);
CHECK(((uint64_t) 1 << mantissa) > val - vmin);
CHECK(((uint64_t) 1 << (mantissa - 1)) <= val - vmin);
CHECK(min_value == vmin);
CHECK(max_value >= val);
CHECK(secp256k1_rangeproof_info(none, NULL, &mantissa, &min_value, &max_value, proof, len) == 0);
CHECK(*ecount == 12);
CHECK(secp256k1_rangeproof_info(none, &exp, NULL, &min_value, &max_value, proof, len) == 0);
CHECK(*ecount == 13);
CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, NULL, &max_value, proof, len) == 0);
CHECK(*ecount == 14);
CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, NULL, proof, len) == 0);
CHECK(*ecount == 15);
CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, NULL, len) == 0);
CHECK(*ecount == 16);
CHECK(secp256k1_rangeproof_info(none, &exp, &mantissa, &min_value, &max_value, proof, 0) == 0);
CHECK(*ecount == 16);
}
{
uint64_t min_value;
uint64_t max_value;
CHECK(secp256k1_rangeproof_verify(none, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 17);
CHECK(secp256k1_rangeproof_verify(sign, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 18);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 18);
CHECK(secp256k1_rangeproof_verify(vrfy, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 19);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 20);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 21);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 22);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 22);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 23);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0);
CHECK(*ecount == 23);
CHECK(secp256k1_rangeproof_verify(vrfy, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0);
CHECK(*ecount == 24);
}
{
unsigned char blind_out[32];
unsigned char message_out[68];
uint64_t value_out;
uint64_t min_value;
uint64_t max_value;
size_t message_len = sizeof(message_out);
CHECK(secp256k1_rangeproof_rewind(none, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 25);
CHECK(secp256k1_rangeproof_rewind(sign, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 26);
CHECK(secp256k1_rangeproof_rewind(vrfy, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 27);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 27);
CHECK(min_value == vmin);
CHECK(max_value >= val);
CHECK(value_out == val);
CHECK(message_len == sizeof(message_out));
CHECK(memcmp(message, message_out, sizeof(message_out)) == 0);
CHECK(secp256k1_rangeproof_rewind(both, NULL, &value_out, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 27); /* blindout may be NULL */
CHECK(secp256k1_rangeproof_rewind(both, blind_out, NULL, message_out, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 27); /* valueout may be NULL */
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, &message_len, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 28);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) != 0);
CHECK(*ecount == 28);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, NULL, &min_value, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 29);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, NULL, &max_value, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 30);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, NULL, &commit, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 31);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, NULL, proof, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 32);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, NULL, len, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 33);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, 0, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 33);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, ext_commit_len, secp256k1_generator_h) == 0);
CHECK(*ecount == 34);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, secp256k1_generator_h) == 0);
CHECK(*ecount == 34);
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0);
CHECK(*ecount == 35);
}
}
static void test_api(void) {
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
int32_t ecount;
int i;
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
for (i = 0; i < count; i++) {
ecount = 0;
test_pedersen_api(none, sign, vrfy, &ecount);
ecount = 0;
test_rangeproof_api(none, sign, vrfy, both, &ecount);
}
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
secp256k1_context_destroy(both);
}
static void test_pedersen(void) {
secp256k1_pedersen_commitment commits[19];
const secp256k1_pedersen_commitment *cptr[19];
unsigned char blinds[32*19];
const unsigned char *bptr[19];
secp256k1_scalar s;
uint64_t values[19];
int64_t totalv;
int i;
int inputs;
int outputs;
int total;
inputs = (secp256k1_rand32() & 7) + 1;
outputs = (secp256k1_rand32() & 7) + 2;
total = inputs + outputs;
for (i = 0; i < 19; i++) {
cptr[i] = &commits[i];
bptr[i] = &blinds[i * 32];
}
totalv = 0;
for (i = 0; i < inputs; i++) {
values[i] = secp256k1_rands64(0, INT64_MAX - totalv);
totalv += values[i];
}
for (i = 0; i < outputs - 1; i++) {
values[i + inputs] = secp256k1_rands64(0, totalv);
totalv -= values[i + inputs];
}
values[total - 1] = totalv;
for (i = 0; i < total - 1; i++) {
random_scalar_order(&s);
secp256k1_scalar_get_b32(&blinds[i * 32], &s);
}
CHECK(secp256k1_pedersen_blind_sum(ctx, &blinds[(total - 1) * 32], bptr, total - 1, inputs));
for (i = 0; i < total; i++) {
CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h));
}
CHECK(secp256k1_pedersen_verify_tally(ctx, cptr, inputs, &cptr[inputs], outputs));
CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[inputs], outputs, cptr, inputs));
if (inputs > 0 && values[0] > 0) {
CHECK(!secp256k1_pedersen_verify_tally(ctx, cptr, inputs - 1, &cptr[inputs], outputs));
}
random_scalar_order(&s);
for (i = 0; i < 4; i++) {
secp256k1_scalar_get_b32(&blinds[i * 32], &s);
}
values[0] = INT64_MAX;
values[1] = 0;
values[2] = 1;
for (i = 0; i < 3; i++) {
CHECK(secp256k1_pedersen_commit(ctx, &commits[i], &blinds[i * 32], values[i], secp256k1_generator_h));
}
CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[0], 1, &cptr[0], 1));
CHECK(secp256k1_pedersen_verify_tally(ctx, &cptr[1], 1, &cptr[1], 1));
}
static void test_borromean(void) {
unsigned char e0[32];
secp256k1_scalar s[64];
secp256k1_gej pubs[64];
secp256k1_scalar k[8];
secp256k1_scalar sec[8];
secp256k1_ge ge;
secp256k1_scalar one;
unsigned char m[32];
size_t rsizes[8];
size_t secidx[8];
size_t nrings;
size_t i;
size_t 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));
}
}
static 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};
secp256k1_pedersen_commitment commit;
secp256k1_pedersen_commitment commit2;
unsigned char proof[5134];
unsigned char blind[32];
unsigned char blindout[32];
unsigned char message[4096];
size_t mlen;
uint64_t v;
uint64_t vout;
uint64_t vmin;
uint64_t minv;
uint64_t maxv;
size_t len;
size_t i;
size_t j;
size_t k;
/* Short message is a Simone de Beauvoir quote */
const unsigned char message_short[120] = "When I see my own likeness in the depths of someone else's consciousness, I always experience a moment of panic.";
/* Long message is 0xA5 with a bunch of this quote in the middle */
unsigned char message_long[3968];
memset(message_long, 0xa5, sizeof(message_long));
for (i = 1200; i < 3600; i += 120) {
memcpy(&message_long[i], message_short, sizeof(message_short));
}
secp256k1_rand256(blind);
for (i = 0; i < 11; i++) {
v = testvs[i];
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h));
for (vmin = 0; vmin < (i<9 && i > 0 ? 2 : 1); vmin++) {
const unsigned char *input_message = NULL;
size_t input_message_len = 0;
/* vmin is always either 0 or 1; if it is 1, then we have no room for a message.
* If it's 0, we use "minimum encoding" and only have room for a small message when
* `testvs[i]` is >= 4; for a large message when it's >= 2^32. */
if (vmin == 0 && i > 2) {
input_message = message_short;
input_message_len = sizeof(message_short);
}
if (vmin == 0 && i > 7) {
input_message = message_long;
input_message_len = sizeof(message_long);
}
len = 5134;
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, NULL, 0, secp256k1_generator_h));
CHECK(len <= 5134);
mlen = 4096;
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
if (input_message != NULL) {
CHECK(memcmp(message, input_message, input_message_len) == 0);
}
for (j = input_message_len; 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.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h));
CHECK(len <= 73);
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
CHECK(memcmp(blindout, blind, 32) == 0);
CHECK(vout == v);
CHECK(minv == v);
CHECK(maxv == v);
/* Check with a committed message */
len = 5134;
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), secp256k1_generator_h));
CHECK(len <= 73);
CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h));
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h));
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, secp256k1_generator_h));
for (i = 0; i < 19; i++) {
len = 5134;
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h));
CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
CHECK(len <= 5134);
CHECK(minv <= v);
CHECK(maxv >= v);
/* Make sure it fails when validating with a committed message */
CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h));
}
secp256k1_rand256(blind);
{
/*Malleability test.*/
v = secp256k1_rands64(0, 255);
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h));
len = 5134;
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h));
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, NULL, 0, secp256k1_generator_h));
proof[i >> 3] ^= 1 << (i & 7);
}
CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
CHECK(minv <= v);
CHECK(maxv >= v);
}
memcpy(&commit2, &commit, sizeof(commit));
for (i = 0; i < 10 * (size_t) 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, secp256k1_generator_h));
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.data, exp, min_bits, v, NULL, 0, NULL, 0, secp256k1_generator_h));
CHECK(len <= 5134);
mlen = 4096;
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
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.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
memcpy(&commit2, &commit, sizeof(commit));
}
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, NULL, 0, secp256k1_generator_h));
}
len = secp256k1_rands64(0, 3072);
CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h));
}
}
#define MAX_N_GENS 30
void test_multiple_generators(void) {
const size_t n_inputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1;
const size_t n_outputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1;
const size_t n_generators = n_inputs + n_outputs;
unsigned char *generator_blind[MAX_N_GENS];
unsigned char *pedersen_blind[MAX_N_GENS];
secp256k1_generator generator[MAX_N_GENS];
secp256k1_pedersen_commitment commit[MAX_N_GENS];
const secp256k1_pedersen_commitment *commit_ptr[MAX_N_GENS];
size_t i;
int64_t total_value;
uint64_t value[MAX_N_GENS];
secp256k1_scalar s;
unsigned char generator_seed[32];
random_scalar_order(&s);
secp256k1_scalar_get_b32(generator_seed, &s);
/* Create all the needed generators */
for (i = 0; i < n_generators; i++) {
generator_blind[i] = (unsigned char*) malloc(32);
pedersen_blind[i] = (unsigned char*) malloc(32);
random_scalar_order(&s);
secp256k1_scalar_get_b32(generator_blind[i], &s);
random_scalar_order(&s);
secp256k1_scalar_get_b32(pedersen_blind[i], &s);
CHECK(secp256k1_generator_generate_blinded(ctx, &generator[i], generator_seed, generator_blind[i]));
commit_ptr[i] = &commit[i];
}
/* Compute all the values -- can be positive or negative */
total_value = 0;
for (i = 0; i < n_outputs; i++) {
value[n_inputs + i] = secp256k1_rands64(0, INT64_MAX - total_value);
total_value += value[n_inputs + i];
}
for (i = 0; i < n_inputs - 1; i++) {
value[i] = secp256k1_rands64(0, total_value);
total_value -= value[i];
}
value[i] = total_value;
/* Correct for blinding factors and do the commitments */
CHECK(secp256k1_pedersen_blind_generator_blind_sum(ctx, value, (const unsigned char * const *) generator_blind, pedersen_blind, n_generators, n_inputs));
for (i = 0; i < n_generators; i++) {
CHECK(secp256k1_pedersen_commit(ctx, &commit[i], pedersen_blind[i], value[i], &generator[i]));
}
/* Verify */
CHECK(secp256k1_pedersen_verify_tally(ctx, &commit_ptr[0], n_inputs, &commit_ptr[n_inputs], n_outputs));
/* Cleanup */
for (i = 0; i < n_generators; i++) {
free(generator_blind[i]);
free(pedersen_blind[i]);
}
}
void run_rangeproof_tests(void) {
int i;
test_api();
for (i = 0; i < 10*count; i++) {
test_pedersen();
}
for (i = 0; i < 10*count; i++) {
test_borromean();
}
test_rangeproof();
test_multiple_generators();
}
#endif

View File

@ -0,0 +1,6 @@
include_HEADERS += include/secp256k1_surjectionproof.h
noinst_HEADERS += src/modules/surjection/main_impl.h
noinst_HEADERS += src/modules/surjection/surjection.h
noinst_HEADERS += src/modules/surjection/surjection_impl.h
noinst_HEADERS += src/modules/surjection/tests_impl.h

View File

@ -0,0 +1,338 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_SURJECTION_MAIN
#define SECP256K1_MODULE_SURJECTION_MAIN
#include <assert.h>
#include <string.h>
#include "modules/rangeproof/borromean.h"
#include "modules/surjection/surjection_impl.h"
#include "hash.h"
#include "include/secp256k1_rangeproof.h"
#include "include/secp256k1_surjectionproof.h"
static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) {
size_t ret = 0;
size_t i;
for (i = 0; i < count; i++) {
#ifdef HAVE_BUILTIN_POPCOUNT
ret += __builtin_popcount(data[i]);
#else
ret += !!(data[i] & 0x1);
ret += !!(data[i] & 0x2);
ret += !!(data[i] & 0x4);
ret += !!(data[i] & 0x8);
ret += !!(data[i] & 0x10);
ret += !!(data[i] & 0x20);
ret += !!(data[i] & 0x40);
ret += !!(data[i] & 0x80);
#endif
}
return ret;
}
int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surjectionproof *proof, const unsigned char *input, size_t inputlen) {
size_t n_inputs;
size_t signature_len;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
ARG_CHECK(input != NULL);
(void) ctx;
if (inputlen < 2) {
return 0;
}
n_inputs = ((size_t) (input[1] << 8)) + input[0];
if (n_inputs > SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) {
return 0;
}
if (inputlen < 2 + (n_inputs + 7) / 8) {
return 0;
}
signature_len = 32 * (1 + secp256k1_count_bits_set(&input[2], (n_inputs + 7) / 8));
if (inputlen < 2 + (n_inputs + 7) / 8 + signature_len) {
return 0;
}
proof->n_inputs = n_inputs;
memcpy(proof->used_inputs, &input[2], (n_inputs + 7) / 8);
memcpy(proof->data, &input[2 + (n_inputs + 7) / 8], signature_len);
return 1;
}
int secp256k1_surjectionproof_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_surjectionproof *proof) {
size_t signature_len;
size_t serialized_len;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(outputlen != NULL);
ARG_CHECK(proof != NULL);
(void) ctx;
signature_len = 32 * (1 + secp256k1_count_bits_set(proof->used_inputs, (proof->n_inputs + 7) / 8));
serialized_len = 2 + (proof->n_inputs + 7) / 8 + signature_len;
if (*outputlen < serialized_len) {
return 0;
}
output[0] = proof->n_inputs % 0x100;
output[1] = proof->n_inputs / 0x100;
memcpy(&output[2], proof->used_inputs, (proof->n_inputs + 7) / 8);
memcpy(&output[2 + (proof->n_inputs + 7) / 8], proof->data, signature_len);
*outputlen = serialized_len;
return 1;
}
size_t secp256k1_surjectionproof_n_total_inputs(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
(void) ctx;
return proof->n_inputs;
}
size_t secp256k1_surjectionproof_n_used_inputs(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
(void) ctx;
return secp256k1_count_bits_set(proof->used_inputs, (proof->n_inputs + 7) / 8);
}
size_t secp256k1_surjectionproof_serialized_size(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
return 2 + (proof->n_inputs + 7) / 8 + 32 * (1 + secp256k1_surjectionproof_n_used_inputs(ctx, proof));
}
typedef struct {
unsigned char state[32];
size_t state_i;
} secp256k1_surjectionproof_csprng;
static void secp256k1_surjectionproof_csprng_init(secp256k1_surjectionproof_csprng *csprng, const unsigned char* state) {
memcpy(csprng->state, state, 32);
csprng->state_i = 0;
}
static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_csprng *csprng, size_t rand_max) {
/* The number of random bytes to read for each random sample */
const size_t increment = rand_max > 256 ? 2 : 1;
/* The maximum value expressable by the number of random bytes we read */
const size_t selection_range = rand_max > 256 ? 0xffff : 0xff;
/* The largest multiple of rand_max that fits within selection_range */
const size_t limit = ((selection_range + 1) / rand_max) * rand_max;
while (1) {
size_t val;
if (csprng->state_i + increment >= 32) {
secp256k1_sha256_t sha;
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, csprng->state, 32);
secp256k1_sha256_finalize(&sha, csprng->state);
csprng->state_i = 0;
}
val = csprng->state[csprng->state_i];
if (increment > 1) {
val = (val << 8) + csprng->state[csprng->state_i + 1];
}
csprng->state_i += increment;
/* Accept only values below our limit. Values equal to or above the limit are
* biased because they comprise only a subset of the range (0, rand_max - 1) */
if (val < limit) {
return val % rand_max;
}
}
}
int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) {
secp256k1_surjectionproof_csprng csprng;
size_t n_iterations = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
ARG_CHECK(input_index != NULL);
ARG_CHECK(fixed_input_tags != NULL);
ARG_CHECK(fixed_output_tag != NULL);
ARG_CHECK(random_seed32 != NULL);
ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
ARG_CHECK(n_input_tags_to_use <= n_input_tags);
(void) ctx;
secp256k1_surjectionproof_csprng_init(&csprng, random_seed32);
memset(proof->data, 0, sizeof(proof->data));
proof->n_inputs = n_input_tags;
while (1) {
int has_output_tag = 0;
size_t i;
/* obtain a random set of indices */
memset(proof->used_inputs, 0, sizeof(proof->used_inputs));
for (i = 0; i < n_input_tags_to_use; i++) {
while (1) {
size_t next_input_index;
next_input_index = secp256k1_surjectionproof_csprng_next(&csprng, n_input_tags);
if (memcmp(&fixed_input_tags[next_input_index], fixed_output_tag, sizeof(*fixed_output_tag)) == 0) {
*input_index = next_input_index;
has_output_tag = 1;
}
if (!(proof->used_inputs[next_input_index / 8] & (1 << (next_input_index % 8)))) {
proof->used_inputs[next_input_index / 8] |= (1 << (next_input_index % 8));
break;
}
}
}
/* Check if we succeeded */
n_iterations++;
if (has_output_tag) {
#ifdef VERIFY
proof->initialized = 1;
#endif
return n_iterations;
}
if (n_iterations >= n_max_iterations) {
#ifdef VERIFY
proof->initialized = 0;
#endif
return 0;
}
}
}
int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag, size_t input_index, const unsigned char *input_blinding_key, const unsigned char *output_blinding_key) {
secp256k1_scalar blinding_key;
secp256k1_scalar tmps;
secp256k1_scalar nonce;
int overflow = 0;
size_t rsizes[1]; /* array needed for borromean sig API */
size_t indices[1]; /* array needed for borromean sig API */
size_t i;
size_t n_total_pubkeys;
size_t n_used_pubkeys;
size_t ring_input_index = 0;
secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
secp256k1_ge output;
unsigned char msg32[32];
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(proof != NULL);
ARG_CHECK(ephemeral_input_tags != NULL);
ARG_CHECK(ephemeral_output_tag != NULL);
ARG_CHECK(input_blinding_key != NULL);
ARG_CHECK(output_blinding_key != NULL);
#ifdef VERIFY
CHECK(proof->initialized == 1);
#endif
/* Compute secret key */
secp256k1_scalar_set_b32(&tmps, input_blinding_key, &overflow);
if (overflow) {
return 0;
}
secp256k1_scalar_set_b32(&blinding_key, output_blinding_key, &overflow);
if (overflow) {
return 0;
}
/* The only time the input may equal the output is if neither one was blinded in the first place,
* i.e. both blinding keys are zero. Otherwise this is a privacy leak. */
if (secp256k1_scalar_eq(&tmps, &blinding_key) && !secp256k1_scalar_is_zero(&blinding_key)) {
return 0;
}
secp256k1_scalar_negate(&tmps, &tmps);
secp256k1_scalar_add(&blinding_key, &blinding_key, &tmps);
/* Compute public keys */
n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof);
n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof);
if (n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) {
return 0;
}
secp256k1_generator_load(&output, ephemeral_output_tag);
for (i = 0; i < n_total_pubkeys; i++) {
secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]);
}
secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, input_index, &ring_input_index);
/* Produce signature */
rsizes[0] = (int) n_used_pubkeys;
indices[0] = (int) ring_input_index;
secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output);
if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) {
return 0;
}
/* Borromean sign will overwrite one of the s values we just generated, so use
* it as a nonce instead. This avoids extra random generation and also is an
* homage to the rangeproof code which does this very cleverly to encode messages. */
nonce = borromean_s[ring_input_index];
secp256k1_scalar_clear(&borromean_s[ring_input_index]);
if (secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &proof->data[0], borromean_s, ring_pubkeys, &nonce, &blinding_key, rsizes, indices, 1, msg32, 32) == 0) {
return 0;
}
for (i = 0; i < n_used_pubkeys; i++) {
secp256k1_scalar_get_b32(&proof->data[32 + 32 * i], &borromean_s[i]);
}
return 1;
}
int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag) {
size_t rsizes[1]; /* array needed for borromean sig API */
size_t i;
size_t n_total_pubkeys;
size_t n_used_pubkeys;
secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
secp256k1_ge output;
unsigned char msg32[32];
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(proof != NULL);
ARG_CHECK(ephemeral_input_tags != NULL);
ARG_CHECK(ephemeral_output_tag != NULL);
/* Compute public keys */
n_total_pubkeys = secp256k1_surjectionproof_n_total_inputs(ctx, proof);
n_used_pubkeys = secp256k1_surjectionproof_n_used_inputs(ctx, proof);
if (n_used_pubkeys == 0 || n_used_pubkeys > n_total_pubkeys || n_total_pubkeys != n_ephemeral_input_tags) {
return 0;
}
secp256k1_generator_load(&output, ephemeral_output_tag);
for (i = 0; i < n_total_pubkeys; i++) {
secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]);
}
if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, 0, NULL) == 0) {
return 0;
}
/* Verify signature */
rsizes[0] = (int) n_used_pubkeys;
for (i = 0; i < n_used_pubkeys; i++) {
int overflow = 0;
secp256k1_scalar_set_b32(&borromean_s[i], &proof->data[32 + 32 * i], &overflow);
if (overflow == 1) {
return 0;
}
}
secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output);
return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32);
}
#endif

View File

@ -0,0 +1,19 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_SURJECTION_H_
#define _SECP256K1_SURJECTION_H_
#include "group.h"
#include "scalar.h"
SECP256K1_INLINE static int secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag);
SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key);
SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index);
#endif

View File

@ -0,0 +1,108 @@
Surjection Proof Module
===========================
This module implements a scheme by which a given point can be proven to be
equal to one of a set of points, plus a known difference. This is used in
Confidential Assets when reblinding "asset commitments", which are NUMS
points, to prove that the underlying NUMS point does not change during
reblinding.
Assets are represented, in general, by a 32-byte seed (a hash of some
transaction data) which is hashed to form a NUMS generator, which appears
on the blockchain only in blinded form. We refer to the seed as an
"asset ID" and the blinded generator as an "(ephemeral) asset commitment".
These asset commitments are unique per-output, and their NUMS components
are in general known only to the holder of the output.
The result is that within a transaction, all outputs are able to have
a new uniformly-random asset commitment which cannot be associated with
any individual input asset id, but verifiers are nonetheless assured that
all assets coming out of a transaction are ones that went in.
### Terminology
Assets are identified by a 32-byte "asset ID". In this library these IDs
are used as input to a point-valued hash function `H`. We usually refer
to the hash output as `A`, since this output is the only thing that appears
in the algebra.
Then transaction outputs have "asset commitments", which are curvepoints
of the form `A + rG`, where `A` is the hash of the asset ID and `r` is
some random "blinding factor".
### Design Rationale
Confidential Assets essentially works by replacing the second NUMS generator
`H` in Confidental Transactions with a per-asset unique NUMS generator. This
allows the same verification equation (the sum of all blinded inputs must
equal the sum of all blinded outputs) to imply that quantity of *every* asset
type is preserved in each transaction.
It turns out that even if outputs are reblinded by the addition of `rG` for
some known `r`, this verification equation has the same meaning, with one
caveat: verifiers must be assured that the reblinding preserves the original
generators (and does not, for example, negate them).
This assurance is what surjection proofs provide.
### Limitations
The naive scheme works as follows: every output asset is shown to have come
from some input asset. However, the proofs scale with the number of input
assets, so for all outputs the total size of all surjection proofs is `O(mn)`
for `m`, `n` the number of inputs and outputs.
We therefore restrict the number of inputs that each output may have come
from to 3 (well, some fixed number, which is passed into the API), which
provides a weaker form of blinding, but gives `O(n)` scaling. Over many
transactions, the privacy afforded by this increases exponentially.
### Our Scheme
Our scheme works as follows. Proofs are generated in two steps, "initialization"
which selects a subset of inputs and "generation" which does the mathematical
part of proof generation.
Every input has an asset commitment for which we know the blinding key and
underlying asset ID.
#### Initialization
The initialization function takes a list of input asset IDs and one output
asset ID. It chooses an input subset of some fixed size repeatedly until it
the output ID appears at least once in its subset.
It stores a bitmap representing this subset in the proof object and returns
the number of iterations it needed to choose the subset. The reciprocal of
this represents the probability that a uniformly random input-output
mapping would correspond to the actual input-output mapping, and therefore
gives a measure of privacy. (Lower iteration counts are better.)
It also informs the caller the index of the input whose ID matches the output.
As the API works on only a single output at a time, the total probability
should be computed by multiplying together the counts for each output.
#### Generation
The generation function takes a list of input asset commitments, an output
asset commitment, the input index returned by the initialization step, and
blinding keys for (a) the output commitment, (b) the input commitment. Here
"the input commitment" refers specifically to the input whose index was
chosen during initialization.
Next, it computes a ring signature over the differences between the output
commitment and every input commitment chosen during initialization. Since
the discrete log of one of these is the difference between the output and
input blinding keys, it is possible to create a ring signature over every
differences will be the blinding factor of the output. We create such a
signature, which completes the proof.
#### Verification
Verification takes a surjection proof object, a list of input commitments,
and an output commitment. The proof object contains a ring signature and
a bitmap describing which input commitments to use, and verification
succeeds iff the signature verifies.

View File

@ -0,0 +1,86 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_SURJECTION_IMPL_H_
#define _SECP256K1_SURJECTION_IMPL_H_
#include <assert.h>
#include <string.h>
#include "eckey.h"
#include "group.h"
#include "scalar.h"
#include "hash.h"
SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag) {
/* compute message */
size_t i;
unsigned char pk_ser[33];
size_t pk_len = sizeof(pk_ser);
secp256k1_sha256_t sha256_en;
secp256k1_sha256_initialize(&sha256_en);
for (i = 0; i < n_input_tags; i++) {
secp256k1_eckey_pubkey_serialize(&ephemeral_input_tags[i], pk_ser, &pk_len, 1);
assert(pk_len == sizeof(pk_ser));
secp256k1_sha256_write(&sha256_en, pk_ser, pk_len);
}
secp256k1_eckey_pubkey_serialize(ephemeral_output_tag, pk_ser, &pk_len, 1);
assert(pk_len == sizeof(pk_ser));
secp256k1_sha256_write(&sha256_en, pk_ser, pk_len);
secp256k1_sha256_finalize(&sha256_en, msg32);
}
SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, size_t ns, const secp256k1_scalar *blinding_key) {
size_t i;
unsigned char sec_input[36];
secp256k1_sha256_t sha256_en;
/* compute s values */
secp256k1_scalar_get_b32(&sec_input[4], blinding_key);
for (i = 0; i < ns; i++) {
int overflow = 0;
sec_input[0] = i;
sec_input[1] = i >> 8;
sec_input[2] = i >> 16;
sec_input[3] = i >> 24;
secp256k1_sha256_initialize(&sha256_en);
secp256k1_sha256_write(&sha256_en, sec_input, 36);
secp256k1_sha256_finalize(&sha256_en, sec_input);
secp256k1_scalar_set_b32(&s[i], sec_input, &overflow);
if (overflow == 1) {
memset(sec_input, 0, 32);
return 0;
}
}
memset(sec_input, 0, 32);
return 1;
}
SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index) {
size_t i;
size_t j = 0;
for (i = 0; i < n_input_tags; i++) {
if (used_tags[i / 8] & (1 << (i % 8))) {
secp256k1_ge tmpge;
secp256k1_ge_neg(&tmpge, &input_tags[i]);
secp256k1_gej_set_ge(&pubkeys[j], &tmpge);
secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], output_tag, NULL);
if (ring_input_index != NULL && input_index == i) {
*ring_input_index = j;
}
j++;
if (j > n_pubkeys) {
return 0;
}
}
}
return 1;
}
#endif

View File

@ -0,0 +1,487 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_SURJECTIONPROOF_TESTS
#define SECP256K1_MODULE_SURJECTIONPROOF_TESTS
#include "testrand.h"
#include "group.h"
#include "include/secp256k1_generator.h"
#include "include/secp256k1_rangeproof.h"
#include "include/secp256k1_surjectionproof.h"
static void test_surjectionproof_api(void) {
unsigned char seed[32];
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
secp256k1_fixed_asset_tag fixed_input_tags[10];
secp256k1_fixed_asset_tag fixed_output_tag;
secp256k1_generator ephemeral_input_tags[10];
secp256k1_generator ephemeral_output_tag;
unsigned char input_blinding_key[10][32];
unsigned char output_blinding_key[32];
unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX];
size_t serialized_len;
secp256k1_surjectionproof proof;
size_t n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]);
size_t input_index;
int32_t ecount = 0;
size_t i;
secp256k1_rand256(seed);
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
for (i = 0; i < n_inputs; i++) {
secp256k1_rand256(input_blinding_key[i]);
secp256k1_rand256(fixed_input_tags[i].data);
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i]));
}
secp256k1_rand256(output_blinding_key);
memcpy(&fixed_output_tag, &fixed_input_tags[0], sizeof(fixed_input_tags[0]));
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, output_blinding_key));
/* check initialize */
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0);
CHECK(ecount == 0);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0);
CHECK(ecount == 0);
CHECK(secp256k1_surjectionproof_initialize(none, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed) == 0);
CHECK(ecount == 4);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0);
CHECK(ecount == 4);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed) == 0);
CHECK(ecount == 5);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed) == 0);
CHECK(ecount == 6);
CHECK((secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0);
CHECK(ecount == 6);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL) == 0);
CHECK(ecount == 7);
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0);
/* check generate */
CHECK(secp256k1_surjectionproof_generate(none, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 8);
CHECK(secp256k1_surjectionproof_generate(vrfy, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 9);
CHECK(secp256k1_surjectionproof_generate(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 10);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0);
CHECK(ecount == 10);
CHECK(secp256k1_surjectionproof_generate(both, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 11);
CHECK(secp256k1_surjectionproof_generate(both, &proof, NULL, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 12);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 12);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 12);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, 0, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 12);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, NULL, 0, input_blinding_key[0], output_blinding_key) == 0);
CHECK(ecount == 13);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 1, input_blinding_key[0], output_blinding_key) != 0);
CHECK(ecount == 13); /* the above line "succeeds" but generates an invalid proof as the input_index is wrong. it is fairly expensive to detect this. should we? */
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, n_inputs + 1, input_blinding_key[0], output_blinding_key) != 0);
CHECK(ecount == 13);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, NULL, output_blinding_key) == 0);
CHECK(ecount == 14);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], NULL) == 0);
CHECK(ecount == 15);
CHECK(secp256k1_surjectionproof_generate(both, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag, 0, input_blinding_key[0], output_blinding_key) != 0);
/* check verify */
CHECK(secp256k1_surjectionproof_verify(none, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0);
CHECK(ecount == 16);
CHECK(secp256k1_surjectionproof_verify(sign, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0);
CHECK(ecount == 17);
CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) != 0);
CHECK(ecount == 17);
CHECK(secp256k1_surjectionproof_verify(vrfy, NULL, ephemeral_input_tags, n_inputs, &ephemeral_output_tag) == 0);
CHECK(ecount == 18);
CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, NULL, n_inputs, &ephemeral_output_tag) == 0);
CHECK(ecount == 19);
CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs - 1, &ephemeral_output_tag) == 0);
CHECK(ecount == 19);
CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs + 1, &ephemeral_output_tag) == 0);
CHECK(ecount == 19);
CHECK(secp256k1_surjectionproof_verify(vrfy, &proof, ephemeral_input_tags, n_inputs, NULL) == 0);
CHECK(ecount == 20);
/* Check serialize */
serialized_len = sizeof(serialized_proof);
CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0);
CHECK(ecount == 20);
serialized_len = sizeof(serialized_proof);
CHECK(secp256k1_surjectionproof_serialize(none, NULL, &serialized_len, &proof) == 0);
CHECK(ecount == 21);
serialized_len = sizeof(serialized_proof);
CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, NULL, &proof) == 0);
CHECK(ecount == 22);
serialized_len = sizeof(serialized_proof);
CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, NULL) == 0);
CHECK(ecount == 23);
serialized_len = sizeof(serialized_proof);
CHECK(secp256k1_surjectionproof_serialize(none, serialized_proof, &serialized_len, &proof) != 0);
/* Check parse */
CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, serialized_len) != 0);
CHECK(ecount == 23);
CHECK(secp256k1_surjectionproof_parse(none, NULL, serialized_proof, serialized_len) == 0);
CHECK(ecount == 24);
CHECK(secp256k1_surjectionproof_parse(none, &proof, NULL, serialized_len) == 0);
CHECK(ecount == 25);
CHECK(secp256k1_surjectionproof_parse(none, &proof, serialized_proof, 0) == 0);
CHECK(ecount == 25);
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
secp256k1_context_destroy(both);
}
static void test_input_selection(size_t n_inputs) {
unsigned char seed[32];
size_t i;
size_t result;
size_t input_index;
size_t try_count = n_inputs * 100;
secp256k1_surjectionproof proof;
secp256k1_fixed_asset_tag fixed_input_tags[1000];
const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1;
CHECK(n_inputs < max_n_inputs);
secp256k1_rand256(seed);
for (i = 0; i < n_inputs + 1; i++) {
secp256k1_rand256(fixed_input_tags[i].data);
}
/* cannot match output when told to use zero keys */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], try_count, seed);
CHECK(result == 0);
CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 0);
CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs);
CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 34 + (n_inputs + 7) / 8);
if (n_inputs > 0) {
/* succeed in 100*n_inputs tries (probability of failure e^-100) */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 1, &fixed_input_tags[0], try_count, seed);
CHECK(result > 0);
CHECK(result < n_inputs * 10);
CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 1);
CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs);
CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 66 + (n_inputs + 7) / 8);
CHECK(input_index == 0);
}
if (n_inputs >= 3) {
/* succeed in 10*n_inputs tries (probability of failure e^-10) */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[1], try_count, seed);
CHECK(result > 0);
CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == 3);
CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs);
CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 130 + (n_inputs + 7) / 8);
CHECK(input_index == 1);
/* fail, key not found */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[n_inputs], try_count, seed);
CHECK(result == 0);
/* succeed on first try when told to use all keys */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], try_count, seed);
CHECK(result == 1);
CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs);
CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs);
CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs + 1) + (n_inputs + 7) / 8);
CHECK(input_index == 0);
/* succeed in less than 64 tries when told to use half keys. (probability of failure 2^-64) */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_inputs / 2, &fixed_input_tags[0], 64, seed);
CHECK(result > 0);
CHECK(result < 64);
CHECK(secp256k1_surjectionproof_n_used_inputs(ctx, &proof) == n_inputs / 2);
CHECK(secp256k1_surjectionproof_n_total_inputs(ctx, &proof) == n_inputs);
CHECK(secp256k1_surjectionproof_serialized_size(ctx, &proof) == 2 + 32 * (n_inputs / 2 + 1) + (n_inputs + 7) / 8);
CHECK(input_index == 0);
}
}
/** Runs surjectionproof_initilize multiple times and records the number of times each input was used.
*/
static void test_input_selection_distribution_helper(const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, size_t *used_inputs) {
secp256k1_surjectionproof proof;
size_t input_index;
size_t i;
size_t j;
unsigned char seed[32];
size_t result;
for (i = 0; i < n_input_tags; i++) {
used_inputs[i] = 0;
}
for(j = 0; j < 10000; j++) {
secp256k1_rand256(seed);
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, &fixed_input_tags[0], 64, seed);
CHECK(result > 0);
for (i = 0; i < n_input_tags; i++) {
if (proof.used_inputs[i / 8] & (1 << (i % 8))) {
used_inputs[i] += 1;
}
}
}
}
/** Probabilistic test of the distribution of used_inputs after surjectionproof_initialize.
* Each confidence interval assertion fails incorrectly with a probability of 2^-128.
*/
static void test_input_selection_distribution(void) {
size_t i;
size_t n_input_tags_to_use;
const size_t n_inputs = 4;
secp256k1_fixed_asset_tag fixed_input_tags[4];
size_t used_inputs[4];
for (i = 0; i < n_inputs; i++) {
secp256k1_rand256(fixed_input_tags[i].data);
}
/* If there is one input tag to use, initialize must choose the one equal to fixed_output_tag. */
n_input_tags_to_use = 1;
test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs);
CHECK(used_inputs[0] == 10000);
CHECK(used_inputs[1] == 0);
CHECK(used_inputs[2] == 0);
CHECK(used_inputs[3] == 0);
n_input_tags_to_use = 2;
/* The input equal to the fixed_output_tag must be included in all used_inputs sets.
* For each fixed_input_tag != fixed_output_tag the probability that it's included
* in the used_inputs set is P(used_input|not fixed_output_tag) = 1/3.
*/
test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs);
CHECK(used_inputs[0] == 10000);
CHECK(used_inputs[1] > 2725 && used_inputs[1] < 3961);
CHECK(used_inputs[2] > 2725 && used_inputs[2] < 3961);
CHECK(used_inputs[3] > 2725 && used_inputs[3] < 3961);
n_input_tags_to_use = 3;
/* P(used_input|not fixed_output_tag) = 2/3 */
test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs);
CHECK(used_inputs[0] == 10000);
CHECK(used_inputs[1] > 6039 && used_inputs[1] < 7275);
CHECK(used_inputs[2] > 6039 && used_inputs[2] < 7275);
CHECK(used_inputs[3] > 6039 && used_inputs[3] < 7275);
n_input_tags_to_use = 1;
/* Create second input tag that is equal to the output tag. Therefore, when using only
* one input we have P(used_input|fixed_output_tag) = 1/2 and P(used_input|not fixed_output_tag) = 0
*/
memcpy(fixed_input_tags[0].data, fixed_input_tags[1].data, 32);
test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs);
CHECK(used_inputs[0] > 4345 && used_inputs[0] < 5655);
CHECK(used_inputs[1] > 4345 && used_inputs[1] < 5655);
CHECK(used_inputs[2] == 0);
CHECK(used_inputs[3] == 0);
n_input_tags_to_use = 2;
/* When choosing 2 inputs in initialization there are 5 possible combinations of
* input indexes {(0, 1), (1, 2), (0, 3), (1, 3), (0, 2)}. Therefore we have
* P(used_input|fixed_output_tag) = 3/5 and P(used_input|not fixed_output_tag) = 2/5.
*/
test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs);
CHECK(used_inputs[0] > 5352 && used_inputs[0] < 6637);
CHECK(used_inputs[1] > 5352 && used_inputs[1] < 6637);
CHECK(used_inputs[2] > 3363 && used_inputs[2] < 4648);
CHECK(used_inputs[3] > 3363 && used_inputs[3] < 4648);
n_input_tags_to_use = 3;
/* There are 4 combinations, each with all inputs except one. Therefore we have
* P(used_input|fixed_output_tag) = 3/4 and P(used_input|not fixed_output_tag) = 3/4.
*/
test_input_selection_distribution_helper(fixed_input_tags, n_inputs, n_input_tags_to_use, used_inputs);
CHECK(used_inputs[0] > 6918 && used_inputs[0] < 8053);
CHECK(used_inputs[1] > 6918 && used_inputs[1] < 8053);
CHECK(used_inputs[2] > 6918 && used_inputs[2] < 8053);
CHECK(used_inputs[3] > 6918 && used_inputs[3] < 8053);
}
static void test_gen_verify(size_t n_inputs, size_t n_used) {
unsigned char seed[32];
secp256k1_surjectionproof proof;
unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX];
size_t serialized_len = SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX;
secp256k1_fixed_asset_tag fixed_input_tags[1000];
secp256k1_generator ephemeral_input_tags[1000];
unsigned char *input_blinding_key[1000];
const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1;
size_t try_count = n_inputs * 100;
size_t key_index;
size_t input_index;
size_t i;
int result;
/* setup */
CHECK(n_used <= n_inputs);
CHECK(n_inputs < max_n_inputs);
secp256k1_rand256(seed);
key_index = (((size_t) seed[0] << 8) + seed[1]) % n_inputs;
for (i = 0; i < n_inputs + 1; i++) {
input_blinding_key[i] = malloc(32);
secp256k1_rand256(input_blinding_key[i]);
/* choose random fixed tag, except that for the output one copy from the key_index */
if (i < n_inputs) {
secp256k1_rand256(fixed_input_tags[i].data);
} else {
memcpy(&fixed_input_tags[i], &fixed_input_tags[key_index], sizeof(fixed_input_tags[i]));
}
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i]));
}
/* test */
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_inputs, n_used, &fixed_input_tags[key_index], try_count, seed);
if (n_used == 0) {
CHECK(result == 0);
return;
}
CHECK(result > 0);
CHECK(input_index == key_index);
result = secp256k1_surjectionproof_generate(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]);
CHECK(result == 1);
CHECK(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof));
CHECK(serialized_len == secp256k1_surjectionproof_serialized_size(ctx, &proof));
CHECK(serialized_len == SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(n_inputs, n_used));
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len));
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]);
CHECK(result == 1);
/* various fail cases */
if (n_inputs > 1) {
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]);
CHECK(result == 0);
/* number of entries in ephemeral_input_tags array is less than proof.n_inputs */
n_inputs -= 1;
result = secp256k1_surjectionproof_generate(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs], input_index, input_blinding_key[input_index], input_blinding_key[n_inputs]);
CHECK(result == 0);
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]);
CHECK(result == 0);
n_inputs += 1;
}
/* cleanup */
for (i = 0; i < n_inputs + 1; i++) {
free(input_blinding_key[i]);
}
}
/* check that a proof with empty n_used_inputs is invalid */
static void test_no_used_inputs_verify(void) {
secp256k1_surjectionproof proof;
secp256k1_fixed_asset_tag fixed_input_tag;
secp256k1_fixed_asset_tag fixed_output_tag;
secp256k1_generator ephemeral_input_tags[1];
size_t n_ephemeral_input_tags = 1;
secp256k1_generator ephemeral_output_tag;
unsigned char blinding_key[32];
secp256k1_ge inputs[1];
secp256k1_ge output;
secp256k1_sha256_t sha256_e0;
int result;
/* Create proof that doesn't use inputs. secp256k1_surjectionproof_initialize
* will not work here since it insists on selecting an input that matches the output. */
proof.n_inputs = 1;
memset(proof.used_inputs, 0, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8);
/* create different fixed input and output tags */
secp256k1_rand256(fixed_input_tag.data);
secp256k1_rand256(fixed_output_tag.data);
/* blind fixed output tags with random blinding key */
secp256k1_rand256(blinding_key);
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[0], fixed_input_tag.data, blinding_key));
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, blinding_key));
/* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */
secp256k1_generator_load(&output, &ephemeral_output_tag);
secp256k1_generator_load(&inputs[0], &ephemeral_input_tags[0]);
secp256k1_surjection_genmessage(proof.data, inputs, 1, &output);
secp256k1_sha256_initialize(&sha256_e0);
secp256k1_sha256_write(&sha256_e0, proof.data, 32);
secp256k1_sha256_finalize(&sha256_e0, proof.data);
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_ephemeral_input_tags, &ephemeral_output_tag);
CHECK(result == 0);
}
void test_bad_serialize(void) {
secp256k1_surjectionproof proof;
unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX];
size_t serialized_len;
proof.n_inputs = 0;
serialized_len = 2 + 31;
/* e0 is one byte too short */
CHECK(secp256k1_surjectionproof_serialize(ctx, serialized_proof, &serialized_len, &proof) == 0);
}
void test_bad_parse(void) {
secp256k1_surjectionproof proof;
unsigned char serialized_proof0[] = { 0x00 };
unsigned char serialized_proof1[] = { 0x01, 0x00 };
unsigned char serialized_proof2[33] = { 0 };
/* Missing total input count */
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof0, sizeof(serialized_proof0)) == 0);
/* Missing bitmap */
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof1, sizeof(serialized_proof1)) == 0);
/* Missing e0 value */
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0);
}
void run_surjection_tests(void) {
int i;
for (i = 0; i < count; i++) {
test_surjectionproof_api();
}
test_input_selection(0);
test_input_selection(1);
test_input_selection(5);
test_input_selection(100);
test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
test_input_selection_distribution();
test_gen_verify(10, 3);
test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
test_no_used_inputs_verify();
test_bad_serialize();
test_bad_parse();
}
#endif

View File

@ -0,0 +1,4 @@
include_HEADERS += include/secp256k1_whitelist.h
noinst_HEADERS += src/modules/whitelist/whitelist_impl.h
noinst_HEADERS += src/modules/whitelist/main_impl.h
noinst_HEADERS += src/modules/whitelist/tests_impl.h

View File

@ -0,0 +1,174 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_WHITELIST_MAIN
#define SECP256K1_MODULE_WHITELIST_MAIN
#include "include/secp256k1_whitelist.h"
#include "modules/whitelist/whitelist_impl.h"
#define MAX_KEYS SECP256K1_WHITELIST_MAX_N_KEYS /* shorter alias */
int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey, const unsigned char *online_seckey, const unsigned char *summed_seckey, const size_t index, secp256k1_nonce_function noncefp, const void *noncedata) {
secp256k1_gej pubs[MAX_KEYS];
secp256k1_scalar s[MAX_KEYS];
secp256k1_scalar sec, non;
unsigned char msg32[32];
int ret;
if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_default;
}
/* Sanity checks */
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(sig != NULL);
ARG_CHECK(online_pubkeys != NULL);
ARG_CHECK(offline_pubkeys != NULL);
ARG_CHECK(n_keys <= MAX_KEYS);
ARG_CHECK(sub_pubkey != NULL);
ARG_CHECK(online_seckey != NULL);
ARG_CHECK(summed_seckey != NULL);
ARG_CHECK(index < n_keys);
/* Compute pubkeys: online_pubkey + tweaked(offline_pubkey + address), and message */
ret = secp256k1_whitelist_compute_keys_and_message(ctx, msg32, pubs, online_pubkeys, offline_pubkeys, n_keys, sub_pubkey);
/* Compute signing key: online_seckey + tweaked(summed_seckey) */
if (ret) {
ret = secp256k1_whitelist_compute_tweaked_privkey(ctx, &sec, online_seckey, summed_seckey);
}
/* Compute nonce and random s-values */
if (ret) {
unsigned char seckey32[32];
unsigned int count = 0;
int overflow = 0;
secp256k1_scalar_get_b32(seckey32, &sec);
while (1) {
size_t i;
unsigned char nonce32[32];
int done;
ret = noncefp(nonce32, msg32, seckey32, NULL, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
memset(nonce32, 0, 32);
if (overflow || secp256k1_scalar_is_zero(&non)) {
count++;
continue;
}
done = 1;
for (i = 0; i < n_keys; i++) {
msg32[0] ^= i + 1;
msg32[1] ^= (i + 1) / 0x100;
ret = noncefp(&sig->data[32 * (i + 1)], msg32, seckey32, NULL, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&s[i], &sig->data[32 * (i + 1)], &overflow);
msg32[0] ^= i + 1;
msg32[1] ^= (i + 1) / 0x100;
if (overflow || secp256k1_scalar_is_zero(&s[i])) {
count++;
done = 0;
break;
}
}
if (done) {
break;
}
}
memset(seckey32, 0, 32);
}
/* Actually sign */
if (ret) {
sig->n_keys = n_keys;
ret = secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, &sig->data[0], s, pubs, &non, &sec, &n_keys, &index, 1, msg32, 32);
/* Signing will change s[index], so update in the sig structure */
secp256k1_scalar_get_b32(&sig->data[32 * (index + 1)], &s[index]);
}
secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec);
return ret;
}
int secp256k1_whitelist_verify(const secp256k1_context* ctx, const secp256k1_whitelist_signature *sig, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const size_t n_keys, const secp256k1_pubkey *sub_pubkey) {
secp256k1_scalar s[MAX_KEYS];
secp256k1_gej pubs[MAX_KEYS];
unsigned char msg32[32];
size_t i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(sig != NULL);
ARG_CHECK(online_pubkeys != NULL);
ARG_CHECK(offline_pubkeys != NULL);
ARG_CHECK(sub_pubkey != NULL);
if (sig->n_keys > MAX_KEYS || sig->n_keys != n_keys) {
return 0;
}
for (i = 0; i < sig->n_keys; i++) {
int overflow = 0;
secp256k1_scalar_set_b32(&s[i], &sig->data[32 * (i + 1)], &overflow);
if (overflow || secp256k1_scalar_is_zero(&s[i])) {
return 0;
}
}
/* Compute pubkeys: online_pubkey + tweaked(offline_pubkey + address), and message */
if (!secp256k1_whitelist_compute_keys_and_message(ctx, msg32, pubs, online_pubkeys, offline_pubkeys, sig->n_keys, sub_pubkey)) {
return 0;
}
/* Do verification */
return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &sig->data[0], s, pubs, &sig->n_keys, 1, msg32, 32);
}
size_t secp256k1_whitelist_signature_n_keys(const secp256k1_whitelist_signature *sig) {
return sig->n_keys;
}
int secp256k1_whitelist_signature_parse(const secp256k1_context* ctx, secp256k1_whitelist_signature *sig, const unsigned char *input, size_t input_len) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(input != NULL);
if (input_len == 0) {
return 0;
}
sig->n_keys = input[0];
if (sig->n_keys >= MAX_KEYS || input_len != 1 + 32 * (sig->n_keys + 1)) {
return 0;
}
memcpy(&sig->data[0], &input[1], 32 * (sig->n_keys + 1));
return 1;
}
int secp256k1_whitelist_signature_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *output_len, const secp256k1_whitelist_signature *sig) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(output_len != NULL);
ARG_CHECK(sig != NULL);
if (*output_len < 1 + 32 * (sig->n_keys + 1)) {
return 0;
}
output[0] = sig->n_keys;
memcpy(&output[1], &sig->data[0], 32 * (sig->n_keys + 1));
*output_len = 1 + 32 * (sig->n_keys + 1);
return 1;
}
#endif

View File

@ -0,0 +1,151 @@
/**********************************************************************
* Copyright (c) 2014-2016 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef SECP256K1_MODULE_WHITELIST_TESTS
#define SECP256K1_MODULE_WHITELIST_TESTS
#include "include/secp256k1_whitelist.h"
void test_whitelist_end_to_end(const size_t n_keys) {
unsigned char **online_seckey = (unsigned char **) malloc(n_keys * sizeof(*online_seckey));
unsigned char **summed_seckey = (unsigned char **) malloc(n_keys * sizeof(*summed_seckey));
secp256k1_pubkey *online_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*online_pubkeys));
secp256k1_pubkey *offline_pubkeys = (secp256k1_pubkey *) malloc(n_keys * sizeof(*offline_pubkeys));
secp256k1_scalar ssub;
unsigned char csub[32];
secp256k1_pubkey sub_pubkey;
/* Generate random keys */
size_t i;
/* Start with subkey */
random_scalar_order_test(&ssub);
secp256k1_scalar_get_b32(csub, &ssub);
CHECK(secp256k1_ec_seckey_verify(ctx, csub) == 1);
CHECK(secp256k1_ec_pubkey_create(ctx, &sub_pubkey, csub) == 1);
/* Then offline and online whitelist keys */
for (i = 0; i < n_keys; i++) {
secp256k1_scalar son, soff;
online_seckey[i] = (unsigned char *) malloc(32);
summed_seckey[i] = (unsigned char *) malloc(32);
/* Create two keys */
random_scalar_order_test(&son);
secp256k1_scalar_get_b32(online_seckey[i], &son);
CHECK(secp256k1_ec_seckey_verify(ctx, online_seckey[i]) == 1);
CHECK(secp256k1_ec_pubkey_create(ctx, &online_pubkeys[i], online_seckey[i]) == 1);
random_scalar_order_test(&soff);
secp256k1_scalar_get_b32(summed_seckey[i], &soff);
CHECK(secp256k1_ec_seckey_verify(ctx, summed_seckey[i]) == 1);
CHECK(secp256k1_ec_pubkey_create(ctx, &offline_pubkeys[i], summed_seckey[i]) == 1);
/* Make summed_seckey correspond to the sum of offline_pubkey and sub_pubkey */
secp256k1_scalar_add(&soff, &soff, &ssub);
secp256k1_scalar_get_b32(summed_seckey[i], &soff);
CHECK(secp256k1_ec_seckey_verify(ctx, summed_seckey[i]) == 1);
}
/* Sign/verify with each one */
for (i = 0; i < n_keys; i++) {
unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0};
size_t slen = sizeof(serialized);
secp256k1_whitelist_signature sig;
secp256k1_whitelist_signature sig1;
CHECK(secp256k1_whitelist_sign(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey, online_seckey[i], summed_seckey[i], i, NULL, NULL));
CHECK(secp256k1_whitelist_verify(ctx, &sig, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey) == 1);
/* Check that exchanging keys causes a failure */
CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1);
/* Serialization round trip */
CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &slen, &sig) == 1);
CHECK(slen == 33 + 32 * n_keys);
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen) == 1);
/* (Check various bad-length conditions) */
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 32) == 0);
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen + 1) == 0);
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, slen - 1) == 0);
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig1, serialized, 0) == 0);
CHECK(secp256k1_whitelist_verify(ctx, &sig1, online_pubkeys, offline_pubkeys, n_keys, &sub_pubkey) == 1);
CHECK(secp256k1_whitelist_verify(ctx, &sig1, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1);
/* Test n_keys */
CHECK(secp256k1_whitelist_signature_n_keys(&sig) == n_keys);
CHECK(secp256k1_whitelist_signature_n_keys(&sig1) == n_keys);
/* Test bad number of keys in signature */
sig.n_keys = n_keys + 1;
CHECK(secp256k1_whitelist_verify(ctx, &sig, offline_pubkeys, online_pubkeys, n_keys, &sub_pubkey) != 1);
sig.n_keys = n_keys;
}
for (i = 0; i < n_keys; i++) {
free(online_seckey[i]);
free(summed_seckey[i]);
}
free(online_seckey);
free(summed_seckey);
free(online_pubkeys);
free(offline_pubkeys);
}
void test_whitelist_bad_parse(void) {
secp256k1_whitelist_signature sig;
const unsigned char serialized0[] = { 1+32*(0+1) };
const unsigned char serialized1[] = {
0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
};
const unsigned char serialized2[] = {
0x01,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
};
/* Empty input */
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized0, 0) == 0);
/* Misses one byte of e0 */
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized1, sizeof(serialized1)) == 0);
/* Enough bytes for e0, but there is no s value */
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized2, sizeof(serialized2)) == 0);
}
void test_whitelist_bad_serialize(void) {
unsigned char serialized[] = {
0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
};
size_t serialized_len;
secp256k1_whitelist_signature sig;
CHECK(secp256k1_whitelist_signature_parse(ctx, &sig, serialized, sizeof(serialized)) == 1);
serialized_len = sizeof(serialized) - 1;
/* Output buffer is one byte too short */
CHECK(secp256k1_whitelist_signature_serialize(ctx, serialized, &serialized_len, &sig) == 0);
}
void run_whitelist_tests(void) {
int i;
test_whitelist_bad_parse();
test_whitelist_bad_serialize();
for (i = 0; i < count; i++) {
test_whitelist_end_to_end(1);
test_whitelist_end_to_end(10);
test_whitelist_end_to_end(50);
}
}
#endif

View File

@ -0,0 +1,96 @@
Address Whitelisting Module
===========================
This module implements a scheme by which members of some group, having fixed
signing keys, can prove control of an arbitrary other key without associating
their own identity (only that they belong to the group) to the new key. The
application is to patch ring-signature-like behaviour onto systems such as
Bitcoin or PGP which do not directly support this.
We refer to such delegation as "whitelisting" because we expect it to be used
to build a dynamic whitelist of authorized keys.
For example, imagine a private sidechain with a fixed membership set but
stronger privacy properties than Bitcoin. When moving coins from this system
to Bitcoin, it is desirable that the destination Bitcoin addresses be provably
in control of some user of the sidechain. This prevents malicious or erroneous
behaviour on the sidechain, which can likely be resolved by its participants,
from translating to theft on the wider Bitcoin network, which is irreversible.
### Unused Schemes and Design Rationale
#### Direct Signing
An obvious scheme for such delegation is to simply have participants sign the
key they want to whitelist. To avoid revealing their specific identity, they
could use a ring signature. The problem with this is that it really only proves
that a participant *signed off* on a key, not that they control it. Thus any
security failure that allows text substitution could be used to subvert this
and redirect coins to an attacker-controlled address.
#### Signing with Difference-of-Keys
A less obvious scheme is to have a participant sign an arbitrary message with
the sum of her key `P` and the whitelisted key `W`. Such a signature with the key
`P + W` proves knowledge of either (a) discrete logarithms of both `P` and `W`;
or (b) neither. This makes directly attacking participants' signing schemes much
harder, but allows an attacker to whitelist arbitrary "garbage" keys by computing
`W` as the difference between an attacker-controlled key and `P`. For Bitcoin,
the effect of garbage keys is to "burn" stolen coins, destroying them.
In an important sense, this "burning coins" attack is a good thing: it enables
*offline delegation*. That is, the key `P` does not need to be available at the
time of delegation. Instead, participants could choose `S = P + W`, sign with
this to delegate, and only later compute the discrete logarithm of `W = P - S`.
This allows `P` to be in cold storage or be otherwise inaccessible, improving
the overall system security.
#### Signing with Tweaked-Difference-of-Keys
A modification of this scheme, which prevents this "garbage key" attack, is to
instead have participants sign some message with the key `P + H(W)W`, for `H`
some random-oracle hash that maps group elements to scalars. This key, and its
discrete logarithm, cannot be known until after `W` is chosen, so `W` cannot
be selected as the difference between it and `P`. (Note that `P` could still
be some chosen difference; however `P` is a fixed key and must be verified
out-of-band to have come from a legitimate participant anyway.)
This scheme is almost what we want, but it no longer supports offline
delegation. However, we can get this back by introducing a new key, `P'`,
and signing with the key `P + H(W + P')(W + P')`. This gives us the best
of both worlds: `P'` does not need to be online to delegate, allowing it
to be securely stored and preventing real-time attacks; `P` does need to
be online, but its compromise only allows an attacker to whitelist "garbage
keys", not attacker-controlled ones.
### Our Scheme
Our scheme works as follows: each participant `i` chooses two keys, `P_i` and `Q_i`.
We refer to `P_i` as the "online key" and `Q_i` as the "offline key". To whitelist
a key `W`, the participant computes the key `L_j = P_j + H(W + Q_j)(W + Q_j)` for
every participant `j`. Then she will know the discrete logarithm of `L_i` for her
own `i`.
Next, she signs a message containing every `P_i` and `Q_i` as well as `W` with
a ring signature over all the keys `L_j`. This proves that she knows the discrete
logarithm of some `L_i` (though it is zero-knowledge which one), and therefore
knows:
1. The discrete logarithms of all of `W`, `P_i` and `Q_i`; or
2. The discrete logarithm of `P_i` but of *neither* `W` nor `Q_i`.
In other words, compromise of the online key `P_i` allows an attacker to whitelist
"garbage keys" for which nobody knows the discrete logarithm; to whitelist an
attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult
because by design, only the sum `S = W + Q_i` is used when signing; then by choosing
`S` freely, a participant can delegate without the secret key to `Q_i` ever being online.
(Later, when she wants to actually use `W`, she will need to compute its key as the
difference between `S` and `Q_i`; but this can be done offline and much later
and with more expensive security requirements.)
The message to be signed contains all public keys to prevent a class of attacks
centered around choosing keys to match pre-computed signatures. In our proposed
use case, whitelisted keys already must be computed before they are signed, and
the remaining public keys are verified out-of-band when setting up the system,
so there is no direct benefit to this. We do it only to reduce fragility and
increase safety of unforeseen uses.

View File

@ -0,0 +1,129 @@
/**********************************************************************
* Copyright (c) 2016 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_WHITELIST_IMPL_H_
#define _SECP256K1_WHITELIST_IMPL_H_
static int secp256k1_whitelist_hash_pubkey(secp256k1_scalar* output, secp256k1_gej* pubkey) {
unsigned char h[32];
unsigned char c[33];
secp256k1_sha256_t sha;
int overflow = 0;
size_t size = 33;
secp256k1_ge ge;
secp256k1_ge_set_gej(&ge, pubkey);
secp256k1_sha256_initialize(&sha);
if (!secp256k1_eckey_pubkey_serialize(&ge, c, &size, SECP256K1_EC_COMPRESSED)) {
return 0;
}
secp256k1_sha256_write(&sha, c, size);
secp256k1_sha256_finalize(&sha, h);
secp256k1_scalar_set_b32(output, h, &overflow);
if (overflow || secp256k1_scalar_is_zero(output)) {
/* This return path is mathematically impossible to hit */
secp256k1_scalar_clear(output);
return 0;
}
return 1;
}
static int secp256k1_whitelist_tweak_pubkey(const secp256k1_context* ctx, secp256k1_gej* pub_tweaked) {
secp256k1_scalar tweak;
secp256k1_scalar zero;
int ret;
secp256k1_scalar_set_int(&zero, 0);
ret = secp256k1_whitelist_hash_pubkey(&tweak, pub_tweaked);
if (ret) {
secp256k1_ecmult(&ctx->ecmult_ctx, pub_tweaked, pub_tweaked, &tweak, &zero);
}
return ret;
}
static int secp256k1_whitelist_compute_tweaked_privkey(const secp256k1_context* ctx, secp256k1_scalar* skey, const unsigned char *online_key, const unsigned char *summed_key) {
secp256k1_scalar tweak;
int ret = 1;
int overflow = 0;
secp256k1_scalar_set_b32(skey, summed_key, &overflow);
if (overflow || secp256k1_scalar_is_zero(skey)) {
ret = 0;
}
if (ret) {
secp256k1_gej pkeyj;
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkeyj, skey);
ret = secp256k1_whitelist_hash_pubkey(&tweak, &pkeyj);
}
if (ret) {
secp256k1_scalar sonline;
secp256k1_scalar_mul(skey, skey, &tweak);
secp256k1_scalar_set_b32(&sonline, online_key, &overflow);
if (overflow || secp256k1_scalar_is_zero(&sonline)) {
ret = 0;
}
secp256k1_scalar_add(skey, skey, &sonline);
secp256k1_scalar_clear(&sonline);
secp256k1_scalar_clear(&tweak);
}
if (!ret) {
secp256k1_scalar_clear(skey);
}
return ret;
}
/* Takes a list of pubkeys and combines them to form the public keys needed
* for the ring signature; also produce a commitment to every one that will
* be our "message". */
static int secp256k1_whitelist_compute_keys_and_message(const secp256k1_context* ctx, unsigned char *msg32, secp256k1_gej *keys, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const int n_keys, const secp256k1_pubkey *sub_pubkey) {
unsigned char c[33];
size_t size = 33;
secp256k1_sha256_t sha;
int i;
secp256k1_ge subkey_ge;
secp256k1_sha256_initialize(&sha);
secp256k1_pubkey_load(ctx, &subkey_ge, sub_pubkey);
/* commit to sub-key */
if (!secp256k1_eckey_pubkey_serialize(&subkey_ge, c, &size, SECP256K1_EC_COMPRESSED)) {
return 0;
}
secp256k1_sha256_write(&sha, c, size);
for (i = 0; i < n_keys; i++) {
secp256k1_ge offline_ge;
secp256k1_ge online_ge;
secp256k1_gej tweaked_gej;
/* commit to fixed keys */
secp256k1_pubkey_load(ctx, &offline_ge, &offline_pubkeys[i]);
if (!secp256k1_eckey_pubkey_serialize(&offline_ge, c, &size, SECP256K1_EC_COMPRESSED)) {
return 0;
}
secp256k1_sha256_write(&sha, c, size);
secp256k1_pubkey_load(ctx, &online_ge, &online_pubkeys[i]);
if (!secp256k1_eckey_pubkey_serialize(&online_ge, c, &size, SECP256K1_EC_COMPRESSED)) {
return 0;
}
secp256k1_sha256_write(&sha, c, size);
/* compute tweaked keys */
secp256k1_gej_set_ge(&tweaked_gej, &offline_ge);
secp256k1_gej_add_ge_var(&tweaked_gej, &tweaked_gej, &subkey_ge, NULL);
secp256k1_whitelist_tweak_pubkey(ctx, &tweaked_gej);
secp256k1_gej_add_ge_var(&keys[i], &tweaked_gej, &online_ge, NULL);
}
secp256k1_sha256_finalize(&sha, msg32);
return 1;
}
#endif

View File

@ -38,6 +38,9 @@ static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b
/** Set a scalar to an unsigned integer. */
static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v);
/** Set a scalar to an unsigned 64-bit integer */
static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v);
/** Convert a scalar to a byte array. */
static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a);

View File

@ -7,6 +7,8 @@
#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
#define _SECP256K1_SCALAR_REPR_IMPL_H_
#include "scalar.h"
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL)
#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL)
@ -38,6 +40,13 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig
r->d[3] = 0;
}
SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) {
r->d[0] = v;
r->d[1] = 0;
r->d[2] = 0;
r->d[3] = 0;
}
SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6);
return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1);

View File

@ -56,6 +56,17 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig
r->d[7] = 0;
}
SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) {
r->d[0] = v;
r->d[1] = v >> 32;
r->d[2] = 0;
r->d[3] = 0;
r->d[4] = 0;
r->d[5] = 0;
r->d[6] = 0;
r->d[7] = 0;
}
SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5);
return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1);

View File

@ -17,6 +17,7 @@ SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a)
SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; }
SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; }
SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { *r = v % EXHAUSTIVE_TEST_ORDER; }
SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
if (offset < 32)

View File

@ -18,6 +18,16 @@
#include "eckey_impl.h"
#include "hash_impl.h"
#ifdef ENABLE_MODULE_GENERATOR
# include "include/secp256k1_generator.h"
#endif
#ifdef ENABLE_MODULE_RANGEPROOF
# include "include/secp256k1_rangeproof.h"
# include "modules/rangeproof/pedersen.h"
# include "modules/rangeproof/rangeproof.h"
#endif
#define ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
secp256k1_callback_call(&ctx->illegal_callback, #cond); \
@ -586,3 +596,19 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
#ifdef ENABLE_MODULE_RECOVERY
# include "modules/recovery/main_impl.h"
#endif
#ifdef ENABLE_MODULE_GENERATOR
# include "modules/generator/main_impl.h"
#endif
#ifdef ENABLE_MODULE_RANGEPROOF
# include "modules/rangeproof/main_impl.h"
#endif
#ifdef ENABLE_MODULE_WHITELIST
# include "modules/whitelist/main_impl.h"
#endif
#ifdef ENABLE_MODULE_SURJECTIONPROOF
# include "modules/surjection/main_impl.h"
#endif

View File

@ -35,4 +35,7 @@ static void secp256k1_rand256_test(unsigned char *b32);
/** Generate pseudorandom bytes with long sequences of zero and one bits. */
static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
/** Generate a pseudorandom 64-bit integer in the range min..max, inclusive. */
static int64_t secp256k1_rands64(uint64_t min, uint64_t max);
#endif

View File

@ -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.*
**********************************************************************/
@ -107,4 +107,21 @@ static void secp256k1_rand256_test(unsigned char *b32) {
secp256k1_rand_bytes_test(b32, 32);
}
SECP256K1_INLINE static int64_t secp256k1_rands64(uint64_t min, uint64_t max) {
uint64_t range;
uint64_t r;
uint64_t clz;
VERIFY_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;
}
#endif

View File

@ -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.*
**********************************************************************/
@ -134,6 +134,52 @@ void random_scalar_order(secp256k1_scalar *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_pubkey pubkey;
secp256k1_pubkey zero_pubkey;
@ -2405,7 +2451,7 @@ void ecmult_const_random_mult(void) {
0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956
);
secp256k1_gej b;
secp256k1_ecmult_const(&b, &a, &xn);
secp256k1_ecmult_const(&b, &a, &xn, 256);
CHECK(secp256k1_ge_is_valid_var(&a));
ge_equals_gej(&expected_b, &b);
@ -2421,12 +2467,12 @@ void ecmult_const_commutativity(void) {
random_scalar_order_test(&a);
random_scalar_order_test(&b);
secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a);
secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b);
secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a, 256);
secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b, 256);
secp256k1_ge_set_gej(&mid1, &res1);
secp256k1_ge_set_gej(&mid2, &res2);
secp256k1_ecmult_const(&res1, &mid1, &b);
secp256k1_ecmult_const(&res2, &mid2, &a);
secp256k1_ecmult_const(&res1, &mid1, &b, 256);
secp256k1_ecmult_const(&res2, &mid2, &a, 256);
secp256k1_ge_set_gej(&mid1, &res1);
secp256k1_ge_set_gej(&mid2, &res2);
ge_equals_ge(&mid1, &mid2);
@ -2442,13 +2488,13 @@ void ecmult_const_mult_zero_one(void) {
secp256k1_scalar_negate(&negone, &one);
random_group_element_test(&point);
secp256k1_ecmult_const(&res1, &point, &zero);
secp256k1_ecmult_const(&res1, &point, &zero, 3);
secp256k1_ge_set_gej(&res2, &res1);
CHECK(secp256k1_ge_is_infinity(&res2));
secp256k1_ecmult_const(&res1, &point, &one);
secp256k1_ecmult_const(&res1, &point, &one, 2);
secp256k1_ge_set_gej(&res2, &res1);
ge_equals_ge(&res2, &point);
secp256k1_ecmult_const(&res1, &point, &negone);
secp256k1_ecmult_const(&res1, &point, &negone, 256);
secp256k1_gej_neg(&res1, &res1);
secp256k1_ge_set_gej(&res2, &res1);
ge_equals_ge(&res2, &point);
@ -2474,7 +2520,7 @@ void ecmult_const_chain_multiply(void) {
for (i = 0; i < 100; ++i) {
secp256k1_ge tmp;
secp256k1_ge_set_gej(&tmp, &point);
secp256k1_ecmult_const(&point, &tmp, &scalar);
secp256k1_ecmult_const(&point, &tmp, &scalar, 256);
}
secp256k1_ge_set_gej(&res, &point);
ge_equals_gej(&res, &expected_point);
@ -2541,6 +2587,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
int wnaf[256] = {0};
int i;
int skew;
int bits = 256;
secp256k1_scalar num = *number;
secp256k1_scalar_set_int(&x, 0);
@ -2550,10 +2597,11 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
bits = 128;
#endif
skew = secp256k1_wnaf_const(wnaf, num, w);
skew = secp256k1_wnaf_const(wnaf, num, w, bits, 1);
for (i = WNAF_SIZE(w); i >= 0; --i) {
for (i = WNAF_SIZE(bits, w); i >= 0; --i) {
secp256k1_scalar t;
int v = wnaf[i];
CHECK(v != 0); /* check nonzero */
@ -4411,6 +4459,22 @@ void run_ecdsa_openssl(void) {
# include "modules/recovery/tests_impl.h"
#endif
#ifdef ENABLE_MODULE_GENERATOR
# include "modules/generator/tests_impl.h"
#endif
#ifdef ENABLE_MODULE_RANGEPROOF
# include "modules/rangeproof/tests_impl.h"
#endif
#ifdef ENABLE_MODULE_WHITELIST
# include "modules/whitelist/tests_impl.h"
#endif
#ifdef ENABLE_MODULE_SURJECTIONPROOF
# include "modules/surjection/tests_impl.h"
#endif
int main(int argc, char **argv) {
unsigned char seed16[16] = {0};
unsigned char run32[32] = {0};
@ -4463,6 +4527,7 @@ int main(int argc, char **argv) {
run_rand_bits();
run_rand_int();
run_util_tests();
run_sha256_tests();
run_hmac_sha256_tests();
@ -4534,6 +4599,23 @@ int main(int argc, char **argv) {
run_recovery_tests();
#endif
#ifdef ENABLE_MODULE_GENERATOR
run_generator_tests();
#endif
#ifdef ENABLE_MODULE_RANGEPROOF
run_rangeproof_tests();
#endif
#ifdef ENABLE_MODULE_WHITELIST
/* Key whitelisting tests */
run_whitelist_tests();
#endif
#ifdef ENABLE_MODULE_SURJECTIONPROOF
run_surjection_tests();
#endif
secp256k1_rand256(run32);
printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);

View File

@ -174,7 +174,7 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr
ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
if (i > 0) {
secp256k1_ecmult_const(&tmp, &group[i], &ng);
secp256k1_ecmult_const(&tmp, &group[i], &ng, 256);
ge_equals_gej(&group[(i * j) % order], &tmp);
}
}

View File

@ -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.*
**********************************************************************/
@ -76,6 +76,33 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_
return ret;
}
/* 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;
}
/* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT