diff --git a/.gitignore b/.gitignore index 87fea161ba..55d325aeef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bench_inv bench_ecdh +bench_ecmult bench_sign bench_verify bench_schnorr_verify diff --git a/.travis.yml b/.travis.yml index 2439529242..c4154e9a89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ cache: - src/java/guava/ env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar matrix: - SCALAR=32bit RECOVERY=yes @@ -29,7 +29,7 @@ env: - BUILD=distcheck - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC - EXTRAFLAGS=CFLAGS=-O0 - - BUILD=check-java ECDH=yes EXPERIMENTAL=yes + - BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes matrix: fast_finish: true include: @@ -65,5 +65,5 @@ before_script: ./autogen.sh script: - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD os: linux diff --git a/Makefile.am b/Makefile.am index c071fbe275..ec3c64ccf0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,6 +42,8 @@ noinst_HEADERS += src/field_5x52_asm_impl.h noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h noinst_HEADERS += src/util.h +noinst_HEADERS += src/scratch.h +noinst_HEADERS += src/scratch_impl.h noinst_HEADERS += src/testrand.h noinst_HEADERS += src/testrand_impl.h noinst_HEADERS += src/hash.h @@ -79,7 +81,7 @@ libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES) noinst_PROGRAMS = if USE_BENCHMARK -noinst_PROGRAMS += bench_verify bench_sign bench_internal +noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult bench_verify_SOURCES = src/bench_verify.c bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) bench_sign_SOURCES = src/bench_sign.c @@ -87,6 +89,9 @@ bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) bench_internal_SOURCES = src/bench_internal.c bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) +bench_ecmult_SOURCES = src/bench_ecmult.c +bench_ecmult_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_ecmult_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) endif TESTS = @@ -159,6 +164,7 @@ $(gen_context_BIN): $(gen_context_OBJECTS) $(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h $(tests_OBJECTS): src/ecmult_static_context.h $(bench_internal_OBJECTS): src/ecmult_static_context.h +$(bench_ecmult_OBJECTS): src/ecmult_static_context.h src/ecmult_static_context.h: $(gen_context_BIN) ./$(gen_context_BIN) @@ -175,3 +181,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 diff --git a/build-aux/m4/ax_jni_include_dir.m4 b/build-aux/m4/ax_jni_include_dir.m4 index 1fc3627614..cdc78d87d4 100644 --- a/build-aux/m4/ax_jni_include_dir.m4 +++ b/build-aux/m4/ax_jni_include_dir.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html +# https://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html # =========================================================================== # # SYNOPSIS @@ -44,7 +44,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 10 +#serial 14 AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) AC_DEFUN([AX_JNI_INCLUDE_DIR],[ @@ -66,9 +66,17 @@ else fi case "$host_os" in - darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` - _JINC="$_JTOPDIR/Headers";; - *) _JINC="$_JTOPDIR/include";; + darwin*) # Apple Java headers are inside the Xcode bundle. + macos_version=$(sw_vers -productVersion | sed -n -e 's/^@<:@0-9@:>@*.\(@<:@0-9@:>@*\).@<:@0-9@:>@*/\1/p') + if @<:@ "$macos_version" -gt "7" @:>@; then + _JTOPDIR="$(xcrun --show-sdk-path)/System/Library/Frameworks/JavaVM.framework" + _JINC="$_JTOPDIR/Headers" + else + _JTOPDIR="/System/Library/Frameworks/JavaVM.framework" + _JINC="$_JTOPDIR/Headers" + fi + ;; + *) _JINC="$_JTOPDIR/include";; esac _AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) _AS_ECHO_LOG([_JINC=$_JINC]) @@ -76,30 +84,27 @@ _AS_ECHO_LOG([_JINC=$_JINC]) # On Mac OS X 10.6.4, jni.h is a symlink: # /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h # -> ../../CurrentJDK/Headers/jni.h. - AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path, [ -if test -f "$_JINC/jni.h"; then - ac_cv_jni_header_path="$_JINC" - JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" -else - _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` - if test -f "$_JTOPDIR/include/jni.h"; then - ac_cv_jni_header_path="$_JTOPDIR/include" + if test -f "$_JINC/jni.h"; then + ac_cv_jni_header_path="$_JINC" JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" else - ac_cv_jni_header_path=none + _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + if test -f "$_JTOPDIR/include/jni.h"; then + ac_cv_jni_header_path="$_JTOPDIR/include" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" + else + ac_cv_jni_header_path=none + fi fi -fi ]) - - # get the likely subdirectories for system specific java includes case "$host_os" in bsdi*) _JNI_INC_SUBDIRS="bsdos";; -darwin*) _JNI_INC_SUBDIRS="darwin";; freebsd*) _JNI_INC_SUBDIRS="freebsd";; +darwin*) _JNI_INC_SUBDIRS="darwin";; linux*) _JNI_INC_SUBDIRS="linux genunix";; osf*) _JNI_INC_SUBDIRS="alpha";; solaris*) _JNI_INC_SUBDIRS="solaris";; @@ -112,9 +117,9 @@ if test "x$ac_cv_jni_header_path" != "xnone"; then # add any subdirectories that are present for JINCSUBDIR in $_JNI_INC_SUBDIRS do - if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then - JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" - fi + if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" + fi done fi ]) diff --git a/build-aux/m4/bitcoin_secp.m4 b/build-aux/m4/bitcoin_secp.m4 index b74acb8c13..3b3975cbdd 100644 --- a/build-aux/m4/bitcoin_secp.m4 +++ b/build-aux/m4/bitcoin_secp.m4 @@ -48,7 +48,6 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then EC_KEY_free(eckey); ECDSA_SIG *sig_openssl; sig_openssl = ECDSA_SIG_new(); - (void)sig_openssl->r; ECDSA_SIG_free(sig_openssl); ]])],[has_openssl_ec=yes],[has_openssl_ec=no]) AC_MSG_RESULT([$has_openssl_ec]) diff --git a/configure.ac b/configure.ac index e5fcbcb4ed..c4442eb258 100644 --- a/configure.ac +++ b/configure.ac @@ -85,9 +85,9 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], ]) AC_ARG_ENABLE(benchmark, - AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), + AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]), [use_benchmark=$enableval], - [use_benchmark=no]) + [use_benchmark=yes]) AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), @@ -134,10 +134,30 @@ 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)]), + AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]), [use_jni=$enableval], - [use_jni=auto]) + [use_jni=no]) + +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 @@ -447,6 +495,7 @@ AC_MSG_NOTICE([Using field implementation: $set_field]) AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) +AC_MSG_NOTICE([Building benchmarks: $use_benchmark]) AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) @@ -457,7 +506,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 +533,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 +561,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" diff --git a/include/secp256k1.h b/include/secp256k1.h index 3e9c098d19..f1f78ab7d0 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -42,6 +42,19 @@ extern "C" { */ typedef struct secp256k1_context_struct secp256k1_context; +/** Opaque data structure that holds rewriteable "scratch space" + * + * The purpose of this structure is to replace dynamic memory allocations, + * because we target architectures where this may not be available. It is + * essentially a resizable (within specified parameters) block of bytes, + * which is initially created either by memory allocation or TODO as a pointer + * into some fixed rewritable space. + * + * Unlike the context object, this cannot safely be shared between threads + * without additional synchronization logic. + */ +typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space; + /** Opaque data structure that holds a parsed and valid public key. * * The exact representation of data inside is implementation defined and not @@ -166,6 +179,13 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 #define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 +/** A simple secp256k1 context object with no precomputed tables. These are useful for + * type serialization/parsing functions which require a context object to maintain + * API consistency, but currently do not require expensive precomputations or dynamic + * allocations. + */ +SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp; + /** Create a secp256k1 context object. * * Returns: a newly created context object. @@ -243,6 +263,26 @@ SECP256K1_API void secp256k1_context_set_error_callback( const void* data ) SECP256K1_ARG_NONNULL(1); +/** Create a secp256k1 scratch space object. + * + * Returns: a newly created scratch space. + * Args: ctx: an existing context object (cannot be NULL) + * In: max_size: maximum amount of memory to allocate + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create( + const secp256k1_context* ctx, + size_t max_size +) SECP256K1_ARG_NONNULL(1); + +/** Destroy a secp256k1 scratch space. + * + * The pointer may not be used afterwards. + * Args: scratch: space to destroy + */ +SECP256K1_API void secp256k1_scratch_space_destroy( + secp256k1_scratch_space* scratch +); + /** Parse a variable-length public key into the pubkey object. * * Returns: 1 if the public key was fully valid. @@ -498,7 +538,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( * * Returns: 1 always * Args: ctx: pointer to a context object - * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL) + * In/Out: seckey: pointer to the 32-byte private key to be negated (cannot be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate( const secp256k1_context* ctx, diff --git a/include/secp256k1_ecdh.h b/include/secp256k1_ecdh.h index 88492dc1a4..df5fde235c 100644 --- a/include/secp256k1_ecdh.h +++ b/include/secp256k1_ecdh.h @@ -7,21 +7,45 @@ extern "C" { #endif +/** A pointer to a function that applies hash function to a point + * + * Returns: 1 if a point was successfully hashed. 0 will cause ecdh to fail + * Out: output: pointer to an array to be filled by the function + * In: x: pointer to a 32-byte x coordinate + * y: pointer to a 32-byte y coordinate + * data: Arbitrary data pointer that is passed through + */ +typedef int (*secp256k1_ecdh_hash_function)( + unsigned char *output, + const unsigned char *x, + const unsigned char *y, + void *data +); + +/** An implementation of SHA256 hash function that applies to compressed public key. */ +SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; + +/** A default ecdh hash function (currently equal to secp256k1_ecdh_hash_function_sha256). */ +SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; + /** Compute an EC Diffie-Hellman secret in constant time * Returns: 1: exponentiation was successful * 0: scalar was invalid (zero or overflow) * Args: ctx: pointer to a context object (cannot be NULL) - * Out: result: a 32-byte array which will be populated by an ECDH - * secret computed from the point and scalar + * Out: output: pointer to an array to be filled by the function * In: pubkey: a pointer to a secp256k1_pubkey containing an * initialized public key * privkey: a 32-byte scalar with which to multiply the point + * hashfp: pointer to a hash function. If NULL, secp256k1_ecdh_hash_function_sha256 is used + * data: Arbitrary data pointer that is passed through */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( const secp256k1_context* ctx, - unsigned char *result, + unsigned char *output, const secp256k1_pubkey *pubkey, - const unsigned char *privkey + const unsigned char *privkey, + secp256k1_ecdh_hash_function hashfp, + void *data ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); #ifdef __cplusplus diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h new file mode 100644 index 0000000000..c2743a6e0d --- /dev/null +++ b/include/secp256k1_generator.h @@ -0,0 +1,93 @@ +#ifndef _SECP256K1_GENERATOR_ +# define _SECP256K1_GENERATOR_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** 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 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_generator_serialize and secp256k1_generator_parse. + */ +typedef struct { + unsigned char data[64]; +} 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 successful a valid generator will be placed in gen. The produced + * generators are distributed uniformly over the curve, and will not have a + * known discrete 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 diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h new file mode 100644 index 0000000000..22cc53eb08 --- /dev/null +++ b/include/secp256k1_rangeproof.h @@ -0,0 +1,297 @@ +#ifndef _SECP256K1_RANGEPROOF_ +# define _SECP256K1_RANGEPROOF_ + +# include "secp256k1.h" +# include "secp256k1_generator.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** Opaque data structure that stores a Pedersen commitment + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_pedersen_commitment_serialize and + * secp256k1_pedersen_commitment_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pedersen_commitment; + +/** + * Static constant generator 'h' maintained for historical reasons. + */ +SECP256K1_API extern const secp256k1_generator *secp256k1_generator_h; + +/** Parse a 33-byte commitment into a commitment object. + * + * Returns: 1 if input contains a valid commitment. + * Args: ctx: a secp256k1 context object. + * Out: commit: pointer to the output commitment object + * In: input: pointer to a 33-byte serialized commitment key + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commitment_parse( + const secp256k1_context* ctx, + secp256k1_pedersen_commitment* commit, + const unsigned char *input +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a commitment object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 33-byte byte array + * In: commit: a pointer to a secp256k1_pedersen_commitment containing an + * initialized commitment + */ +SECP256K1_API int secp256k1_pedersen_commitment_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_pedersen_commitment* commit +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** 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 diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h new file mode 100644 index 0000000000..38f67990ff --- /dev/null +++ b/include/secp256k1_surjectionproof.h @@ -0,0 +1,216 @@ +#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. Because the + * maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is + * limited to 256 the probability of giving up is smaller than + * (255/256)^(n_input_tags_to_use*max_n_iterations). + * + * 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 diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h new file mode 100644 index 0000000000..c536c11a7b --- /dev/null +++ b/include/secp256k1_whitelist.h @@ -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 diff --git a/sage/shallue_van_de_woestijne.sage b/sage/shallue_van_de_woestijne.sage new file mode 100644 index 0000000000..1cc97b6556 --- /dev/null +++ b/sage/shallue_van_de_woestijne.sage @@ -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() diff --git a/src/bench.h b/src/bench.h index d5ebe01301..5b59783f68 100644 --- a/src/bench.h +++ b/src/bench.h @@ -8,6 +8,7 @@ #define SECP256K1_BENCH_H #include +#include #include #include "sys/time.h" @@ -63,4 +64,19 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v printf("us\n"); } +int have_flag(int argc, char** argv, char *flag) { + char** argm = argv + argc; + argv++; + if (argv == argm) { + return 1; + } + while (argv != NULL && argv != argm) { + if (strcmp(*argv, flag) == 0) { + return 1; + } + argv++; + } + return 0; +} + #endif /* SECP256K1_BENCH_H */ diff --git a/src/bench_ecdh.c b/src/bench_ecdh.c index cde5e2dbb4..c1dd5a6ac9 100644 --- a/src/bench_ecdh.c +++ b/src/bench_ecdh.c @@ -15,11 +15,11 @@ typedef struct { secp256k1_context *ctx; secp256k1_pubkey point; unsigned char scalar[32]; -} bench_ecdh_t; +} bench_ecdh_data; static void bench_ecdh_setup(void* arg) { int i; - bench_ecdh_t *data = (bench_ecdh_t*)arg; + bench_ecdh_data *data = (bench_ecdh_data*)arg; const unsigned char point[] = { 0x03, 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, @@ -39,15 +39,15 @@ static void bench_ecdh_setup(void* arg) { static void bench_ecdh(void* arg) { int i; unsigned char res[32]; - bench_ecdh_t *data = (bench_ecdh_t*)arg; + bench_ecdh_data *data = (bench_ecdh_data*)arg; for (i = 0; i < 20000; i++) { - CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1); } } int main(void) { - bench_ecdh_t data; + bench_ecdh_data data; run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); return 0; diff --git a/src/bench_ecmult.c b/src/bench_ecmult.c new file mode 100644 index 0000000000..c96f7fb6e4 --- /dev/null +++ b/src/bench_ecmult.c @@ -0,0 +1,196 @@ +/********************************************************************** + * Copyright (c) 2017 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.h" + +#include "util.h" +#include "hash_impl.h" +#include "num_impl.h" +#include "field_impl.h" +#include "group_impl.h" +#include "scalar_impl.h" +#include "ecmult_impl.h" +#include "bench.h" +#include "secp256k1.c" + +#define POINTS 32768 +#define ITERS 10000 + +typedef struct { + /* Setup once in advance */ + secp256k1_context* ctx; + secp256k1_scratch_space* scratch; + secp256k1_scalar* scalars; + secp256k1_ge* pubkeys; + secp256k1_scalar* seckeys; + secp256k1_gej* expected_output; + secp256k1_ecmult_multi_func ecmult_multi; + + /* Changes per test */ + size_t count; + int includes_g; + + /* Changes per test iteration */ + size_t offset1; + size_t offset2; + + /* Test output. */ + secp256k1_gej* output; +} bench_data; + +static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, void* arg) { + bench_data* data = (bench_data*)arg; + if (data->includes_g) ++idx; + if (idx == 0) { + *sc = data->scalars[data->offset1]; + *ge = secp256k1_ge_const_g; + } else { + *sc = data->scalars[(data->offset1 + idx) % POINTS]; + *ge = data->pubkeys[(data->offset2 + idx - 1) % POINTS]; + } + return 1; +} + +static void bench_ecmult(void* arg) { + bench_data* data = (bench_data*)arg; + + size_t count = data->count; + int includes_g = data->includes_g; + size_t iters = 1 + ITERS / count; + size_t iter; + + for (iter = 0; iter < iters; ++iter) { + data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g); + data->offset1 = (data->offset1 + count) % POINTS; + data->offset2 = (data->offset2 + count - 1) % POINTS; + } +} + +static void bench_ecmult_setup(void* arg) { + bench_data* data = (bench_data*)arg; + data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS; + data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS; +} + +static void bench_ecmult_teardown(void* arg) { + bench_data* data = (bench_data*)arg; + size_t iters = 1 + ITERS / data->count; + size_t iter; + /* Verify the results in teardown, to avoid doing comparisons while benchmarking. */ + for (iter = 0; iter < iters; ++iter) { + secp256k1_gej tmp; + secp256k1_gej_add_var(&tmp, &data->output[iter], &data->expected_output[iter], NULL); + CHECK(secp256k1_gej_is_infinity(&tmp)); + } +} + +static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) { + secp256k1_sha256 sha256; + unsigned char c[11] = {'e', 'c', 'm', 'u', 'l', 't', 0, 0, 0, 0}; + unsigned char buf[32]; + int overflow = 0; + c[6] = num; + c[7] = num >> 8; + c[8] = num >> 16; + c[9] = num >> 24; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, c, sizeof(c)); + secp256k1_sha256_finalize(&sha256, buf); + secp256k1_scalar_set_b32(scalar, buf, &overflow); + CHECK(!overflow); +} + +static void run_test(bench_data* data, size_t count, int includes_g) { + char str[32]; + static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + size_t iters = 1 + ITERS / count; + size_t iter; + + data->count = count; + data->includes_g = includes_g; + + /* Compute (the negation of) the expected results directly. */ + data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS; + data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS; + for (iter = 0; iter < iters; ++iter) { + secp256k1_scalar tmp; + secp256k1_scalar total = data->scalars[(data->offset1++) % POINTS]; + size_t i = 0; + for (i = 0; i + 1 < count; ++i) { + secp256k1_scalar_mul(&tmp, &data->seckeys[(data->offset2++) % POINTS], &data->scalars[(data->offset1++) % POINTS]); + secp256k1_scalar_add(&total, &total, &tmp); + } + secp256k1_scalar_negate(&total, &total); + secp256k1_ecmult(&data->ctx->ecmult_ctx, &data->expected_output[iter], NULL, &zero, &total); + } + + /* Run the benchmark. */ + sprintf(str, includes_g ? "ecmult_%ig" : "ecmult_%i", (int)count); + run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * (1 + ITERS / count)); +} + +int main(int argc, char **argv) { + bench_data data; + int i, p; + secp256k1_gej* pubkeys_gej; + size_t scratch_size; + + if (argc > 1) { + if(have_flag(argc, argv, "pippenger_wnaf")) { + printf("Using pippenger_wnaf:\n"); + data.ecmult_multi = secp256k1_ecmult_pippenger_batch_single; + } else if(have_flag(argc, argv, "strauss_wnaf")) { + printf("Using strauss_wnaf:\n"); + data.ecmult_multi = secp256k1_ecmult_strauss_batch_single; + } + } else { + data.ecmult_multi = secp256k1_ecmult_multi_var; + } + + /* Allocate stuff */ + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16; + data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size); + data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS); + data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS); + data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS); + data.expected_output = malloc(sizeof(secp256k1_gej) * (ITERS + 1)); + data.output = malloc(sizeof(secp256k1_gej) * (ITERS + 1)); + + /* Generate a set of scalars, and private/public keypairs. */ + pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS); + secp256k1_gej_set_ge(&pubkeys_gej[0], &secp256k1_ge_const_g); + secp256k1_scalar_set_int(&data.seckeys[0], 1); + for (i = 0; i < POINTS; ++i) { + generate_scalar(i, &data.scalars[i]); + if (i) { + secp256k1_gej_double_var(&pubkeys_gej[i], &pubkeys_gej[i - 1], NULL); + secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]); + } + } + secp256k1_ge_set_all_gej_var(data.pubkeys, pubkeys_gej, POINTS); + free(pubkeys_gej); + + for (i = 1; i <= 8; ++i) { + run_test(&data, i, 1); + } + + for (p = 0; p <= 11; ++p) { + for (i = 9; i <= 16; ++i) { + run_test(&data, i << p, 1); + } + } + secp256k1_context_destroy(data.ctx); + secp256k1_scratch_space_destroy(data.scratch); + free(data.scalars); + free(data.pubkeys); + free(data.seckeys); + free(data.output); + free(data.expected_output); + + return(0); +} diff --git a/src/bench_generator.c b/src/bench_generator.c new file mode 100644 index 0000000000..7d7bb4de07 --- /dev/null +++ b/src/bench_generator.c @@ -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 +#include + +#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; +} diff --git a/src/bench_internal.c b/src/bench_internal.c index 0809f77bda..9c0a07fbbd 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -25,10 +25,10 @@ typedef struct { secp256k1_gej gej_x, gej_y; unsigned char data[64]; int wnaf[256]; -} bench_inv_t; +} bench_inv; void bench_setup(void* arg) { - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; static const unsigned char init_x[32] = { 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, @@ -58,7 +58,7 @@ void bench_setup(void* arg) { void bench_scalar_add(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); @@ -67,7 +67,7 @@ void bench_scalar_add(void* arg) { void bench_scalar_negate(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); @@ -76,7 +76,7 @@ void bench_scalar_negate(void* arg) { void bench_scalar_sqr(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); @@ -85,7 +85,7 @@ void bench_scalar_sqr(void* arg) { void bench_scalar_mul(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); @@ -95,7 +95,7 @@ void bench_scalar_mul(void* arg) { #ifdef USE_ENDOMORPHISM void bench_scalar_split(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_scalar l, r; @@ -107,7 +107,7 @@ void bench_scalar_split(void* arg) { void bench_scalar_inverse(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000; i++) { secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); @@ -117,7 +117,7 @@ void bench_scalar_inverse(void* arg) { void bench_scalar_inverse_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000; i++) { secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); @@ -127,7 +127,7 @@ void bench_scalar_inverse_var(void* arg) { void bench_field_normalize(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_fe_normalize(&data->fe_x); @@ -136,7 +136,7 @@ void bench_field_normalize(void* arg) { void bench_field_normalize_weak(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 2000000; i++) { secp256k1_fe_normalize_weak(&data->fe_x); @@ -145,7 +145,7 @@ void bench_field_normalize_weak(void* arg) { void bench_field_mul(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y); @@ -154,7 +154,7 @@ void bench_field_mul(void* arg) { void bench_field_sqr(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_fe_sqr(&data->fe_x, &data->fe_x); @@ -163,7 +163,7 @@ void bench_field_sqr(void* arg) { void bench_field_inverse(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_fe_inv(&data->fe_x, &data->fe_x); @@ -173,7 +173,7 @@ void bench_field_inverse(void* arg) { void bench_field_inverse_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_fe_inv_var(&data->fe_x, &data->fe_x); @@ -183,7 +183,7 @@ void bench_field_inverse_var(void* arg) { void bench_field_sqrt(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); @@ -193,7 +193,7 @@ void bench_field_sqrt(void* arg) { void bench_group_double_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); @@ -202,7 +202,7 @@ void bench_group_double_var(void* arg) { void bench_group_add_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL); @@ -211,7 +211,7 @@ void bench_group_add_var(void* arg) { void bench_group_add_affine(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y); @@ -220,7 +220,7 @@ void bench_group_add_affine(void* arg) { void bench_group_add_affine_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 200000; i++) { secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL); @@ -229,7 +229,7 @@ void bench_group_add_affine_var(void* arg) { void bench_group_jacobi_var(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_gej_has_quad_y_var(&data->gej_x); @@ -238,7 +238,7 @@ void bench_group_jacobi_var(void* arg) { void bench_ecmult_wnaf(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; for (i = 0; i < 20000; i++) { secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); @@ -248,10 +248,10 @@ void bench_ecmult_wnaf(void* arg) { void bench_wnaf_const(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)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); secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); } } @@ -259,8 +259,8 @@ void bench_wnaf_const(void* arg) { void bench_sha256(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; - secp256k1_sha256_t sha; + bench_inv *data = (bench_inv*)arg; + secp256k1_sha256 sha; for (i = 0; i < 20000; i++) { secp256k1_sha256_initialize(&sha); @@ -271,8 +271,8 @@ void bench_sha256(void* arg) { void bench_hmac_sha256(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; - secp256k1_hmac_sha256_t hmac; + bench_inv *data = (bench_inv*)arg; + secp256k1_hmac_sha256 hmac; for (i = 0; i < 20000; i++) { secp256k1_hmac_sha256_initialize(&hmac, data->data, 32); @@ -283,8 +283,8 @@ void bench_hmac_sha256(void* arg) { void bench_rfc6979_hmac_sha256(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; - secp256k1_rfc6979_hmac_sha256_t rng; + bench_inv *data = (bench_inv*)arg; + secp256k1_rfc6979_hmac_sha256 rng; for (i = 0; i < 20000; i++) { secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64); @@ -311,7 +311,7 @@ void bench_context_sign(void* arg) { #ifndef USE_NUM_NONE void bench_num_jacobi(void* arg) { int i; - bench_inv_t *data = (bench_inv_t*)arg; + bench_inv *data = (bench_inv*)arg; secp256k1_num nx, norder; secp256k1_scalar_get_num(&nx, &data->scalar_x); @@ -324,23 +324,8 @@ void bench_num_jacobi(void* arg) { } #endif -int have_flag(int argc, char** argv, char *flag) { - char** argm = argv + argc; - argv++; - if (argv == argm) { - return 1; - } - while (argv != NULL && argv != argm) { - if (strcmp(*argv, flag) == 0) { - return 1; - } - argv++; - } - return 0; -} - int main(int argc, char **argv) { - bench_inv_t data; + bench_inv data; if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000); if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000); if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000); diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c new file mode 100644 index 0000000000..9724f5b46d --- /dev/null +++ b/src/bench_rangeproof.c @@ -0,0 +1,63 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1_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; +} diff --git a/src/bench_recover.c b/src/bench_recover.c index 6489378cc6..b806eed94e 100644 --- a/src/bench_recover.c +++ b/src/bench_recover.c @@ -13,11 +13,11 @@ typedef struct { secp256k1_context *ctx; unsigned char msg[32]; unsigned char sig[64]; -} bench_recover_t; +} bench_recover_data; void bench_recover(void* arg) { int i; - bench_recover_t *data = (bench_recover_t*)arg; + bench_recover_data *data = (bench_recover_data*)arg; secp256k1_pubkey pubkey; unsigned char pubkeyc[33]; @@ -38,7 +38,7 @@ void bench_recover(void* arg) { void bench_recover_setup(void* arg) { int i; - bench_recover_t *data = (bench_recover_t*)arg; + bench_recover_data *data = (bench_recover_data*)arg; for (i = 0; i < 32; i++) { data->msg[i] = 1 + i; @@ -49,7 +49,7 @@ void bench_recover_setup(void* arg) { } int main(void) { - bench_recover_t data; + bench_recover_data data; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); diff --git a/src/bench_sign.c b/src/bench_sign.c index ed7224d757..544b43963c 100644 --- a/src/bench_sign.c +++ b/src/bench_sign.c @@ -12,11 +12,11 @@ typedef struct { secp256k1_context* ctx; unsigned char msg[32]; unsigned char key[32]; -} bench_sign_t; +} bench_sign; static void bench_sign_setup(void* arg) { int i; - bench_sign_t *data = (bench_sign_t*)arg; + bench_sign *data = (bench_sign*)arg; for (i = 0; i < 32; i++) { data->msg[i] = i + 1; @@ -26,9 +26,9 @@ static void bench_sign_setup(void* arg) { } } -static void bench_sign(void* arg) { +static void bench_sign_run(void* arg) { int i; - bench_sign_t *data = (bench_sign_t*)arg; + bench_sign *data = (bench_sign*)arg; unsigned char sig[74]; for (i = 0; i < 20000; i++) { @@ -45,11 +45,11 @@ static void bench_sign(void* arg) { } int main(void) { - bench_sign_t data; + bench_sign data; data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000); + run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000); secp256k1_context_destroy(data.ctx); return 0; diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c new file mode 100644 index 0000000000..d011e9214f --- /dev/null +++ b/src/bench_whitelist.c @@ -0,0 +1,104 @@ +/********************************************************************** + * Copyright (c) 2017 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.h" + +#include "include/secp256k1_whitelist.h" +#include "bench.h" +#include "util.h" +#include "hash_impl.h" +#include "num_impl.h" +#include "scalar_impl.h" +#include "testrand_impl.h" + +#define MAX_N_KEYS 30 + +typedef struct { + secp256k1_context* ctx; + unsigned char online_seckey[MAX_N_KEYS][32]; + unsigned char summed_seckey[MAX_N_KEYS][32]; + secp256k1_pubkey online_pubkeys[MAX_N_KEYS]; + secp256k1_pubkey offline_pubkeys[MAX_N_KEYS]; + unsigned char csub[32]; + secp256k1_pubkey sub_pubkey; + secp256k1_whitelist_signature sig; + size_t n_keys; +} bench_data; + +static void bench_whitelist(void* arg) { + bench_data* data = (bench_data*)arg; + CHECK(secp256k1_whitelist_verify(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey) == 1); +} + +static void bench_whitelist_setup(void* arg) { + bench_data* data = (bench_data*)arg; + int i = 0; + CHECK(secp256k1_whitelist_sign(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey, data->online_seckey[i], data->summed_seckey[i], i, NULL, NULL)); +} + +static void run_test(bench_data* data) { + char str[32]; + sprintf(str, "whitelist_%i", (int)data->n_keys); + run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, 1); +} + +void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +int main(void) { + bench_data data; + size_t i; + size_t n_keys = 30; + secp256k1_scalar ssub; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + /* Start with subkey */ + random_scalar_order(&ssub); + secp256k1_scalar_get_b32(data.csub, &ssub); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.csub) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.sub_pubkey, data.csub) == 1); + /* Then offline and online whitelist keys */ + for (i = 0; i < n_keys; i++) { + secp256k1_scalar son, soff; + + /* Create two keys */ + random_scalar_order(&son); + secp256k1_scalar_get_b32(data.online_seckey[i], &son); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.online_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.online_pubkeys[i], data.online_seckey[i]) == 1); + + random_scalar_order(&soff); + secp256k1_scalar_get_b32(data.summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &data.offline_pubkeys[i], data.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(data.summed_seckey[i], &soff); + CHECK(secp256k1_ec_seckey_verify(data.ctx, data.summed_seckey[i]) == 1); + } + + /* Run test */ + for (i = 1; i <= n_keys; ++i) { + data.n_keys = i; + run_test(&data); + } + + secp256k1_context_destroy(data.ctx); + return(0); +} diff --git a/src/ecmult.h b/src/ecmult.h index 6d44aba60b..ea1cd8a21f 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -9,6 +9,8 @@ #include "num.h" #include "group.h" +#include "scalar.h" +#include "scratch.h" typedef struct { /* For accelerating the computation of a*P + b*G: */ @@ -28,4 +30,18 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx /** Double multiply: R = na*A + ng*G */ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); +typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data); + +/** + * Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai. + * Chooses the right algorithm for a given number of points and scratch space + * size. Resets and overwrites the given scratch space. If the points do not + * fit in the scratch space the algorithm is repeatedly run with batches of + * points. + * Returns: 1 on success (including when inp_g_sc is NULL and n is 0) + * 0 if there is not enough scratch space for a single point or + * callback returns 0 + */ +static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); + #endif /* SECP256K1_ECMULT_H */ diff --git a/src/ecmult_const.h b/src/ecmult_const.h index 72bf7d7582..d4804b8b68 100644 --- a/src/ecmult_const.h +++ b/src/ecmult_const.h @@ -10,6 +10,8 @@ #include "scalar.h" #include "group.h" -static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); +/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus + * one because we internally sometimes add 2 to the number during the WNAF conversion. */ +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); #endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h index 7d7a172b7b..8411752eb0 100644 --- a/src/ecmult_const_impl.h +++ b/src/ecmult_const_impl.h @@ -12,13 +12,6 @@ #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)) - /* This is like `ECMULT_TABLE_GET_GE` but is constant time */ #define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ int m; \ @@ -55,7 +48,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 global_sign; int skew = 0; int word = 0; @@ -74,9 +67,14 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { * and we'd lose any performance benefit. Instead, we use a technique from * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) * 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 */ + * this, and having the caller compensate after doing the multiplication. + * + * In fact, we _do_ want to negate numbers to minimize their bit-lengths (and in + * particular, to ensure that the outputs from the endomorphism-split fit into + * 128 bits). If we negate, the parity of our number flips, inverting which of + * {1, 2} we want to add to the scalar when ensuring that it's odd. Further + * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and + * we need to special-case it in this logic. */ 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); @@ -95,7 +93,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { /* 4 */ u_last = secp256k1_scalar_shr_int(&s, w); - while (word * w < WNAF_BITS) { + while (word * w < size) { int sign; int even; @@ -115,37 +113,44 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { wnaf[word] = u * global_sign; VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); - VERIFY_CHECK(word == WNAF_SIZE(w)); + VERIFY_CHECK(word == WNAF_SIZE_BITS(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 skew_lam; secp256k1_scalar q_1, q_lam; #endif + int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; int i; secp256k1_scalar sc = *scalar; /* build wnaf representation for q. */ + int rsize = size; +#ifdef USE_ENDOMORPHISM + 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); + skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128); + } else +#endif + { + skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, 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); + skew_lam = 0; #endif + } /* Calculate odd multiples of a. * All multiples are brought to the same Z 'denominator', which is stored @@ -159,26 +164,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_BITS(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_BITS(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_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) { int n; int j; for (j = 0; j < WINDOW_A - 1; ++j) { @@ -190,10 +199,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 } @@ -213,14 +224,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 */ @@ -229,10 +244,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 } } diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 9615b932dd..d64505dc00 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -77,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); } } - secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb); + secp256k1_ge_set_all_gej_var(prec, precj, 1024); } for (j = 0; j < 64; j++) { for (i = 0; i < 16; i++) { @@ -161,7 +161,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_gej gb; secp256k1_fe s; unsigned char nonce32[32]; - secp256k1_rfc6979_hmac_sha256_t rng; + secp256k1_rfc6979_hmac_sha256 rng; int retry; unsigned char keydata[64] = {0}; if (seed32 == NULL) { diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 93d3794cb4..c00578beda 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -1,13 +1,14 @@ -/********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ +/***************************************************************************** + * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php. * + *****************************************************************************/ #ifndef SECP256K1_ECMULT_IMPL_H #define SECP256K1_ECMULT_IMPL_H #include +#include #include "group.h" #include "scalar.h" @@ -41,9 +42,36 @@ #endif #endif +#ifdef USE_ENDOMORPHISM + #define WNAF_BITS 128 +#else + #define WNAF_BITS 256 +#endif +#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w)) +#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w) + /** The number of entries a table with precomputed multiples needs to have. */ #define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) +/* The number of objects allocated on the scratch space for ecmult_multi algorithms */ +#define PIPPENGER_SCRATCH_OBJECTS 6 +#define STRAUSS_SCRATCH_OBJECTS 6 + +#define PIPPENGER_MAX_BUCKET_WINDOW 12 + +/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */ +#ifdef USE_ENDOMORPHISM + #define ECMULT_PIPPENGER_THRESHOLD 88 +#else + #define ECMULT_PIPPENGER_THRESHOLD 160 +#endif + +#ifdef USE_ENDOMORPHISM + #define ECMULT_MAX_POINTS_PER_BATCH 5000000 +#else + #define ECMULT_MAX_POINTS_PER_BATCH 10000000 +#endif + /** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. @@ -109,24 +137,135 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); } -static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) { - secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n); - secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n); - secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n); +static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) { + secp256k1_gej d; + secp256k1_ge d_ge, p_ge; + secp256k1_gej pj; + secp256k1_fe zi; + secp256k1_fe zr; + secp256k1_fe dx_over_dz_squared; int i; - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(n, prej, zr, a); - /* Convert them in batch to affine coordinates. */ - secp256k1_ge_set_table_gej_var(prea, prej, zr, n); - /* Convert them to compact storage form. */ - for (i = 0; i < n; i++) { - secp256k1_ge_to_storage(&pre[i], &prea[i]); + VERIFY_CHECK(!a->infinity); + + secp256k1_gej_double_var(&d, a, NULL); + + /* First, we perform all the additions in an isomorphic curve obtained by multiplying + * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use + * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store + * the resulting y-coordinate and the z-ratio, since we only have enough memory to + * store two field elements. These are sufficient to efficiently undo the isomorphism + * and recompute all the `x`s. + */ + d_ge.x = d.x; + d_ge.y = d.y; + d_ge.infinity = 0; + + secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z); + pj.x = p_ge.x; + pj.y = p_ge.y; + pj.z = a->z; + pj.infinity = 0; + + for (i = 0; i < (n - 1); i++) { + secp256k1_fe_normalize_var(&pj.y); + secp256k1_fe_to_storage(&pre[i].y, &pj.y); + secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr); + secp256k1_fe_normalize_var(&zr); + secp256k1_fe_to_storage(&pre[i].x, &zr); } - free(prea); - free(prej); - free(zr); + /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */ + secp256k1_fe_mul(&zi, &pj.z, &d.z); + secp256k1_fe_inv_var(&zi, &zi); + + /* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so + * that we can combine it with the saved z-ratios to compute the other zs + * without any more inversions. */ + secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi); + secp256k1_ge_to_storage(&pre[n - 1], &p_ge); + + /* Compute the actual x-coordinate of D, which will be needed below. */ + secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */ + secp256k1_fe_sqr(&dx_over_dz_squared, &d.z); + secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x); + + /* Going into the second loop, we have set `pre[n-1]` to its final affine + * form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We + * have `zi = (p.z * d.z)^-1`, where + * + * `p.z` is the z-coordinate of the point on the isomorphic curve + * which was ultimately assigned to `pre[n-1]`. + * `d.z` is the multiplier that must be applied to all z-coordinates + * to move from our isomorphic curve back to secp256k1; so the + * product `p.z * d.z` is the z-coordinate of the secp256k1 + * point assigned to `pre[n-1]`. + * + * All subsequent inverse-z-coordinates can be obtained by multiplying this + * factor by successive z-ratios, which is much more efficient than directly + * computing each one. + * + * Importantly, these inverse-zs will be coordinates of points on secp256k1, + * while our other stored values come from computations on the isomorphic + * curve. So in the below loop, we will take care not to actually use `zi` + * or any derived values until we're back on secp256k1. + */ + i = n - 1; + while (i > 0) { + secp256k1_fe zi2, zi3; + const secp256k1_fe *rzr; + i--; + + secp256k1_ge_from_storage(&p_ge, &pre[i]); + + /* For each remaining point, we extract the z-ratio from the stored + * x-coordinate, compute its z^-1 from that, and compute the full + * point from that. */ + rzr = &p_ge.x; + secp256k1_fe_mul(&zi, &zi, rzr); + secp256k1_fe_sqr(&zi2, &zi); + secp256k1_fe_mul(&zi3, &zi2, &zi); + /* To compute the actual x-coordinate, we use the stored z ratio and + * y-coordinate, which we obtained from `secp256k1_gej_add_ge_var` + * in the loop above, as well as the inverse of the square of its + * z-coordinate. We store the latter in the `zi2` variable, which is + * computed iteratively starting from the overall Z inverse then + * multiplying by each z-ratio in turn. + * + * Denoting the z-ratio as `rzr`, we observe that it is equal to `h` + * from the inside of the above `gej_add_ge_var` call. This satisfies + * + * rzr = d_x * z^2 - x * d_z^2 + * + * where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)` + * are Jacobian coordinates of our desired point -- except both are on + * the isomorphic curve that we were using when we called `gej_add_ge_var`. + * To get back to secp256k1, we must multiply both `z`s by `d_z`, or + * equivalently divide both `x`s by `d_z^2`. Our equation then becomes + * + * rzr = d_x * z^2 / d_z^2 - x + * + * (The left-hand-side, being a ratio of z-coordinates, is unaffected + * by the isomorphism.) + * + * Rearranging to solve for `x`, we have + * + * x = d_x * z^2 / d_z^2 - rzr + * + * But what we actually want is the affine coordinate `X = x/z^2`, + * which will satisfy + * + * X = d_x / d_z^2 - rzr / z^2 + * = dx_over_dz_squared - rzr * zi2 + */ + secp256k1_fe_mul(&p_ge.x, rzr, &zi2); + secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1); + secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared); + /* y is stored_y/z^3, as we expect */ + secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3); + /* Store */ + secp256k1_ge_to_storage(&pre[i], &p_ge); + } } /** The following two macro retrieves a particular odd multiple from a table @@ -174,7 +313,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); /* precompute the tables with odd multiples */ - secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj); #ifdef USE_ENDOMORPHISM { @@ -188,7 +327,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const for (i = 0; i < 128; i++) { secp256k1_gej_double_var(&g_128j, &g_128j, NULL); } - secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j); } #endif } @@ -283,50 +422,78 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, return last_set_bit + 1; } -static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { - secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_ge tmpa; - secp256k1_fe Z; +struct secp256k1_strauss_point_state { #ifdef USE_ENDOMORPHISM - secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_scalar na_1, na_lam; - /* Splitted G factors. */ - secp256k1_scalar ng_1, ng_128; int wnaf_na_1[130]; int wnaf_na_lam[130]; int bits_na_1; int bits_na_lam; - int wnaf_ng_1[129]; - int bits_ng_1; - int wnaf_ng_128[129]; - int bits_ng_128; #else int wnaf_na[256]; int bits_na; +#endif + size_t input_pos; +}; + +struct secp256k1_strauss_state { + secp256k1_gej* prej; + secp256k1_fe* zr; + secp256k1_ge* pre_a; +#ifdef USE_ENDOMORPHISM + secp256k1_ge* pre_a_lam; +#endif + struct secp256k1_strauss_point_state* ps; +}; + +static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, int num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_ge tmpa; + secp256k1_fe Z; +#ifdef USE_ENDOMORPHISM + /* Splitted G factors. */ + secp256k1_scalar ng_1, ng_128; + int wnaf_ng_1[129]; + int bits_ng_1 = 0; + int wnaf_ng_128[129]; + int bits_ng_128 = 0; +#else int wnaf_ng[256]; - int bits_ng; + int bits_ng = 0; #endif int i; - int bits; + int bits = 0; + int np; + int no = 0; + for (np = 0; np < num; ++np) { + if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) { + continue; + } + state->ps[no].input_pos = np; #ifdef USE_ENDOMORPHISM - /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&na_1, &na_lam, na); - - /* build wnaf representation for na_1 and na_lam. */ - bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A); - bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A); - VERIFY_CHECK(bits_na_1 <= 130); - VERIFY_CHECK(bits_na_lam <= 130); - bits = bits_na_1; - if (bits_na_lam > bits) { - bits = bits_na_lam; - } + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]); + + /* build wnaf representation for na_1 and na_lam. */ + state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 130, &state->ps[no].na_1, WINDOW_A); + state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 130, &state->ps[no].na_lam, WINDOW_A); + VERIFY_CHECK(state->ps[no].bits_na_1 <= 130); + VERIFY_CHECK(state->ps[no].bits_na_lam <= 130); + if (state->ps[no].bits_na_1 > bits) { + bits = state->ps[no].bits_na_1; + } + if (state->ps[no].bits_na_lam > bits) { + bits = state->ps[no].bits_na_lam; + } #else - /* build wnaf representation for na. */ - bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A); - bits = bits_na; + /* build wnaf representation for na. */ + state->ps[no].bits_na = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na, 256, &na[np], WINDOW_A); + if (state->ps[no].bits_na > bits) { + bits = state->ps[no].bits_na; + } #endif + ++no; + } /* Calculate odd multiples of a. * All multiples are brought to the same Z 'denominator', which is stored @@ -338,29 +505,51 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same * isomorphism to efficiently add with a known Z inverse. */ - secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a); + if (no > 0) { + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]); + for (np = 1; np < no; ++np) { + secp256k1_gej tmp = a[state->ps[np].input_pos]; +#ifdef VERIFY + secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); +#endif + secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp); + secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); + } + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr); + } else { + secp256k1_fe_set_int(&Z, 1); + } #ifdef USE_ENDOMORPHISM - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + for (np = 0; np < no; ++np) { + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]); + } } - /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ - secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + if (ng) { + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); - /* Build wnaf representation for ng_1 and ng_128 */ - bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); - bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); - if (bits_ng_1 > bits) { - bits = bits_ng_1; - } - if (bits_ng_128 > bits) { - bits = bits_ng_128; + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) { + bits = bits_ng_1; + } + if (bits_ng_128 > bits) { + bits = bits_ng_128; + } } #else - bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); - if (bits_ng > bits) { - bits = bits_ng; + if (ng) { + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); + if (bits_ng > bits) { + bits = bits_ng; + } } #endif @@ -370,13 +559,15 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej int n; secp256k1_gej_double_var(r, r, NULL); #ifdef USE_ENDOMORPHISM - if (i < bits_na_1 && (n = wnaf_na_1[i])) { - ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); - } - if (i < bits_na_lam && (n = wnaf_na_lam[i])) { - ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + for (np = 0; np < no; ++np) { + if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) { + ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) { + ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } } if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); @@ -387,9 +578,11 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } #else - if (i < bits_na && (n = wnaf_na[i])) { - ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + for (np = 0; np < no; ++np) { + if (i < state->ps[np].bits_na && (n = state->ps[np].wnaf_na[i])) { + ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } } if (i < bits_ng && (n = wnaf_ng[i])) { ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); @@ -403,4 +596,543 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej } } +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + struct secp256k1_strauss_point_state ps[1]; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; +#endif + struct secp256k1_strauss_state state; + + state.prej = prej; + state.zr = zr; + state.pre_a = pre_a; +#ifdef USE_ENDOMORPHISM + state.pre_a_lam = pre_a_lam; +#endif + state.ps = ps; + secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng); +} + +static size_t secp256k1_strauss_scratch_size(size_t n_points) { +#ifdef USE_ENDOMORPHISM + static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); +#else + static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); +#endif + return n_points*point_size; +} + +static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { + secp256k1_gej* points; + secp256k1_scalar* scalars; + struct secp256k1_strauss_state state; + size_t i; + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n_points == 0) { + return 1; + } + + if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) { + return 0; + } + points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej)); + scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar)); + state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); + state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); +#ifdef USE_ENDOMORPHISM + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A); +#else + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); +#endif + state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); + + for (i = 0; i < n_points; i++) { + secp256k1_ge point; + if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_gej_set_ge(&points[i], &point); + } + secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc); + secp256k1_scratch_deallocate_frame(scratch); + return 1; +} + +/* Wrapper for secp256k1_ecmult_multi_func interface */ +static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); +} + +static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) { + return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); +} + +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: + * - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w) + * - the number of words set is always WNAF_SIZE(w) + * - the returned skew is 0 or 1 + */ +static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) { + int skew = 0; + int pos; + int max_pos; + int last_w; + const secp256k1_scalar *work = s; + + if (secp256k1_scalar_is_zero(s)) { + for (pos = 0; pos < WNAF_SIZE(w); pos++) { + wnaf[pos] = 0; + } + return 0; + } + + if (secp256k1_scalar_is_even(s)) { + skew = 1; + } + + wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew; + /* Compute last window size. Relevant when window size doesn't divide the + * number of bits in the scalar */ + last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w; + + /* Store the position of the first nonzero word in max_pos to allow + * skipping leading zeros when calculating the wnaf. */ + for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) { + int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); + if(val != 0) { + break; + } + wnaf[pos] = 0; + } + max_pos = pos; + pos = 1; + + while (pos <= max_pos) { + int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); + if ((val & 1) == 0) { + wnaf[pos - 1] -= (1 << w); + wnaf[pos] = (val + 1); + } else { + wnaf[pos] = val; + } + /* Set a coefficient to zero if it is 1 or -1 and the proceeding digit + * is strictly negative or strictly positive respectively. Only change + * coefficients at previous positions because above code assumes that + * wnaf[pos - 1] is odd. + */ + if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) { + if (wnaf[pos - 1] == 1) { + wnaf[pos - 2] += 1 << w; + } else { + wnaf[pos - 2] -= 1 << w; + } + wnaf[pos - 1] = 0; + } + ++pos; + } + + return skew; +} + +struct secp256k1_pippenger_point_state { + int skew_na; + size_t input_pos; +}; + +struct secp256k1_pippenger_state { + int *wnaf_na; + struct secp256k1_pippenger_point_state* ps; +}; + +/* + * pippenger_wnaf computes the result of a multi-point multiplication as + * follows: The scalars are brought into wnaf with n_wnaf elements each. Then + * for every i < n_wnaf, first each point is added to a "bucket" corresponding + * to the point's wnaf[i]. Second, the buckets are added together such that + * r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ... + */ +static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) { + size_t n_wnaf = WNAF_SIZE(bucket_window+1); + size_t np; + size_t no = 0; + int i; + int j; + + for (np = 0; np < num; ++np) { + if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) { + continue; + } + state->ps[no].input_pos = np; + state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1); + no++; + } + secp256k1_gej_set_infinity(r); + + if (no == 0) { + return 1; + } + + for (i = n_wnaf - 1; i >= 0; i--) { + secp256k1_gej running_sum; + + for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) { + secp256k1_gej_set_infinity(&buckets[j]); + } + + for (np = 0; np < no; ++np) { + int n = state->wnaf_na[np*n_wnaf + i]; + struct secp256k1_pippenger_point_state point_state = state->ps[np]; + secp256k1_ge tmp; + int idx; + + if (i == 0) { + /* correct for wnaf skew */ + int skew = point_state.skew_na; + if (skew) { + secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); + secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL); + } + } + if (n > 0) { + idx = (n - 1)/2; + secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL); + } else if (n < 0) { + idx = -(n + 1)/2; + secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); + secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL); + } + } + + for(j = 0; j < bucket_window; j++) { + secp256k1_gej_double_var(r, r, NULL); + } + + secp256k1_gej_set_infinity(&running_sum); + /* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ... + * = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ... + * + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...) + * using an intermediate running sum: + * running_sum = bucket[0] + bucket[1] + bucket[2] + ... + * + * The doubling is done implicitly by deferring the final window doubling (of 'r'). + */ + for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) { + secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL); + secp256k1_gej_add_var(r, r, &running_sum, NULL); + } + + secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL); + secp256k1_gej_double_var(r, r, NULL); + secp256k1_gej_add_var(r, r, &running_sum, NULL); + } + return 1; +} + +/** + * Returns optimal bucket_window (number of bits of a scalar represented by a + * set of buckets) for a given number of points. + */ +static int secp256k1_pippenger_bucket_window(size_t n) { +#ifdef USE_ENDOMORPHISM + if (n <= 1) { + return 1; + } else if (n <= 4) { + return 2; + } else if (n <= 20) { + return 3; + } else if (n <= 57) { + return 4; + } else if (n <= 136) { + return 5; + } else if (n <= 235) { + return 6; + } else if (n <= 1260) { + return 7; + } else if (n <= 4420) { + return 9; + } else if (n <= 7880) { + return 10; + } else if (n <= 16050) { + return 11; + } else { + return PIPPENGER_MAX_BUCKET_WINDOW; + } +#else + if (n <= 1) { + return 1; + } else if (n <= 11) { + return 2; + } else if (n <= 45) { + return 3; + } else if (n <= 100) { + return 4; + } else if (n <= 275) { + return 5; + } else if (n <= 625) { + return 6; + } else if (n <= 1850) { + return 7; + } else if (n <= 3400) { + return 8; + } else if (n <= 9630) { + return 9; + } else if (n <= 17900) { + return 10; + } else if (n <= 32800) { + return 11; + } else { + return PIPPENGER_MAX_BUCKET_WINDOW; + } +#endif +} + +/** + * Returns the maximum optimal number of points for a bucket_window. + */ +static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) { + switch(bucket_window) { +#ifdef USE_ENDOMORPHISM + case 1: return 1; + case 2: return 4; + case 3: return 20; + case 4: return 57; + case 5: return 136; + case 6: return 235; + case 7: return 1260; + case 8: return 1260; + case 9: return 4420; + case 10: return 7880; + case 11: return 16050; + case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX; +#else + case 1: return 1; + case 2: return 11; + case 3: return 45; + case 4: return 100; + case 5: return 275; + case 6: return 625; + case 7: return 1850; + case 8: return 3400; + case 9: return 9630; + case 10: return 17900; + case 11: return 32800; + case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX; +#endif + } + return 0; +} + + +#ifdef USE_ENDOMORPHISM +SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) { + secp256k1_scalar tmp = *s1; + secp256k1_scalar_split_lambda(s1, s2, &tmp); + secp256k1_ge_mul_lambda(p2, p1); + + if (secp256k1_scalar_is_high(s1)) { + secp256k1_scalar_negate(s1, s1); + secp256k1_ge_neg(p1, p1); + } + if (secp256k1_scalar_is_high(s2)) { + secp256k1_scalar_negate(s2, s2); + secp256k1_ge_neg(p2, p2); + } +} +#endif + +/** + * Returns the scratch size required for a given number of points (excluding + * base point G) without considering alignment. + */ +static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) { +#ifdef USE_ENDOMORPHISM + size_t entries = 2*n_points + 2; +#else + size_t entries = n_points + 1; +#endif + size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int); + return ((1<ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps)); + state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); + buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, (1<ps[i].skew_na = 0; + for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) { + state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0; + } + } + for(i = 0; i < 1< max_alloc) { + break; + } + space_for_points = max_alloc - space_overhead; + + n_points = space_for_points/entry_size; + n_points = n_points > max_points ? max_points : n_points; + if (n_points > res) { + res = n_points; + } + if (n_points < max_points) { + /* A larger bucket_window may support even more points. But if we + * would choose that then the caller couldn't safely use any number + * smaller than what this function returns */ + break; + } + } + return res; +} + +typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t); +static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + size_t i; + + int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t); + size_t max_points; + size_t n_batches; + size_t n_batch_points; + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n == 0) { + return 1; + } else if (n == 0) { + secp256k1_scalar szero; + secp256k1_scalar_set_int(&szero, 0); + secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc); + return 1; + } + + max_points = secp256k1_pippenger_max_points(scratch); + if (max_points == 0) { + return 0; + } else if (max_points > ECMULT_MAX_POINTS_PER_BATCH) { + max_points = ECMULT_MAX_POINTS_PER_BATCH; + } + n_batches = (n+max_points-1)/max_points; + n_batch_points = (n+n_batches-1)/n_batches; + + if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) { + f = secp256k1_ecmult_pippenger_batch; + } else { + max_points = secp256k1_strauss_max_points(scratch); + if (max_points == 0) { + return 0; + } + n_batches = (n+max_points-1)/max_points; + n_batch_points = (n+n_batches-1)/n_batches; + f = secp256k1_ecmult_strauss_batch; + } + for(i = 0; i < n_batches; i++) { + size_t nbp = n < n_batch_points ? n : n_batch_points; + size_t offset = n_batch_points*i; + secp256k1_gej tmp; + if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { + return 0; + } + secp256k1_gej_add_var(r, r, &tmp, NULL); + n -= nbp; + } + return 1; +} + #endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/gen_context.c b/src/gen_context.c index 1835fd491d..87d296ebf0 100644 --- a/src/gen_context.c +++ b/src/gen_context.c @@ -41,7 +41,7 @@ int main(int argc, char **argv) { fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); - fprintf(fp, "#include \"group.h\"\n"); + fprintf(fp, "#include \"src/group.h\"\n"); fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); diff --git a/src/group.h b/src/group.h index ea1302deb8..8e122ab429 100644 --- a/src/group.h +++ b/src/group.h @@ -65,12 +65,7 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a); static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); /** Set a batch of group elements equal to the inputs given in jacobian coordinates */ -static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); - -/** Set a batch of group elements equal to the inputs given in jacobian - * coordinates (with known z-ratios). zr must contain the known z-ratios such - * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */ -static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len); +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len); /** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to * the same global z "denominator". zr must contain the known z-ratios such @@ -79,6 +74,9 @@ static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej * stored in globalz. */ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); +/** Set a group element (affine) equal to the point at infinity. */ +static void secp256k1_ge_set_infinity(secp256k1_ge *r); + /** Set a group element (jacobian) equal to the point at infinity. */ static void secp256k1_gej_set_infinity(secp256k1_gej *r); diff --git a/src/group_impl.h b/src/group_impl.h index b31b6c12ef..5caf421b5e 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -126,46 +126,43 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { r->y = a->y; } -static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) { - secp256k1_fe *az; - secp256k1_fe *azi; +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { + secp256k1_fe u; size_t i; - size_t count = 0; - az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); + size_t last_i = SIZE_MAX; + for (i = 0; i < len; i++) { if (!a[i].infinity) { - az[count++] = a[i].z; + /* Use destination's x coordinates as scratch space */ + if (last_i == SIZE_MAX) { + r[i].x = a[i].z; + } else { + secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z); + } + last_i = i; } } + if (last_i == SIZE_MAX) { + return; + } + secp256k1_fe_inv_var(&u, &r[last_i].x); - azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); - secp256k1_fe_inv_all_var(azi, az, count); - free(az); - - count = 0; - for (i = 0; i < len; i++) { - r[i].infinity = a[i].infinity; + i = last_i; + while (i > 0) { + i--; if (!a[i].infinity) { - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); + secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u); + secp256k1_fe_mul(&u, &u, &a[last_i].z); + last_i = i; } } - free(azi); -} - -static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) { - size_t i = len - 1; - secp256k1_fe zi; - - if (len > 0) { - /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */ - secp256k1_fe_inv(&zi, &a[i].z); - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + VERIFY_CHECK(!a[last_i].infinity); + r[last_i].x = u; - /* Work out way backwards, using the z-ratios to scale the x/y values. */ - while (i > 0) { - secp256k1_fe_mul(&zi, &zi, &zr[i]); - i--; - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + for (i = 0; i < len; i++) { + r[i].infinity = a[i].infinity; + if (!a[i].infinity) { + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x); } } } @@ -200,6 +197,12 @@ static void secp256k1_gej_set_infinity(secp256k1_gej *r) { secp256k1_fe_clear(&r->z); } +static void secp256k1_ge_set_infinity(secp256k1_ge *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); +} + static void secp256k1_gej_clear(secp256k1_gej *r) { r->infinity = 0; secp256k1_fe_clear(&r->x); diff --git a/src/hash.h b/src/hash.h index e08d25d225..de26e4b89f 100644 --- a/src/hash.h +++ b/src/hash.h @@ -14,28 +14,28 @@ typedef struct { uint32_t s[8]; uint32_t buf[16]; /* In big endian */ size_t bytes; -} secp256k1_sha256_t; +} secp256k1_sha256; -static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash); -static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size); -static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32); +static void secp256k1_sha256_initialize(secp256k1_sha256 *hash); +static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size); +static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32); typedef struct { - secp256k1_sha256_t inner, outer; -} secp256k1_hmac_sha256_t; + secp256k1_sha256 inner, outer; +} secp256k1_hmac_sha256; -static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size); -static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size); -static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32); +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size); +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size); +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32); typedef struct { unsigned char v[32]; unsigned char k[32]; int retry; -} secp256k1_rfc6979_hmac_sha256_t; +} secp256k1_rfc6979_hmac_sha256; -static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen); -static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); -static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen); +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen); +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng); #endif /* SECP256K1_HASH_H */ diff --git a/src/hash_impl.h b/src/hash_impl.h index 4c9964ee06..009f26beba 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -33,7 +33,7 @@ #define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #endif -static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) { +static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { hash->s[0] = 0x6a09e667ul; hash->s[1] = 0xbb67ae85ul; hash->s[2] = 0x3c6ef372ul; @@ -128,14 +128,15 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { s[7] += h; } -static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) { +static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) { size_t bufsize = hash->bytes & 0x3F; hash->bytes += len; while (bufsize + len >= 64) { /* Fill the buffer, and process it. */ - memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize); - data += 64 - bufsize; - len -= 64 - bufsize; + size_t chunk_len = 64 - bufsize; + memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len); + data += chunk_len; + len -= chunk_len; secp256k1_sha256_transform(hash->s, hash->buf); bufsize = 0; } @@ -145,7 +146,7 @@ static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char } } -static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) { +static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) { static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t sizedesc[2]; uint32_t out[8]; @@ -161,14 +162,14 @@ static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *o memcpy(out32, (const unsigned char*)out, 32); } -static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) { - int n; +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { + size_t n; unsigned char rkey[64]; - if (keylen <= 64) { + if (keylen <= sizeof(rkey)) { memcpy(rkey, key, keylen); - memset(rkey + keylen, 0, 64 - keylen); + memset(rkey + keylen, 0, sizeof(rkey) - keylen); } else { - secp256k1_sha256_t sha256; + secp256k1_sha256 sha256; secp256k1_sha256_initialize(&sha256); secp256k1_sha256_write(&sha256, key, keylen); secp256k1_sha256_finalize(&sha256, rkey); @@ -176,24 +177,24 @@ static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, cons } secp256k1_sha256_initialize(&hash->outer); - for (n = 0; n < 64; n++) { + for (n = 0; n < sizeof(rkey); n++) { rkey[n] ^= 0x5c; } - secp256k1_sha256_write(&hash->outer, rkey, 64); + secp256k1_sha256_write(&hash->outer, rkey, sizeof(rkey)); secp256k1_sha256_initialize(&hash->inner); - for (n = 0; n < 64; n++) { + for (n = 0; n < sizeof(rkey); n++) { rkey[n] ^= 0x5c ^ 0x36; } - secp256k1_sha256_write(&hash->inner, rkey, 64); - memset(rkey, 0, 64); + secp256k1_sha256_write(&hash->inner, rkey, sizeof(rkey)); + memset(rkey, 0, sizeof(rkey)); } -static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) { +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) { secp256k1_sha256_write(&hash->inner, data, size); } -static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) { +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32) { unsigned char temp[32]; secp256k1_sha256_finalize(&hash->inner, temp); secp256k1_sha256_write(&hash->outer, temp, 32); @@ -202,8 +203,8 @@ static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsign } -static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen) { - secp256k1_hmac_sha256_t hmac; +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) { + secp256k1_hmac_sha256 hmac; static const unsigned char zero[1] = {0x00}; static const unsigned char one[1] = {0x01}; @@ -232,11 +233,11 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 rng->retry = 0; } -static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen) { +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) { /* RFC6979 3.2.h. */ static const unsigned char zero[1] = {0x00}; if (rng->retry) { - secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256 hmac; secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); secp256k1_hmac_sha256_write(&hmac, zero, 1); @@ -247,7 +248,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 } while (outlen > 0) { - secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256 hmac; int now = outlen; secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); @@ -263,7 +264,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 rng->retry = 1; } -static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) { +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng) { memset(rng->k, 0, 32); memset(rng->v, 0, 32); rng->retry = 0; diff --git a/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/java/org/bitcoin/NativeSecp256k1Test.java index c00d08899b..d766a1029c 100644 --- a/src/java/org/bitcoin/NativeSecp256k1Test.java +++ b/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -52,7 +52,7 @@ public static void testSecKeyVerifyPos() throws AssertFailException{ } /** - * This tests secret key verify() for a invalid secretkey + * This tests secret key verify() for an invalid secretkey */ public static void testSecKeyVerifyNeg() throws AssertFailException{ boolean result = false; diff --git a/src/java/org_bitcoin_NativeSecp256k1.c b/src/java/org_bitcoin_NativeSecp256k1.c index bcef7b32ce..b50970b4f2 100644 --- a/src/java/org_bitcoin_NativeSecp256k1.c +++ b/src/java/org_bitcoin_NativeSecp256k1.c @@ -83,7 +83,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e secp256k1_ecdsa_signature sig[72]; - int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); + int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL); unsigned char outputSer[72]; size_t outputLen = 72; @@ -353,7 +353,9 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e ctx, nonce_res, &pubkey, - secdata + secdata, + NULL, + NULL ); } diff --git a/src/modules/ecdh/main_impl.h b/src/modules/ecdh/main_impl.h index 01ecba4d53..44cb68e750 100644 --- a/src/modules/ecdh/main_impl.h +++ b/src/modules/ecdh/main_impl.h @@ -10,16 +10,35 @@ #include "include/secp256k1_ecdh.h" #include "ecmult_const_impl.h" -int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { +static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { + unsigned char version = (y[31] & 0x01) | 0x02; + secp256k1_sha256 sha; + (void)data; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &version, 1); + secp256k1_sha256_write(&sha, x, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256; +const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256; + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) { int ret = 0; int overflow = 0; secp256k1_gej res; secp256k1_ge pt; secp256k1_scalar s; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(result != NULL); + ARG_CHECK(output != NULL); ARG_CHECK(point != NULL); ARG_CHECK(scalar != NULL); + if (hashfp == NULL) { + hashfp = secp256k1_ecdh_hash_function_default; + } secp256k1_pubkey_load(ctx, &pt, point); secp256k1_scalar_set_b32(&s, scalar, &overflow); @@ -27,24 +46,18 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se ret = 0; } else { unsigned char x[32]; - unsigned char y[1]; - secp256k1_sha256_t sha; + unsigned char y[32]; - 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 - * expect its output to be secret and has a timing sidechannel. */ + + /* Compute a hash of the point */ secp256k1_fe_normalize(&pt.x); secp256k1_fe_normalize(&pt.y); secp256k1_fe_get_b32(x, &pt.x); - y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y); + secp256k1_fe_get_b32(y, &pt.y); - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, y, sizeof(y)); - secp256k1_sha256_write(&sha, x, sizeof(x)); - secp256k1_sha256_finalize(&sha, result); - ret = 1; + ret = hashfp(output, x, y, data); } secp256k1_scalar_clear(&s); diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index cec30b67c6..fe26e8fb69 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -7,6 +7,23 @@ #ifndef SECP256K1_MODULE_ECDH_TESTS_H #define SECP256K1_MODULE_ECDH_TESTS_H +int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { + (void)output; + (void)x; + (void)y; + (void)data; + return 0; +} + +int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { + (void)data; + /* Save x and y as uncompressed public key */ + output[0] = 0x04; + memcpy(output + 1, x, 32); + memcpy(output + 33, y, 32); + return 1; +} + void test_ecdh_api(void) { /* Setup context that just counts errors */ secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); @@ -21,15 +38,15 @@ void test_ecdh_api(void) { CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1); /* Check all NULLs are detected */ - CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL, NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0); + CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one, NULL, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0); + CHECK(secp256k1_ecdh(tctx, res, NULL, s_one, NULL, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0); + CHECK(secp256k1_ecdh(tctx, res, &point, NULL, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL, NULL) == 1); CHECK(ecount == 3); /* Cleanup */ @@ -44,29 +61,36 @@ void test_ecdh_generator_basepoint(void) { s_one[31] = 1; /* Check against pubkey creation when the basepoint is the generator */ for (i = 0; i < 100; ++i) { - secp256k1_sha256_t sha; + secp256k1_sha256 sha; unsigned char s_b32[32]; - unsigned char output_ecdh[32]; + unsigned char output_ecdh[65]; unsigned char output_ser[32]; - unsigned char point_ser[33]; + unsigned char point_ser[65]; size_t point_ser_len = sizeof(point_ser); secp256k1_scalar s; random_scalar_order(&s); secp256k1_scalar_get_b32(s_b32, &s); - /* compute using ECDH function */ CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); - CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1); - /* compute "explicitly" */ CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); + + /* compute using ECDH function with custom hash function */ + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1); + /* compute "explicitly" */ + CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1); + /* compare */ + CHECK(memcmp(output_ecdh, point_ser, 65) == 0); + + /* compute using ECDH function with default hash function */ + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL, NULL) == 1); + /* compute "explicitly" */ CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); - CHECK(point_ser_len == sizeof(point_ser)); secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, point_ser, point_ser_len); secp256k1_sha256_finalize(&sha, output_ser); /* compare */ - CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + CHECK(memcmp(output_ecdh, output_ser, 32) == 0); } } @@ -89,11 +113,14 @@ void test_bad_scalar(void) { CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); /* Try to multiply it by bad values */ - CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0); - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_zero, NULL, NULL) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 0); /* ...and a good one */ s_overflow[31] -= 1; - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL, NULL) == 1); + + /* Hash function failure results in ecdh failure */ + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0); } void run_ecdh_tests(void) { diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include new file mode 100644 index 0000000000..69933e999c --- /dev/null +++ b/src/modules/generator/Makefile.am.include @@ -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 diff --git a/src/modules/generator/main_impl.h b/src/modules/generator/main_impl.h new file mode 100644 index 0000000000..12447591d4 --- /dev/null +++ b/src/modules/generator/main_impl.h @@ -0,0 +1,225 @@ +/********************************************************************** + * 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 + +#include "field.h" +#include "group.h" +#include "hash.h" +#include "scalar.h" + +static void secp256k1_generator_load(secp256k1_ge* ge, const secp256k1_generator* gen) { + int succeed; + succeed = secp256k1_fe_set_b32(&ge->x, &gen->data[0]); + VERIFY_CHECK(succeed != 0); + succeed = secp256k1_fe_set_b32(&ge->y, &gen->data[32]); + VERIFY_CHECK(succeed != 0); + ge->infinity = 0; + (void) succeed; +} + +static void secp256k1_generator_save(secp256k1_generator *gen, secp256k1_ge* ge) { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(&gen->data[0], &ge->x); + secp256k1_fe_get_b32(&gen->data[32], &ge->y); +} + +int secp256k1_generator_parse(const secp256k1_context* ctx, secp256k1_generator* gen, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(gen != NULL); + ARG_CHECK(input != NULL); + + if ((input[0] & 0xFE) != 10 || + !secp256k1_fe_set_b32(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { + return 0; + } + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_generator_save(gen, &ge); + return 1; +} + +int secp256k1_generator_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_generator* gen) { + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(gen != NULL); + + secp256k1_generator_load(&ge, gen); + + output[0] = 11 ^ secp256k1_fe_is_quad_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); + 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[17] = "1st generation: "; + static const unsigned char prefix2[17] = "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 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 diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h new file mode 100644 index 0000000000..20acf2e7da --- /dev/null +++ b/src/modules/generator/tests_impl.h @@ -0,0 +1,219 @@ +/********************************************************************** + * 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 +#include + +#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 test_generator_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x0b, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_generator parse; + + CHECK(secp256k1_generator_parse(ctx, &parse, two_g)); + CHECK(secp256k1_generator_serialize(ctx, result, &parse)); + CHECK(memcmp(two_g, result, 33) == 0); + + result[0] = 0x0a; + CHECK(secp256k1_generator_parse(ctx, &parse, result)); + result[0] = 0x08; + CHECK(!secp256k1_generator_parse(ctx, &parse, result)); +} + +void run_generator_tests(void) { + test_shallue_van_de_woestijne(); + test_generator_fixed_vector(); + test_generator_api(); + test_generator_generate(); +} + +#endif diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include new file mode 100644 index 0000000000..ff8b8d3803 --- /dev/null +++ b/src/modules/rangeproof/Makefile.am.include @@ -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 diff --git a/src/modules/rangeproof/borromean.h b/src/modules/rangeproof/borromean.h new file mode 100644 index 0000000000..8f8cfedd07 --- /dev/null +++ b/src/modules/rangeproof/borromean.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_BORROMEAN_H_ +#define _SECP256K1_BORROMEAN_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +int secp256k1_borromean_verify(const secp256k1_ecmult_context* 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 diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h new file mode 100644 index 0000000000..3a82f096a5 --- /dev/null +++ b/src/modules/rangeproof/borromean_impl.h @@ -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 +#include + +#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 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 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 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 diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h new file mode 100644 index 0000000000..d3f1dd337b --- /dev/null +++ b/src/modules/rangeproof/main_impl.h @@ -0,0 +1,295 @@ +/********************************************************************** + * 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(F(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16))) + * sage: '%x %x' % G2.xy() + */ +static const secp256k1_generator secp256k1_generator_h_internal = {{ + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, + 0x31, 0xd3, 0xc6, 0x86, 0x39, 0x73, 0x92, 0x6e, 0x04, 0x9e, 0x63, 0x7c, 0xb1, 0xb5, 0xf4, 0x0a, + 0x36, 0xda, 0xc2, 0x8a, 0xf1, 0x76, 0x69, 0x68, 0xc3, 0x0c, 0x23, 0x13, 0xf3, 0xa3, 0x89, 0x04 +}}; + +const secp256k1_generator *secp256k1_generator_h = &secp256k1_generator_h_internal; + +static void secp256k1_pedersen_commitment_load(secp256k1_ge* ge, const secp256k1_pedersen_commitment* commit) { + secp256k1_fe fe; + secp256k1_fe_set_b32(&fe, &commit->data[1]); + secp256k1_ge_set_xquad(ge, &fe); + if (commit->data[0] & 1) { + secp256k1_ge_neg(ge, ge); + } +} + +static void secp256k1_pedersen_commitment_save(secp256k1_pedersen_commitment* commit, secp256k1_ge* ge) { + secp256k1_fe_normalize(&ge->x); + secp256k1_fe_get_b32(&commit->data[1], &ge->x); + commit->data[0] = 9 ^ secp256k1_fe_is_quad_var(&ge->y); +} + +int secp256k1_pedersen_commitment_parse(const secp256k1_context* ctx, secp256k1_pedersen_commitment* commit, const unsigned char *input) { + secp256k1_fe x; + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(input != NULL); + (void) ctx; + + if ((input[0] & 0xFE) != 8 || + !secp256k1_fe_set_b32(&x, &input[1]) || + !secp256k1_ge_set_xquad(&ge, &x)) { + return 0; + } + if (input[0] & 1) { + secp256k1_ge_neg(&ge, &ge); + } + secp256k1_pedersen_commitment_save(commit, &ge); + return 1; +} + +int secp256k1_pedersen_commitment_serialize(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pedersen_commitment* commit) { + secp256k1_ge ge; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(commit != NULL); + + secp256k1_pedersen_commitment_load(&ge, commit); + + output[0] = 9 ^ secp256k1_fe_is_quad_var(&ge.y); + secp256k1_fe_normalize_var(&ge.x); + secp256k1_fe_get_b32(&output[1], &ge.x); + return 1; +} + +/* Generates a pedersen commitment: *commit = blind * G + value * G2. The blinding factor is 32 bytes.*/ +int secp256k1_pedersen_commit(const secp256k1_context* ctx, secp256k1_pedersen_commitment *commit, const unsigned char *blind, uint64_t value, const secp256k1_generator* gen) { + secp256k1_ge genp; + secp256k1_gej rj; + secp256k1_ge r; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(commit != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(gen != NULL); + secp256k1_generator_load(&genp, gen); + secp256k1_scalar_set_b32(&sec, blind, &overflow); + if (!overflow) { + secp256k1_pedersen_ecmult(&ctx->ecmult_gen_ctx, &rj, &sec, value, &genp); + if (!secp256k1_gej_is_infinity(&rj)) { + secp256k1_ge_set_gej(&r, &rj); + secp256k1_pedersen_commitment_save(commit, &r); + ret = 1; + } + secp256k1_gej_clear(&rj); + secp256k1_ge_clear(&r); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +/** Takes a list of n pointers to 32 byte blinding values, the first negs of which are treated with positive sign and the rest + * negative, then calculates an additional blinding value that adds to zero. + */ +int secp256k1_pedersen_blind_sum(const secp256k1_context* ctx, unsigned char *blind_out, const unsigned char * const *blinds, size_t n, size_t npositive) { + secp256k1_scalar acc; + secp256k1_scalar x; + size_t i; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(blind_out != NULL); + ARG_CHECK(blinds != NULL); + ARG_CHECK(npositive <= n); + (void) ctx; + secp256k1_scalar_set_int(&acc, 0); + for (i = 0; i < n; i++) { + secp256k1_scalar_set_b32(&x, blinds[i], &overflow); + if (overflow) { + return 0; + } + if (i >= npositive) { + secp256k1_scalar_negate(&x, &x); + } + secp256k1_scalar_add(&acc, &acc, &x); + } + secp256k1_scalar_get_b32(blind_out, &acc); + secp256k1_scalar_clear(&acc); + secp256k1_scalar_clear(&x); + return 1; +} + +/* Takes two lists of commitments and sums the first set and subtracts the second and verifies that they sum to excess. */ +int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k1_pedersen_commitment * const* commits, size_t pcnt, const secp256k1_pedersen_commitment * const* ncommits, size_t ncnt) { + secp256k1_gej accj; + secp256k1_ge add; + size_t i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(!pcnt || (commits != NULL)); + ARG_CHECK(!ncnt || (ncommits != NULL)); + (void) ctx; + secp256k1_gej_set_infinity(&accj); + for (i = 0; i < ncnt; i++) { + secp256k1_pedersen_commitment_load(&add, ncommits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + secp256k1_gej_neg(&accj, &accj); + for (i = 0; i < pcnt; i++) { + secp256k1_pedersen_commitment_load(&add, commits[i]); + secp256k1_gej_add_ge_var(&accj, &accj, &add, NULL); + } + return secp256k1_gej_is_infinity(&accj); +} + +int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) { + secp256k1_scalar sum; + secp256k1_scalar tmp; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(n_total == 0 || value != NULL); + ARG_CHECK(n_total == 0 || generator_blind != NULL); + ARG_CHECK(n_total == 0 || blinding_factor != NULL); + ARG_CHECK(n_total > n_inputs); + (void) ctx; + + if (n_total == 0) { + return 1; + } + + secp256k1_scalar_set_int(&sum, 0); + 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 diff --git a/src/modules/rangeproof/pedersen.h b/src/modules/rangeproof/pedersen.h new file mode 100644 index 0000000000..14d9920ebc --- /dev/null +++ b/src/modules/rangeproof/pedersen.h @@ -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 + +/** 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 diff --git a/src/modules/rangeproof/pedersen_impl.h b/src/modules/rangeproof/pedersen_impl.h new file mode 100644 index 0000000000..69f22e382a --- /dev/null +++ b/src/modules/rangeproof/pedersen_impl.h @@ -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 + +#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 diff --git a/src/modules/rangeproof/rangeproof.h b/src/modules/rangeproof/rangeproof.h new file mode 100644 index 0000000000..840a09ae61 --- /dev/null +++ b/src/modules/rangeproof/rangeproof.h @@ -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 diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h new file mode 100644 index 0000000000..8056f0a78c --- /dev/null +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -0,0 +1,685 @@ +/********************************************************************** + * 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 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 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 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; + if (!secp256k1_fe_set_b32(&fe, &proof[offset]) || + !secp256k1_ge_set_xquad(&c, &fe)) { + return 0; + } + 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 diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h new file mode 100644 index 0000000000..e8cf1f1778 --- /dev/null +++ b/src/modules/rangeproof/tests_impl.h @@ -0,0 +1,709 @@ +/********************************************************************** + * 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 + +#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 + 1]; /* One additional byte to test if trailing bytes are rejected */ + 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); + /* Test if trailing bytes are rejected. */ + proof[len] = v; + CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len + 1, NULL, 0, secp256k1_generator_h)); + 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 test_rangeproof_fixed_vectors(void) { + const unsigned char vector_1[] = { + 0x62, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x02, 0x2a, 0x5c, 0x42, 0x0e, 0x1d, + 0x51, 0xe1, 0xb7, 0xf3, 0x69, 0x04, 0xb5, 0xbb, 0x9b, 0x41, 0x66, 0x14, 0xf3, 0x64, 0x42, 0x26, + 0xe3, 0xa7, 0x6a, 0x06, 0xbb, 0xa8, 0x5a, 0x49, 0x6f, 0x19, 0x76, 0xfb, 0xe5, 0x75, 0x77, 0x88, + 0xab, 0xa9, 0x66, 0x44, 0x80, 0xea, 0x29, 0x95, 0x7f, 0xdf, 0x72, 0x4a, 0xaf, 0x02, 0xbe, 0xdd, + 0x5d, 0x15, 0xd8, 0xae, 0xff, 0x74, 0xc9, 0x8c, 0x1a, 0x67, 0x0e, 0xb2, 0x57, 0x22, 0x99, 0xc3, + 0x21, 0x46, 0x6f, 0x15, 0x58, 0x0e, 0xdb, 0xe6, 0x6e, 0xc4, 0x0d, 0xfe, 0x6f, 0x04, 0x6b, 0x0d, + 0x18, 0x3d, 0x78, 0x40, 0x98, 0x56, 0x4e, 0xe4, 0x4a, 0x74, 0x90, 0xa7, 0xac, 0x9c, 0x16, 0xe0, + 0x3e, 0x81, 0xaf, 0x0f, 0xe3, 0x4f, 0x34, 0x99, 0x52, 0xf7, 0xa7, 0xf6, 0xd3, 0x83, 0xa0, 0x17, + 0x4b, 0x2d, 0xa7, 0xd4, 0xfd, 0xf7, 0x84, 0x45, 0xc4, 0x11, 0x71, 0x3d, 0x4a, 0x22, 0x34, 0x09, + 0x9c, 0xa7, 0xe5, 0xc8, 0xba, 0x04, 0xbf, 0xfd, 0x25, 0x11, 0x7d, 0xa4, 0x43, 0x45, 0xc7, 0x62, + 0x9e, 0x7b, 0x80, 0xf6, 0x09, 0xbb, 0x1b, 0x2e, 0xf3, 0xcd, 0x23, 0xe0, 0xed, 0x81, 0x43, 0x42, + 0xbe, 0xc4, 0x9f, 0x58, 0x8a, 0x0d, 0x66, 0x79, 0x09, 0x70, 0x11, 0x68, 0x3d, 0x87, 0x38, 0x1c, + 0x3c, 0x85, 0x52, 0x5b, 0x62, 0xf7, 0x3e, 0x7e, 0x87, 0xa2, 0x99, 0x24, 0xd0, 0x7d, 0x18, 0x63, + 0x56, 0x48, 0xa4, 0x3a, 0xfe, 0x65, 0xfa, 0xa4, 0xd0, 0x67, 0xaa, 0x98, 0x65, 0x4d, 0xe4, 0x22, + 0x75, 0x45, 0x52, 0xe8, 0x41, 0xc7, 0xed, 0x38, 0xeb, 0xf5, 0x02, 0x90, 0xc9, 0x45, 0xa3, 0xb0, + 0x4d, 0x03, 0xd7, 0xab, 0x43, 0xe4, 0x21, 0xfc, 0x83, 0xd6, 0x12, 0x1d, 0x76, 0xb1, 0x3c, 0x67, + 0x63, 0x1f, 0x52, 0x9d, 0xc3, 0x23, 0x5c, 0x4e, 0xa6, 0x8d, 0x01, 0x4a, 0xba, 0x9a, 0xf4, 0x16, + 0x5b, 0x67, 0xc8, 0xe1, 0xd2, 0x42, 0x6d, 0xdf, 0xcd, 0x08, 0x6a, 0x73, 0x41, 0x6a, 0xc2, 0x84, + 0xc6, 0x31, 0xbe, 0x57, 0xcb, 0x0e, 0xde, 0xbf, 0x71, 0xd5, 0x8a, 0xf7, 0x24, 0xb2, 0xa7, 0x89, + 0x96, 0x62, 0x4f, 0xd9, 0xf7, 0xc3, 0xde, 0x4c, 0xab, 0x13, 0x72, 0xb4, 0xb3, 0x35, 0x04, 0x82, + 0xa8, 0x75, 0x1d, 0xde, 0x46, 0xa8, 0x0d, 0xb8, 0x23, 0x44, 0x00, 0x44, 0xfa, 0x53, 0x6c, 0x2d, + 0xce, 0xd3, 0xa6, 0x80, 0xa1, 0x20, 0xca, 0xd1, 0x63, 0xbb, 0xbe, 0x39, 0x5f, 0x9d, 0x27, 0x69, + 0xb3, 0x33, 0x1f, 0xdb, 0xda, 0x67, 0x05, 0x37, 0xbe, 0x65, 0xe9, 0x7e, 0xa9, 0xc3, 0xff, 0x37, + 0x8a, 0xb4, 0x2d, 0xfe, 0xf2, 0x16, 0x85, 0xc7, 0x0f, 0xd9, 0xbe, 0x14, 0xd1, 0x80, 0x14, 0x9f, + 0x58, 0x56, 0x98, 0x41, 0xf6, 0x26, 0xf7, 0xa2, 0x71, 0x66, 0xb4, 0x7a, 0x9c, 0x12, 0x73, 0xd3, + 0xdf, 0x77, 0x2b, 0x49, 0xe5, 0xca, 0x50, 0x57, 0x44, 0x6e, 0x3f, 0x58, 0x56, 0xbc, 0x21, 0x70, + 0x4f, 0xc6, 0xaa, 0x12, 0xff, 0x7c, 0xa7, 0x3d, 0xed, 0x46, 0xc1, 0x40, 0xe6, 0x58, 0x09, 0x2a, + 0xda, 0xb3, 0x76, 0xab, 0x44, 0xb5, 0x4e, 0xb3, 0x12, 0xe0, 0x26, 0x8a, 0x52, 0xac, 0x49, 0x1d, + 0xe7, 0x06, 0x53, 0x3a, 0x01, 0x35, 0x21, 0x2e, 0x86, 0x48, 0xc5, 0x75, 0xc1, 0xa2, 0x7d, 0x22, + 0x53, 0xf6, 0x3f, 0x41, 0xc5, 0xb3, 0x08, 0x7d, 0xa3, 0x67, 0xc0, 0xbb, 0xb6, 0x8d, 0xf0, 0xd3, + 0x01, 0x72, 0xd3, 0x63, 0x82, 0x01, 0x1a, 0xe7, 0x1d, 0x22, 0xfa, 0x95, 0x33, 0xf6, 0xf2, 0xde, + 0xa2, 0x53, 0x86, 0x55, 0x5a, 0xb4, 0x2e, 0x75, 0x75, 0xc6, 0xd5, 0x93, 0x9c, 0x57, 0xa9, 0x1f, + 0xb9, 0x3e, 0xe8, 0x1c, 0xbf, 0xac, 0x1c, 0x54, 0x6f, 0xf5, 0xab, 0x41, 0xee, 0xb3, 0x0e, 0xd0, + 0x76, 0xc4, 0x1a, 0x45, 0xcd, 0xf1, 0xd6, 0xcc, 0xb0, 0x83, 0x70, 0x73, 0xbc, 0x88, 0x74, 0xa0, + 0x5b, 0xe7, 0x98, 0x10, 0x36, 0xbf, 0xec, 0x23, 0x1c, 0xc2, 0xb5, 0xba, 0x4b, 0x9d, 0x7f, 0x8c, + 0x8a, 0xe2, 0xda, 0x18, 0xdd, 0xab, 0x27, 0x8a, 0x15, 0xeb, 0xb0, 0xd4, 0x3a, 0x8b, 0x77, 0x00, + 0xc7, 0xbb, 0xcc, 0xfa, 0xba, 0xa4, 0x6a, 0x17, 0x5c, 0xf8, 0x51, 0x5d, 0x8d, 0x16, 0xcd, 0xa7, + 0x0e, 0x71, 0x97, 0x98, 0x78, 0x5a, 0x41, 0xb3, 0xf0, 0x1f, 0x87, 0x2d, 0x65, 0xcd, 0x29, 0x49, + 0xd2, 0x87, 0x2c, 0x91, 0xa9, 0x5f, 0xcc, 0xa9, 0xd8, 0xbb, 0x53, 0x18, 0xe7, 0xd6, 0xec, 0x65, + 0xa6, 0x45, 0xf6, 0xce, 0xcf, 0x48, 0xf6, 0x1e, 0x3d, 0xd2, 0xcf, 0xcb, 0x3a, 0xcd, 0xbb, 0x92, + 0x29, 0x24, 0x16, 0x7f, 0x8a, 0xa8, 0x5c, 0x0c, 0x45, 0x71, 0x33 + }; + const unsigned char commit_1[] = { + 0x08, + 0xf5, 0x1e, 0x0d, 0xc5, 0x86, 0x78, 0x51, 0xa9, 0x00, 0x00, 0xef, 0x4d, 0xe2, 0x94, 0x60, 0x89, + 0x83, 0x04, 0xb4, 0x0e, 0x90, 0x10, 0x05, 0x1c, 0x7f, 0xd7, 0x33, 0x92, 0x1f, 0xe7, 0x74, 0x59 + }; + size_t min_value_1; + size_t max_value_1; + secp256k1_pedersen_commitment pc; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &pc, commit_1)); + + CHECK(secp256k1_rangeproof_verify( + ctx, + &min_value_1, &max_value_1, + &pc, + vector_1, sizeof(vector_1), + NULL, 0, + secp256k1_generator_h + )); +} + +void test_pedersen_commitment_fixed_vector(void) { + const unsigned char two_g[33] = { + 0x09, + 0xc6, 0x04, 0x7f, 0x94, 0x41, 0xed, 0x7d, 0x6d, 0x30, 0x45, 0x40, 0x6e, 0x95, 0xc0, 0x7c, 0xd8, + 0x5c, 0x77, 0x8e, 0x4b, 0x8c, 0xef, 0x3c, 0xa7, 0xab, 0xac, 0x09, 0xb9, 0x5c, 0x70, 0x9e, 0xe5 + }; + unsigned char result[33]; + secp256k1_pedersen_commitment parse; + + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, two_g)); + CHECK(secp256k1_pedersen_commitment_serialize(ctx, result, &parse)); + CHECK(memcmp(two_g, result, 33) == 0); + + result[0] = 0x08; + CHECK(secp256k1_pedersen_commitment_parse(ctx, &parse, result)); + result[0] = 0x0c; + CHECK(!secp256k1_pedersen_commitment_parse(ctx, &parse, result)); +} + +void run_rangeproof_tests(void) { + int i; + test_api(); + test_rangeproof_fixed_vectors(); + test_pedersen_commitment_fixed_vector(); + for (i = 0; i < 10*count; i++) { + test_pedersen(); + } + for (i = 0; i < 10*count; i++) { + test_borromean(); + } + test_rangeproof(); + test_multiple_generators(); +} + +#endif diff --git a/src/modules/surjection/Makefile.am.include b/src/modules/surjection/Makefile.am.include new file mode 100644 index 0000000000..51ece21fca --- /dev/null +++ b/src/modules/surjection/Makefile.am.include @@ -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 + diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h new file mode 100644 index 0000000000..c67d4c0d3d --- /dev/null +++ b/src/modules/surjection/main_impl.h @@ -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 +#include + +#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 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 diff --git a/src/modules/surjection/surjection.h b/src/modules/surjection/surjection.h new file mode 100644 index 0000000000..20ac493163 --- /dev/null +++ b/src/modules/surjection/surjection.h @@ -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 diff --git a/src/modules/surjection/surjection.md b/src/modules/surjection/surjection.md new file mode 100644 index 0000000000..e7bd4db150 --- /dev/null +++ b/src/modules/surjection/surjection.md @@ -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. + + diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h new file mode 100644 index 0000000000..f58026dec4 --- /dev/null +++ b/src/modules/surjection/surjection_impl.h @@ -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 +#include + +#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 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 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 diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h new file mode 100644 index 0000000000..a0856e2296 --- /dev/null +++ b/src/modules/surjection/tests_impl.h @@ -0,0 +1,494 @@ +/********************************************************************** + * 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]; + unsigned char serialized_proof_trailing[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX + 1]; + 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)); + + /* trailing garbage */ + memcpy(&serialized_proof_trailing, &serialized_proof, serialized_len); + serialized_proof_trailing[serialized_len] = seed[0]; + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len + 1) == 0); + + 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 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 diff --git a/src/modules/whitelist/Makefile.am.include b/src/modules/whitelist/Makefile.am.include new file mode 100644 index 0000000000..0dc5a64db2 --- /dev/null +++ b/src/modules/whitelist/Makefile.am.include @@ -0,0 +1,10 @@ +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 +if USE_BENCHMARK +noinst_PROGRAMS += bench_whitelist +bench_whitelist_SOURCES = src/bench_whitelist.c +bench_whitelist_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_generator_LDFLAGS = -static +endif diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h new file mode 100644 index 0000000000..0b2d6c9c95 --- /dev/null +++ b/src/modules/whitelist/main_impl.h @@ -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 diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h new file mode 100644 index 0000000000..7cf1fb0965 --- /dev/null +++ b/src/modules/whitelist/tests_impl.h @@ -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 diff --git a/src/modules/whitelist/whitelist.md b/src/modules/whitelist/whitelist.md new file mode 100644 index 0000000000..89d19caff6 --- /dev/null +++ b/src/modules/whitelist/whitelist.md @@ -0,0 +1,105 @@ +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. + +Having to access the offline key `Q_i` to compute the secret to the sum `W + +Q_i` for every authorization is onerous. Instead, if the whitelisted keys are +created using +[BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) +unhardened derivation, the sum can be computed on an online machine. In order +to achieve that, the offline key `Q_j` is set to the negated last hardened +BIP32 derived parent key (typically, the public key corresponding to the xpub). +As a result `W + Q_i = I_L*G` where `I_L` is the public tweak used +to derive `W` and can be easily computed online using the extended public key +and the derivation path. diff --git a/src/modules/whitelist/whitelist_impl.h b/src/modules/whitelist/whitelist_impl.h new file mode 100644 index 0000000000..ff8d87f4a5 --- /dev/null +++ b/src/modules/whitelist/whitelist_impl.h @@ -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 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 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 diff --git a/src/scalar.h b/src/scalar.h index 59304cb66e..1fc3a73ff2 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -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); diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index db1ebf94be..97401968b4 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -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); diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 4f9ed61fea..ad4d050df9 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -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); diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index c80e70c5a2..37af136e41 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -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) diff --git a/src/scratch.h b/src/scratch.h new file mode 100644 index 0000000000..fef377af0d --- /dev/null +++ b/src/scratch.h @@ -0,0 +1,39 @@ +/********************************************************************** + * Copyright (c) 2017 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCRATCH_ +#define _SECP256K1_SCRATCH_ + +#define SECP256K1_SCRATCH_MAX_FRAMES 5 + +/* The typedef is used internally; the struct name is used in the public API + * (where it is exposed as a different typedef) */ +typedef struct secp256k1_scratch_space_struct { + void *data[SECP256K1_SCRATCH_MAX_FRAMES]; + size_t offset[SECP256K1_SCRATCH_MAX_FRAMES]; + size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES]; + size_t frame; + size_t max_size; + const secp256k1_callback* error_callback; +} secp256k1_scratch; + +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size); + +static void secp256k1_scratch_destroy(secp256k1_scratch* scratch); + +/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */ +static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects); + +/** Deallocates a stack frame */ +static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch); + +/** Returns the maximum allocation the scratch space will allow */ +static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects); + +/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */ +static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n); + +#endif diff --git a/src/scratch_impl.h b/src/scratch_impl.h new file mode 100644 index 0000000000..abed713b21 --- /dev/null +++ b/src/scratch_impl.h @@ -0,0 +1,86 @@ +/********************************************************************** + * Copyright (c) 2017 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCRATCH_IMPL_H_ +#define _SECP256K1_SCRATCH_IMPL_H_ + +#include "scratch.h" + +/* Using 16 bytes alignment because common architectures never have alignment + * requirements above 8 for any of the types we care about. In addition we + * leave some room because currently we don't care about a few bytes. + * TODO: Determine this at configure time. */ +#define ALIGNMENT 16 + +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) { + secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret)); + if (ret != NULL) { + memset(ret, 0, sizeof(*ret)); + ret->max_size = max_size; + ret->error_callback = error_callback; + } + return ret; +} + +static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) { + if (scratch != NULL) { + VERIFY_CHECK(scratch->frame == 0); + free(scratch); + } +} + +static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) { + size_t i = 0; + size_t allocated = 0; + for (i = 0; i < scratch->frame; i++) { + allocated += scratch->frame_size[i]; + } + if (scratch->max_size - allocated <= objects * ALIGNMENT) { + return 0; + } + return scratch->max_size - allocated - objects * ALIGNMENT; +} + +static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) { + VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES); + + if (n <= secp256k1_scratch_max_allocation(scratch, objects)) { + n += objects * ALIGNMENT; + scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n); + if (scratch->data[scratch->frame] == NULL) { + return 0; + } + scratch->frame_size[scratch->frame] = n; + scratch->offset[scratch->frame] = 0; + scratch->frame++; + return 1; + } else { + return 0; + } +} + +static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) { + VERIFY_CHECK(scratch->frame > 0); + scratch->frame -= 1; + free(scratch->data[scratch->frame]); +} + +static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) { + void *ret; + size_t frame = scratch->frame - 1; + size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + + if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) { + return NULL; + } + ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]); + memset(ret, 0, size); + scratch->offset[frame] += size; + + return ret; +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 4f8c01655b..9aedec5b2a 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -17,6 +17,17 @@ #include "ecdsa_impl.h" #include "eckey_impl.h" #include "hash_impl.h" +#include "scratch_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)) { \ @@ -55,6 +66,14 @@ struct secp256k1_context_struct { secp256k1_callback error_callback; }; +static const secp256k1_context secp256k1_context_no_precomp_ = { + { 0 }, + { 0 }, + { default_illegal_callback_fn, 0 }, + { default_error_callback_fn, 0 } +}; +const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_; + secp256k1_context* secp256k1_context_create(unsigned int flags) { secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); ret->illegal_callback = default_illegal_callback; @@ -90,6 +109,7 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { } void secp256k1_context_destroy(secp256k1_context* ctx) { + CHECK(ctx != secp256k1_context_no_precomp); if (ctx != NULL) { secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); @@ -99,6 +119,7 @@ void secp256k1_context_destroy(secp256k1_context* ctx) { } void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + CHECK(ctx != secp256k1_context_no_precomp); if (fun == NULL) { fun = default_illegal_callback_fn; } @@ -107,6 +128,7 @@ void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)( } void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + CHECK(ctx != secp256k1_context_no_precomp); if (fun == NULL) { fun = default_error_callback_fn; } @@ -114,13 +136,22 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co ctx->error_callback.data = data; } +secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) { + VERIFY_CHECK(ctx != NULL); + return secp256k1_scratch_create(&ctx->error_callback, max_size); +} + +void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) { + secp256k1_scratch_destroy(scratch); +} + static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { if (sizeof(secp256k1_ge_storage) == 64) { /* When the secp256k1_ge_storage type is exactly 64 byte, use its * representation inside secp256k1_pubkey, as conversion is very fast. * Note that secp256k1_pubkey_save must use the same representation. */ secp256k1_ge_storage s; - memcpy(&s, &pubkey->data[0], 64); + memcpy(&s, &pubkey->data[0], sizeof(s)); secp256k1_ge_from_storage(ge, &s); } else { /* Otherwise, fall back to 32-byte big endian for X and Y. */ @@ -137,7 +168,7 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { if (sizeof(secp256k1_ge_storage) == 64) { secp256k1_ge_storage s; secp256k1_ge_to_storage(&s, ge); - memcpy(&pubkey->data[0], &s, 64); + memcpy(&pubkey->data[0], &s, sizeof(s)); } else { VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); secp256k1_fe_normalize_var(&ge->x); @@ -307,10 +338,15 @@ int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_s secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m)); } +static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *offset, const void *data, unsigned int len) { + memcpy(buf + *offset, data, len); + *offset += len; +} + static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; - int keylen = 64; - secp256k1_rfc6979_hmac_sha256_t rng; + unsigned int offset = 0; + secp256k1_rfc6979_hmac_sha256 rng; unsigned int i; /* We feed a byte array to the PRNG as input, consisting of: * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. @@ -320,17 +356,15 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m * different argument mixtures to emulate each other and result in the same * nonces. */ - memcpy(keydata, key32, 32); - memcpy(keydata + 32, msg32, 32); + buffer_append(keydata, &offset, key32, 32); + buffer_append(keydata, &offset, msg32, 32); if (data != NULL) { - memcpy(keydata + 64, data, 32); - keylen = 96; + buffer_append(keydata, &offset, data, 32); } if (algo16 != NULL) { - memcpy(keydata + keylen, algo16, 16); - keylen += 16; + buffer_append(keydata, &offset, algo16, 16); } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, offset); memset(keydata, 0, sizeof(keydata)); for (i = 0; i <= counter; i++) { secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); @@ -546,6 +580,7 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { VERIFY_CHECK(ctx != NULL); + CHECK(ctx != secp256k1_context_no_precomp); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); return 1; @@ -582,3 +617,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 diff --git a/src/testrand.h b/src/testrand.h index f1f9be077e..82599593ca 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -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 /* SECP256K1_TESTRAND_H */ diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 1255574209..0db523d24c 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -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.* **********************************************************************/ @@ -13,7 +13,7 @@ #include "testrand.h" #include "hash.h" -static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng; +static secp256k1_rfc6979_hmac_sha256 secp256k1_test_rng; static uint32_t secp256k1_test_rng_precomputed[8]; static int secp256k1_test_rng_precomputed_used = 8; static uint64_t secp256k1_test_rng_integer; @@ -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 /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/tests.c b/src/tests.c index 3d9bd5ebb4..5c8dc8b176 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -23,6 +23,9 @@ #include "openssl/ec.h" #include "openssl/ecdsa.h" #include "openssl/obj_mac.h" +# if OPENSSL_VERSION_NUMBER < 0x10100000L +void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {*pr = sig->r; *ps = sig->s;} +# endif #endif #include "contrib/lax_der_parsing.c" @@ -134,6 +137,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; @@ -248,6 +297,44 @@ void run_context_tests(void) { secp256k1_context_destroy(NULL); } +void run_scratch_tests(void) { + int32_t ecount = 0; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_scratch_space *scratch; + + /* Test public API */ + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + + scratch = secp256k1_scratch_space_create(none, 1000); + CHECK(scratch != NULL); + CHECK(ecount == 0); + + /* Test internal API */ + CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); + CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000); + + /* Allocating 500 bytes with no frame fails */ + CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); + CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); + + /* ...but pushing a new stack frame does affect the max allocation */ + CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1)); + CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */ + CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL); + CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); + + CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0); + + /* ...and this effect is undone by popping the frame */ + secp256k1_scratch_deallocate_frame(scratch); + CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); + CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); + + /* cleanup */ + secp256k1_scratch_space_destroy(scratch); + secp256k1_context_destroy(none); +} + /***** HASH TESTS *****/ void run_sha256_tests(void) { @@ -270,7 +357,7 @@ void run_sha256_tests(void) { int i; for (i = 0; i < 8; i++) { unsigned char out[32]; - secp256k1_sha256_t hasher; + secp256k1_sha256 hasher; secp256k1_sha256_initialize(&hasher); secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); secp256k1_sha256_finalize(&hasher, out); @@ -313,7 +400,7 @@ void run_hmac_sha256_tests(void) { }; int i; for (i = 0; i < 6; i++) { - secp256k1_hmac_sha256_t hasher; + secp256k1_hmac_sha256 hasher; unsigned char out[32]; secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); @@ -345,7 +432,7 @@ void run_rfc6979_hmac_sha256_tests(void) { {0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94} }; - secp256k1_rfc6979_hmac_sha256_t rng; + secp256k1_rfc6979_hmac_sha256 rng; unsigned char out[32]; int i; @@ -2054,7 +2141,6 @@ void test_ge(void) { /* Test batch gej -> ge conversion with and without known z ratios. */ { secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe)); - secp256k1_ge *ge_set_table = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); for (i = 0; i < 4 * runs + 1; i++) { /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */ @@ -2062,20 +2148,33 @@ void test_ge(void) { secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); } } - secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1); - secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback); + secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1); for (i = 0; i < 4 * runs + 1; i++) { secp256k1_fe s; random_fe_non_zero(&s); secp256k1_gej_rescale(&gej[i], &s); - ge_equals_gej(&ge_set_table[i], &gej[i]); ge_equals_gej(&ge_set_all[i], &gej[i]); } - free(ge_set_table); free(ge_set_all); free(zr); } + /* Test batch gej -> ge conversion with many infinities. */ + for (i = 0; i < 4 * runs + 1; i++) { + random_group_element_test(&ge[i]); + /* randomly set half the points to infinitiy */ + if(secp256k1_fe_is_odd(&ge[i].x)) { + secp256k1_ge_set_infinity(&ge[i]); + } + secp256k1_gej_set_ge(&gej[i], &ge[i]); + } + /* batch invert */ + secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1); + /* check result */ + for (i = 0; i < 4 * runs + 1; i++) { + ge_equals_gej(&ge[i], &gej[i]); + } + free(ge); free(gej); free(zinv); @@ -2405,7 +2504,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 +2520,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 +2541,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 +2573,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); @@ -2487,6 +2586,400 @@ void run_ecmult_const_tests(void) { ecmult_const_chain_multiply(); } +typedef struct { + secp256k1_scalar *sc; + secp256k1_ge *pt; +} ecmult_multi_data; + +static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_multi_data *data = (ecmult_multi_data*) cbdata; + *sc = data->sc[idx]; + *pt = data->pt[idx]; + return 1; +} + +static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + (void)sc; + (void)pt; + (void)idx; + (void)cbdata; + return 0; +} + +void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) { + int ncount; + secp256k1_scalar szero; + secp256k1_scalar sc[32]; + secp256k1_ge pt[32]; + secp256k1_gej r; + secp256k1_gej r2; + ecmult_multi_data data; + secp256k1_scratch *scratch_empty; + + data.sc = sc; + data.pt = pt; + secp256k1_scalar_set_int(&szero, 0); + + /* No points to multiply */ + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); + + /* Check 1- and 2-point multiplies against ecmult */ + for (ncount = 0; ncount < count; ncount++) { + secp256k1_ge ptg; + secp256k1_gej ptgj; + random_scalar_order(&sc[0]); + random_scalar_order(&sc[1]); + + random_group_element_test(&ptg); + secp256k1_gej_set_ge(&ptgj, &ptg); + pt[0] = ptg; + pt[1] = secp256k1_ge_const_g; + + /* only G scalar */ + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); + secp256k1_gej_neg(&r2, &r2); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + + /* 1-point */ + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); + secp256k1_gej_neg(&r2, &r2); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + + /* Try to multiply 1 point, but scratch space is empty */ + scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0); + CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1)); + secp256k1_scratch_destroy(scratch_empty); + + /* Try to multiply 1 point, but callback returns false */ + CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); + + /* 2-point */ + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); + secp256k1_gej_neg(&r2, &r2); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + + /* 2-point with G scalar */ + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); + secp256k1_gej_neg(&r2, &r2); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + /* Check infinite outputs of various forms */ + for (ncount = 0; ncount < count; ncount++) { + secp256k1_ge ptg; + size_t i, j; + size_t sizes[] = { 2, 10, 32 }; + + for (j = 0; j < 3; j++) { + for (i = 0; i < 32; i++) { + random_scalar_order(&sc[i]); + secp256k1_ge_set_infinity(&pt[i]); + } + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + for (j = 0; j < 3; j++) { + for (i = 0; i < 32; i++) { + random_group_element_test(&ptg); + pt[i] = ptg; + secp256k1_scalar_set_int(&sc[i], 0); + } + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + for (j = 0; j < 3; j++) { + random_group_element_test(&ptg); + for (i = 0; i < 16; i++) { + random_scalar_order(&sc[2*i]); + secp256k1_scalar_negate(&sc[2*i + 1], &sc[2*i]); + pt[2 * i] = ptg; + pt[2 * i + 1] = ptg; + } + + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + + random_scalar_order(&sc[0]); + for (i = 0; i < 16; i++) { + random_group_element_test(&ptg); + + sc[2*i] = sc[0]; + sc[2*i+1] = sc[0]; + pt[2 * i] = ptg; + secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]); + } + + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + random_group_element_test(&ptg); + secp256k1_scalar_set_int(&sc[0], 0); + pt[0] = ptg; + for (i = 1; i < 32; i++) { + pt[i] = ptg; + + random_scalar_order(&sc[i]); + secp256k1_scalar_add(&sc[0], &sc[0], &sc[i]); + secp256k1_scalar_negate(&sc[i], &sc[i]); + } + + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + /* Check random points, constant scalar */ + for (ncount = 0; ncount < count; ncount++) { + size_t i; + secp256k1_gej_set_infinity(&r); + + random_scalar_order(&sc[0]); + for (i = 0; i < 20; i++) { + secp256k1_ge ptg; + sc[i] = sc[0]; + random_group_element_test(&ptg); + pt[i] = ptg; + secp256k1_gej_add_ge_var(&r, &r, &pt[i], NULL); + } + + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + secp256k1_gej_neg(&r2, &r2); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + /* Check random scalars, constant point */ + for (ncount = 0; ncount < count; ncount++) { + size_t i; + secp256k1_ge ptg; + secp256k1_gej p0j; + secp256k1_scalar rs; + secp256k1_scalar_set_int(&rs, 0); + + random_group_element_test(&ptg); + for (i = 0; i < 20; i++) { + random_scalar_order(&sc[i]); + pt[i] = ptg; + secp256k1_scalar_add(&rs, &rs, &sc[i]); + } + + secp256k1_gej_set_ge(&p0j, &pt[0]); + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + secp256k1_gej_neg(&r2, &r2); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + /* Sanity check that zero scalars don't cause problems */ + for (ncount = 0; ncount < 20; ncount++) { + random_scalar_order(&sc[ncount]); + random_group_element_test(&pt[ncount]); + } + + secp256k1_scalar_clear(&sc[0]); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + secp256k1_scalar_clear(&sc[1]); + secp256k1_scalar_clear(&sc[2]); + secp256k1_scalar_clear(&sc[3]); + secp256k1_scalar_clear(&sc[4]); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); + CHECK(secp256k1_gej_is_infinity(&r)); + + /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */ + { + const size_t TOP = 8; + size_t s0i, s1i; + size_t t0i, t1i; + secp256k1_ge ptg; + secp256k1_gej ptgj; + + random_group_element_test(&ptg); + secp256k1_gej_set_ge(&ptgj, &ptg); + + for(t0i = 0; t0i < TOP; t0i++) { + for(t1i = 0; t1i < TOP; t1i++) { + secp256k1_gej t0p, t1p; + secp256k1_scalar t0, t1; + + secp256k1_scalar_set_int(&t0, (t0i + 1) / 2); + secp256k1_scalar_cond_negate(&t0, t0i & 1); + secp256k1_scalar_set_int(&t1, (t1i + 1) / 2); + secp256k1_scalar_cond_negate(&t1, t1i & 1); + + secp256k1_ecmult(&ctx->ecmult_ctx, &t0p, &ptgj, &t0, &szero); + secp256k1_ecmult(&ctx->ecmult_ctx, &t1p, &ptgj, &t1, &szero); + + for(s0i = 0; s0i < TOP; s0i++) { + for(s1i = 0; s1i < TOP; s1i++) { + secp256k1_scalar tmp1, tmp2; + secp256k1_gej expected, actual; + + secp256k1_ge_set_gej(&pt[0], &t0p); + secp256k1_ge_set_gej(&pt[1], &t1p); + + secp256k1_scalar_set_int(&sc[0], (s0i + 1) / 2); + secp256k1_scalar_cond_negate(&sc[0], s0i & 1); + secp256k1_scalar_set_int(&sc[1], (s1i + 1) / 2); + secp256k1_scalar_cond_negate(&sc[1], s1i & 1); + + secp256k1_scalar_mul(&tmp1, &t0, &sc[0]); + secp256k1_scalar_mul(&tmp2, &t1, &sc[1]); + secp256k1_scalar_add(&tmp1, &tmp1, &tmp2); + + secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero); + CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); + secp256k1_gej_neg(&expected, &expected); + secp256k1_gej_add_var(&actual, &actual, &expected, NULL); + CHECK(secp256k1_gej_is_infinity(&actual)); + } + } + } + } + } +} + +void test_secp256k1_pippenger_bucket_window_inv(void) { + int i; + + CHECK(secp256k1_pippenger_bucket_window_inv(0) == 0); + for(i = 1; i <= PIPPENGER_MAX_BUCKET_WINDOW; i++) { +#ifdef USE_ENDOMORPHISM + /* Bucket_window of 8 is not used with endo */ + if (i == 8) { + continue; + } +#endif + CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)) == i); + if (i != PIPPENGER_MAX_BUCKET_WINDOW) { + CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)+1) > i); + } + } +} + +/** + * Probabilistically test the function returning the maximum number of possible points + * for a given scratch space. + */ +void test_ecmult_multi_pippenger_max_points(void) { + size_t scratch_size = secp256k1_rand_int(256); + size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12); + secp256k1_scratch *scratch; + size_t n_points_supported; + int bucket_window = 0; + + for(; scratch_size < max_size; scratch_size+=256) { + scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size); + CHECK(scratch != NULL); + n_points_supported = secp256k1_pippenger_max_points(scratch); + if (n_points_supported == 0) { + secp256k1_scratch_destroy(scratch); + continue; + } + bucket_window = secp256k1_pippenger_bucket_window(n_points_supported); + CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS)); + secp256k1_scratch_deallocate_frame(scratch); + secp256k1_scratch_destroy(scratch); + } + CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW); +} + +/** + * Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to + * 1 <= i <= num points. + */ +void test_ecmult_multi_batching(void) { + static const int n_points = 2*ECMULT_PIPPENGER_THRESHOLD; + secp256k1_scalar scG; + secp256k1_scalar szero; + secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_scalar) * n_points); + secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * n_points); + secp256k1_gej r; + secp256k1_gej r2; + ecmult_multi_data data; + int i; + secp256k1_scratch *scratch; + + secp256k1_gej_set_infinity(&r2); + secp256k1_scalar_set_int(&szero, 0); + + /* Get random scalars and group elements and compute result */ + random_scalar_order(&scG); + secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r2, &szero, &scG); + for(i = 0; i < n_points; i++) { + secp256k1_ge ptg; + secp256k1_gej ptgj; + random_group_element_test(&ptg); + secp256k1_gej_set_ge(&ptgj, &ptg); + pt[i] = ptg; + random_scalar_order(&sc[i]); + secp256k1_ecmult(&ctx->ecmult_ctx, &ptgj, &ptgj, &sc[i], NULL); + secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL); + } + data.sc = sc; + data.pt = pt; + + /* Test with empty scratch space */ + scratch = secp256k1_scratch_create(&ctx->error_callback, 0); + CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1)); + secp256k1_scratch_destroy(scratch); + + /* Test with space for 1 point in pippenger. That's not enough because + * ecmult_multi selects strauss which requires more memory. */ + scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); + CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1)); + secp256k1_scratch_destroy(scratch); + + secp256k1_gej_neg(&r2, &r2); + for(i = 1; i <= n_points; i++) { + if (i > ECMULT_PIPPENGER_THRESHOLD) { + int bucket_window = secp256k1_pippenger_bucket_window(i); + size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window); + scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); + } else { + size_t scratch_size = secp256k1_strauss_scratch_size(i); + scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); + } + CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + secp256k1_scratch_destroy(scratch); + } + free(sc); + free(pt); +} + +void run_ecmult_multi_tests(void) { + secp256k1_scratch *scratch; + + test_secp256k1_pippenger_bucket_window_inv(); + test_ecmult_multi_pippenger_max_points(); + scratch = secp256k1_scratch_create(&ctx->error_callback, 819200); + test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); + test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single); + test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single); + secp256k1_scratch_destroy(scratch); + + /* Run test_ecmult_multi with space for exactly one point */ + scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); + test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); + secp256k1_scratch_destroy(scratch); + + test_ecmult_multi_batching(); +} + void test_wnaf(const secp256k1_scalar *number, int w) { secp256k1_scalar x, two, t; int wnaf[256]; @@ -2541,6 +3034,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 +3044,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); - for (i = WNAF_SIZE(w); i >= 0; --i) { + for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) { secp256k1_scalar t; int v = wnaf[i]; CHECK(v != 0); /* check nonzero */ @@ -2575,6 +3070,110 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) { CHECK(secp256k1_scalar_eq(&x, &num)); } +void test_fixed_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, shift; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num = *number; + + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&shift, 1 << w); + /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */ +#ifdef USE_ENDOMORPHISM + for (i = 0; i < 16; ++i) { + secp256k1_scalar_shr_int(&num, 8); + } +#endif + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + + for (i = WNAF_SIZE(w)-1; i >= 0; --i) { + secp256k1_scalar t; + int v = wnaf[i]; + CHECK(v == 0 || v & 1); /* check parity */ + CHECK(v > -(1 << w)); /* check range above */ + CHECK(v < (1 << w)); /* check range below */ + + secp256k1_scalar_mul(&x, &x, &shift); + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + /* If skew is 1 then add 1 to num */ + secp256k1_scalar_cadd_bit(&num, 0, skew == 1); + CHECK(secp256k1_scalar_eq(&x, &num)); +} + +/* Checks that the first 8 elements of wnaf are equal to wnaf_expected and the + * rest is 0.*/ +void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) { + int i; + for (i = WNAF_SIZE(w)-1; i >= 8; --i) { + CHECK(wnaf[i] == 0); + } + for (i = 7; i >= 0; --i) { + CHECK(wnaf[i] == wnaf_expected[i]); + } +} + +void test_fixed_wnaf_small(void) { + int w = 4; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num; + + secp256k1_scalar_set_int(&num, 0); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + for (i = WNAF_SIZE(w)-1; i >= 0; --i) { + int v = wnaf[i]; + CHECK(v == 0); + } + CHECK(skew == 0); + + secp256k1_scalar_set_int(&num, 1); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + for (i = WNAF_SIZE(w)-1; i >= 1; --i) { + int v = wnaf[i]; + CHECK(v == 0); + } + CHECK(wnaf[0] == 1); + CHECK(skew == 0); + + { + int wnaf_expected[8] = { 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf }; + secp256k1_scalar_set_int(&num, 0xffffffff); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 0); + } + { + int wnaf_expected[8] = { -1, -1, -1, -1, -1, -1, -1, 0xf }; + secp256k1_scalar_set_int(&num, 0xeeeeeeee); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 1); + } + { + int wnaf_expected[8] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + secp256k1_scalar_set_int(&num, 0x01010101); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 0); + } + { + int wnaf_expected[8] = { -0xf, 0, 0xf, -0xf, 0, 0xf, 1, 0 }; + secp256k1_scalar_set_int(&num, 0x01ef1ef1); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 0); + } +} + void run_wnaf(void) { int i; secp256k1_scalar n = {{0}}; @@ -2585,12 +3184,15 @@ void run_wnaf(void) { test_constant_wnaf(&n, 4); n.d[0] = 2; test_constant_wnaf(&n, 4); + /* Test 0 */ + test_fixed_wnaf_small(); /* Random tests */ for (i = 0; i < count; i++) { random_scalar_order(&n); test_wnaf(&n, 4+(i%10)); test_constant_wnaf_negate(&n); test_constant_wnaf(&n, 4 + (i % 10)); + test_fixed_wnaf(&n, 4 + (i % 10)); } secp256k1_scalar_set_int(&n, 0); CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); @@ -3055,6 +3657,7 @@ void run_ec_pubkey_parse_test(void) { ecount = 0; VG_UNDEF(&pubkey, sizeof(pubkey)); CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1); + CHECK(secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp, &pubkey, pubkeyc, 65) == 1); VG_CHECK(&pubkey, sizeof(pubkey)); CHECK(ecount == 0); VG_UNDEF(&ge, sizeof(ge)); @@ -3177,7 +3780,7 @@ void run_eckey_edge_case_test(void) { VG_CHECK(&pubkey, sizeof(pubkey)); CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); pubkey_negone = pubkey; - /* Tweak of zero leaves the value changed. */ + /* Tweak of zero leaves the value unchanged. */ memset(ctmp2, 0, 32); CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); @@ -3668,6 +4271,7 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ #ifdef ENABLE_OPENSSL_TESTS ECDSA_SIG *sig_openssl; + const BIGNUM *r = NULL, *s = NULL; const unsigned char *sigptr; unsigned char roundtrip_openssl[2048]; int len_openssl = 2048; @@ -3719,15 +4323,16 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ sigptr = sig; parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL); if (parsed_openssl) { - valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256; + ECDSA_SIG_get0(sig_openssl, &r, &s); + valid_openssl = !BN_is_negative(r) && !BN_is_negative(s) && BN_num_bits(r) > 0 && BN_num_bits(r) <= 256 && BN_num_bits(s) > 0 && BN_num_bits(s) <= 256; if (valid_openssl) { unsigned char tmp[32] = {0}; - BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r)); + BN_bn2bin(r, tmp + 32 - BN_num_bytes(r)); valid_openssl = memcmp(tmp, max_scalar, 32) < 0; } if (valid_openssl) { unsigned char tmp[32] = {0}; - BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s)); + BN_bn2bin(s, tmp + 32 - BN_num_bytes(s)); valid_openssl = memcmp(tmp, max_scalar, 32) < 0; } } @@ -4407,6 +5012,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}; @@ -4431,7 +5052,7 @@ int main(int argc, char **argv) { } } else { FILE *frand = fopen("/dev/urandom", "r"); - if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) { + if ((frand == NULL) || fread(&seed16, sizeof(seed16), 1, frand) != sizeof(seed16)) { uint64_t t = time(NULL) * (uint64_t)1337; seed16[0] ^= t; seed16[1] ^= t >> 8; @@ -4442,7 +5063,9 @@ int main(int argc, char **argv) { seed16[6] ^= t >> 48; seed16[7] ^= t >> 56; } - fclose(frand); + if (frand) { + fclose(frand); + } } secp256k1_rand_seed(seed16); @@ -4451,6 +5074,7 @@ int main(int argc, char **argv) { /* initialize */ run_context_tests(); + run_scratch_tests(); ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); if (secp256k1_rand_bits(1)) { secp256k1_rand256(run32); @@ -4459,6 +5083,7 @@ int main(int argc, char **argv) { run_rand_bits(); run_rand_int(); + run_util_tests(); run_sha256_tests(); run_hmac_sha256_tests(); @@ -4492,6 +5117,7 @@ int main(int argc, char **argv) { run_ecmult_constants(); run_ecmult_gen_blind(); run_ecmult_const_tests(); + run_ecmult_multi_tests(); run_ec_combine(); /* endomorphism tests */ @@ -4525,6 +5151,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]); diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c index b040bb0733..ab9779b02f 100644 --- a/src/tests_exhaustive.c +++ b/src/tests_exhaustive.c @@ -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); } } @@ -182,6 +182,46 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr } } +typedef struct { + secp256k1_scalar sc[2]; + secp256k1_ge pt[2]; +} ecmult_multi_data; + +static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_multi_data *data = (ecmult_multi_data*) cbdata; + *sc = data->sc[idx]; + *pt = data->pt[idx]; + return 1; +} + +void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k, x, y; + secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096); + for (i = 0; i < order; i++) { + for (j = 0; j < order; j++) { + for (k = 0; k < order; k++) { + for (x = 0; x < order; x++) { + for (y = 0; y < order; y++) { + secp256k1_gej tmp; + secp256k1_scalar g_sc; + ecmult_multi_data data; + + secp256k1_scalar_set_int(&data.sc[0], i); + secp256k1_scalar_set_int(&data.sc[1], j); + secp256k1_scalar_set_int(&g_sc, k); + data.pt[0] = group[x]; + data.pt[1] = group[y]; + + secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2); + ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp); + } + } + } + } + } + secp256k1_scratch_destroy(scratch); +} + void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { secp256k1_fe x; unsigned char x_bin[32]; @@ -456,6 +496,7 @@ int main(void) { #endif test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER); test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_ecmult_multi(ctx, group, EXHAUSTIVE_TEST_ORDER); test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); diff --git a/src/util.h b/src/util.h index b0441d8e30..aca79d7be1 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013-2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -76,6 +76,40 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ return ret; } +static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void *ptr, size_t size) { + void *ret = realloc(ptr, size); + if (ret == NULL) { + secp256k1_callback_call(cb, "Out of memory"); + } + 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