Compare commits
33 Commits
frost
...
secp256k1-
Author | SHA1 | Date | |
---|---|---|---|
|
84faa7eb03 | ||
|
450e64a427 | ||
|
b2df03da55 | ||
|
911a08f245 | ||
|
9da79c3782 | ||
|
f73e50653a | ||
|
1e2fda1480 | ||
|
4564081e26 | ||
|
619b8f16dc | ||
|
8584c066c7 | ||
|
d5b3440c3b | ||
|
12bd9fd8e5 | ||
|
a809ac484f | ||
|
cb8a394102 | ||
|
0f21e05dfa | ||
|
4f7a50e173 | ||
|
5b72133255 | ||
|
7dd7883de6 | ||
|
0cfa29f87a | ||
|
d8295970d2 | ||
|
5eae1b9793 | ||
|
8454a98521 | ||
|
d702d210b8 | ||
|
d78f12b04e | ||
|
da035050f8 | ||
|
6eebf82d8a | ||
|
4577c24234 | ||
|
e7f7b3c941 | ||
|
9722b11506 | ||
|
9f21e1b518 | ||
|
dc05520096 | ||
|
8de58308d8 | ||
|
f7b48c577d |
16
Makefile.am
16
Makefile.am
@ -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
|
||||
|
83
configure.ac
83
configure.ac
@ -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"
|
||||
|
96
include/secp256k1_generator.h
Normal file
96
include/secp256k1_generator.h
Normal 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
|
299
include/secp256k1_rangeproof.h
Normal file
299
include/secp256k1_rangeproof.h
Normal 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
|
212
include/secp256k1_surjectionproof.h
Normal file
212
include/secp256k1_surjectionproof.h
Normal 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
|
152
include/secp256k1_whitelist.h
Normal file
152
include/secp256k1_whitelist.h
Normal 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
|
51
sage/shallue_van_de_woestijne.sage
Normal file
51
sage/shallue_van_de_woestijne.sage
Normal 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
59
src/bench_generator.c
Normal 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;
|
||||
}
|
@ -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
63
src/bench_rangeproof.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
9
src/modules/generator/Makefile.am.include
Normal file
9
src/modules/generator/Makefile.am.include
Normal 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
|
206
src/modules/generator/main_impl.h
Normal file
206
src/modules/generator/main_impl.h
Normal 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
|
199
src/modules/generator/tests_impl.h
Normal file
199
src/modules/generator/tests_impl.h
Normal 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
|
15
src/modules/rangeproof/Makefile.am.include
Normal file
15
src/modules/rangeproof/Makefile.am.include
Normal 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
|
24
src/modules/rangeproof/borromean.h
Normal file
24
src/modules/rangeproof/borromean.h
Normal 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
|
204
src/modules/rangeproof/borromean_impl.h
Normal file
204
src/modules/rangeproof/borromean_impl.h
Normal 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
|
278
src/modules/rangeproof/main_impl.h
Normal file
278
src/modules/rangeproof/main_impl.h
Normal 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
|
22
src/modules/rangeproof/pedersen.h
Normal file
22
src/modules/rangeproof/pedersen.h
Normal 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
|
51
src/modules/rangeproof/pedersen_impl.h
Normal file
51
src/modules/rangeproof/pedersen_impl.h
Normal 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
|
21
src/modules/rangeproof/rangeproof.h
Normal file
21
src/modules/rangeproof/rangeproof.h
Normal 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
|
683
src/modules/rangeproof/rangeproof_impl.h
Normal file
683
src/modules/rangeproof/rangeproof_impl.h
Normal 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
|
620
src/modules/rangeproof/tests_impl.h
Normal file
620
src/modules/rangeproof/tests_impl.h
Normal 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
|
6
src/modules/surjection/Makefile.am.include
Normal file
6
src/modules/surjection/Makefile.am.include
Normal 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
|
||||
|
338
src/modules/surjection/main_impl.h
Normal file
338
src/modules/surjection/main_impl.h
Normal 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
|
19
src/modules/surjection/surjection.h
Normal file
19
src/modules/surjection/surjection.h
Normal 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
|
108
src/modules/surjection/surjection.md
Normal file
108
src/modules/surjection/surjection.md
Normal 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.
|
||||
|
||||
|
86
src/modules/surjection/surjection_impl.h
Normal file
86
src/modules/surjection/surjection_impl.h
Normal 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
|
487
src/modules/surjection/tests_impl.h
Normal file
487
src/modules/surjection/tests_impl.h
Normal 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
|
4
src/modules/whitelist/Makefile.am.include
Normal file
4
src/modules/whitelist/Makefile.am.include
Normal 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
|
174
src/modules/whitelist/main_impl.h
Normal file
174
src/modules/whitelist/main_impl.h
Normal 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
|
151
src/modules/whitelist/tests_impl.h
Normal file
151
src/modules/whitelist/tests_impl.h
Normal 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
|
96
src/modules/whitelist/whitelist.md
Normal file
96
src/modules/whitelist/whitelist.md
Normal 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.
|
||||
|
||||
|
129
src/modules/whitelist/whitelist_impl.h
Normal file
129
src/modules/whitelist/whitelist_impl.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
106
src/tests.c
106
src/tests.c
@ -1,5 +1,5 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
|
||||
* Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
@ -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]);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
29
src/util.h
29
src/util.h
@ -1,5 +1,5 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user