diff --git a/.gitignore b/.gitignore index ccdef02b294..085bd54d0de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ bench_inv bench_ecdh bench_ecmult +bench_generator +bench_rangeproof bench_schnorrsig bench_sign bench_verify diff --git a/.travis.yml b/.travis.yml index ce8d6391b2f..9b7fe6f3757 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,23 +17,26 @@ compiler: - gcc env: global: - - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 + - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no matrix: + - WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int64 RECOVERY=yes - - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 - - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 ASM=x86_64 - BIGNUM=no - - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes + - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no - CPPFLAGS=-DDETERMINISTIC - CFLAGS=-O0 CTIMETEST=no + - CFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST=no - ECMULTGENPRECISION=2 - ECMULTGENPRECISION=8 - - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD= + - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD= matrix: fast_finish: true include: @@ -81,7 +84,7 @@ matrix: - libc6-dbg:i386 # S390x build (big endian system) - compiler: gcc - env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST= + env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST= arch: s390x # We use this to install macOS dependencies instead of the built in `homebrew` plugin, diff --git a/Makefile.am b/Makefile.am index 023fa6067fb..1cba7a34f51 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,10 +151,30 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif +if ENABLE_MODULE_MUSIG +include src/modules/musig/Makefile.am.include +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 + if ENABLE_MODULE_EXTRAKEYS include src/modules/extrakeys/Makefile.am.include endif diff --git a/configure.ac b/configure.ac index eb3b449beca..f4a341bc0b7 100644 --- a/configure.ac +++ b/configure.ac @@ -126,11 +126,31 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) +AC_ARG_ENABLE(module_musig, + AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), + [enable_module_musig=$enableval], + [enable_module_musig=no]) + AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [enable_module_recovery=$enableval], [enable_module_recovery=no]) +AC_ARG_ENABLE(module_generator, + AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=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=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=no]]), + [enable_module_whitelist=$enableval], + [enable_module_whitelist=no]) + AC_ARG_ENABLE(module_extrakeys, AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]), [enable_module_extrakeys=$enableval], @@ -146,6 +166,16 @@ AC_ARG_ENABLE(external_default_callbacks, [use_external_default_callbacks=$enableval], [use_external_default_callbacks=no]) +AC_ARG_ENABLE(module_surjectionproof, + AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), + [enable_module_surjectionproof=$enableval], + [enable_module_surjectionproof=no]) + +AC_ARG_ENABLE(reduced_surjection_proof_size, + AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), + [use_reduced_surjection_proof_size=$enableval], + [use_reduced_surjection_proof_size=no]) + dnl Test-only override of the (autodetected by the C code) "widemul" setting. dnl Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default). AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto]) @@ -197,6 +227,12 @@ else CFLAGS="-O2 $CFLAGS" 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 # Temporarily switch to an environment for the native compiler save_cross_compiling=$cross_compiling @@ -252,6 +288,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 @@ -432,10 +474,30 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi +if test x"$enable_module_musig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) +fi + 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 + if test x"$enable_module_schnorrsig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) enable_module_extrakeys=yes @@ -455,14 +517,48 @@ if test x"$use_external_default_callbacks" = x"yes"; then AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used]) fi +if test x"$use_reduced_surjection_proof_size" = x"yes"; then + AC_DEFINE(USE_REDUCED_SURJECTION_PROOF_SIZE, 1, [Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, disabling parsing and verification]) +fi + if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([******]) 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 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([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) + + + if test x"$enable_module_schnorrsig" != x"yes"; then + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) + fi + fi + + 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_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_extrakeys" = x"yes"; then AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.]) fi @@ -472,6 +568,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]) @@ -486,11 +594,17 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) 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_MUSIG], [test x"$enable_module_musig" = 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([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = 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"]) +AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = 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/contrib/travis.sh b/contrib/travis.sh index 24cc9315cb1..c667c151150 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -17,7 +17,8 @@ fi --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ - --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG"\ --with-valgrind="$WITH_VALGRIND" \ --host="$HOST" $EXTRAFLAGS diff --git a/include/secp256k1_generator.h b/include/secp256k1_generator.h new file mode 100644 index 00000000000..5b5ee647763 --- /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: gen: 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* gen, + 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: gen: a pointer to a generator + */ +SECP256K1_API int secp256k1_generator_serialize( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_generator* gen +) 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_musig.h b/include/secp256k1_musig.h new file mode 100644 index 00000000000..46fe6c2850f --- /dev/null +++ b/include/secp256k1_musig.h @@ -0,0 +1,488 @@ +#ifndef SECP256K1_MUSIG_H +#define SECP256K1_MUSIG_H + +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** This module implements a Schnorr-based multi-signature scheme called MuSig + * (https://eprint.iacr.org/2018/068.pdf). It is compatible with bip-schnorr. + * There's an example C source file in the module's directory + * (src/modules/musig/example.c) that demonstrates how it can be used. + * + * The documentation in this include file is for reference and may not be sufficient + * for users to begin using the library. A full description of API usage can be found + * in src/modules/musig/musig.md + */ + +/** Data structure containing auxiliary data generated in `pubkey_combine` and + * required for `session_*_init`. + * Fields: + * magic: Set during initialization in `pubkey_combine` to allow + * detecting an uninitialized object. + * pk_hash: The 32-byte hash of the original public keys + * pk_parity: Whether the MuSig-aggregated point was negated when + * converting it to the combined xonly pubkey. + * is_tweaked: Whether the combined pubkey was tweaked + * tweak: If is_tweaked, array with the 32-byte tweak + * internal_key_parity: If is_tweaked, the parity of the combined pubkey + * before tweaking + */ +typedef struct { + uint64_t magic; + unsigned char pk_hash[32]; + int pk_parity; + int is_tweaked; + unsigned char tweak[32]; + int internal_key_parity; +} secp256k1_musig_pre_session; + +/** Data structure containing data related to a signing session resulting in a single + * signature. + * + * This structure is not opaque, but it MUST NOT be copied or read or written to it + * directly. A signer who is online throughout the whole process and can keep this + * structure in memory can use the provided API functions for a safe standard + * workflow. See https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ + * for more details about the risks associated with serializing or deserializing this + * structure. + * + * Fields: + * magic: Set in `musig_session_init` to allow detecting an + * uninitialized object. + * round: Current round of the session + * pre_session: Auxiliary data created in `pubkey_combine` + * combined_pk: MuSig-computed combined xonly public key + * n_signers: Number of signers + * msg: The 32-byte message (hash) to be signed + * is_msg_set: Whether the above message has been set + * has_secret_data: Whether this session object has a signers' secret data; if this + * is `false`, it may still be used for verification purposes. + * seckey: If `has_secret_data`, the signer's secret key + * secnonce: If `has_secret_data`, the signer's secret nonce + * nonce: If `has_secret_data`, the signer's public nonce + * nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all + * signers' commitments + * combined_nonce: If round >= 2, the summed combined public nonce + * combined_nonce_parity: If round >= 2, the parity of the Y coordinate of above + * nonce. + */ +typedef struct { + uint64_t magic; + int round; + secp256k1_musig_pre_session pre_session; + secp256k1_xonly_pubkey combined_pk; + uint32_t n_signers; + int is_msg_set; + unsigned char msg[32]; + int has_secret_data; + unsigned char seckey[32]; + unsigned char secnonce[32]; + secp256k1_xonly_pubkey nonce; + int partial_nonce_parity; + unsigned char nonce_commitments_hash[32]; + secp256k1_xonly_pubkey combined_nonce; + int combined_nonce_parity; +} secp256k1_musig_session; + +/** Data structure containing data on all signers in a single session. + * + * The workflow for this structure is as follows: + * + * 1. This structure is initialized with `musig_session_init` or + * `musig_session_init_verifier`, which set the `index` field, and zero out + * all other fields. The public session is initialized with the signers' + * nonce_commitments. + * + * 2. In a non-public session the nonce_commitments are set with the function + * `musig_get_public_nonce`, which also returns the signer's public nonce. This + * ensures that the public nonce is not exposed until all commitments have been + * received. + * + * 3. Each individual data struct should be updated with `musig_set_nonce` once a + * nonce is available. This function takes a single signer data struct rather than + * an array because it may fail in the case that the provided nonce does not match + * the commitment. In this case, it is desirable to identify the exact party whose + * nonce was inconsistent. + * + * Fields: + * present: indicates whether the signer's nonce is set + * index: index of the signer in the MuSig key aggregation + * nonce: public nonce, must be a valid curvepoint if the signer is `present` + * nonce_commitment: commitment to the nonce, or all-bits zero if a commitment + * has not yet been set + */ +typedef struct { + int present; + uint32_t index; + secp256k1_xonly_pubkey nonce; + unsigned char nonce_commitment[32]; +} secp256k1_musig_session_signer_data; + +/** Opaque data structure that holds a MuSig partial signature. + * + * 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 32 bytes in size, and can be safely copied/moved. If you need + * to convert to a format suitable for storage, transmission, or comparison, use the + * `musig_partial_signature_serialize` and `musig_partial_signature_parse` + * functions. + */ +typedef struct { + unsigned char data[32]; +} secp256k1_musig_partial_signature; + +/** Computes a combined public key and the hash of the given public keys. + * Different orders of `pubkeys` result in different `combined_pk`s. + * + * Returns: 1 if the public keys were successfully combined, 0 otherwise + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * scratch: scratch space used to compute the combined pubkey by + * multiexponentiation. If NULL, an inefficient algorithm is used. + * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) + * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in + * `musig_session_init` or `musig_pubkey_tweak_add`. + * In: pubkeys: input array of public keys to combine. The order is important; + * a different order will result in a different combined public + * key (cannot be NULL) + * n_pubkeys: length of pubkeys array. Must be greater than 0. + */ +SECP256K1_API int secp256k1_musig_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + secp256k1_xonly_pubkey *combined_pk, + secp256k1_musig_pre_session *pre_session, + const secp256k1_xonly_pubkey *pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Tweak an x-only public key by adding the generator multiplied with tweak32 + * to it. The resulting output_pubkey with the given internal_pubkey and tweak + * passes `secp256k1_xonly_pubkey_tweak_test`. + * + * This function is only useful before initializing a signing session. If you + * are only computing a public key, but not intending to create a signature for + * it, you can just use `secp256k1_xonly_pubkey_tweak_add`. Can only be called + * once with a given pre_session. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * pre_session: pointer to a `musig_pre_session` struct initialized in + * `musig_pubkey_combine` (cannot be NULL) + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0 (cannot + * be NULL) + * In: internal_pubkey: pointer to the `combined_pk` from + * `musig_pubkey_combine` to which the tweak is applied. + * (cannot be NULL). + * tweak32: pointer to a 32-byte tweak. If the tweak is invalid + * according to secp256k1_ec_seckey_verify, this function + * returns 0. For uniformly random 32-byte arrays the + * chance of being invalid is negligible (around 1 in + * 2^128) (cannot be NULL). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_musig_pre_session *pre_session, + secp256k1_pubkey *output_pubkey, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Initializes a signing session for a signer + * + * Returns: 1: session is successfully initialized + * 0: session could not be initialized: secret key or secret nonce overflow + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers` (cannot be NULL) + * nonce_commitment32: filled with a 32-byte commitment to the generated nonce + * (cannot be NULL) + * In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be + * NULL). If a non-unique session_id32 was given then a partial + * signature will LEAK THE SECRET KEY. + * msg32: the 32-byte message to be signed. Shouldn't be NULL unless you + * require sharing nonce commitments before the message is known + * because it reduces nonce misuse resistance. If NULL, must be + * set with `musig_session_get_public_nonce`. + * combined_pk: the combined xonly public key of all signers (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct after initializing + * it with `musig_pubkey_combine` and optionally provided to + * `musig_pubkey_tweak_add` (cannot be NULL). + * n_signers: length of signers array. Number of signers participating in + * the MuSig. Must be greater than 0 and at most 2^32 - 1. + * my_index: index of this signer in the signers array. Must be less + * than `n_signers`. + * seckey: the signer's 32-byte secret key (cannot be NULL) + */ +SECP256K1_API int secp256k1_musig_session_init( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + unsigned char *nonce_commitment32, + const unsigned char *session_id32, + const unsigned char *msg32, + const secp256k1_xonly_pubkey *combined_pk, + const secp256k1_musig_pre_session *pre_session, + size_t n_signers, + size_t my_index, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); + +/** Gets the signer's public nonce given a list of all signers' data with + * commitments. Called by participating signers after + * `secp256k1_musig_session_init` and after all nonce commitments have + * been collected + * + * Returns: 1: public nonce is written in nonce + * 0: signer data is missing commitments or session isn't initialized + * for signing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: the signing session to get the nonce from (cannot be NULL) + * signers: an array of signers' data initialized with + * `musig_session_init`. Array length must equal to + * `n_commitments` (cannot be NULL) + * Out: nonce32: filled with a 32-byte public nonce which is supposed to be + * sent to the other signers and then used in `musig_set nonce` + * (cannot be NULL) + * In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL) + * n_commitments: the length of commitments and signers array. Must be the total + * number of signers participating in the MuSig. + * msg32: the 32-byte message to be signed. Must be NULL if already + * set with `musig_session_init` otherwise can not be NULL. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + unsigned char *nonce32, + const unsigned char *const *commitments, + size_t n_commitments, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Initializes a verifier session that can be used for verifying nonce commitments + * and partial signatures. It does not have secret key material and therefore can not + * be used to create signatures. + * + * Returns: 1 when session is successfully initialized, 0 otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers`(cannot be NULL) + * In: msg32: the 32-byte message to be signed (cannot be NULL) + * combined_pk: the combined xonly public key of all signers (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct from + * `musig_pubkey_combine` (cannot be NULL) + * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) + * commitments: array of pointers to 32-byte nonce commitments. Array + * length must equal to `n_signers` (cannot be NULL) + * n_signers: length of signers and commitments array. Number of signers + * participating in the MuSig. Must be greater than 0 and at most + * 2^32 - 1. + */ +SECP256K1_API int secp256k1_musig_session_init_verifier( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + const unsigned char *msg32, + const secp256k1_xonly_pubkey *combined_pk, + const secp256k1_musig_pre_session *pre_session, + const unsigned char *const *commitments, + size_t n_signers +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Checks a signer's public nonce against a commitment to said nonce, and update + * data structure if they match + * + * Returns: 1: commitment was valid, data structure updated + * 0: commitment was invalid, nothing happened + * Args: ctx: pointer to a context object (cannot be NULL) + * signer: pointer to the signer data to update (cannot be NULL). Must have + * been used with `musig_session_get_public_nonce` or initialized + * with `musig_session_init_verifier`. + * In: nonce32: signer's alleged public nonce (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( + const secp256k1_context* ctx, + secp256k1_musig_session_signer_data *signer, + const unsigned char *nonce32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates a session with the combined public nonce of all signers. The combined + * public nonce is the sum of every signer's public nonce. + * + * Returns: 1: nonces are successfully combined + * 0: a signer's nonce is missing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: session to update with the combined public nonce (cannot be + * NULL) + * signers: an array of signers' data, which must have had public nonces + * set with `musig_set_nonce`. Array length must equal to `n_signers` + * (cannot be NULL) + * n_signers: the length of the signers array. Must be the total number of + * signers participating in the MuSig. + * Out: nonce_parity: if non-NULL, a pointer to an integer that indicates the + * parity of the combined public nonce. Used for adaptor + * signatures. + * adaptor: point to add to the combined public nonce. If NULL, nothing is + * added to the combined nonce. + */ +SECP256K1_API int secp256k1_musig_session_combine_nonces( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + const secp256k1_musig_session_signer_data *signers, + size_t n_signers, + int *nonce_parity, + const secp256k1_pubkey *adaptor +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a MuSig partial signature or adaptor signature + * + * Returns: 1 when the signature could be serialized, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: out32: pointer to a 32-byte array to store the serialized signature + * In: sig: pointer to the signature + */ +SECP256K1_API int secp256k1_musig_partial_signature_serialize( + const secp256k1_context* ctx, + unsigned char *out32, + const secp256k1_musig_partial_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse and verify a MuSig partial signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in32: pointer to the 32-byte signature to be parsed + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature verification with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_musig_partial_signature_parse( + const secp256k1_context* ctx, + secp256k1_musig_partial_signature* sig, + const unsigned char *in32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Produces a partial signature + * + * Returns: 1: partial signature constructed + * 0: session in incorrect or inconsistent state + * Args: ctx: pointer to a context object (cannot be NULL) + * session: active signing session for which the combined nonce has been + * computed (cannot be NULL) + * Out: partial_sig: partial signature (cannot be NULL) + */ +SECP256K1_API int secp256k1_musig_partial_sign( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + secp256k1_musig_partial_signature *partial_sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Checks that an individual partial signature verifies + * + * This function is essential when using protocols with adaptor signatures. + * However, it is not essential for regular MuSig's, in the sense that if any + * partial signatures does not verify, the full signature will also not verify, so the + * problem will be caught. But this function allows determining the specific party + * who produced an invalid signature, so that signing can be restarted without them. + * + * Returns: 1: partial signature verifies + * 0: invalid signature or bad data + * Args: ctx: pointer to a context object (cannot be NULL) + * session: active session for which the combined nonce has been computed + * (cannot be NULL) + * signer: data for the signer who produced this signature (cannot be NULL) + * In: partial_sig: signature to verify (cannot be NULL) + * pubkey: public key of the signer who produced the signature (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + const secp256k1_musig_session_signer_data *signer, + const secp256k1_musig_partial_signature *partial_sig, + const secp256k1_xonly_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Combines partial signatures + * + * Returns: 1: all partial signatures have values in range. Does NOT mean the + * resulting signature verifies. + * 0: some partial signature are missing or had s or r out of range + * Args: ctx: pointer to a context object (cannot be NULL) + * session: initialized session for which the combined nonce has been + * computed (cannot be NULL) + * Out: sig64: complete signature (cannot be NULL) + * In: partial_sigs: array of partial signatures to combine (cannot be NULL) + * n_sigs: number of signatures in the partial_sigs array + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + unsigned char *sig64, + const secp256k1_musig_partial_signature *partial_sigs, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Converts a partial signature to an adaptor signature by adding a given secret + * adaptor. + * + * Returns: 1: signature and secret adaptor contained valid values + * 0: otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: adaptor_sig: adaptor signature to produce (cannot be NULL) + * In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL) + * sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot + * be NULL) + * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces` + */ +SECP256K1_API int secp256k1_musig_partial_sig_adapt( + const secp256k1_context* ctx, + secp256k1_musig_partial_signature *adaptor_sig, + const secp256k1_musig_partial_signature *partial_sig, + const unsigned char *sec_adaptor32, + int nonce_parity +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Extracts a secret adaptor from a MuSig, given all parties' partial + * signatures. This function will not fail unless given grossly invalid data; if it + * is merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier steps of + * any protocol that uses this function. + * + * Returns: 1: signatures contained valid data such that an adaptor could be extracted + * 0: otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL) + * In: sig64: complete 2-of-2 signature (cannot be NULL) + * partial_sigs: array of partial signatures (cannot be NULL) + * n_partial_sigs: number of elements in partial_sigs array + * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( + const secp256k1_context* ctx, + unsigned char *sec_adaptor32, + const unsigned char *sig64, + const secp256k1_musig_partial_signature *partial_sigs, + size_t n_partial_sigs, + int nonce_parity +) 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 00000000000..74671061063 --- /dev/null +++ b/include/secp256k1_rangeproof.h @@ -0,0 +1,291 @@ +#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); + +/** 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 +); + +/** 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 00000000000..ab7a4a9ec50 --- /dev/null +++ b/include/secp256k1_surjectionproof.h @@ -0,0 +1,270 @@ +#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 + +/** Maximum number of inputs that may be used in a surjection proof */ +#define SECP256K1_SURJECTIONPROOF_MAX_USED_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_USED_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_USED_INPUTS)]; +} secp256k1_surjectionproof; + +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE +/** 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); +#endif + +/** 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 + * To be used to initialize stack-allocated secp256k1_surjectionproof struct + * 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 + * Must be <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS + * 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 allocation and initialization function; decides on inputs to use + * Returns 0: inputs could not be selected, or malloc failure + * n: inputs were selected after n iterations of random selection + * + * In: ctx: pointer to a context object + * proof_out_p: a pointer to a pointer to `secp256k1_surjectionproof*`. + * the newly-allocated struct pointer will be saved here. + * 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_out_p: The pointer to newly-allocated proof whose bitvector will be initialized. + * In case of failure, the pointer will be NULL. + * input_index: The index of the actual input that is secretly mapped to the output + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_allocate_initialized( + const secp256k1_context* ctx, + secp256k1_surjectionproof** proof_out_p, + 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 destroy function + * deallocates the struct that was allocated with secp256k1_surjectionproof_allocate_initialized + * + * In: proof: pointer to secp256k1_surjectionproof struct + */ +SECP256K1_API void secp256k1_surjectionproof_destroy( + secp256k1_surjectionproof* proof +) SECP256K1_ARG_NONNULL(1); + +/** 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); + + +#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE +/** 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); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/secp256k1_whitelist.h b/include/secp256k1_whitelist.h new file mode 100644 index 00000000000..c536c11a7bf --- /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 00000000000..1cc97b65560 --- /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_generator.c b/src/bench_generator.c new file mode 100644 index 00000000000..d3b251e4085 --- /dev/null +++ b/src/bench_generator.c @@ -0,0 +1,60 @@ +/********************************************************************** + * 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 iters) { + int i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < iters; 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 iters) { + int i; + bench_generator_t *data = (bench_generator_t*)arg; + + for (i = 0; i < iters; 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; + int iters = get_iters(20000); + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, iters); + run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/bench_rangeproof.c b/src/bench_rangeproof.c new file mode 100644 index 00000000000..14e22e8a801 --- /dev/null +++ b/src/bench_rangeproof.c @@ -0,0 +1,65 @@ +/********************************************************************** + * 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 iters) { + int i; + bench_rangeproof_t *data = (bench_rangeproof_t*)arg; + + for (i = 0; i < iters/data->min_bits; 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; + int iters; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + data.min_bits = 32; + iters = data.min_bits*get_iters(32); + + run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/bench_sign.c b/src/bench_sign.c index c6b2942cc0c..0fd6c005214 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; +} bench_sign_data; static void bench_sign_setup(void* arg) { int i; - bench_sign *data = (bench_sign*)arg; + bench_sign_data *data = (bench_sign_data*)arg; for (i = 0; i < 32; i++) { data->msg[i] = i + 1; @@ -28,7 +28,7 @@ static void bench_sign_setup(void* arg) { static void bench_sign_run(void* arg, int iters) { int i; - bench_sign *data = (bench_sign*)arg; + bench_sign_data *data = (bench_sign_data*)arg; unsigned char sig[74]; for (i = 0; i < iters; i++) { @@ -45,7 +45,7 @@ static void bench_sign_run(void* arg, int iters) { } int main(void) { - bench_sign data; + bench_sign_data data; int iters = get_iters(20000); diff --git a/src/bench_verify.c b/src/bench_verify.c index 272d3e5cc4b..c9efa5fb5fd 100644 --- a/src/bench_verify.c +++ b/src/bench_verify.c @@ -29,11 +29,11 @@ typedef struct { #ifdef ENABLE_OPENSSL_TESTS EC_GROUP* ec_group; #endif -} benchmark_verify_t; +} bench_verify_data; -static void benchmark_verify(void* arg, int iters) { +static void bench_verify(void* arg, int iters) { int i; - benchmark_verify_t* data = (benchmark_verify_t*)arg; + bench_verify_data* data = (bench_verify_data*)arg; for (i = 0; i < iters; i++) { secp256k1_pubkey pubkey; @@ -51,9 +51,9 @@ static void benchmark_verify(void* arg, int iters) { } #ifdef ENABLE_OPENSSL_TESTS -static void benchmark_verify_openssl(void* arg, int iters) { +static void bench_verify_openssl(void* arg, int iters) { int i; - benchmark_verify_t* data = (benchmark_verify_t*)arg; + bench_verify_data* data = (bench_verify_data*)arg; for (i = 0; i < iters; i++) { data->sig[data->siglen - 1] ^= (i & 0xFF); @@ -84,7 +84,7 @@ int main(void) { int i; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; - benchmark_verify_t data; + bench_verify_data data; int iters = get_iters(20000); @@ -103,10 +103,10 @@ int main(void) { data.pubkeylen = 33; CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, iters); + run_benchmark("ecdsa_verify", bench_verify, NULL, NULL, &data, 10, iters); #ifdef ENABLE_OPENSSL_TESTS data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1); - run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, iters); + run_benchmark("ecdsa_verify_openssl", bench_verify_openssl, NULL, NULL, &data, 10, iters); EC_GROUP_free(data.ec_group); #endif diff --git a/src/bench_whitelist.c b/src/bench_whitelist.c new file mode 100644 index 00000000000..a725f1835b7 --- /dev/null +++ b/src/bench_whitelist.c @@ -0,0 +1,108 @@ +/********************************************************************** + * 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 "util.h" +#include "bench.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, int iters) { + bench_data* data = (bench_data*)arg; + int i; + for (i = 0; i < iters; i++) { + 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, int iters) { + char str[32]; + sprintf(str, "whitelist_%i", (int)data->n_keys); + run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, iters); +} + +void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_testrand256(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; + int iters = get_iters(5); + + 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, iters); + } + + secp256k1_context_destroy(data.ctx); + return(0); +} diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index a9e8b3c76c4..caa87e3871b 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -595,11 +595,11 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); - state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, 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); + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); - if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) { + if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 5378d2f301b..e365f92aa66 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -180,7 +180,7 @@ int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *ke ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32); secp256k1_keypair_save(keypair, &sk, &pk); - memczero(keypair, sizeof(*keypair), !ret); + secp256k1_memczero(keypair, sizeof(*keypair), !ret); secp256k1_scalar_clear(&sk); return ret; diff --git a/src/modules/generator/Makefile.am.include b/src/modules/generator/Makefile.am.include new file mode 100644 index 00000000000..69933e999ca --- /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 00000000000..9217169c21b --- /dev/null +++ b/src/modules/generator/main_impl.h @@ -0,0 +1,222 @@ +/********************************************************************** + * 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; + 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); + 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); + 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 00000000000..b173ddfac0d --- /dev/null +++ b/src/modules/generator/tests_impl.h @@ -0,0 +1,227 @@ +/********************************************************************** + * 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_testrand256(key); + secp256k1_testrand256(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]; + 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); + } + + /* There is no range restriction on the value, but the blinder must be a + * valid scalar. Check that an invalid blinder causes the call to fail + * but not crash. */ + memset(v, 0xff, 32); + CHECK(secp256k1_generator_generate(ctx, &gen, v)); + memset(s, 0xff, 32); + CHECK(!secp256k1_generator_generate_blinded(ctx, &gen, v, s)); +} + +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/musig/Makefile.am.include b/src/modules/musig/Makefile.am.include new file mode 100644 index 00000000000..0cd254d8a01 --- /dev/null +++ b/src/modules/musig/Makefile.am.include @@ -0,0 +1,16 @@ +include_HEADERS += include/secp256k1_musig.h +noinst_HEADERS += src/modules/musig/main_impl.h +noinst_HEADERS += src/modules/musig/tests_impl.h + +noinst_PROGRAMS += example_musig +example_musig_SOURCES = src/modules/musig/example.c +example_musig_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include $(SECP_INCLUDES) +if !ENABLE_COVERAGE +example_musig_CPPFLAGS += -DVERIFY +endif +example_musig_LDADD = libsecp256k1.la $(SECP_LIBS) +example_musig_LDFLAGS = -static + +if USE_TESTS +TESTS += example_musig +endif diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c new file mode 100644 index 00000000000..2c5b7006116 --- /dev/null +++ b/src/modules/musig/example.c @@ -0,0 +1,168 @@ +/********************************************************************** + * Copyright (c) 2018 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * This file demonstrates how to use the MuSig module to create a multisignature. + * Additionally, see the documentation in include/secp256k1_musig.h. + */ + +#include +#include +#include +#include +#include + + /* Number of public keys involved in creating the aggregate signature */ +#define N_SIGNERS 3 + /* Create a key pair and store it in seckey and pubkey */ +int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k1_xonly_pubkey *pubkey) { + int ret; + secp256k1_keypair keypair; + FILE *frand = fopen("/dev/urandom", "r"); + if (frand == NULL) { + return 0; + } + do { + if(!fread(seckey, 32, 1, frand)) { + fclose(frand); + return 0; + } + /* The probability that this not a valid secret key is approximately 2^-128 */ + } while (!secp256k1_ec_seckey_verify(ctx, seckey)); + fclose(frand); + ret = secp256k1_keypair_create(ctx, &keypair, seckey); + ret &= secp256k1_keypair_xonly_pub(ctx, pubkey, NULL, &keypair); + + return ret; +} + +/* Sign a message hash with the given key pairs and store the result in sig */ +int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msg32, unsigned char *sig64) { + secp256k1_musig_session musig_session[N_SIGNERS]; + unsigned char nonce_commitment[N_SIGNERS][32]; + const unsigned char *nonce_commitment_ptr[N_SIGNERS]; + secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; + unsigned char nonce[N_SIGNERS][32]; + int i, j; + secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; + + for (i = 0; i < N_SIGNERS; i++) { + FILE *frand; + unsigned char session_id32[32]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + + /* Create combined pubkey and initialize signer data */ + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pubkeys, N_SIGNERS)) { + return 0; + } + /* Create random session ID. It is absolutely necessary that the session ID + * is unique for every call of secp256k1_musig_session_init. Otherwise + * it's trivial for an attacker to extract the secret key! */ + frand = fopen("/dev/urandom", "r"); + if(frand == NULL) { + return 0; + } + if (!fread(session_id32, 32, 1, frand)) { + fclose(frand); + return 0; + } + fclose(frand); + /* Initialize session */ + if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { + return 0; + } + nonce_commitment_ptr[i] = &nonce_commitment[i][0]; + } + /* Communication round 1: Exchange nonce commitments */ + for (i = 0; i < N_SIGNERS; i++) { + /* Set nonce commitments in the signer data and get the own public nonce */ + if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) { + return 0; + } + } + /* Communication round 2: Exchange nonces */ + for (i = 0; i < N_SIGNERS; i++) { + for (j = 0; j < N_SIGNERS; j++) { + if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], nonce[j])) { + /* Signer j's nonce does not match the nonce commitment. In this case + * abort the protocol. If you make another attempt at finishing the + * protocol, create a new session (with a fresh session ID!). */ + return 0; + } + } + if (!secp256k1_musig_session_combine_nonces(ctx, &musig_session[i], signer_data[i], N_SIGNERS, NULL, NULL)) { + return 0; + } + } + for (i = 0; i < N_SIGNERS; i++) { + if (!secp256k1_musig_partial_sign(ctx, &musig_session[i], &partial_sig[i])) { + return 0; + } + } + /* Communication round 3: Exchange partial signatures */ + for (i = 0; i < N_SIGNERS; i++) { + for (j = 0; j < N_SIGNERS; j++) { + /* To check whether signing was successful, it suffices to either verify + * the the combined signature with the combined public key using + * secp256k1_schnorrsig_verify, or verify all partial signatures of all + * signers individually. Verifying the combined signature is cheaper but + * verifying the individual partial signatures has the advantage that it + * can be used to determine which of the partial signatures are invalid + * (if any), i.e., which of the partial signatures cause the combined + * signature to be invalid and thus the protocol run to fail. It's also + * fine to first verify the combined sig, and only verify the individual + * sigs if it does not work. + */ + if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], &pubkeys[j])) { + return 0; + } + } + } + return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig64, partial_sig, N_SIGNERS); +} + + int main(void) { + secp256k1_context* ctx; + int i; + unsigned char seckeys[N_SIGNERS][32]; + secp256k1_xonly_pubkey pubkeys[N_SIGNERS]; + secp256k1_xonly_pubkey combined_pk; + unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; + unsigned char sig[64]; + + /* Create a context for signing and verification */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + printf("Creating key pairs......"); + for (i = 0; i < N_SIGNERS; i++) { + if (!create_keypair(ctx, seckeys[i], &pubkeys[i])) { + printf("FAILED\n"); + return 1; + } + } + printf("ok\n"); + printf("Combining public keys..."); + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys, N_SIGNERS)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Signing message........."); + if (!sign(ctx, seckeys, pubkeys, msg, sig)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Verifying signature....."); + if (!secp256k1_schnorrsig_verify(ctx, sig, msg, &combined_pk)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + secp256k1_context_destroy(ctx); + return 0; +} + diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h new file mode 100644 index 00000000000..d397eab31cf --- /dev/null +++ b/src/modules/musig/main_impl.h @@ -0,0 +1,702 @@ +/********************************************************************** + * Copyright (c) 2018 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_MODULE_MUSIG_MAIN_ +#define _SECP256K1_MODULE_MUSIG_MAIN_ + +#include +#include "include/secp256k1.h" +#include "include/secp256k1_musig.h" +#include "hash.h" + +/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ +static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey *pk, size_t np) { + secp256k1_sha256 sha; + size_t i; + + secp256k1_sha256_initialize(&sha); + for (i = 0; i < np; i++) { + unsigned char ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, ser, &pk[i])) { + return 0; + } + secp256k1_sha256_write(&sha, ser, 32); + } + secp256k1_sha256_finalize(&sha, ell); + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */ +static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + + sha->s[0] = 0x0fd0690cul; + sha->s[1] = 0xfefeae97ul; + sha->s[2] = 0x996eac7ful; + sha->s[3] = 0x5c30d864ul; + sha->s[4] = 0x8c4a0573ul; + sha->s[5] = 0xaca1a22ful; + sha->s[6] = 0x6f43b801ul; + sha->s[7] = 0x85ce27cdul; + sha->bytes = 64; +} + +/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */ +static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) { + secp256k1_sha256 sha; + unsigned char buf[32]; + size_t i; + + secp256k1_musig_sha256_init_tagged(&sha); + secp256k1_sha256_write(&sha, ell, 32); + /* We're hashing the index of the signer instead of its public key as specified + * in the MuSig paper. This reduces the total amount of data that needs to be + * hashed. + * Additionally, it prevents creating identical musig_coefficients for identical + * public keys. A participant Bob could choose his public key to be the same as + * Alice's, then replay Alice's messages (nonce and partial signature) to create + * a valid partial signature. This is not a problem for MuSig per se, but could + * result in subtle issues with protocols building on threshold signatures. + * With the assumption that public keys are unique, hashing the index is + * equivalent to hashing the public key. Because the public key can be + * identified by the index given the ordered list of public keys (included in + * ell), the index is just a different encoding of the public key.*/ + for (i = 0; i < sizeof(uint32_t); i++) { + unsigned char c = idx; + secp256k1_sha256_write(&sha, &c, 1); + idx >>= 8; + } + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(r, buf, NULL); +} + +typedef struct { + const secp256k1_context *ctx; + unsigned char ell[32]; + const secp256k1_xonly_pubkey *pks; +} secp256k1_musig_pubkey_combine_ecmult_data; + +/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ +static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; + secp256k1_musig_coefficient(sc, ctx->ell, idx); + return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); +} + +static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { + uint32_t i; + for (i = 0; i < n_signers; i++) { + memset(&signers[i], 0, sizeof(signers[i])); + signers[i].index = i; + signers[i].present = 0; + } +} + +static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL; + +int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys) { + secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; + secp256k1_gej pkj; + secp256k1_ge pkp; + int pk_parity; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(n_pubkeys > 0); + + ecmult_data.ctx = ctx; + ecmult_data.pks = pubkeys; + if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) { + return 0; + } + if (!secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) { + return 0; + } + secp256k1_ge_set_gej(&pkp, &pkj); + secp256k1_fe_normalize(&pkp.y); + pk_parity = secp256k1_extrakeys_ge_even_y(&pkp); + secp256k1_xonly_pubkey_save(combined_pk, &pkp); + + if (pre_session != NULL) { + pre_session->magic = pre_session_magic; + memcpy(pre_session->pk_hash, ecmult_data.ell, 32); + pre_session->pk_parity = pk_parity; + pre_session->is_tweaked = 0; + } + return 1; +} + +int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_ge pk; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); + /* This function can only be called once because otherwise signing would not + * succeed */ + ARG_CHECK(pre_session->is_tweaked == 0); + + pre_session->internal_key_parity = pre_session->pk_parity; + if(!secp256k1_xonly_pubkey_tweak_add(ctx, output_pubkey, internal_pubkey, tweak32)) { + return 0; + } + + memcpy(pre_session->tweak, tweak32, 32); + pre_session->is_tweaked = 1; + + if (!secp256k1_pubkey_load(ctx, &pk, output_pubkey)) { + return 0; + } + pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk); + return 1; +} + +static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL; + +int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { + unsigned char combined_ser[32]; + int overflow; + secp256k1_scalar secret; + secp256k1_scalar mu; + secp256k1_sha256 sha; + secp256k1_gej pj; + secp256k1_ge p; + unsigned char nonce_ser[32]; + size_t nonce_ser_size = sizeof(nonce_ser); + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(nonce_commitment32 != NULL); + ARG_CHECK(session_id32 != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); + ARG_CHECK(seckey != NULL); + + ARG_CHECK(n_signers > 0); + ARG_CHECK(n_signers <= UINT32_MAX); + ARG_CHECK(my_index < n_signers); + + memset(session, 0, sizeof(*session)); + + session->magic = session_magic; + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->is_msg_set = 1; + } else { + session->is_msg_set = 0; + } + memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + session->pre_session = *pre_session; + session->has_secret_data = 1; + session->n_signers = (uint32_t) n_signers; + secp256k1_musig_signers_init(signers, session->n_signers); + + /* Compute secret key */ + secp256k1_scalar_set_b32(&secret, seckey, &overflow); + if (overflow) { + secp256k1_scalar_clear(&secret); + return 0; + } + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); + /* Compute the signer's public key point and determine if the secret is + * negated before signing. That happens if if the signer's pubkey has an odd + * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR + * (if tweaked) the internal key has an odd Y coordinate. + * + * This can be seen by looking at the secret key belonging to `combined_pk`. + * Let's define + * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key + * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function + * that normalizes a point to an even Y by negating if necessary similar to + * secp256k1_extrakeys_ge_even_y. Then we have + * P := |P'| + t*G where t is the tweak. + * And the combined xonly public key is + * |P| = x*G + * where x = sum_i(b_i*mu_i*x_i) + b'*t + * b' = -1 if P != |P|, 1 otherwise + * b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1 + * otherwise. + */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_fe_normalize(&p.y); + if((secp256k1_fe_is_odd(&p.y) + + session->pre_session.pk_parity + + (session->pre_session.is_tweaked + && session->pre_session.internal_key_parity)) + % 2 == 1) { + secp256k1_scalar_negate(&secret, &secret); + } + secp256k1_scalar_mul(&secret, &secret, &mu); + secp256k1_scalar_get_b32(session->seckey, &secret); + + /* Compute secret nonce */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, session_id32, 32); + if (session->is_msg_set) { + secp256k1_sha256_write(&sha, msg32, 32); + } + secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk); + secp256k1_sha256_write(&sha, combined_ser, 32); + secp256k1_sha256_write(&sha, seckey, 32); + secp256k1_sha256_finalize(&sha, session->secnonce); + secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow); + if (overflow) { + secp256k1_scalar_clear(&secret); + return 0; + } + + /* Compute public nonce and commitment */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_fe_normalize_var(&p.y); + session->partial_nonce_parity = secp256k1_extrakeys_ge_even_y(&p); + secp256k1_xonly_pubkey_save(&session->nonce, &p); + + secp256k1_sha256_initialize(&sha); + secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce); + secp256k1_sha256_write(&sha, nonce_ser, nonce_ser_size); + secp256k1_sha256_finalize(&sha, nonce_commitment32); + + session->round = 0; + secp256k1_scalar_clear(&secret); + return 1; +} + +int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) { + secp256k1_sha256 sha; + unsigned char nonce_commitments_hash[32]; + size_t i; + unsigned char nonce_ser[32]; + size_t nonce_ser_size = sizeof(nonce_ser); + (void) ctx; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(session->magic == session_magic); + ARG_CHECK(signers != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(commitments != NULL); + + ARG_CHECK(session->round == 0); + /* If the message was not set during initialization it must be set now. */ + ARG_CHECK(!(!session->is_msg_set && msg32 == NULL)); + /* The message can only be set once. */ + ARG_CHECK(!(session->is_msg_set && msg32 != NULL)); + ARG_CHECK(session->has_secret_data); + ARG_CHECK(n_commitments == session->n_signers); + for (i = 0; i < n_commitments; i++) { + ARG_CHECK(commitments[i] != NULL); + } + + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->is_msg_set = 1; + } + secp256k1_sha256_initialize(&sha); + for (i = 0; i < n_commitments; i++) { + memcpy(signers[i].nonce_commitment, commitments[i], 32); + secp256k1_sha256_write(&sha, commitments[i], 32); + } + secp256k1_sha256_finalize(&sha, nonce_commitments_hash); + memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); + + secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce); + memcpy(nonce, &nonce_ser, nonce_ser_size); + session->round = 1; + return 1; +} + +int secp256k1_musig_session_init_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); + ARG_CHECK(commitments != NULL); + /* Check n_signers before checking commitments to allow testing the case where + * n_signers is big without allocating the space. */ + ARG_CHECK(n_signers > 0); + ARG_CHECK(n_signers <= UINT32_MAX); + for (i = 0; i < n_signers; i++) { + ARG_CHECK(commitments[i] != NULL); + } + (void) ctx; + + memset(session, 0, sizeof(*session)); + + session->magic = session_magic; + memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + session->pre_session = *pre_session; + session->n_signers = (uint32_t) n_signers; + secp256k1_musig_signers_init(signers, session->n_signers); + + session->pre_session = *pre_session; + session->is_msg_set = 1; + memcpy(session->msg, msg32, 32); + session->has_secret_data = 0; + + for (i = 0; i < n_signers; i++) { + memcpy(signers[i].nonce_commitment, commitments[i], 32); + } + session->round = 1; + return 1; +} + +int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const unsigned char *nonce) { + secp256k1_sha256 sha; + unsigned char commit[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(signer != NULL); + ARG_CHECK(nonce != NULL); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, nonce, 32); + secp256k1_sha256_finalize(&sha, commit); + + if (memcmp(commit, signer->nonce_commitment, 32) != 0) { + return 0; + } + memcpy(&signer->nonce, nonce, sizeof(*nonce)); + if (!secp256k1_xonly_pubkey_parse(ctx, &signer->nonce, nonce)) { + return 0; + } + signer->present = 1; + return 1; +} + +int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_parity, const secp256k1_pubkey *adaptor) { + secp256k1_gej combined_noncej; + secp256k1_ge combined_noncep; + secp256k1_ge noncep; + secp256k1_sha256 sha; + unsigned char nonce_commitments_hash[32]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(session->magic == session_magic); + ARG_CHECK(session->round == 1); + ARG_CHECK(n_signers == session->n_signers); + + secp256k1_sha256_initialize(&sha); + secp256k1_gej_set_infinity(&combined_noncej); + for (i = 0; i < n_signers; i++) { + if (!signers[i].present) { + return 0; + } + secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32); + secp256k1_xonly_pubkey_load(ctx, &noncep, &signers[i].nonce); + secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); + } + secp256k1_sha256_finalize(&sha, nonce_commitments_hash); + /* If the signers' commitments changed between get_public_nonce and now we + * have to abort because in that case they may have seen our nonce before + * creating their commitment. That can happen if the signer_data given to + * this function is different to the signer_data given to get_public_nonce. + * */ + if (session->has_secret_data + && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { + return 0; + } + + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_pubkey_load(ctx, &noncep, adaptor); + secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); + } + + /* Negate nonce if Y coordinate is not square */ + secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); + secp256k1_fe_normalize_var(&combined_noncep.y); + session->combined_nonce_parity = secp256k1_extrakeys_ge_even_y(&combined_noncep); + if (nonce_parity != NULL) { + *nonce_parity = session->combined_nonce_parity; + } + secp256k1_xonly_pubkey_save(&session->combined_nonce, &combined_noncep); + session->round = 2; + return 1; +} + +int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out32 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out32, sig->data, 32); + return 1; +} + +int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp256k1_musig_partial_signature* sig, const unsigned char *in32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in32 != NULL); + memcpy(sig->data, in32, 32); + return 1; +} + +/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ +static void secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { + unsigned char buf[32]; + secp256k1_ge rp; + secp256k1_sha256 sha; + + VERIFY_CHECK(session->round >= 2); + + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_xonly_pubkey_load(ctx, &rp, &session->combined_nonce); + secp256k1_fe_get_b32(buf, &rp.x); + secp256k1_sha256_write(&sha, buf, 32); + + secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha, session->msg, 32); + secp256k1_sha256_finalize(&sha, msghash); +} + +int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) { + unsigned char msghash[32]; + int overflow; + secp256k1_scalar sk; + secp256k1_scalar e, k; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(session->magic == session_magic); + ARG_CHECK(session->round == 2); + ARG_CHECK(session->has_secret_data); + + /* build message hash */ + secp256k1_musig_compute_messagehash(ctx, msghash, session); + secp256k1_scalar_set_b32(&e, msghash, NULL); + + secp256k1_scalar_set_b32(&sk, session->seckey, &overflow); + if (overflow) { + secp256k1_scalar_clear(&sk); + return 0; + } + + secp256k1_scalar_set_b32(&k, session->secnonce, &overflow); + if (overflow || secp256k1_scalar_is_zero(&k)) { + secp256k1_scalar_clear(&sk); + secp256k1_scalar_clear(&k); + return 0; + } + if (session->partial_nonce_parity != session->combined_nonce_parity) { + secp256k1_scalar_negate(&k, &k); + } + + /* Sign */ + secp256k1_scalar_mul(&e, &e, &sk); + secp256k1_scalar_add(&e, &e, &k); + secp256k1_scalar_get_b32(&partial_sig->data[0], &e); + secp256k1_scalar_clear(&sk); + secp256k1_scalar_clear(&k); + + return 1; +} + +int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { + size_t i; + secp256k1_scalar s; + secp256k1_ge noncep; + (void) ctx; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(partial_sigs != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(session->magic == session_magic); + ARG_CHECK(session->round == 2); + + if (n_sigs != session->n_signers) { + return 0; + } + secp256k1_scalar_clear(&s); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + + secp256k1_scalar_set_b32(&term, partial_sigs[i].data, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&s, &s, &term); + } + + /* If there is a tweak then add (or subtract) `msghash` times `tweak` to `s`.*/ + if (session->pre_session.is_tweaked) { + unsigned char msghash[32]; + secp256k1_scalar e, scalar_tweak; + int overflow = 0; + + secp256k1_musig_compute_messagehash(ctx, msghash, session); + secp256k1_scalar_set_b32(&e, msghash, NULL); + secp256k1_scalar_set_b32(&scalar_tweak, session->pre_session.tweak, &overflow); + if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) { + /* This mimics the behavior of secp256k1_ec_seckey_tweak_mul regarding + * overflow and tweak being 0. */ + return 0; + } + if (session->pre_session.pk_parity) { + secp256k1_scalar_negate(&e, &e); + } + secp256k1_scalar_add(&s, &s, &e); + } + + secp256k1_xonly_pubkey_load(ctx, &noncep, &session->combined_nonce); + VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); + secp256k1_fe_normalize(&noncep.x); + secp256k1_fe_get_b32(&sig64[0], &noncep.x); + secp256k1_scalar_get_b32(&sig64[32], &s); + + return 1; +} + +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_xonly_pubkey *pubkey) { + unsigned char msghash[32]; + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_scalar mu; + secp256k1_gej pkj; + secp256k1_gej rj; + secp256k1_ge pkp; + secp256k1_ge rp; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(session != NULL); + ARG_CHECK(signer != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(session->magic == session_magic); + ARG_CHECK(session->round == 2); + ARG_CHECK(signer->present); + + secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); + if (overflow) { + return 0; + } + secp256k1_musig_compute_messagehash(ctx, msghash, session); + secp256k1_scalar_set_b32(&e, msghash, NULL); + + /* Multiplying the messagehash by the musig coefficient is equivalent + * to multiplying the signer's public key by the coefficient, except + * much easier to do. */ + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index); + secp256k1_scalar_mul(&e, &e, &mu); + + if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) { + return 0; + } + + /* If the MuSig-combined point has an odd Y coordinate, the signers will + * sign for the negation of their individual xonly public key such that the + * combined signature is valid for the MuSig aggregated xonly key. If the + * MuSig-combined point was tweaked then `e` is negated if the combined key + * has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/ + if (session->pre_session.pk_parity + != (session->pre_session.is_tweaked + && session->pre_session.internal_key_parity)) { + secp256k1_scalar_negate(&e, &e); + } + + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { + return 0; + } + secp256k1_gej_set_ge(&pkj, &pkp); + secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); + + if (!session->combined_nonce_parity) { + secp256k1_ge_neg(&rp, &rp); + } + secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL); + + return secp256k1_gej_is_infinity(&rj); +} + +int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_parity) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(adaptor_sig != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + + secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + if (overflow) { + secp256k1_scalar_clear(&t); + return 0; + } + + if (nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(adaptor_sig->data, &s); + secp256k1_scalar_clear(&t); + return 1; +} + +int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_parity) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + size_t i; + + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(partial_sigs != NULL); + + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_negate(&t, &t); + + for (i = 0; i < n_partial_sigs; i++) { + secp256k1_scalar_set_b32(&s, partial_sigs[i].data, &overflow); + if (overflow) { + secp256k1_scalar_clear(&t); + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + } + + if (!nonce_parity) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return 1; +} + +#endif diff --git a/src/modules/musig/musig.md b/src/modules/musig/musig.md new file mode 100644 index 00000000000..240e85ca2f3 --- /dev/null +++ b/src/modules/musig/musig.md @@ -0,0 +1,198 @@ +MuSig - Rogue-Key-Resistant Multisignatures Module +=========================== + +This module implements the MuSig [1] multisignature scheme. The majority of +the module is an API designed to be used by signing or auditing participants +in a multisignature scheme. This involves a somewhat complex state machine +and significant effort has been taken to prevent accidental misuse of the +API in ways that could lead to accidental signatures or loss of key material. + +The resulting signatures are valid Schnorr signatures as described in [2]. + +# Theory + +In MuSig all signers contribute key material to a single signing key, +using the equation + + P = sum_i µ_i * P_i + +where `P_i` is the public key of the `i`th signer and `µ_i` is a so-called +_MuSig coefficient_ computed according to the following equation + + L = H(P_1 || P_2 || ... || P_n) + µ_i = H(L || i) + +where H is a hash function modelled as a random oracle. + +To produce a multisignature `(s, R)` on a message `m` using verification key +`P`, signers act as follows: + +1. Each computes a nonce, or ephemeral keypair, `(k_i, R_i)`. Every signer + communicates `H(R_i)` to every participant (both signers and auditors). +2. Upon receipt of every `H(R_i)`, each signer communicates `R_i` to every + participant. The recipients check that each `R_i` is consistent with the + previously-communicated hash. +3. Each signer computes a combined nonce + `R = sum_i R_i` + and shared challenge + `e = H(R || P || m)` + and partial signature + `s_i = k_i + µ_i*x_i*e` + where `x_i` is the secret key corresponding to `P_i`. + +The complete signature is then the `(s, R)` where `s = sum_i s_i` and `R = sum_i R_i`. + +# API Usage + +The following sections describe use of our API, and are mirrored in code in `src/modules/musig/example.c`. + +It is essential to security that signers use a unique uniformly random nonce for all +signing sessions, and that they do not reuse these nonces even in the case that a +signing session fails to complete. To that end, all signing state is encapsulated +in the data structure `secp256k1_musig_session`. The API does not expose any +functionality to serialize or deserialize this structure; it is designed to exist +only in memory. + +Users who need to persist this structure must take additional security measures +which cannot be enforced by a C API. Some guidance is provided in the documentation +for this data structure in `include/secp256k1_musig.h`. + +## Key Generation + +To use MuSig, users must first compute their combined public key `P`, which is +suitable for use on a blockchain or other public key repository. They do this +by calling `secp256k1_musig_pubkey_combine`. + +This function takes as input a list of public keys `P_i` in the argument +`pubkeys`. It outputs the combined public key `P` in the out-pointer `combined_pk` +and hash `L` in the out-pointer `pk_hash32`, if this pointer is non-NULL. + +## Signing + +A participant who wishes to sign a message (as opposed to observing/auditing the +signature process, which is also a supported mode) acts as follows. + +### Signing Participant + +1. The signer starts the session by calling `secp256k1_musig_session_init`. + This function outputs + - an initialized session state in the out-pointer `session` + - an array of initialized signer data in the out-pointer `signers` + - a commitment `H(R_i)` to a nonce in the out-pointer `nonce_commitment32` + It takes as input + - a unique session ID `session_id32` + - (optionally) a message to be signed `msg32` + - the combined public key output from `secp256k1_musig_pubkey_combine` + - the public key hash output from `secp256k1_musig_pubkey_combine` + - the signer's index `i` `my_index` + - the signer's secret key `seckey` +2. The signer then communicates `H(R_i)` to all other signers, and receives + commitments `H(R_j)` from all other signers `j`. These hashes are simply + length-32 byte arrays which can be communicated however is communicated. +3. Once all signers nonce commitments have been received, the signer records + these commitments with the function `secp256k1_musig_session_get_public_nonce`. + If the signer did not provide a message to `secp256k1_musig_session_init`, + a message must be provided now. + This function updates in place + - the session state `session` + - the array of signer data `signers` + taking in as input the list of commitments `commitments` and outputting the + signer's public nonce `R_i` in the out-pointer `nonce`. +4. The signer then communicates `R_i` to all other signers, and receives `R_j` + from each signer `j`. On receipt of a nonce `R_j` he calls the function + `secp256k1_musig_set_nonce` to record this fact. This function checks that + the received nonce is consistent with the previously-received nonce and will + return 0 in this case. The signer must also call this function with his own + nonce and his own index `i`. + These nonces `R_i` are secp256k1 public keys; they should be serialized using + `secp256k1_ec_pubkey_serialize` and parsed with `secp256k1_ec_pubkey_parse`. +5. Once all nonces have been exchanged in this way, signers are able to compute + their partial signatures. They do so by calling `secp256k1_musig_session_combine_nonces` + which updates in place + - the session state `session` + - the array of signer data `signers` + It outputs an auxiliary integer `nonce_is_negated` and has an auxiliary input + `adaptor`. Both of these may be set to NULL for ordinary signing purposes. +6. The signer computes a partial signature `s_i` using the function + `secp256k1_musig_partial_sign` which takes the session state as input and + partial signature as output. +7. The signer then communicates the partial signature `s_i` to all other signers, or + to a central coordinator. These partial signatures should be serialized using + `musig_partial_signature_serialize` and parsed using `musig_partial_signature_parse`. +8. Each signer calls `secp256k1_musig_partial_sig_verify` on the other signers' partial + signatures to verify their correctness. If only the validity of the final signature + is important, not assigning blame, this step can be skipped. +9. Any signer, or central coordinator, may combine the partial signatures to obtain + a complete signature using `secp256k1_musig_partial_sig_combine`. This function takes + a signing session and array of MuSig partial signatures, and outputs a single + Schnorr signature. + +### Non-signing Participant + +A participant who wants to verify the signing process, i.e. check that nonce commitments +are consistent and partial signatures are correct without contributing a partial signature, +may do so using the above instructions except for the following changes: + +1. A signing session should be produced using `musig_session_init_verifier` + rather than `musig_session_init`; this function takes no secret data or + signer index. +2. The participant receives nonce commitments, public nonces and partial signatures, + but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce` + and `secp256k1_musig_partial_sign` are not called. + +### Verifier + +The final signature is simply a valid Schnorr signature using the combined public key. It +can be verified using the `secp256k1_schnorrsig_verify` with the correct message and +public key output from `secp256k1_musig_pubkey_combine`. + +## Atomic Swaps + +The signing API supports the production of "adaptor signatures", modified partial signatures +which are offset by an auxiliary secret known to one party. That is, +1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`. +2. When combining nonces, each party adds `T` to the total nonce used in the signature. +3. The party who knows `t` must "adapt" their partial signature with `t` to complete the + signature. +4. Any party who sees both the final signature and the original partial signatures + can compute `t`. + +Using these adaptor signatures, two 2-of-2 MuSig signing protocols can be executed in +parallel such that one party's partial signatures are made atomic. That is, when the other +party learns one partial signature, she automatically learns the other. This has applications +in cross-chain atomic swaps. + +Such a protocol can be executed as follows. Consider two participants, Alice and Bob, who +are simultaneously producing 2-of-2 multisignatures for two blockchains A and B. They act +as follows. + +1. Before the protocol begins, Bob chooses a 32-byte auxiliary secret `t` at random and + computes a corresponding public point `T` by calling `secp256k1_ec_pubkey_create`. + He communicates `T` to Alice. +2. Together, the parties execute steps 1-4 of the signing protocol above. +3. At step 5, when combining the two parties' public nonces, both parties call + `secp256k1_musig_session_combine_nonces` with `adaptor` set to `T` and `nonce_is_negated` + set to a non-NULL pointer to int. +4. Steps 6 and 7 proceed as before. Step 8, verifying the partial signatures, is now + essential to the security of the protocol and must not be omitted! + +The above steps are executed identically for both signing sessions. However, step 9 will +not work as before, since the partial signatures will not add up to a valid total signature. +Additional steps must be taken, and it is at this point that the two signing sessions +diverge. From here on we consider "Session A" which benefits Alice (e.g. which sends her +coins) and "Session B" which benefits Bob (e.g. which sends him coins). + +5. In Session B, Bob calls `secp256k1_musig_partial_sig_adapt` with his partial signature + and `t`, to produce an adaptor signature. He can then call `secp256k1_musig_partial_sig_combine` + with this adaptor signature and Alice's partial signature, to produce a complete + signature for blockchain B. +6. Alice reads this signature from blockchain B. She calls `secp256k1_musig_extract_secret_adaptor`, + passing the complete signature along with her and Bob's partial signatures from Session B. + This function outputs `t`, which until this point was only known to Bob. +7. In Session A, Alice is now able to replicate Bob's action, calling + `secp256k1_musig_partial_sig_adapt` with her own partial signature and `t`, ultimately + producing a complete signature on blockchain A. + +[1] https://eprint.iacr.org/2018/068 +[2] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki + diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h new file mode 100644 index 00000000000..22a5d05b132 --- /dev/null +++ b/src/modules/musig/tests_impl.h @@ -0,0 +1,969 @@ +/********************************************************************** + * Copyright (c) 2018 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_MUSIG_TESTS_ +#define _SECP256K1_MODULE_MUSIG_TESTS_ + +#include "secp256k1_musig.h" + +int secp256k1_xonly_pubkey_create(secp256k1_xonly_pubkey *pk, const unsigned char *seckey) { + int ret; + secp256k1_keypair keypair; + ret = secp256k1_keypair_create(ctx, &keypair, seckey); + ret &= secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair); + return ret; +} + +/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig combine, sign, verify + * test. */ +void musig_simple_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signer0[2]; + secp256k1_musig_session_signer_data signer1[2]; + unsigned char nonce_commitment[2][32]; + unsigned char msg[32]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + unsigned char session_id[2][32]; + secp256k1_xonly_pubkey pk[2]; + const unsigned char *ncs[2]; + unsigned char public_nonce[3][32]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char final_sig[64]; + + secp256k1_testrand256(session_id[0]); + secp256k1_testrand256(session_id[1]); + secp256k1_testrand256(sk[0]); + secp256k1_testrand256(sk[1]); + secp256k1_testrand256(msg); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1); + + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], public_nonce[1]) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1); + + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + + CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, &combined_pk) == 1); +} + +void musig_api_tests(secp256k1_scratch_space *scratch) { + secp256k1_scratch_space *scratch_small; + secp256k1_musig_session session[2]; + secp256k1_musig_session session_uninitialized; + secp256k1_musig_session verifier_session; + secp256k1_musig_session_signer_data signer0[2]; + secp256k1_musig_session_signer_data signer1[2]; + secp256k1_musig_session_signer_data verifier_signer_data[2]; + secp256k1_musig_partial_signature partial_sig[2]; + secp256k1_musig_partial_signature partial_sig_adapted[2]; + secp256k1_musig_partial_signature partial_sig_overflow; + unsigned char final_sig[64]; + unsigned char final_sig_cmp[64]; + + unsigned char buf[32]; + unsigned char sk[2][32]; + unsigned char ones[32]; + unsigned char session_id[2][32]; + unsigned char nonce_commitment[2][32]; + int combined_nonce_parity; + const unsigned char *ncs[2]; + unsigned char msg[32]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + secp256k1_musig_pre_session pre_session_uninitialized; + secp256k1_xonly_pubkey pk[2]; + unsigned char tweak[32]; + + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor1[32]; + secp256k1_pubkey adaptor; + + /** setup **/ + 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); + int ecount; + + 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); + + memset(ones, 0xff, 32); + /* Simulate structs being uninitialized by setting it to 0s. We don't want + * to produce undefined behavior by actually providing uninitialized + * structs. */ + memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); + memset(&session_uninitialized, 0, sizeof(session_uninitialized)); + + secp256k1_testrand256(session_id[0]); + secp256k1_testrand256(session_id[1]); + secp256k1_testrand256(sk[0]); + secp256k1_testrand256(sk[1]); + secp256k1_testrand256(msg); + secp256k1_testrand256(sec_adaptor); + secp256k1_testrand256(tweak); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); + + + /** main test body **/ + + /* Key combination */ + ecount = 0; + CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(ecount == 2); + /* pubkey_combine does not require a scratch space */ + CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(ecount == 2); + /* A small scratch space works too, but will result in using an ineffecient algorithm */ + scratch_small = secp256k1_scratch_space_create(ctx, 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk, 2) == 1); + secp256k1_scratch_space_destroy(ctx, scratch_small); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 0) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + + /** Tweaking */ + ecount = 0; + { + secp256k1_xonly_pubkey tmp_internal_pk = combined_pk; + secp256k1_pubkey tmp_output_pk; + secp256k1_musig_pre_session tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); + /* Reset pre_session */ + tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); + CHECK(ecount == 2); + tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 3); + /* Uninitialized pre_session */ + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &pre_session_uninitialized, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 4); + /* Using the same pre_session twice does not work */ + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 5); + tmp_pre_session = pre_session; + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, NULL, &tmp_internal_pk, tweak) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, NULL, tweak) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, NULL) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, ones) == 0); + CHECK(ecount == 8); + } + + /** Session creation **/ + ecount = 0; + CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); + CHECK(ecount == 8); + /* Uninitialized pre_session */ + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); + CHECK(ecount == 10); + /* If more than UINT32_MAX fits in a size_t, test that session_init + * rejects n_signers that high. */ + if (SIZE_MAX > UINT32_MAX) { + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + } + CHECK(ecount == 11); + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); + CHECK(ecount == 12); + /* secret key overflows */ + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); + CHECK(ecount == 12); + + CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + + ecount = 0; + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_init_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); + CHECK(ecount == 6); + if (SIZE_MAX > UINT32_MAX) { + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); + } + CHECK(ecount == 7); + CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); + + /** Signing step 0 -- exchange nonce commitments */ + ecount = 0; + { + unsigned char nonce[32]; + secp256k1_musig_session session_0_tmp; + + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + + /* Can obtain public nonce after commitments have been exchanged; still can't sign */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, nonce, ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session_0_tmp, &partial_sig[0]) == 0); + CHECK(ecount == 1); + } + + /** Signing step 1 -- exchange nonces */ + ecount = 0; + { + unsigned char public_nonce[3][32]; + secp256k1_musig_session session_0_tmp; + + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 2, NULL) == 1); + CHECK(ecount == 0); + /* Reset session */ + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, public_nonce[0], ncs, 2, NULL) == 0); + CHECK(ecount == 1); + /* uninitialized session */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, public_nonce[0], ncs, 2, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, public_nonce[0], ncs, 2, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], NULL, 2, NULL) == 0); + CHECK(ecount == 5); + /* Number of commitments and number of signers are different */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 1, NULL) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1); + + CHECK(secp256k1_musig_set_nonce(none, &signer0[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[0]) == 0); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_set_nonce(none, NULL, public_nonce[0]) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); + CHECK(ecount == 8); + + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer1[1], public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], public_nonce[1]) == 1); + + ecount = 0; + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, &adaptor) == 1); + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &combined_nonce_parity, &adaptor) == 0); + CHECK(ecount == 1); + /* Uninitialized session */ + CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &combined_nonce_parity, &adaptor) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &combined_nonce_parity, &adaptor) == 0); + CHECK(ecount == 3); + /* Number of signers differs from number during intialization */ + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &combined_nonce_parity, &adaptor) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1); + CHECK(ecount == 4); + memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp)); + CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, NULL) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &combined_nonce_parity, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &combined_nonce_parity, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &combined_nonce_parity, &adaptor) == 1); + } + + /** Signing step 2 -- partial signatures */ + ecount = 0; + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0); + CHECK(ecount == 1); + /* Uninitialized session */ + CHECK(secp256k1_musig_partial_sign(none, &session_uninitialized, &partial_sig[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); + CHECK(ecount == 3); + + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); + /* observer can't sign */ + CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); + CHECK(ecount == 4); + + ecount = 0; + CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_signature_serialize(none, NULL, &partial_sig[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_signature_serialize(none, buf, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], buf) == 1); + CHECK(secp256k1_musig_partial_signature_parse(none, NULL, buf) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig_overflow, ones) == 1); + + /** Partial signature verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_verify(none, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_verify(sign, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[1], &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 3); + /* Unitialized session */ + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session_uninitialized, &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0); + CHECK(ecount == 7); + + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1); + CHECK(ecount == 7); + + /** Adaptor signature verification */ + memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); + ecount = 0; + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, combined_nonce_parity) == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, combined_nonce_parity) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, combined_nonce_parity) == 0); + CHECK(ecount == 3); + + /** Signing combining and verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); + + CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0); + CHECK(ecount == 1); + /* Unitialized session */ + CHECK(secp256k1_musig_partial_sig_combine(none, &session_uninitialized, final_sig, partial_sig_adapted, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); + CHECK(ecount == 4); + { + secp256k1_musig_partial_signature partial_sig_tmp[2]; + partial_sig_tmp[0] = partial_sig_adapted[0]; + partial_sig_tmp[1] = partial_sig_overflow; + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0); + } + CHECK(ecount == 4); + /* Wrong number of partial sigs */ + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); + CHECK(ecount == 4); + + CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1); + + /** Secret adaptor can be extracted from signature */ + ecount = 0; + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, combined_nonce_parity) == 1); + CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0); + CHECK(ecount == 2); + { + unsigned char final_sig_tmp[64]; + memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp)); + memcpy(&final_sig_tmp[32], ones, 32); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, combined_nonce_parity) == 0); + } + CHECK(ecount == 2); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0); + CHECK(ecount == 3); + { + secp256k1_musig_partial_signature partial_sig_tmp[2]; + partial_sig_tmp[0] = partial_sig[0]; + partial_sig_tmp[1] = partial_sig_overflow; + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, combined_nonce_parity) == 0); + } + CHECK(ecount == 3); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, 1) == 1); + + /** cleanup **/ + memset(&session, 0, sizeof(session)); + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + +/* Initializes two sessions, one use the given parameters (session_id, + * nonce_commitments, etc.) except that `session_tmp` uses new signers with different + * public keys. The point of this test is to call `musig_session_get_public_nonce` + * with signers from `session_tmp` who have different public keys than the correct + * ones and return the resulting messagehash. This should not result in a different + * messagehash because the public keys of the signers are only used during session + * initialization. */ +void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id) { + secp256k1_musig_session session; + secp256k1_musig_session session_tmp; + unsigned char nonce_commitment[32]; + secp256k1_musig_session_signer_data signers[2]; + secp256k1_musig_session_signer_data signers_tmp[2]; + unsigned char sk_dummy[32]; + secp256k1_xonly_pubkey pks_tmp[2]; + secp256k1_xonly_pubkey combined_pk_tmp; + secp256k1_musig_pre_session pre_session_tmp; + unsigned char nonce[32]; + + /* Set up signers with different public keys */ + secp256k1_testrand256(sk_dummy); + pks_tmp[0] = pks[0]; + CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); + + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); + CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); + /* Call get_public_nonce with different signers than the signers the session was + * initialized with. */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, nonce, nonce_commitments, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, nonce, nonce_commitments, 2, NULL) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + + secp256k1_musig_compute_messagehash(ctx, msghash, &session); +} + +/* Creates a new session (with a different session id) and tries to use that session + * to combine nonces with given signers_other. This should fail, because the nonce + * commitments of signers_other do not match the nonce commitments the new session + * was initialized with. If do_test is 0, the correct signers are being used and + * therefore the function should return 1. */ +int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + secp256k1_musig_session_signer_data *signers_to_use; + unsigned char nonce_commitment[32]; + unsigned char session_id[32]; + unsigned char nonce[32]; + const unsigned char *ncs[2]; + + /* Initialize new signers */ + secp256k1_testrand256(session_id); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); + secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL); + if (do_test) { + signers_to_use = signers_other; + } else { + signers_to_use = signers; + } + return secp256k1_musig_session_combine_nonces(ctx, &session, signers_to_use, 2, NULL, NULL); +} + +/* Initializaes a session with the given session_id, signers, pk, msg etc. + * parameters but without a message. Will test that the message must be + * provided with `get_public_nonce`. + */ +void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { + /* Create context for testing ARG_CHECKs by setting an illegal_callback. */ + secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + int ecount = 0; + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + unsigned char nonce_commitment[32]; + const unsigned char *ncs[2]; + unsigned char nonce[32]; + secp256k1_musig_partial_signature partial_sig; + + secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + + /* Trying to get the nonce without providing a message fails. */ + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, NULL) == 0); + CHECK(ecount == 1); + + /* Providing a message should make get_public_nonce succeed. */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, msg) == 1); + /* Trying to set the message again fails. */ + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, msg) == 0); + CHECK(ecount == 2); + + /* Check that it's working */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig)); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); + secp256k1_context_destroy(ctx_tmp); +} + +void musig_state_machine_tests(secp256k1_scratch_space *scratch) { + secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_VERIFY); + size_t i; + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signers0[2]; + secp256k1_musig_session_signer_data signers1[2]; + unsigned char nonce_commitment[2][32]; + unsigned char session_id[2][32]; + unsigned char msg[32]; + unsigned char sk[2][32]; + secp256k1_xonly_pubkey pk[2]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + unsigned char nonce[2][32]; + const unsigned char *ncs[2]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char sig[64]; + unsigned char msghash1[32]; + unsigned char msghash2[32]; + int ecount; + + secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); + ecount = 0; + + /* Run state machine with the same objects twice to test that it's allowed to + * reinitialize session and session_signer_data. */ + for (i = 0; i < 2; i++) { + /* Setup */ + secp256k1_testrand256(session_id[0]); + secp256k1_testrand256(session_id[1]); + secp256k1_testrand256(sk[0]); + secp256k1_testrand256(sk[1]); + secp256k1_testrand256(msg); + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + /* Can't combine nonces unless we're through round 1 already */ + ecount = 0; + CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0); + CHECK(ecount == 1); + + /* Set nonce commitments */ + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1); + /* Calling the function again is not okay */ + ecount = 0; + CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, nonce[0], ncs, 2, NULL) == 0); + CHECK(ecount == 1); + + /* Get nonce for signer 1 */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1); + + /* Set nonces */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1); + /* Can't set nonce that doesn't match nonce commitment */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[0]) == 0); + /* Set correct nonce */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1); + + /* Combine nonces */ + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); + /* Not everyone is present from signer 1's view */ + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0); + /* Make everyone present */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1); + + /* Can't combine nonces from signers of a different session */ + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 1) == 0); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 0) == 1); + + /* Partially sign */ + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + /* Can't verify, sign or combine signatures until nonce is combined */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_verify(ctx_tmp, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sign(ctx_tmp, &session[1], &partial_sig[1]) == 0); + CHECK(ecount == 2); + memset(&partial_sig[1], 0, sizeof(partial_sig[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx_tmp, &session[1], sig, partial_sig, 2) == 0); + CHECK(ecount == 3); + + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); + /* messagehash should be the same as a session whose get_public_nonce was called + * with different signers (i.e. they diff in public keys). This is because the + * public keys of the signers is set in stone when initializing the session. */ + secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]); + musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, nonce[0], sk[1], session_id[1]); + CHECK(memcmp(msghash1, msghash2, 32) == 0); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[1], &pk[1]) == 1); + /* Wrong signature */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); + /* Can't get the public nonce until msg is set */ + musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], nonce[0], sk[1], session_id[1], msg); + } + secp256k1_context_destroy(ctx_tmp); +} + +void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { + /* Throughout this test "a" and "b" refer to two hypothetical blockchains, + * while the indices 0 and 1 refer to the two signers. Here signer 0 is + * sending a-coins to signer 1, while signer 1 is sending b-coins to signer + * 0. Signer 0 produces the adaptor signatures. */ + unsigned char final_sig_a[64]; + unsigned char final_sig_b[64]; + secp256k1_musig_partial_signature partial_sig_a[2]; + secp256k1_musig_partial_signature partial_sig_b_adapted[2]; + secp256k1_musig_partial_signature partial_sig_b[2]; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor_extracted[32]; + secp256k1_pubkey pub_adaptor; + + unsigned char seckey_a[2][32]; + unsigned char seckey_b[2][32]; + secp256k1_xonly_pubkey pk_a[2]; + secp256k1_xonly_pubkey pk_b[2]; + secp256k1_musig_pre_session pre_session_a; + secp256k1_musig_pre_session pre_session_b; + secp256k1_xonly_pubkey combined_pk_a; + secp256k1_xonly_pubkey combined_pk_b; + secp256k1_musig_session musig_session_a[2]; + secp256k1_musig_session musig_session_b[2]; + unsigned char noncommit_a[2][32]; + unsigned char noncommit_b[2][32]; + const unsigned char *noncommit_a_ptr[2]; + const unsigned char *noncommit_b_ptr[2]; + unsigned char pubnon_a[2][32]; + unsigned char pubnon_b[2][32]; + int combined_nonce_parity_a; + int combined_nonce_parity_b; + secp256k1_musig_session_signer_data data_a[2]; + secp256k1_musig_session_signer_data data_b[2]; + + const unsigned char seed[32] = "still tired of choosing seeds..."; + const unsigned char msg32_a[32] = "this is the message blockchain a"; + const unsigned char msg32_b[32] = "this is the message blockchain b"; + + /* Step 1: key setup */ + secp256k1_testrand256(seckey_a[0]); + secp256k1_testrand256(seckey_a[1]); + secp256k1_testrand256(seckey_b[0]); + secp256k1_testrand256(seckey_b[1]); + secp256k1_testrand256(sec_adaptor); + + CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0])); + CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1])); + CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0])); + CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); + + CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); + noncommit_a_ptr[0] = noncommit_a[0]; + noncommit_a_ptr[1] = noncommit_a[1]; + + CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); + CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); + noncommit_b_ptr[0] = noncommit_b[0]; + noncommit_b_ptr[1] = noncommit_b[1]; + + /* Step 2: Exchange nonces */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, pubnon_a[0], noncommit_a_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, pubnon_a[1], noncommit_a_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, pubnon_b[0], noncommit_b_ptr, 2, NULL)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, pubnon_b[1], noncommit_b_ptr, 2, NULL)); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], pubnon_a[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], pubnon_a[1])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], pubnon_b[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], pubnon_b[1])); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &combined_nonce_parity_a, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &combined_nonce_parity_b, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor)); + + /* Step 3: Signer 0 produces partial signatures for both chains. */ + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[0], &partial_sig_a[0])); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[0], &partial_sig_b[0])); + + /* Step 4: Signer 1 receives partial signatures, verifies them and creates a + * partial signature to send B-coins to signer 0. */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_a[1], data_a, &partial_sig_a[0], &pk_a[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_b[1], data_b, &partial_sig_b[0], &pk_b[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[1], &partial_sig_b[1])); + + /* Step 5: Signer 0 adapts its own partial signature and combines it with the + * partial signature from signer 1. This results in a complete signature which + * is broadcasted by signer 0 to take B-coins. */ + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, combined_nonce_parity_b)); + memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1); + + /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to + * other partial signature, and takes A-coins. */ + CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, combined_nonce_parity_b) == 1); + CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, combined_nonce_parity_a)); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); +} + +/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the + * expected state. */ +void sha256_tag_test(void) { + char tag[17] = "MuSig coefficient"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_tagged; + unsigned char buf[32]; + unsigned char buf2[32]; + size_t i; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, (unsigned char *) tag, 17); + secp256k1_sha256_finalize(&sha, buf); + /* buf = SHA256("MuSig coefficient") */ + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha, buf, 32); + /* Is buffer fully consumed? */ + CHECK((sha.bytes & 0x3F) == 0); + + /* Compare with tagged SHA */ + secp256k1_musig_sha256_init_tagged(&sha_tagged); + for (i = 0; i < 8; i++) { + CHECK(sha_tagged.s[i] == sha.s[i]); + } + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha_tagged, buf, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_finalize(&sha_tagged, buf2); + CHECK(memcmp(buf, buf2, 32) == 0); +} + +/* Attempts to create a signature for the combined public key using given secret + * keys and pre_session. */ +void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_pre_session *pre_session) { + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signers0[2]; + secp256k1_musig_session_signer_data signers1[2]; + secp256k1_xonly_pubkey pk[2]; + unsigned char session_id[2][32]; + unsigned char msg[32]; + unsigned char nonce_commitment[2][32]; + unsigned char nonce[2][32]; + const unsigned char *ncs[2]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char final_sig[64]; + + secp256k1_testrand256(session_id[0]); + secp256k1_testrand256(session_id[1]); + secp256k1_testrand256(msg); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1); + + CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, 0, sk0) == 1); + CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, 1, sk1) == 1); + /* Set nonce commitments */ + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1); + /* Set nonces */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2)); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, combined_pubkey) == 1); +} + +/* In this test we create a combined public key P and a commitment Q = P + + * hash(P, contract)*G. Then we test that we can sign for both public keys. In + * order to sign for Q we use the tweak32 argument of partial_sig_combine. */ +void musig_tweak_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_xonly_pubkey pk[2]; + secp256k1_musig_pre_session pre_session_P; + secp256k1_musig_pre_session pre_session_Q; + secp256k1_xonly_pubkey P; + unsigned char P_serialized[32]; + secp256k1_pubkey Q; + int Q_parity; + secp256k1_xonly_pubkey Q_xonly; + unsigned char Q_serialized[32]; + + secp256k1_sha256 sha; + unsigned char contract[32]; + unsigned char ec_commit_tweak[32]; + + /* Setup */ + secp256k1_testrand256(sk[0]); + secp256k1_testrand256(sk[1]); + secp256k1_testrand256(contract); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk, 2) == 1); + + CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, P_serialized, 32); + secp256k1_sha256_write(&sha, contract, 32); + secp256k1_sha256_finalize(&sha, ec_commit_tweak); + pre_session_Q = pre_session_P; + CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &pre_session_Q, &Q, &P, ec_commit_tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &Q_xonly, &Q_parity, &Q)); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, Q_serialized, &Q_xonly)); + /* Check that musig_pubkey_tweak_add produces same result as + * xonly_pubkey_tweak_add. */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, Q_serialized, Q_parity, &P, ec_commit_tweak) == 1); + + /* Test signing for P */ + musig_tweak_test_helper(&P, sk[0], sk[1], &pre_session_P); + /* Test signing for Q */ + musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q); +} + +void run_musig_tests(void) { + int i; + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + for (i = 0; i < count; i++) { + musig_simple_test(scratch); + } + musig_api_tests(scratch); + musig_state_machine_tests(scratch); + for (i = 0; i < count; i++) { + /* Run multiple times to ensure that pk and nonce have different y + * parities */ + scriptless_atomic_swap(scratch); + musig_tweak_test(scratch); + } + sha256_tag_test(); + + secp256k1_scratch_space_destroy(ctx, scratch); +} + +#endif diff --git a/src/modules/rangeproof/Makefile.am.include b/src/modules/rangeproof/Makefile.am.include new file mode 100644 index 00000000000..ff8b8d38031 --- /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 00000000000..8f8cfedd075 --- /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 00000000000..e11db181a43 --- /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 + +#if defined(SECP256K1_BIG_ENDIAN) +#define BE32(x) (x) +#elif defined(SECP256K1_LITTLE_ENDIAN) +#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 00000000000..c7f921fcc3d --- /dev/null +++ b/src/modules/rangeproof/main_impl.h @@ -0,0 +1,300 @@ +/********************************************************************** + * 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 standard encoding (without compression), + * which happens to be a point on the curve. More precisely, the generator is + * derived by running the following script with the sage mathematics software. + + import hashlib + F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' + H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16))) + print('%x %x' % H.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 00000000000..14d9920ebc4 --- /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 00000000000..69f22e382a8 --- /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 00000000000..840a09ae610 --- /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 00000000000..8056f0a78c2 --- /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 00000000000..7b8a610ce2d --- /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_testrand32(); + + secp256k1_testrand256(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_testrand32(); + uint64_t val = vmin + secp256k1_testrand32(); + 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_testrand256(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_testrand32() & 7) + 1; + outputs = (secp256k1_testrand32() & 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_testrandi64(0, INT64_MAX - totalv); + totalv += values[i]; + } + for (i = 0; i < outputs - 1; i++) { + values[i + inputs] = secp256k1_testrandi64(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_testrand256_test(m); + nrings = 1 + (secp256k1_testrand32()&7); + c = 0; + secp256k1_scalar_set_int(&one, 1); + if (secp256k1_testrand32()&1) { + secp256k1_scalar_negate(&one, &one); + } + for (i = 0; i < nrings; i++) { + rsizes[i] = 1 + (secp256k1_testrand32()&7); + secidx[i] = secp256k1_testrand32() % rsizes[i]; + random_scalar_order(&sec[i]); + random_scalar_order(&k[i]); + if(secp256k1_testrand32()&7) { + sec[i] = one; + } + if(secp256k1_testrand32()&7) { + k[i] = one; + } + for (j = 0; j < rsizes[i]; j++) { + random_scalar_order(&s[c + j]); + if(secp256k1_testrand32()&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_testrand32() % 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_testrand32() % c; + if (secp256k1_testrand32() & 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_testrand256(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_testrand256(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_testrand256(blind); + { + /*Malleability test.*/ + v = secp256k1_testrandi64(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 < (size_t) 2*count; i++) { + int exp; + int min_bits; + v = secp256k1_testrandi64(0, UINT64_MAX >> (secp256k1_testrand32()&63)); + vmin = 0; + if ((v < INT64_MAX) && (secp256k1_testrand32()&1)) { + vmin = secp256k1_testrandi64(0, v); + } + secp256k1_testrand256(blind); + CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h)); + len = 5134; + exp = (int)secp256k1_testrandi64(0,18)-(int)secp256k1_testrandi64(0,18); + if (exp < 0) { + exp = -exp; + } + min_bits = (int)secp256k1_testrandi64(0,64)-(int)secp256k1_testrandi64(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(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 < 5; j++) { + for (i = 0; i < 96; i++) { + secp256k1_testrand256(&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_testrandi64(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_testrand32() % (MAX_N_GENS / 2)) + 1; + const size_t n_outputs = (secp256k1_testrand32() % (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_testrandi64(0, INT64_MAX - total_value); + total_value += value[n_inputs + i]; + } + for (i = 0; i < n_inputs - 1; i++) { + value[i] = secp256k1_testrandi64(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 + }; + uint64_t min_value_1; + uint64_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/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index b0d8481f9be..da747fe14b9 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -179,7 +179,7 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64 secp256k1_scalar_add(&e, &e, &k); secp256k1_scalar_get_b32(&sig64[32], &e); - memczero(sig64, 64, !ret); + secp256k1_memczero(sig64, 64, !ret); secp256k1_scalar_clear(&k); secp256k1_scalar_clear(&sk); memset(seckey, 0, sizeof(seckey)); diff --git a/src/modules/surjection/Makefile.am.include b/src/modules/surjection/Makefile.am.include new file mode 100644 index 00000000000..51ece21fca9 --- /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 00000000000..9614e5f7db9 --- /dev/null +++ b/src/modules/surjection/main_impl.h @@ -0,0 +1,398 @@ +/********************************************************************** + * 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 + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "include/secp256k1_rangeproof.h" +#include "include/secp256k1_surjectionproof.h" +#include "modules/rangeproof/borromean.h" +#include "modules/surjection/surjection_impl.h" +#include "hash.h" + +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +#undef SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS +#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16 +#endif + +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; +} + +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif +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; + } + + /* Check that the bitvector of used inputs is of the claimed + * length; i.e. the final byte has no "padding bits" set */ + if (n_inputs % 8 != 0) { + const unsigned char padding_mask = (~0U) << (n_inputs % 8); + if ((input[2 + (n_inputs + 7) / 8 - 1] & padding_mask) != 0) { + 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; + } + } +} + +/* While '_allocate_initialized' may be a wordy suffix for this function, and '_create' + * may have been more appropriate, '_create' could be confused with '_generate', + * as the meanings for the words are close. Therefore, more wordy, but less + * ambiguous suffix was chosen. */ +int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, secp256k1_surjectionproof** proof_out_p, 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) { + int ret = 0; + secp256k1_surjectionproof* proof; + + VERIFY_CHECK(ctx != NULL); + + ARG_CHECK(proof_out_p != NULL); + *proof_out_p = 0; + + proof = (secp256k1_surjectionproof*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_surjectionproof)); + if (proof != NULL) { + ret = secp256k1_surjectionproof_initialize(ctx, proof, input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, fixed_output_tag, n_max_iterations, random_seed32); + if (ret) { + *proof_out_p = proof; + } + else { + free(proof); + } + } + return ret; +} + +/* secp256k1_surjectionproof structure may also be allocated on the stack, + * and initialized explicitly via secp256k1_surjectionproof_initialize(). + * Supplying stack-allocated struct to _destroy() will result in calling + * free() with the pointer that points at the stack, with disasterous + * consequences. Thus, it is not advised to mix heap- and stack-allocating + * approaches to working with this struct. It is possible to detect this + * situation by using additional field in the struct that can be set to + * special value depending on the allocation path, and check it here. + * But currently, it is not seen as big enough concern to warrant this extra code .*/ +void secp256k1_surjectionproof_destroy(secp256k1_surjectionproof* proof) { + if (proof != NULL) { + VERIFY_CHECK(proof->n_inputs <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS); + free(proof); + } +} + +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 <= SECP256K1_SURJECTIONPROOF_MAX_USED_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_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + 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; + } + + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index) == 0) { + return 0; + } + + /* Produce signature */ + rsizes[0] = (int) n_used_pubkeys; + indices[0] = (int) ring_input_index; + secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); + 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; +} + +#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE +static +#endif +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_USED_INPUTS]; + secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS]; + 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; + } + + /* Reject proofs with too many used inputs in USE_REDUCED_SURJECTION_PROOF_SIZE mode */ + if (n_used_pubkeys > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) { + return 0; + } + + if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 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, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag); + 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 00000000000..20ac4931630 --- /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 00000000000..e7bd4db1500 --- /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 00000000000..f3652567fa5 --- /dev/null +++ b/src/modules/surjection/surjection_impl.h @@ -0,0 +1,91 @@ +/********************************************************************** + * 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, const secp256k1_generator *ephemeral_input_tags, size_t n_input_tags, const secp256k1_generator *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++) { + pk_ser[0] = 2 + (ephemeral_input_tags[i].data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_input_tags[i].data[0], 32); + secp256k1_sha256_write(&sha256_en, pk_ser, pk_len); + } + pk_ser[0] = 2 + (ephemeral_output_tag->data[63] & 1); + memcpy(&pk_ser[1], &ephemeral_output_tag->data[0], 32); + 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_generator *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_generator *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_generator_load(&tmpge, &input_tags[i]); + secp256k1_ge_neg(&tmpge, &tmpge); + + VERIFY_CHECK(j < SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); + VERIFY_CHECK(j < n_pubkeys); + secp256k1_gej_set_ge(&pubkeys[j], &tmpge); + + secp256k1_generator_load(&tmpge, output_tag); + secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], &tmpge, NULL); + if (ring_input_index != NULL && input_index == i) { + *ring_input_index = j; + } + j++; + } + } + /* Caller needs to ensure that the number of set bits in used_tags (which we counted in j) equals n_pubkeys. */ + VERIFY_CHECK(j == n_pubkeys); + return 1; +} + + +#endif diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h new file mode 100644 index 00000000000..e90d2aa0478 --- /dev/null +++ b/src/modules/surjection/tests_impl.h @@ -0,0 +1,689 @@ +/********************************************************************** + * 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; + secp256k1_surjectionproof* proof_on_heap; + 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_testrand256(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_testrand256(input_blinding_key[i]); + secp256k1_testrand256(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_testrand256(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 allocate_initialized */ + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 0); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0); + CHECK(proof_on_heap != 0); + secp256k1_surjectionproof_destroy(proof_on_heap); + CHECK(ecount == 0); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 2); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 3); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 4); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0); + CHECK(proof_on_heap != 0); + secp256k1_surjectionproof_destroy(proof_on_heap); + CHECK(ecount == 4); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 5); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 6); + CHECK((secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 6); + CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL) == 0); + CHECK(proof_on_heap == 0); + CHECK(ecount == 7); + + /* we are now going to test essentially the same functions, just without heap allocation. + * reset ecount. */ + ecount = 0; + + /* 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_testrand256(seed); + + for (i = 0; i < n_inputs + 1; i++) { + secp256k1_testrand256(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_testrand256(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_testrand256(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_testrand256(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_testrand256(input_blinding_key[i]); + /* choose random fixed tag, except that for the output one copy from the key_index */ + if (i < n_inputs) { + secp256k1_testrand256(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_trailing, 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; + } + + for (i = 0; i < n_inputs; i++) { + /* flip bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); + CHECK(result == 0); + /* reset the bit */ + proof.used_inputs[i / 8] ^= (1 << (i % 8)); + } + + /* 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 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_testrand256(fixed_input_tag.data); + secp256k1_testrand256(fixed_output_tag.data); + + /* blind fixed output tags with random blinding key */ + secp256k1_testrand256(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_surjection_genmessage(proof.data, ephemeral_input_tags, 1, &ephemeral_output_tag); + 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 test_fixed_vectors(void) { + const unsigned char tag0_ser[] = { + 0x0a, + 0x1c, 0xa3, 0xdd, 0x12, 0x48, 0xdd, 0x4d, 0xd0, 0x04, 0x30, 0x47, 0x48, 0x75, 0xf5, 0xf5, 0xff, + 0x2a, 0xd5, 0x0d, 0x1d, 0x86, 0x2b, 0xa4, 0xa4, 0x2f, 0x46, 0xe9, 0xb4, 0x54, 0x21, 0xf0, 0x85 + }; + const unsigned char tag1_ser[] = { + 0x0a, + 0x09, 0x0d, 0x5a, 0xd4, 0xed, 0xae, 0x9c, 0x0c, 0x69, 0x79, 0xf3, 0x8d, 0x22, 0x03, 0x0a, 0x3d, + 0x38, 0xd4, 0x78, 0xe1, 0x03, 0x0d, 0x70, 0x57, 0xd9, 0x9a, 0x23, 0x03, 0xf0, 0x7f, 0xfb, 0xef + }; + const unsigned char tag2_ser[] = { + 0x0a, + 0xfd, 0xed, 0xba, 0x15, 0x20, 0x8a, 0xb2, 0xaf, 0x0b, 0x76, 0x6d, 0xd2, 0x5f, 0xd4, 0x15, 0x11, + 0x90, 0xec, 0xcb, 0x3f, 0xcd, 0x08, 0xb5, 0x35, 0xd9, 0x24, 0x18, 0xb1, 0xd3, 0x47, 0x83, 0x54 + }; + const unsigned char tag3_ser[] = { + 0x0b, + 0x8b, 0x47, 0xca, 0xee, 0x20, 0x52, 0x17, 0xbf, 0xee, 0xcc, 0x84, 0xcd, 0x34, 0x32, 0x6c, 0x36, + 0xf1, 0xd9, 0x3f, 0xe1, 0x6f, 0x77, 0xfe, 0x89, 0x3e, 0x4a, 0xc8, 0x2a, 0x75, 0xfa, 0x2d, 0x36 + }; + const unsigned char tag4_ser[] = { + 0x0b, + 0x3c, 0x5c, 0xf4, 0x61, 0x45, 0xa8, 0x53, 0xc1, 0x64, 0x32, 0x0e, 0x92, 0x68, 0x52, 0xbd, 0x12, + 0xe9, 0x45, 0x31, 0xeb, 0x04, 0x4c, 0xf4, 0xe2, 0x9e, 0x9f, 0x60, 0x26, 0x50, 0xbf, 0xd6, 0x9f + }; + const unsigned char output_tag_ser[] = { + 0x0b, + 0xf7, 0x3c, 0x03, 0xed, 0xae, 0x83, 0xa1, 0xa6, 0x94, 0x8c, 0xe3, 0xb8, 0x54, 0x02, 0xa8, 0xbd, + 0x66, 0xca, 0x28, 0xef, 0x44, 0xf5, 0x3a, 0xcb, 0xc7, 0x5b, 0x16, 0xac, 0xce, 0x29, 0x4b, 0xc6 + }; + + const unsigned char total1_used1[] = { + 0x01, 0x00, 0x01, 0x8e, 0x6b, 0x8d, 0x8b, 0x96, 0x29, 0x10, 0x29, 0xcb, 0xf8, 0x48, 0xd9, 0xc8, + 0x5b, 0x77, 0xdc, 0xdf, 0x16, 0x67, 0x19, 0xfe, 0x8d, 0xee, 0x8f, 0x56, 0x6f, 0x9c, 0xe9, 0xae, + 0xb9, 0xd9, 0x12, 0xb8, 0x95, 0x6c, 0xf1, 0x48, 0x07, 0x7d, 0x49, 0xe4, 0x3e, 0x7f, 0xc1, 0x2c, + 0xe2, 0xe1, 0x94, 0x10, 0xb1, 0xda, 0x86, 0x5f, 0xbc, 0x03, 0x59, 0xe1, 0x09, 0xd2, 0x1b, 0x18, + 0xce, 0x58, 0x15 + }; + const size_t total1_used1_len = sizeof(total1_used1); + + const unsigned char total2_used1[] = { + 0x02, 0x00, 0x01, 0x35, 0x3a, 0x29, 0x4b, 0xe4, 0x99, 0xc6, 0xbf, 0x99, 0x4d, 0x6c, 0xc8, 0x18, + 0x14, 0xad, 0x10, 0x22, 0x3a, 0xb8, 0x1c, 0xb9, 0xc5, 0x77, 0xda, 0xe0, 0x8a, 0x71, 0x2d, 0x0d, + 0x8e, 0x80, 0xf5, 0x8d, 0x74, 0xf9, 0x01, 0x6b, 0x35, 0x88, 0xf4, 0x8e, 0x43, 0xa5, 0x9c, 0x0f, + 0x7e, 0x37, 0x86, 0x77, 0x44, 0x72, 0x7c, 0xaa, 0xff, 0x14, 0x5b, 0x7a, 0x42, 0x41, 0x75, 0xb2, + 0x5e, 0x3d, 0x6c + }; + const size_t total2_used1_len = sizeof(total2_used1); + + const unsigned char total3_used2[] = { + 0x03, 0x00, 0x03, 0xf2, 0x3f, 0xca, 0x49, 0x52, 0x05, 0xaf, 0x81, 0x83, 0x01, 0xd7, 0xf4, 0x92, + 0xc0, 0x50, 0xe3, 0x15, 0xfc, 0x94, 0xc1, 0x27, 0x10, 0xd7, 0x8f, 0x57, 0xb1, 0x23, 0xcf, 0x68, + 0x31, 0xf8, 0xcb, 0x58, 0x3d, 0xca, 0x2f, 0x7a, 0x3b, 0x0b, 0xb6, 0x10, 0x52, 0x94, 0xc8, 0x5f, + 0x0a, 0xf8, 0xca, 0x5d, 0x4c, 0x38, 0x44, 0x92, 0xb3, 0xc7, 0xe4, 0x46, 0x9f, 0x96, 0x64, 0xbd, + 0xd2, 0xda, 0x40, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91 + }; + const size_t total3_used2_len = sizeof(total3_used2); + + const unsigned char total5_used3[] = { + 0x05, 0x00, 0x15, 0x36, 0x3b, 0x92, 0x97, 0x84, 0x25, 0x75, 0xd6, 0xa6, 0xaf, 0xb7, 0x32, 0x5b, + 0x2c, 0xf8, 0x31, 0xe2, 0x15, 0x3a, 0x9b, 0xb7, 0x20, 0x14, 0xc0, 0x67, 0x96, 0x7d, 0xa9, 0xc4, + 0xa2, 0xb4, 0x22, 0x57, 0x5f, 0xb8, 0x20, 0xf1, 0xe8, 0x82, 0xaf, 0xbc, 0x8a, 0xbc, 0x01, 0xc9, + 0x35, 0xf2, 0x7f, 0x6f, 0x0c, 0x0d, 0xba, 0x87, 0xa4, 0xc3, 0xec, 0x60, 0x54, 0x49, 0x35, 0xeb, + 0x1e, 0x48, 0x2c, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b, + 0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee, + 0x9c, 0xf5, 0xb9 + }; + const size_t total5_used3_len = sizeof(total5_used3); + + const unsigned char total5_used5[] = { + 0x05, 0x00, 0x1f, 0xfd, 0xbb, 0xb6, 0xc2, 0x78, 0x82, 0xad, 0xe1, 0x66, 0x6d, 0x20, 0x4d, 0xfe, + 0x6b, 0xd2, 0x0b, 0x21, 0x6e, 0xa8, 0x5b, 0xc8, 0xe4, 0x88, 0x42, 0x11, 0x30, 0x3b, 0x6b, 0x02, + 0xc9, 0x7f, 0x44, 0x1c, 0xee, 0xd8, 0x37, 0x6a, 0xf8, 0xfd, 0xc8, 0x4b, 0x0b, 0xa1, 0x43, 0x1f, + 0x68, 0x77, 0x8d, 0x1b, 0xac, 0x9e, 0xc1, 0xc1, 0xda, 0x60, 0xa8, 0xcf, 0x10, 0x9d, 0x80, 0x07, + 0x90, 0x57, 0xb6, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe, + 0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90, + 0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b, + 0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee, + 0x9c, 0xf5, 0xb9, 0x5a, 0xc8, 0x03, 0x8d, 0x4f, 0xe3, 0x1d, 0x79, 0x38, 0x5a, 0xfa, 0xe5, 0xa8, + 0x9d, 0x56, 0x77, 0xb3, 0xf9, 0xa8, 0x70, 0x46, 0x27, 0x26, 0x6c, 0x6e, 0x54, 0xaf, 0xf9, 0xd0, + 0x37, 0xa4, 0x86, 0x68, 0x8f, 0xac, 0x3e, 0x78, 0xaa, 0x3d, 0x83, 0x1a, 0xca, 0x05, 0xfe, 0x10, + 0x95, 0xa4, 0x6a, 0x10, 0xc6, 0x62, 0xf3, 0xf7, 0xf3, 0x4d, 0x0b, 0xd4, 0x94, 0xe5, 0x51, 0x6c, + 0x85, 0xd7, 0xc7 + }; + const size_t total5_used5_len = sizeof(total5_used5); + + unsigned char bad[sizeof(total5_used5) + 32] = { 0 }; + + secp256k1_generator input_tags[5]; + secp256k1_generator output_tag; + secp256k1_surjectionproof proof; + + CHECK(secp256k1_generator_parse(ctx, &input_tags[0], tag0_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[1], tag1_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[2], tag2_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[3], tag3_ser)); + CHECK(secp256k1_generator_parse(ctx, &input_tags[4], tag4_ser)); + CHECK(secp256k1_generator_parse(ctx, &output_tag, output_tag_ser)); + + /* check 1-of-1 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &output_tag)); + /* check 1-of-2 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total2_used1, total2_used1_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 2, &output_tag)); + /* check 2-of-3 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total3_used2, total3_used2_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 3, &output_tag)); + /* check 3-of-5 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total5_used3, total5_used3_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 5, &output_tag)); + /* check 5-of-5 */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total5_used5, total5_used5_len)); + CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 5, &output_tag)); + + /* check invalid length fails */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, total5_used5, total5_used3_len)); + /* check invalid keys fail */ + CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len)); + CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, &input_tags[1], 1, &output_tag)); + CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &input_tags[0])); + + /* Try setting 6 bits on the total5-used-5; check that parsing fails */ + memcpy(bad, total5_used5, total5_used5_len); + bad[2] = 0x3f; /* 0x1f -> 0x3f */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); + /* Correct for the length */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len + 32)); + /* Alternately just turn off one of the "legit" bits */ + bad[2] = 0x37; /* 0x1f -> 0x37 */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len)); + + /* Similarly try setting 4 bits on the total5-used-3, with one bit out of range */ + memcpy(bad, total5_used3, total5_used3_len); + bad[2] = 0x35; /* 0x15 -> 0x35 */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len + 32)); + bad[2] = 0x34; /* 0x15 -> 0x34 */ + CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len)); +} + +void run_surjection_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_surjectionproof_api(); + } + test_fixed_vectors(); + + test_input_selection(0); + test_input_selection(1); + test_input_selection(5); + test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); + + test_input_selection_distribution(); + test_gen_verify(10, 3); + test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_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 00000000000..0dc5a64db28 --- /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 00000000000..0b2d6c9c958 --- /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 00000000000..7cf1fb09659 --- /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 00000000000..28307f4b6e1 --- /dev/null +++ b/src/modules/whitelist/whitelist.md @@ -0,0 +1,107 @@ +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 "cancellation" keys by +computing `W` as the difference between an attacker-controlled key and `P`. +Because to spend the funds the attacker must produce a signature with `W`, the +coins will be unspendable until attacker and the legitimate participant owning +`P` cooperate. + +In an important sense, this "cancellation" 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 "cancellation" 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 keys he does +not control alone. + +### 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 +"cancellation keys" for which the attacker alone does not know 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 00000000000..ff8d87f4a5d --- /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 fb3fb187cec..a6229314101 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -47,6 +47,9 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c /** 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); @@ -114,4 +117,7 @@ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_ /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); +/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 73cbd5e18a4..c227750d026 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -7,6 +7,9 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "scalar.h" +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) #define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) @@ -38,6 +41,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); @@ -955,4 +965,91 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#if defined(SECP256K1_BIG_ENDIAN) +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#elif defined(SECP256K1_LITTLE_ENDIAN) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[3] = (((uint64_t) x0) << 32) | x1; + r1->d[2] = (((uint64_t) x2) << 32) | x3; + r1->d[1] = (((uint64_t) x4) << 32) | x5; + r1->d[0] = (((uint64_t) x6) << 32) | x7; + r2->d[3] = (((uint64_t) x8) << 32) | x9; + r2->d[2] = (((uint64_t) x10) << 32) | x11; + r2->d[1] = (((uint64_t) x12) << 32) | x13; + r2->d[0] = (((uint64_t) x14) << 32) | x15; + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 6853f79eccb..c2dbdeb76f6 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) #define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) @@ -56,6 +58,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); @@ -731,4 +744,99 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#if defined(SECP256K1_BIG_ENDIAN) +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#elif defined(SECP256K1_LITTLE_ENDIAN) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[7] = x0; + r1->d[6] = x1; + r1->d[5] = x2; + r1->d[4] = x3; + r1->d[3] = x4; + r1->d[2] = x5; + r1->d[1] = x6; + r1->d[0] = x7; + r2->d[7] = x8; + r2->d[6] = x9; + r2->d[5] = x10; + r2->d[4] = x11; + r2->d[3] = x12; + r2->d[2] = x13; + r2->d[1] = x14; + r2->d[0] = x15; + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index a615ec074b2..d24d56cbab4 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) @@ -125,4 +126,9 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se *r = (*r & mask0) | (*a & mask1); } +SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { + *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; + *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index dae506d08c9..42309a03d00 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -26,6 +26,16 @@ # include #endif +#ifdef ENABLE_MODULE_GENERATOR +# include "include/secp256k1_generator.h" +#endif + +#ifdef ENABLE_MODULE_RANGEPROOF +# include "include/secp256k1_rangeproof.h" +# include "modules/rangeproof/pedersen.h" +# include "modules/rangeproof/rangeproof.h" +#endif + #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ secp256k1_callback_call(&ctx->illegal_callback, #cond); \ @@ -86,6 +96,8 @@ const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_pr size_t secp256k1_context_preallocated_size(unsigned int flags) { size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context)); + /* A return value of 0 is reserved as an indicator for errors when we call this function internally. */ + VERIFY_CHECK(ret != 0); if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { secp256k1_callback_call(&default_illegal_callback, @@ -122,21 +134,21 @@ secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigne if (!secp256k1_selftest()) { secp256k1_callback_call(&default_error_callback, "self test failed"); } - VERIFY_CHECK(prealloc != NULL); + prealloc_size = secp256k1_context_preallocated_size(flags); + if (prealloc_size == 0) { + return NULL; + } + VERIFY_CHECK(prealloc != NULL); ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size); ret->illegal_callback = default_illegal_callback; ret->error_callback = default_error_callback; - if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { - secp256k1_callback_call(&ret->illegal_callback, - "Invalid flags"); - return NULL; - } - secp256k1_ecmult_context_init(&ret->ecmult_ctx); secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + /* Flags have been checked by secp256k1_context_preallocated_size. */ + VERIFY_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_CONTEXT); if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc); } @@ -580,7 +592,7 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey); secp256k1_pubkey_save(pubkey, &p); - memczero(pubkey, sizeof(*pubkey), !ret); + secp256k1_memczero(pubkey, sizeof(*pubkey), !ret); secp256k1_scalar_clear(&seckey_scalar); return ret; @@ -772,3 +784,24 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/main_impl.h" #endif + +#ifdef ENABLE_MODULE_MUSIG +# include "modules/musig/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 a76003d5b8e..6b0cbc05bea 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -35,6 +35,9 @@ static void secp256k1_testrand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len); +/** Generate a pseudorandom 64-bit integer in the range min..max, inclusive. */ +static int64_t secp256k1_testrandi64(uint64_t min, uint64_t max); + /** Flip a single random bit in a byte array */ static void secp256k1_testrand_flip(unsigned char *b, size_t len); diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 33925663298..d80cae03df9 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.* **********************************************************************/ @@ -10,6 +10,7 @@ #include #include #include +#include #include "testrand.h" #include "hash.h" @@ -108,6 +109,23 @@ static void secp256k1_testrand256_test(unsigned char *b32) { secp256k1_testrand_bytes_test(b32, 32); } +SECP256K1_INLINE static int64_t secp256k1_testrandi64(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_testrand32() << 32) | secp256k1_testrand32(); + r >>= clz; + } while (r > range); + return min + (int64_t)r; +} + static void secp256k1_testrand_flip(unsigned char *b, size_t len) { b[secp256k1_testrand_int(len)] ^= (1 << secp256k1_testrand_int(8)); } diff --git a/src/tests.c b/src/tests.c index bb4b5b4c077..33aef6eddf9 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.* **********************************************************************/ @@ -136,6 +136,52 @@ void random_scalar_order_b32(unsigned char *b32) { secp256k1_scalar_get_b32(b32, &num); } +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_testrand32()) == 0); + r = ((uint64_t)secp256k1_testrand32() << 32) | secp256k1_testrand32(); + r2 = secp256k1_testrandi64(0, r); + CHECK(r2 <= r); + r3 = secp256k1_testrandi64(r2, r); + CHECK((r3 >= r2) && (r3 <= r)); + r = secp256k1_testrandi64(0, INT64_MAX); + s = (int64_t)r * (secp256k1_testrand32()&1?-1:1); + CHECK(secp256k1_sign_and_abs64(&r2, s) == (s < 0)); + CHECK(r2 == r); + } +} + void run_context_tests(int use_prealloc) { secp256k1_pubkey pubkey; secp256k1_pubkey zero_pubkey; @@ -1105,6 +1151,114 @@ void run_scalar_set_b32_seckey_tests(void) { CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } +void scalar_chacha_tests(void) { + /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A + * Note that scalar_set_b32 and scalar_get_b32 represent integers + * underlying the scalar in big-endian format. */ + unsigned char expected1[64] = { + 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, + 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, + 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, + 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, + 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, + 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, + 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, + 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 + }; + unsigned char expected2[64] = { + 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, + 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, + 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, + 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, + 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, + 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, + 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, + 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b + }; + unsigned char seed3[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + unsigned char expected3[64] = { + 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, + 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, + 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, + 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, + 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, + 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, + 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, + 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 + }; + unsigned char seed4[32] = { + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char expected4[64] = { + 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, + 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, + 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, + 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, + 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, + 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, + 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, + 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e + }; + unsigned char seed5[32] = { + 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, + 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, + 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, + 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 + }; + unsigned char expected5[64] = { + 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, + 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, + 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, + 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, + 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, + 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, + 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, + 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 + }; + + secp256k1_scalar exp_r1, exp_r2; + secp256k1_scalar r1, r2; + unsigned char seed0[32] = { 0 }; + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); + secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); + secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); + secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); +} + void run_scalar_tests(void) { int i; for (i = 0; i < 128 * count; i++) { @@ -1114,6 +1268,8 @@ void run_scalar_tests(void) { run_scalar_set_b32_seckey_tests(); } + scalar_chacha_tests(); + { /* (-1)+1 should be zero. */ secp256k1_scalar s, o; @@ -5432,10 +5588,30 @@ void run_ecdsa_openssl(void) { # include "modules/ecdh/tests_impl.h" #endif +#ifdef ENABLE_MODULE_MUSIG +# include "modules/musig/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # 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 + #ifdef ENABLE_MODULE_EXTRAKEYS # include "modules/extrakeys/tests_impl.h" #endif @@ -5444,18 +5620,18 @@ void run_ecdsa_openssl(void) { # include "modules/schnorrsig/tests_impl.h" #endif -void run_memczero_test(void) { +void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; - /* memczero(..., ..., 0) is a noop. */ + /* secp256k1_memczero(..., ..., 0) is a noop. */ memcpy(buf2, buf1, sizeof(buf1)); - memczero(buf1, sizeof(buf1), 0); + secp256k1_memczero(buf1, sizeof(buf1), 0); CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0); - /* memczero(..., ..., 1) zeros the buffer. */ + /* secp256k1_memczero(..., ..., 1) zeros the buffer. */ memset(buf2, 0, sizeof(buf2)); - memczero(buf1, sizeof(buf1) , 1); + secp256k1_memczero(buf1, sizeof(buf1) , 1); CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0); } @@ -5645,6 +5821,7 @@ int main(int argc, char **argv) { run_rand_bits(); run_rand_int(); + run_util_tests(); run_sha256_tests(); run_hmac_sha256_tests(); @@ -5699,6 +5876,10 @@ int main(int argc, char **argv) { run_ecdh_tests(); #endif +#ifdef ENABLE_MODULE_MUSIG + run_musig_tests(); +#endif + /* ecdsa tests */ run_random_pubkeys(); run_ecdsa_der_parse(); @@ -5714,6 +5895,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 + #ifdef ENABLE_MODULE_EXTRAKEYS run_extrakeys_tests(); #endif @@ -5723,7 +5921,7 @@ int main(int argc, char **argv) { #endif /* util tests */ - run_memczero_test(); + run_secp256k1_memczero_test(); run_cmov_tests(); diff --git a/src/util.h b/src/util.h index 3a88a41bc61..7ada3d5a172 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.* **********************************************************************/ @@ -145,6 +145,32 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz 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 @@ -202,7 +228,7 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz #endif /* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ -static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) { +static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { unsigned char *p = (unsigned char *)s; /* Access flag with a volatile-qualified lvalue. This prevents clang from figuring out (after inlining) that flag can @@ -260,14 +286,20 @@ static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) # define SECP256K1_WIDEMUL_INT128 1 #elif defined(USE_FORCE_WIDEMUL_INT64) # define SECP256K1_WIDEMUL_INT64 1 -#elif defined(__SIZEOF_INT128__) +#elif defined(UINT128_MAX) || defined(__SIZEOF_INT128__) # define SECP256K1_WIDEMUL_INT128 1 #else # define SECP256K1_WIDEMUL_INT64 1 #endif #if defined(SECP256K1_WIDEMUL_INT128) +# if !defined(UINT128_MAX) && defined(__SIZEOF_INT128__) SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; SECP256K1_GNUC_EXT typedef __int128 int128_t; +#define UINT128_MAX ((uint128_t)(-1)) +#define INT128_MAX ((int128_t)(UINT128_MAX >> 1)) +#define INT128_MIN (-INT128_MAX - 1) +/* No (U)INT128_C macros because compilers providing __int128 do not support 128-bit literals. */ +# endif #endif #endif /* SECP256K1_UTIL_H */