Browse Source

Squashed 'src/secp256k1/' changes from 7a49cac..8225239

8225239 Merge #433: Make the libcrypto detection fail the newer API.
12de863 Make the libcrypto detection fail the newer API.
2928420 Merge #427: Remove Schnorr from travis as well
8eecc4a Remove Schnorr from travis as well
a8abae7 Merge #310: Add exhaustive test for group functions on a low-order subgroup
b4ceedf Add exhaustive test for verification
83836a9 Add exhaustive tests for group arithmetic, signing, and ecmult on a small group
20b8877 Add exhaustive test for group functions on a low-order subgroup
80773a6 Merge #425: Remove Schnorr experiment
e06e878 Remove Schnorr experiment
04c8ef3 Merge #407: Modify parameter order of internal functions to match API parameter order
6e06696 Merge #411: Remove guarantees about memcmp-ability
40c8d7e Merge #421: Update scalar_4x64_impl.h
a922365 Merge #422: Restructure nonce clearing
3769783 Restructure nonce clearing
0f9e69d Restructure nonce clearing
9d67afa Update scalar_4x64_impl.h
7d15cd7 Merge #413: fix auto-enabled static precompuatation
00c5d2e fix auto-enabled static precompuatation
91219a1 Remove guarantees about memcmp-ability
353c1bf Fix secp256k1_ge_set_table_gej_var parameter order
541b783 Fix secp256k1_ge_set_all_gej_var parameter order
7d893f4 Fix secp256k1_fe_inv_all_var parameter order

git-subtree-dir: src/secp256k1
git-subtree-split: 8225239f490f79842a5a3b82ad6cc8aa11d5208e
0.14
Pieter Wuille 8 years ago
parent
commit
7b49f22bdb
  1. 1
      .gitignore
  2. 9
      .travis.yml
  3. 18
      Makefile.am
  4. 4
      build-aux/m4/bitcoin_secp.m4
  5. 30
      configure.ac
  6. 14
      include/secp256k1.h
  7. 173
      include/secp256k1_schnorr.h
  8. 18
      src/ecdsa_impl.h
  9. 2
      src/ecmult_const_impl.h
  10. 2
      src/ecmult_gen_impl.h
  11. 23
      src/ecmult_impl.h
  12. 7
      src/field.h
  13. 2
      src/field_impl.h
  14. 4
      src/group.h
  15. 74
      src/group_impl.h
  16. 34
      src/java/org/bitcoin/NativeSecp256k1.java
  17. 21
      src/java/org/bitcoin/NativeSecp256k1Test.java
  18. 34
      src/java/org_bitcoin_NativeSecp256k1.c
  19. 8
      src/java/org_bitcoin_NativeSecp256k1.h
  20. 4
      src/modules/recovery/main_impl.h
  21. 10
      src/modules/schnorr/Makefile.am.include
  22. 164
      src/modules/schnorr/main_impl.h
  23. 20
      src/modules/schnorr/schnorr.h
  24. 207
      src/modules/schnorr/schnorr_impl.h
  25. 175
      src/modules/schnorr/tests_impl.h
  26. 4
      src/scalar.h
  27. 26
      src/scalar_4x64_impl.h
  28. 39
      src/scalar_impl.h
  29. 15
      src/scalar_low.h
  30. 114
      src/scalar_low_impl.h
  31. 4
      src/secp256k1.c
  32. 12
      src/tests.c
  33. 329
      src/tests_exhaustive.c

1
.gitignore vendored

@ -6,6 +6,7 @@ bench_schnorr_verify
bench_recover bench_recover
bench_internal bench_internal
tests tests
exhaustive_tests
gen_context gen_context
*.exe *.exe
*.so *.so

9
.travis.yml

@ -11,7 +11,7 @@ cache:
- src/java/guava/ - src/java/guava/
env: env:
global: global:
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no EXPERIMENTAL=no - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix: matrix:
- SCALAR=32bit RECOVERY=yes - SCALAR=32bit RECOVERY=yes
@ -22,15 +22,14 @@ env:
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- FIELD=64bit ASM=x86_64 - FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- FIELD=32bit SCHNORR=yes EXPERIMENTAL=yes
- FIELD=32bit ENDOMORPHISM=yes - FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no - BIGNUM=no
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes EXPERIMENTAL=yes - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no - BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck - BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0 - EXTRAFLAGS=CFLAGS=-O0
- BUILD=check-java ECDH=yes SCHNORR=yes EXPERIMENTAL=yes - BUILD=check-java ECDH=yes EXPERIMENTAL=yes
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
@ -66,5 +65,5 @@ before_script: ./autogen.sh
script: script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux os: linux

18
Makefile.am

@ -12,9 +12,11 @@ noinst_HEADERS =
noinst_HEADERS += src/scalar.h noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h noinst_HEADERS += src/scalar_4x64.h
noinst_HEADERS += src/scalar_8x32.h noinst_HEADERS += src/scalar_8x32.h
noinst_HEADERS += src/scalar_low.h
noinst_HEADERS += src/scalar_impl.h noinst_HEADERS += src/scalar_impl.h
noinst_HEADERS += src/scalar_4x64_impl.h noinst_HEADERS += src/scalar_4x64_impl.h
noinst_HEADERS += src/scalar_8x32_impl.h noinst_HEADERS += src/scalar_8x32_impl.h
noinst_HEADERS += src/scalar_low_impl.h
noinst_HEADERS += src/group.h noinst_HEADERS += src/group.h
noinst_HEADERS += src/group_impl.h noinst_HEADERS += src/group_impl.h
noinst_HEADERS += src/num_gmp.h noinst_HEADERS += src/num_gmp.h
@ -87,13 +89,23 @@ bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif endif
TESTS =
if USE_TESTS if USE_TESTS
noinst_PROGRAMS += tests noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c tests_SOURCES = src/tests.c
tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
tests_LDFLAGS = -static tests_LDFLAGS = -static
TESTS = tests TESTS += tests
endif
if USE_EXHAUSTIVE_TESTS
noinst_PROGRAMS += exhaustive_tests
exhaustive_tests_SOURCES = src/tests_exhaustive.c
exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src $(SECP_INCLUDES)
exhaustive_tests_LDADD = $(SECP_LIBS)
exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_tests
endif endif
JAVAROOT=src/java JAVAROOT=src/java
@ -154,10 +166,6 @@ if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include include src/modules/ecdh/Makefile.am.include
endif endif
if ENABLE_MODULE_SCHNORR
include src/modules/schnorr/Makefile.am.include
endif
if ENABLE_MODULE_RECOVERY if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include include src/modules/recovery/Makefile.am.include
endif endif

4
build-aux/m4/bitcoin_secp.m4

@ -46,6 +46,10 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
ECDSA_sign(0, NULL, 0, NULL, NULL, eckey); ECDSA_sign(0, NULL, 0, NULL, NULL, eckey);
ECDSA_verify(0, NULL, 0, NULL, 0, eckey); ECDSA_verify(0, NULL, 0, NULL, 0, eckey);
EC_KEY_free(eckey); EC_KEY_free(eckey);
ECDSA_SIG *sig_openssl;
sig_openssl = ECDSA_SIG_new();
(void)sig_openssl->r;
ECDSA_SIG_free(sig_openssl);
]])],[has_openssl_ec=yes],[has_openssl_ec=no]) ]])],[has_openssl_ec=yes],[has_openssl_ec=no])
AC_MSG_RESULT([$has_openssl_ec]) AC_MSG_RESULT([$has_openssl_ec])
fi fi

30
configure.ac

@ -104,6 +104,11 @@ AC_ARG_ENABLE(experimental,
[use_experimental=$enableval], [use_experimental=$enableval],
[use_experimental=no]) [use_experimental=no])
AC_ARG_ENABLE(exhaustive_tests,
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
[use_exhaustive_tests=$enableval],
[use_exhaustive_tests=yes])
AC_ARG_ENABLE(endomorphism, AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
[use_endomorphism=$enableval], [use_endomorphism=$enableval],
@ -119,11 +124,6 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=$enableval], [enable_module_ecdh=$enableval],
[enable_module_ecdh=no]) [enable_module_ecdh=no])
AC_ARG_ENABLE(module_schnorr,
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (experimental)]),
[enable_module_schnorr=$enableval],
[enable_module_schnorr=no])
AC_ARG_ENABLE(module_recovery, AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
[enable_module_recovery=$enableval], [enable_module_recovery=$enableval],
@ -381,9 +381,6 @@ fi
if test x"$use_jni" != x"no"; then if test x"$use_jni" != x"no"; then
AX_JNI_INCLUDE_DIR AX_JNI_INCLUDE_DIR
have_jni_dependencies=yes have_jni_dependencies=yes
if test x"$enable_module_schnorr" = x"no"; then
have_jni_dependencies=no
fi
if test x"$enable_module_ecdh" = x"no"; then if test x"$enable_module_ecdh" = x"no"; then
have_jni_dependencies=no have_jni_dependencies=no
fi fi
@ -392,7 +389,7 @@ if test x"$use_jni" != x"no"; then
fi fi
if test "x$have_jni_dependencies" = "xno"; then if test "x$have_jni_dependencies" = "xno"; then
if test x"$use_jni" = x"yes"; then if test x"$use_jni" = x"yes"; then
AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and Schnorr and try again.]) AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
fi fi
AC_MSG_WARN([jni headers/dependencies not found. jni support disabled]) AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
use_jni=no use_jni=no
@ -413,7 +410,7 @@ if test x"$use_endomorphism" = x"yes"; then
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
fi fi
if test x"$use_ecmult_static_precomputation" = x"yes"; then if test x"$set_precomp" = x"yes"; then
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table]) AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
fi fi
@ -421,10 +418,6 @@ if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi fi
if test x"$enable_module_schnorr" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module])
fi
if test x"$enable_module_recovery" = x"yes"; then 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]) AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi fi
@ -442,7 +435,6 @@ AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
AC_MSG_NOTICE([Using jni: $use_jni]) AC_MSG_NOTICE([Using jni: $use_jni])
@ -451,12 +443,8 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build]) 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([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([******]) AC_MSG_NOTICE([******])
else else
if test x"$enable_module_schnorr" = x"yes"; then
AC_MSG_ERROR([Schnorr signature module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_ecdh" = x"yes"; then if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi fi
@ -473,10 +461,10 @@ AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS) AC_SUBST(SECP_TEST_LIBS)
AC_SUBST(SECP_TEST_INCLUDES) AC_SUBST(SECP_TEST_INCLUDES)
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
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_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = 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_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])

14
include/secp256k1.h

@ -47,11 +47,8 @@ typedef struct secp256k1_context_struct secp256k1_context;
* The exact representation of data inside is implementation defined and not * The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is * 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. * 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 or transmission, use * If you need to convert to a format suitable for storage, transmission, or
* secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
*
* Furthermore, it is guaranteed that identical public keys (ignoring
* compression) will have identical representation, so they can be memcmp'ed.
*/ */
typedef struct { typedef struct {
unsigned char data[64]; unsigned char data[64];
@ -62,12 +59,9 @@ typedef struct {
* The exact representation of data inside is implementation defined and not * The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is * 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. * 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 or transmission, use * If you need to convert to a format suitable for storage, transmission, or
* the secp256k1_ecdsa_signature_serialize_* and * comparison, use the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_serialize_* functions. * secp256k1_ecdsa_signature_serialize_* functions.
*
* Furthermore, it is guaranteed to identical signatures will have identical
* representation, so they can be memcmp'ed.
*/ */
typedef struct { typedef struct {
unsigned char data[64]; unsigned char data[64];

173
include/secp256k1_schnorr.h

@ -1,173 +0,0 @@
#ifndef _SECP256K1_SCHNORR_
# define _SECP256K1_SCHNORR_
# include "secp256k1.h"
# ifdef __cplusplus
extern "C" {
# endif
/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
* produces non-malleable 64-byte signatures which support public key recovery
* batch validation, and multiparty signing.
* Returns: 1: signature created
* 0: the nonce generation function failed, or the private key was
* invalid.
* Args: ctx: pointer to a context object, initialized for signing
* (cannot be NULL)
* Out: sig64: pointer to a 64-byte array where the signature will be
* placed (cannot be NULL)
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
* seckey: pointer to a 32-byte secret key (cannot be NULL)
* 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)
*/
SECP256K1_API int secp256k1_schnorr_sign(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Verify a signature created by secp256k1_schnorr_sign.
* Returns: 1: correct signature
* 0: incorrect signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig64: the 64-byte signature being verified (cannot be NULL)
* msg32: the 32-byte message hash being verified (cannot be NULL)
* pubkey: the public key to verify with (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
const secp256k1_context* ctx,
const unsigned char *sig64,
const unsigned char *msg32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Recover an EC public key from a Schnorr signature created using
* secp256k1_schnorr_sign.
* Returns: 1: public key successfully recovered (which guarantees a correct
* signature).
* 0: otherwise.
* Args: ctx: pointer to a context object, initialized for
* verification (cannot be NULL)
* Out: pubkey: pointer to a pubkey to set to the recovered public key
* (cannot be NULL).
* In: sig64: signature as 64 byte array (cannot be NULL)
* msg32: the 32-byte message hash assumed to be signed (cannot
* be NULL)
*/
SECP256K1_API int secp256k1_schnorr_recover(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const unsigned char *sig64,
const unsigned char *msg32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Generate a nonce pair deterministically for use with
* secp256k1_schnorr_partial_sign.
* Returns: 1: valid nonce pair was generated.
* 0: otherwise (nonce generation function failed)
* Args: ctx: pointer to a context object, initialized for signing
* (cannot be NULL)
* Out: pubnonce: public side of the nonce (cannot be NULL)
* privnonce32: private side of the nonce (32 byte) (cannot be NULL)
* In: msg32: the 32-byte message hash assumed to be signed (cannot
* be NULL)
* sec32: the 32-byte private key (cannot be NULL)
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_default is used
* noncedata: pointer to arbitrary data used by the nonce generation
* function (can be NULL)
*
* Do not use the output as a private/public key pair for signing/validation.
*/
SECP256K1_API int secp256k1_schnorr_generate_nonce_pair(
const secp256k1_context* ctx,
secp256k1_pubkey *pubnonce,
unsigned char *privnonce32,
const unsigned char *msg32,
const unsigned char *sec32,
secp256k1_nonce_function noncefp,
const void* noncedata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Produce a partial Schnorr signature, which can be combined using
* secp256k1_schnorr_partial_combine, to end up with a full signature that is
* verifiable using secp256k1_schnorr_verify.
* Returns: 1: signature created successfully.
* 0: no valid signature exists with this combination of keys, nonces
* and message (chance around 1 in 2^128)
* -1: invalid private key, nonce, or public nonces.
* Args: ctx: pointer to context object, initialized for signing (cannot
* be NULL)
* Out: sig64: pointer to 64-byte array to put partial signature in
* In: msg32: pointer to 32-byte message to sign
* sec32: pointer to 32-byte private key
* pubnonce_others: pointer to pubkey containing the sum of the other's
* nonces (see secp256k1_ec_pubkey_combine)
* secnonce32: pointer to 32-byte array containing our nonce
*
* The intended procedure for creating a multiparty signature is:
* - Each signer S[i] with private key x[i] and public key Q[i] runs
* secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
* private/public nonces.
* - All signers communicate their public nonces to each other (revealing your
* private nonce can lead to discovery of your private key, so it should be
* considered secret).
* - All signers combine all the public nonces they received (excluding their
* own) using secp256k1_ec_pubkey_combine to obtain an
* Rall[i] = sum(R[0..i-1,i+1..n]).
* - All signers produce a partial signature using
* secp256k1_schnorr_partial_sign, passing in their own private key x[i],
* their own private nonce k[i], and the sum of the others' public nonces
* Rall[i].
* - All signers communicate their partial signatures to each other.
* - Someone combines all partial signatures using
* secp256k1_schnorr_partial_combine, to obtain a full signature.
* - The resulting signature is validatable using secp256k1_schnorr_verify, with
* public key equal to the result of secp256k1_ec_pubkey_combine of the
* signers' public keys (sum(Q[0..n])).
*
* Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
* function take their arguments in any order, and it is possible to
* pre-combine several inputs already with one call, and add more inputs later
* by calling the function again (they are commutative and associative).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg32,
const unsigned char *sec32,
const secp256k1_pubkey *pubnonce_others,
const unsigned char *secnonce32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
/** Combine multiple Schnorr partial signatures.
* Returns: 1: the passed signatures were successfully combined.
* 0: the resulting signature is not valid (chance of 1 in 2^256)
* -1: some inputs were invalid, or the signatures were not created
* using the same set of nonces
* Args: ctx: pointer to a context object
* Out: sig64: pointer to a 64-byte array to place the combined signature
* (cannot be NULL)
* In: sig64sin: pointer to an array of n pointers to 64-byte input
* signatures
* n: the number of signatures to combine (at least 1)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char * const * sig64sin,
size_t n
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
# ifdef __cplusplus
}
# endif
#endif

18
src/ecdsa_impl.h

@ -203,7 +203,9 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) {
unsigned char c[32]; unsigned char c[32];
secp256k1_scalar sn, u1, u2; secp256k1_scalar sn, u1, u2;
#if !defined(EXHAUSTIVE_TEST_ORDER)
secp256k1_fe xr; secp256k1_fe xr;
#endif
secp256k1_gej pubkeyj; secp256k1_gej pubkeyj;
secp256k1_gej pr; secp256k1_gej pr;
@ -219,6 +221,21 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
if (secp256k1_gej_is_infinity(&pr)) { if (secp256k1_gej_is_infinity(&pr)) {
return 0; return 0;
} }
#if defined(EXHAUSTIVE_TEST_ORDER)
{
secp256k1_scalar computed_r;
int overflow = 0;
secp256k1_ge pr_ge;
secp256k1_ge_set_gej(&pr_ge, &pr);
secp256k1_fe_normalize(&pr_ge.x);
secp256k1_fe_get_b32(c, &pr_ge.x);
secp256k1_scalar_set_b32(&computed_r, c, &overflow);
/* we fully expect overflow */
return secp256k1_scalar_eq(sigr, &computed_r);
}
#else
secp256k1_scalar_get_b32(c, sigr); secp256k1_scalar_get_b32(c, sigr);
secp256k1_fe_set_b32(&xr, c); secp256k1_fe_set_b32(&xr, c);
@ -252,6 +269,7 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
return 1; return 1;
} }
return 0; return 0;
#endif
} }
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) {

2
src/ecmult_const_impl.h

@ -78,7 +78,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
/* Negative numbers will be negated to keep their bit representation below the maximum width */ /* Negative numbers will be negated to keep their bit representation below the maximum width */
flip = secp256k1_scalar_is_high(&s); flip = secp256k1_scalar_is_high(&s);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ (s.d[0] & 1); bit = flip ^ !secp256k1_scalar_is_even(&s);
/* We check for negative one, since adding 2 to it will cause an overflow */ /* We check for negative one, since adding 2 to it will cause an overflow */
secp256k1_scalar_negate(&neg_s, &s); secp256k1_scalar_negate(&neg_s, &s);
not_neg_one = !secp256k1_scalar_is_one(&neg_s); not_neg_one = !secp256k1_scalar_is_one(&neg_s);

2
src/ecmult_gen_impl.h

@ -77,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
} }
} }
secp256k1_ge_set_all_gej_var(1024, prec, precj, cb); secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb);
} }
for (j = 0; j < 64; j++) { for (j = 0; j < 64; j++) {
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {

23
src/ecmult_impl.h

@ -7,15 +7,29 @@
#ifndef _SECP256K1_ECMULT_IMPL_H_ #ifndef _SECP256K1_ECMULT_IMPL_H_
#define _SECP256K1_ECMULT_IMPL_H_ #define _SECP256K1_ECMULT_IMPL_H_
#include <string.h>
#include "group.h" #include "group.h"
#include "scalar.h" #include "scalar.h"
#include "ecmult.h" #include "ecmult.h"
#include <string.h> #if defined(EXHAUSTIVE_TEST_ORDER)
/* We need to lower these values for exhaustive tests because
* the tables cannot have infinities in them (this breaks the
* affine-isomorphism stuff which tracks z-ratios) */
# if EXHAUSTIVE_TEST_ORDER > 128
# define WINDOW_A 5
# define WINDOW_G 8
# elif EXHAUSTIVE_TEST_ORDER > 8
# define WINDOW_A 4
# define WINDOW_G 4
# else
# define WINDOW_A 2
# define WINDOW_G 2
# endif
#else
/* optimal for 128-bit and 256-bit exponents. */ /* optimal for 128-bit and 256-bit exponents. */
#define WINDOW_A 5 #define WINDOW_A 5
/** larger numbers may result in slightly better performance, at the cost of /** larger numbers may result in slightly better performance, at the cost of
exponentially larger precomputed tables. */ exponentially larger precomputed tables. */
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
@ -25,6 +39,7 @@
/** One table for window size 16: 1.375 MiB. */ /** One table for window size 16: 1.375 MiB. */
#define WINDOW_G 16 #define WINDOW_G 16
#endif #endif
#endif
/** The number of entries a table with precomputed multiples needs to have. */ /** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) #define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
@ -103,7 +118,7 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
/* Compute the odd multiples in Jacobian form. */ /* Compute the odd multiples in Jacobian form. */
secp256k1_ecmult_odd_multiples_table(n, prej, zr, a); secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
/* Convert them in batch to affine coordinates. */ /* Convert them in batch to affine coordinates. */
secp256k1_ge_set_table_gej_var(n, prea, prej, zr); secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
/* Convert them to compact storage form. */ /* Convert them to compact storage form. */
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
secp256k1_ge_to_storage(&pre[i], &prea[i]); secp256k1_ge_to_storage(&pre[i], &prea[i]);

7
src/field.h

@ -30,6 +30,8 @@
#error "Please select field implementation" #error "Please select field implementation"
#endif #endif
#include "util.h"
/** Normalize a field element. */ /** Normalize a field element. */
static void secp256k1_fe_normalize(secp256k1_fe *r); static void secp256k1_fe_normalize(secp256k1_fe *r);
@ -50,6 +52,9 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
/** Set a field element equal to a small integer. Resulting field element is normalized. */ /** Set a field element equal to a small integer. Resulting field element is normalized. */
static void secp256k1_fe_set_int(secp256k1_fe *r, int a); static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
/** Sets a field element equal to zero, initializing all fields. */
static void secp256k1_fe_clear(secp256k1_fe *a);
/** Verify whether a field element is zero. Requires the input to be normalized. */ /** Verify whether a field element is zero. Requires the input to be normalized. */
static int secp256k1_fe_is_zero(const secp256k1_fe *a); static int secp256k1_fe_is_zero(const secp256k1_fe *a);
@ -110,7 +115,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be /** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be
* at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and
* outputs must not overlap in memory. */ * outputs must not overlap in memory. */
static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a); static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len);
/** Convert a field element to the storage type. */ /** Convert a field element to the storage type. */
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);

2
src/field_impl.h

@ -260,7 +260,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
#endif #endif
} }
static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a) { static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) {
secp256k1_fe u; secp256k1_fe u;
size_t i; size_t i;
if (len < 1) { if (len < 1) {

4
src/group.h

@ -65,12 +65,12 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ /** Set a batch of group elements equal to the inputs given in jacobian coordinates */
static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb); static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb);
/** Set a batch of group elements equal to the inputs given in jacobian /** Set a batch of group elements equal to the inputs given in jacobian
* coordinates (with known z-ratios). zr must contain the known z-ratios such * coordinates (with known z-ratios). zr must contain the known z-ratios such
* that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */ * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr); static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to /** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
* the same global z "denominator". zr must contain the known z-ratios such * the same global z "denominator". zr must contain the known z-ratios such

74
src/group_impl.h

@ -11,6 +11,53 @@
#include "field.h" #include "field.h"
#include "group.h" #include "group.h"
/* These points can be generated in sage as follows:
*
* 0. Setup a worksheet with the following parameters.
* b = 4 # whatever CURVE_B will be set to
* F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
* C = EllipticCurve ([F (0), F (b)])
*
* 1. Determine all the small orders available to you. (If there are
* no satisfactory ones, go back and change b.)
* print C.order().factor(limit=1000)
*
* 2. Choose an order as one of the prime factors listed in the above step.
* (You can also multiply some to get a composite order, though the
* tests will crash trying to invert scalars during signing.) We take a
* random point and scale it to drop its order to the desired value.
* There is some probability this won't work; just try again.
* order = 199
* P = C.random_point()
* P = (int(P.order()) / int(order)) * P
* assert(P.order() == order)
*
* 3. Print the values. You'll need to use a vim macro or something to
* split the hex output into 4-byte chunks.
* print "%x %x" % P.xy()
*/
#if defined(EXHAUSTIVE_TEST_ORDER)
# if EXHAUSTIVE_TEST_ORDER == 199
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
);
const int CURVE_B = 4;
# elif EXHAUSTIVE_TEST_ORDER == 13
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
);
const int CURVE_B = 2;
# else
# error No known generator for the specified exhaustive test group order.
# endif
#else
/** Generator for secp256k1, value 'g' defined in /** Generator for secp256k1, value 'g' defined in
* "Standards for Efficient Cryptography" (SEC2) 2.7.1. * "Standards for Efficient Cryptography" (SEC2) 2.7.1.
*/ */
@ -21,6 +68,9 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
); );
const int CURVE_B = 7;
#endif
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
secp256k1_fe zi2; secp256k1_fe zi2;
secp256k1_fe zi3; secp256k1_fe zi3;
@ -76,7 +126,7 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
r->y = a->y; r->y = a->y;
} }
static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb) { static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) {
secp256k1_fe *az; secp256k1_fe *az;
secp256k1_fe *azi; secp256k1_fe *azi;
size_t i; size_t i;
@ -89,7 +139,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
} }
azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count);
secp256k1_fe_inv_all_var(count, azi, az); secp256k1_fe_inv_all_var(azi, az, count);
free(az); free(az);
count = 0; count = 0;
@ -102,7 +152,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
free(azi); free(azi);
} }
static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr) { static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
size_t i = len - 1; size_t i = len - 1;
secp256k1_fe zi; secp256k1_fe zi;
@ -145,9 +195,15 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
static void secp256k1_gej_set_infinity(secp256k1_gej *r) { static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
r->infinity = 1; r->infinity = 1;
secp256k1_fe_set_int(&r->x, 0); secp256k1_fe_clear(&r->x);
secp256k1_fe_set_int(&r->y, 0); secp256k1_fe_clear(&r->y);
secp256k1_fe_set_int(&r->z, 0); secp256k1_fe_clear(&r->z);
}
static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
r->infinity = 1;
secp256k1_fe_clear(&r->x);
secp256k1_fe_clear(&r->y);
} }
static void secp256k1_gej_clear(secp256k1_gej *r) { static void secp256k1_gej_clear(secp256k1_gej *r) {
@ -169,7 +225,7 @@ static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
secp256k1_fe_sqr(&x2, x); secp256k1_fe_sqr(&x2, x);
secp256k1_fe_mul(&x3, x, &x2); secp256k1_fe_mul(&x3, x, &x2);
r->infinity = 0; r->infinity = 0;
secp256k1_fe_set_int(&c, 7); secp256k1_fe_set_int(&c, CURVE_B);
secp256k1_fe_add(&c, &x3); secp256k1_fe_add(&c, &x3);
return secp256k1_fe_sqrt(&r->y, &c); return secp256k1_fe_sqrt(&r->y, &c);
} }
@ -228,7 +284,7 @@ static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) {
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
secp256k1_fe_sqr(&z2, &a->z); secp256k1_fe_sqr(&z2, &a->z);
secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2); secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2);
secp256k1_fe_mul_int(&z6, 7); secp256k1_fe_mul_int(&z6, CURVE_B);
secp256k1_fe_add(&x3, &z6); secp256k1_fe_add(&x3, &z6);
secp256k1_fe_normalize_weak(&x3); secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3); return secp256k1_fe_equal_var(&y2, &x3);
@ -242,7 +298,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
/* y^2 = x^3 + 7 */ /* y^2 = x^3 + 7 */
secp256k1_fe_sqr(&y2, &a->y); secp256k1_fe_sqr(&y2, &a->y);
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
secp256k1_fe_set_int(&c, 7); secp256k1_fe_set_int(&c, CURVE_B);
secp256k1_fe_add(&x3, &c); secp256k1_fe_add(&x3, &c);
secp256k1_fe_normalize_weak(&x3); secp256k1_fe_normalize_weak(&x3);
return secp256k1_fe_equal_var(&y2, &x3); return secp256k1_fe_equal_var(&y2, &x3);

34
src/java/org/bitcoin/NativeSecp256k1.java

@ -32,7 +32,7 @@ import static org.bitcoin.NativeSecp256k1Util.*;
* <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p> * <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
* *
* <p>To build secp256k1 for use with bitcoinj, run * <p>To build secp256k1 for use with bitcoinj, run
* `./configure --enable-jni --enable-experimental --enable-module-schnorr --enable-module-ecdh` * `./configure --enable-jni --enable-experimental --enable-module-ecdh`
* and `make` then copy `.libs/libsecp256k1.so` to your system library path * and `make` then copy `.libs/libsecp256k1.so` to your system library path
* or point the JVM to the folder containing it with -Djava.library.path * or point the JVM to the folder containing it with -Djava.library.path
* </p> * </p>
@ -417,36 +417,6 @@ public class NativeSecp256k1 {
} }
} }
public static byte[] schnorrSign(byte[] data, byte[] sec) throws AssertFailException {
Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
ByteBuffer byteBuff = nativeECDSABuffer.get();
if (byteBuff == null) {
byteBuff = ByteBuffer.allocateDirect(32 + 32);
byteBuff.order(ByteOrder.nativeOrder());
nativeECDSABuffer.set(byteBuff);
}
byteBuff.rewind();
byteBuff.put(data);
byteBuff.put(sec);
byte[][] retByteArray;
r.lock();
try {
retByteArray = secp256k1_schnorr_sign(byteBuff, Secp256k1Context.getContext());
} finally {
r.unlock();
}
byte[] sigArr = retByteArray[0];
int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
assertEquals(sigArr.length, 64, "Got bad signature length.");
return retVal == 0 ? new byte[0] : sigArr;
}
private static native long secp256k1_ctx_clone(long context); private static native long secp256k1_ctx_clone(long context);
private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
@ -471,8 +441,6 @@ public class NativeSecp256k1 {
private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
private static native byte[][] secp256k1_schnorr_sign(ByteBuffer byteBuff, long context);
private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
} }

21
src/java/org/bitcoin/NativeSecp256k1Test.java

@ -167,22 +167,6 @@ public class NativeSecp256k1Test {
assertEquals( result, true, "testRandomize"); assertEquals( result, true, "testRandomize");
} }
/**
* This tests signSchnorr() for a valid secretkey
*/
public static void testSchnorrSign() throws AssertFailException{
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
byte[] resultArr = NativeSecp256k1.schnorrSign(data, sec);
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
assertEquals( sigString, "C5E929AA058B982048760422D3B563749B7D0E50C5EBD8CD2FFC23214BD6A2F1B072C13880997EBA847CF20F2F90FCE07C1CA33A890A4127095A351127F8D95F" , "testSchnorrSign");
}
/**
* This tests signSchnorr() for a valid secretkey
*/
public static void testCreateECDHSecret() throws AssertFailException{ public static void testCreateECDHSecret() throws AssertFailException{
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
@ -216,11 +200,6 @@ public class NativeSecp256k1Test {
testSignPos(); testSignPos();
testSignNeg(); testSignNeg();
//Test Schnorr (partial support) //TODO
testSchnorrSign();
//testSchnorrVerify
//testSchnorrRecovery
//Test privKeyTweakAdd() 1 //Test privKeyTweakAdd() 1
testPrivKeyTweakAdd_1(); testPrivKeyTweakAdd_1();

34
src/java/org_bitcoin_NativeSecp256k1.c

@ -5,7 +5,6 @@
#include "include/secp256k1.h" #include "include/secp256k1.h"
#include "include/secp256k1_ecdh.h" #include "include/secp256k1_ecdh.h"
#include "include/secp256k1_recovery.h" #include "include/secp256k1_recovery.h"
#include "include/secp256k1_schnorr.h"
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
@ -333,39 +332,6 @@ SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1p
return 0; return 0;
} }
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
{
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
unsigned char* secKey = (unsigned char*) (data + 32);
jobjectArray retArray;
jbyteArray sigArray, intsByteArray;
unsigned char intsarray[1];
unsigned char sig[64];
int ret = secp256k1_schnorr_sign(ctx, sig, data, secKey, NULL, NULL);
intsarray[0] = ret;
retArray = (*env)->NewObjectArray(env, 2,
(*env)->FindClass(env, "[B"),
(*env)->NewByteArray(env, 1));
sigArray = (*env)->NewByteArray(env, 64);
(*env)->SetByteArrayRegion(env, sigArray, 0, 64, (jbyte*)sig);
(*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
intsByteArray = (*env)->NewByteArray(env, 1);
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
(void)classObject;
return retArray;
}
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
{ {

8
src/java/org_bitcoin_NativeSecp256k1.h

@ -104,14 +104,6 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
(JNIEnv *, jclass, jobject, jlong, jint); (JNIEnv *, jclass, jobject, jlong, jint);
/*
* Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_schnorr_sign
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
*/
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1schnorr_1sign
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l);
/* /*
* Class: org_bitcoin_NativeSecp256k1 * Class: org_bitcoin_NativeSecp256k1
* Method: secp256k1_ecdh * Method: secp256k1_ecdh

4
src/modules/recovery/main_impl.h

@ -138,16 +138,15 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
secp256k1_scalar_set_b32(&sec, seckey, &overflow); secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */ /* Fail if the secret key is invalid. */
if (!overflow && !secp256k1_scalar_is_zero(&sec)) { if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
unsigned char nonce32[32];
unsigned int count = 0; unsigned int count = 0;
secp256k1_scalar_set_b32(&msg, msg32, NULL); secp256k1_scalar_set_b32(&msg, msg32, NULL);
while (1) { while (1) {
unsigned char nonce32[32];
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
if (!ret) { if (!ret) {
break; break;
} }
secp256k1_scalar_set_b32(&non, nonce32, &overflow); secp256k1_scalar_set_b32(&non, nonce32, &overflow);
memset(nonce32, 0, 32);
if (!secp256k1_scalar_is_zero(&non) && !overflow) { if (!secp256k1_scalar_is_zero(&non) && !overflow) {
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) { if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
break; break;
@ -155,6 +154,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
} }
count++; count++;
} }
memset(nonce32, 0, 32);
secp256k1_scalar_clear(&msg); secp256k1_scalar_clear(&msg);
secp256k1_scalar_clear(&non); secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec); secp256k1_scalar_clear(&sec);

10
src/modules/schnorr/Makefile.am.include

@ -1,10 +0,0 @@
include_HEADERS += include/secp256k1_schnorr.h
noinst_HEADERS += src/modules/schnorr/main_impl.h
noinst_HEADERS += src/modules/schnorr/schnorr.h
noinst_HEADERS += src/modules/schnorr/schnorr_impl.h
noinst_HEADERS += src/modules/schnorr/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_schnorr_verify
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif

164
src/modules/schnorr/main_impl.h

@ -1,164 +0,0 @@
/**********************************************************************
* Copyright (c) 2014-2015 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_SCHNORR_MAIN
#define SECP256K1_MODULE_SCHNORR_MAIN
#include "include/secp256k1_schnorr.h"
#include "modules/schnorr/schnorr_impl.h"
static void secp256k1_schnorr_msghash_sha256(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
secp256k1_sha256_t sha;
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, r32, 32);
secp256k1_sha256_write(&sha, msg32, 32);
secp256k1_sha256_finalize(&sha, h32);
}
static const unsigned char secp256k1_schnorr_algo16[17] = "Schnorr+SHA256 ";
int secp256k1_schnorr_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar sec, non;
int ret = 0;
int overflow = 0;
unsigned int count = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(seckey != NULL);
if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_default;
}
secp256k1_scalar_set_b32(&sec, seckey, NULL);
while (1) {
unsigned char nonce32[32];
ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, (void*)noncedata, count);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
memset(nonce32, 0, 32);
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, NULL, secp256k1_schnorr_msghash_sha256, msg32)) {
break;
}
}
count++;
}
if (!ret) {
memset(sig64, 0, 64);
}
secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec);
return ret;
}
int secp256k1_schnorr_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
secp256k1_ge q;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(pubkey != NULL);
secp256k1_pubkey_load(ctx, &q, pubkey);
return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32);
}
int secp256k1_schnorr_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *sig64, const unsigned char *msg32) {
secp256k1_ge q;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(pubkey != NULL);
if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32)) {
secp256k1_pubkey_save(pubkey, &q);
return 1;
} else {
memset(pubkey, 0, sizeof(*pubkey));
return 0;
}
}
int secp256k1_schnorr_generate_nonce_pair(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, unsigned char *privnonce32, const unsigned char *sec32, const unsigned char *msg32, secp256k1_nonce_function noncefp, const void* noncedata) {
int count = 0;
int ret = 1;
secp256k1_gej Qj;
secp256k1_ge Q;
secp256k1_scalar sec;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(sec32 != NULL);
ARG_CHECK(pubnonce != NULL);
ARG_CHECK(privnonce32 != NULL);
if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_default;
}
do {
int overflow;
ret = noncefp(privnonce32, sec32, msg32, secp256k1_schnorr_algo16, (void*)noncedata, count++);
if (!ret) {
break;
}
secp256k1_scalar_set_b32(&sec, privnonce32, &overflow);
if (overflow || secp256k1_scalar_is_zero(&sec)) {
continue;
}
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sec);
secp256k1_ge_set_gej(&Q, &Qj);
secp256k1_pubkey_save(pubnonce, &Q);
break;
} while(1);
secp256k1_scalar_clear(&sec);
if (!ret) {
memset(pubnonce, 0, sizeof(*pubnonce));
}
return ret;
}
int secp256k1_schnorr_partial_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *sec32, const secp256k1_pubkey *pubnonce_others, const unsigned char *secnonce32) {
int overflow = 0;
secp256k1_scalar sec, non;
secp256k1_ge pubnon;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(msg32 != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(sec32 != NULL);
ARG_CHECK(secnonce32 != NULL);
ARG_CHECK(pubnonce_others != NULL);
secp256k1_scalar_set_b32(&sec, sec32, &overflow);
if (overflow || secp256k1_scalar_is_zero(&sec)) {
return -1;
}
secp256k1_scalar_set_b32(&non, secnonce32, &overflow);
if (overflow || secp256k1_scalar_is_zero(&non)) {
return -1;
}
secp256k1_pubkey_load(ctx, &pubnon, pubnonce_others);
return secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, &pubnon, secp256k1_schnorr_msghash_sha256, msg32);
}
int secp256k1_schnorr_partial_combine(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char * const *sig64sin, size_t n) {
ARG_CHECK(sig64 != NULL);
ARG_CHECK(n >= 1);
ARG_CHECK(sig64sin != NULL);
return secp256k1_schnorr_sig_combine(sig64, n, sig64sin);
}
#endif

20
src/modules/schnorr/schnorr.h

@ -1,20 +0,0 @@
/***********************************************************************
* Copyright (c) 2014-2015 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_SCHNORR_H_
#define _SECP256K1_MODULE_SCHNORR_H_
#include "scalar.h"
#include "group.h"
typedef void (*secp256k1_schnorr_msghash)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32);
static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, size_t n, const unsigned char * const *sig64ins);
#endif

207
src/modules/schnorr/schnorr_impl.h

@ -1,207 +0,0 @@
/***********************************************************************
* Copyright (c) 2014-2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php. *
***********************************************************************/
#ifndef _SECP256K1_SCHNORR_IMPL_H_
#define _SECP256K1_SCHNORR_IMPL_H_
#include <string.h>
#include "schnorr.h"
#include "num.h"
#include "field.h"
#include "group.h"
#include "ecmult.h"
#include "ecmult_gen.h"
/**
* Custom Schnorr-based signature scheme. They support multiparty signing, public key
* recovery and batch validation.
*
* Rationale for verifying R's y coordinate:
* In order to support batch validation and public key recovery, the full R point must
* be known to verifiers, rather than just its x coordinate. In order to not risk
* being more strict in batch validation than normal validation, validators must be
* required to reject signatures with incorrect y coordinate. This is only possible
* by including a (relatively slow) field inverse, or a field square root. However,
* batch validation offers potentially much higher benefits than this cost.
*
* Rationale for having an implicit y coordinate oddness:
* If we commit to having the full R point known to verifiers, there are two mechanism.
* Either include its oddness in the signature, or give it an implicit fixed value.
* As the R y coordinate can be flipped by a simple negation of the nonce, we choose the
* latter, as it comes with nearly zero impact on signing or validation performance, and
* saves a byte in the signature.
*
* Signing:
* Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
*
* Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
* Compute 32-byte r, the serialization of R's x coordinate.
* Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
* Compute scalar s = k - h * x.
* The signature is (r, s).
*
*
* Verification:
* Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
*
* Signature is invalid if s >= order.
* Signature is invalid if r >= p.
* Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
* Option 1 (faster for single verification):
* Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
* Signature is valid if the serialization of R's x coordinate equals r.
* Option 2 (allows batch validation and pubkey recovery):
* Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
* Signature is valid if R + h * Q + s * G == 0.
*/
static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
secp256k1_gej Rj;
secp256k1_ge Ra;
unsigned char h32[32];
secp256k1_scalar h, s;
int overflow;
secp256k1_scalar n;
if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
return 0;
}
n = *nonce;
secp256k1_ecmult_gen(ctx, &Rj, &n);
if (pubnonce != NULL) {
secp256k1_gej_add_ge(&Rj, &Rj, pubnonce);
}
secp256k1_ge_set_gej(&Ra, &Rj);
secp256k1_fe_normalize(&Ra.y);
if (secp256k1_fe_is_odd(&Ra.y)) {
/* R's y coordinate is odd, which is not allowed (see rationale above).
Force it to be even by negating the nonce. Note that this even works
for multiparty signing, as the R point is known to all participants,
which can all decide to flip the sign in unison, resulting in the
overall R point to be negated too. */
secp256k1_scalar_negate(&n, &n);
}
secp256k1_fe_normalize(&Ra.x);
secp256k1_fe_get_b32(sig64, &Ra.x);
hash(h32, sig64, msg32);
overflow = 0;
secp256k1_scalar_set_b32(&h, h32, &overflow);
if (overflow || secp256k1_scalar_is_zero(&h)) {
secp256k1_scalar_clear(&n);
return 0;
}
secp256k1_scalar_mul(&s, &h, key);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_add(&s, &s, &n);
secp256k1_scalar_clear(&n);
secp256k1_scalar_get_b32(sig64 + 32, &s);
return 1;
}
static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
secp256k1_gej Qj, Rj;
secp256k1_ge Ra;
secp256k1_fe Rx;
secp256k1_scalar h, s;
unsigned char hh[32];
int overflow;
if (secp256k1_ge_is_infinity(pubkey)) {
return 0;
}
hash(hh, sig64, msg32);
overflow = 0;
secp256k1_scalar_set_b32(&h, hh, &overflow);
if (overflow || secp256k1_scalar_is_zero(&h)) {
return 0;
}
overflow = 0;
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
if (overflow) {
return 0;
}
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
return 0;
}
secp256k1_gej_set_ge(&Qj, pubkey);
secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
if (secp256k1_gej_is_infinity(&Rj)) {
return 0;
}
secp256k1_ge_set_gej_var(&Ra, &Rj);
secp256k1_fe_normalize_var(&Ra.y);
if (secp256k1_fe_is_odd(&Ra.y)) {
return 0;
}
return secp256k1_fe_equal_var(&Rx, &Ra.x);
}
static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32) {
secp256k1_gej Qj, Rj;
secp256k1_ge Ra;
secp256k1_fe Rx;
secp256k1_scalar h, s;
unsigned char hh[32];
int overflow;
hash(hh, sig64, msg32);
overflow = 0;
secp256k1_scalar_set_b32(&h, hh, &overflow);
if (overflow || secp256k1_scalar_is_zero(&h)) {
return 0;
}
overflow = 0;
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
if (overflow) {
return 0;
}
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
return 0;
}
if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
return 0;
}
secp256k1_gej_set_ge(&Rj, &Ra);
secp256k1_scalar_inverse_var(&h, &h);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_mul(&s, &s, &h);
secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
if (secp256k1_gej_is_infinity(&Qj)) {
return 0;
}
secp256k1_ge_set_gej(pubkey, &Qj);
return 1;
}
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, size_t n, const unsigned char * const *sig64ins) {
secp256k1_scalar s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
size_t i;
for (i = 0; i < n; i++) {
secp256k1_scalar si;
int overflow;
secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow);
if (overflow) {
return -1;
}
if (i) {
if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) {
return -1;
}
}
secp256k1_scalar_add(&s, &s, &si);
}
if (secp256k1_scalar_is_zero(&s)) {
return 0;
}
memcpy(sig64, sig64ins[0], 32);
secp256k1_scalar_get_b32(sig64 + 32, &s);
secp256k1_scalar_clear(&s);
return 1;
}
#endif

175
src/modules/schnorr/tests_impl.h

@ -1,175 +0,0 @@
/**********************************************************************
* Copyright (c) 2014-2015 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_SCHNORR_TESTS
#define SECP256K1_MODULE_SCHNORR_TESTS
#include "include/secp256k1_schnorr.h"
void test_schnorr_end_to_end(void) {
unsigned char privkey[32];
unsigned char message[32];
unsigned char schnorr_signature[64];
secp256k1_pubkey pubkey, recpubkey;
/* Generate a random key and message. */
{
secp256k1_scalar key;
random_scalar_order_test(&key);
secp256k1_scalar_get_b32(privkey, &key);
secp256k1_rand256_test(message);
}
/* Construct and verify corresponding public key. */
CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
/* Schnorr sign. */
CHECK(secp256k1_schnorr_sign(ctx, schnorr_signature, message, privkey, NULL, NULL) == 1);
CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 1);
CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) == 1);
CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
/* Destroy signature and verify again. */
schnorr_signature[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255);
CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 0);
CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) != 1 ||
memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
}
/** Horribly broken hash function. Do not use for anything but tests. */
void test_schnorr_hash(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
int i;
for (i = 0; i < 32; i++) {
h32[i] = r32[i] ^ msg32[i];
}
}
void test_schnorr_sign_verify(void) {
unsigned char msg32[32];
unsigned char sig64[3][64];
secp256k1_gej pubkeyj[3];
secp256k1_ge pubkey[3];
secp256k1_scalar nonce[3], key[3];
int i = 0;
int k;
secp256k1_rand256_test(msg32);
for (k = 0; k < 3; k++) {
random_scalar_order_test(&key[k]);
do {
random_scalar_order_test(&nonce[k]);
if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64[k], &key[k], &nonce[k], NULL, &test_schnorr_hash, msg32)) {
break;
}
} while(1);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj[k], &key[k]);
secp256k1_ge_set_gej_var(&pubkey[k], &pubkeyj[k]);
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32));
for (i = 0; i < 4; i++) {
int pos = secp256k1_rand_bits(6);
int mod = 1 + secp256k1_rand_int(255);
sig64[k][pos] ^= mod;
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32) == 0);
sig64[k][pos] ^= mod;
}
}
}
void test_schnorr_threshold(void) {
unsigned char msg[32];
unsigned char sec[5][32];
secp256k1_pubkey pub[5];
unsigned char nonce[5][32];
secp256k1_pubkey pubnonce[5];
unsigned char sig[5][64];
const unsigned char* sigs[5];
unsigned char allsig[64];
const secp256k1_pubkey* pubs[5];
secp256k1_pubkey allpub;
int n, i;
int damage;
int ret = 0;
damage = secp256k1_rand_bits(1) ? (1 + secp256k1_rand_int(4)) : 0;
secp256k1_rand256_test(msg);
n = 2 + secp256k1_rand_int(4);
for (i = 0; i < n; i++) {
do {
secp256k1_rand256_test(sec[i]);
} while (!secp256k1_ec_seckey_verify(ctx, sec[i]));
CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i]));
CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, &pubnonce[i], nonce[i], msg, sec[i], NULL, NULL));
pubs[i] = &pub[i];
}
if (damage == 1) {
nonce[secp256k1_rand_int(n)][secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
} else if (damage == 2) {
sec[secp256k1_rand_int(n)][secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
}
for (i = 0; i < n; i++) {
secp256k1_pubkey allpubnonce;
const secp256k1_pubkey *pubnonces[4];
int j;
for (j = 0; j < i; j++) {
pubnonces[j] = &pubnonce[j];
}
for (j = i + 1; j < n; j++) {
pubnonces[j - 1] = &pubnonce[j];
}
CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, pubnonces, n - 1));
ret |= (secp256k1_schnorr_partial_sign(ctx, sig[i], msg, sec[i], &allpubnonce, nonce[i]) != 1) * 1;
sigs[i] = sig[i];
}
if (damage == 3) {
sig[secp256k1_rand_int(n)][secp256k1_rand_bits(6)] ^= 1 + secp256k1_rand_int(255);
}
ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, pubs, n) != 1) * 2;
if ((ret & 1) == 0) {
ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, sigs, n) != 1) * 4;
}
if (damage == 4) {
allsig[secp256k1_rand_int(32)] ^= 1 + secp256k1_rand_int(255);
}
if ((ret & 7) == 0) {
ret |= (secp256k1_schnorr_verify(ctx, allsig, msg, &allpub) != 1) * 8;
}
CHECK((ret == 0) == (damage == 0));
}
void test_schnorr_recovery(void) {
unsigned char msg32[32];
unsigned char sig64[64];
secp256k1_ge Q;
secp256k1_rand256_test(msg32);
secp256k1_rand256_test(sig64);
secp256k1_rand256_test(sig64 + 32);
if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1) {
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1);
}
}
void run_schnorr_tests(void) {
int i;
for (i = 0; i < 32*count; i++) {
test_schnorr_end_to_end();
}
for (i = 0; i < 32 * count; i++) {
test_schnorr_sign_verify();
}
for (i = 0; i < 16 * count; i++) {
test_schnorr_recovery();
}
for (i = 0; i < 10 * count; i++) {
test_schnorr_threshold();
}
}
#endif

4
src/scalar.h

@ -13,7 +13,9 @@
#include "libsecp256k1-config.h" #include "libsecp256k1-config.h"
#endif #endif
#if defined(USE_SCALAR_4X64) #if defined(EXHAUSTIVE_TEST_ORDER)
#include "scalar_low.h"
#elif defined(USE_SCALAR_4X64)
#include "scalar_4x64.h" #include "scalar_4x64.h"
#elif defined(USE_SCALAR_8X32) #elif defined(USE_SCALAR_8X32)
#include "scalar_8x32.h" #include "scalar_8x32.h"

26
src/scalar_4x64_impl.h

@ -282,8 +282,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq 56(%%rsi), %%r14\n" "movq 56(%%rsi), %%r14\n"
/* Initialize r8,r9,r10 */ /* Initialize r8,r9,r10 */
"movq 0(%%rsi), %%r8\n" "movq 0(%%rsi), %%r8\n"
"movq $0, %%r9\n" "xorq %%r9, %%r9\n"
"movq $0, %%r10\n" "xorq %%r10, %%r10\n"
/* (r8,r9) += n0 * c0 */ /* (r8,r9) += n0 * c0 */
"movq %8, %%rax\n" "movq %8, %%rax\n"
"mulq %%r11\n" "mulq %%r11\n"
@ -291,7 +291,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n" "adcq %%rdx, %%r9\n"
/* extract m0 */ /* extract m0 */
"movq %%r8, %q0\n" "movq %%r8, %q0\n"
"movq $0, %%r8\n" "xorq %%r8, %%r8\n"
/* (r9,r10) += l1 */ /* (r9,r10) += l1 */
"addq 8(%%rsi), %%r9\n" "addq 8(%%rsi), %%r9\n"
"adcq $0, %%r10\n" "adcq $0, %%r10\n"
@ -309,7 +309,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n" "adcq $0, %%r8\n"
/* extract m1 */ /* extract m1 */
"movq %%r9, %q1\n" "movq %%r9, %q1\n"
"movq $0, %%r9\n" "xorq %%r9, %%r9\n"
/* (r10,r8,r9) += l2 */ /* (r10,r8,r9) += l2 */
"addq 16(%%rsi), %%r10\n" "addq 16(%%rsi), %%r10\n"
"adcq $0, %%r8\n" "adcq $0, %%r8\n"
@ -332,7 +332,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r9\n" "adcq $0, %%r9\n"
/* extract m2 */ /* extract m2 */
"movq %%r10, %q2\n" "movq %%r10, %q2\n"
"movq $0, %%r10\n" "xorq %%r10, %%r10\n"
/* (r8,r9,r10) += l3 */ /* (r8,r9,r10) += l3 */
"addq 24(%%rsi), %%r8\n" "addq 24(%%rsi), %%r8\n"
"adcq $0, %%r9\n" "adcq $0, %%r9\n"
@ -355,7 +355,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r10\n" "adcq $0, %%r10\n"
/* extract m3 */ /* extract m3 */
"movq %%r8, %q3\n" "movq %%r8, %q3\n"
"movq $0, %%r8\n" "xorq %%r8, %%r8\n"
/* (r9,r10,r8) += n3 * c1 */ /* (r9,r10,r8) += n3 * c1 */
"movq %9, %%rax\n" "movq %9, %%rax\n"
"mulq %%r14\n" "mulq %%r14\n"
@ -387,8 +387,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq %q11, %%r13\n" "movq %q11, %%r13\n"
/* Initialize (r8,r9,r10) */ /* Initialize (r8,r9,r10) */
"movq %q5, %%r8\n" "movq %q5, %%r8\n"
"movq $0, %%r9\n" "xorq %%r9, %%r9\n"
"movq $0, %%r10\n" "xorq %%r10, %%r10\n"
/* (r8,r9) += m4 * c0 */ /* (r8,r9) += m4 * c0 */
"movq %12, %%rax\n" "movq %12, %%rax\n"
"mulq %%r11\n" "mulq %%r11\n"
@ -396,7 +396,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n" "adcq %%rdx, %%r9\n"
/* extract p0 */ /* extract p0 */
"movq %%r8, %q0\n" "movq %%r8, %q0\n"
"movq $0, %%r8\n" "xorq %%r8, %%r8\n"
/* (r9,r10) += m1 */ /* (r9,r10) += m1 */
"addq %q6, %%r9\n" "addq %q6, %%r9\n"
"adcq $0, %%r10\n" "adcq $0, %%r10\n"
@ -414,7 +414,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n" "adcq $0, %%r8\n"
/* extract p1 */ /* extract p1 */
"movq %%r9, %q1\n" "movq %%r9, %q1\n"
"movq $0, %%r9\n" "xorq %%r9, %%r9\n"
/* (r10,r8,r9) += m2 */ /* (r10,r8,r9) += m2 */
"addq %q7, %%r10\n" "addq %q7, %%r10\n"
"adcq $0, %%r8\n" "adcq $0, %%r8\n"
@ -472,7 +472,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"movq %%rax, 0(%q6)\n" "movq %%rax, 0(%q6)\n"
/* Move to (r8,r9) */ /* Move to (r8,r9) */
"movq %%rdx, %%r8\n" "movq %%rdx, %%r8\n"
"movq $0, %%r9\n" "xorq %%r9, %%r9\n"
/* (r8,r9) += p1 */ /* (r8,r9) += p1 */
"addq %q2, %%r8\n" "addq %q2, %%r8\n"
"adcq $0, %%r9\n" "adcq $0, %%r9\n"
@ -483,7 +483,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq %%rdx, %%r9\n" "adcq %%rdx, %%r9\n"
/* Extract r1 */ /* Extract r1 */
"movq %%r8, 8(%q6)\n" "movq %%r8, 8(%q6)\n"
"movq $0, %%r8\n" "xorq %%r8, %%r8\n"
/* (r9,r8) += p4 */ /* (r9,r8) += p4 */
"addq %%r10, %%r9\n" "addq %%r10, %%r9\n"
"adcq $0, %%r8\n" "adcq $0, %%r8\n"
@ -492,7 +492,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
"adcq $0, %%r8\n" "adcq $0, %%r8\n"
/* Extract r2 */ /* Extract r2 */
"movq %%r9, 16(%q6)\n" "movq %%r9, 16(%q6)\n"
"movq $0, %%r9\n" "xorq %%r9, %%r9\n"
/* (r8,r9) += p3 */ /* (r8,r9) += p3 */
"addq %q4, %%r8\n" "addq %q4, %%r8\n"
"adcq $0, %%r9\n" "adcq $0, %%r9\n"

39
src/scalar_impl.h

@ -14,7 +14,9 @@
#include "libsecp256k1-config.h" #include "libsecp256k1-config.h"
#endif #endif
#if defined(USE_SCALAR_4X64) #if defined(EXHAUSTIVE_TEST_ORDER)
#include "scalar_low_impl.h"
#elif defined(USE_SCALAR_4X64)
#include "scalar_4x64_impl.h" #include "scalar_4x64_impl.h"
#elif defined(USE_SCALAR_8X32) #elif defined(USE_SCALAR_8X32)
#include "scalar_8x32_impl.h" #include "scalar_8x32_impl.h"
@ -31,17 +33,37 @@ static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a
/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */ /** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */
static void secp256k1_scalar_order_get_num(secp256k1_num *r) { static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
#if defined(EXHAUSTIVE_TEST_ORDER)
static const unsigned char order[32] = {
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER
};
#else
static const unsigned char order[32] = { static const unsigned char order[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41
}; };
#endif
secp256k1_num_set_bin(r, order, 32); secp256k1_num_set_bin(r, order, 32);
} }
#endif #endif
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
#if defined(EXHAUSTIVE_TEST_ORDER)
int i;
*r = 0;
for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
*r = i;
/* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
* have a composite group order; fix it in exhaustive_tests.c). */
VERIFY_CHECK(*r != 0);
}
#else
secp256k1_scalar *t; secp256k1_scalar *t;
int i; int i;
/* First compute x ^ (2^N - 1) for some values of N. */ /* First compute x ^ (2^N - 1) for some values of N. */
@ -233,9 +255,9 @@ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar
} }
SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
/* d[0] is present and is the lowest word for all representations */
return !(a->d[0] & 1); return !(a->d[0] & 1);
} }
#endif
static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
#if defined(USE_SCALAR_INV_BUILTIN) #if defined(USE_SCALAR_INV_BUILTIN)
@ -259,6 +281,18 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
} }
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
#if defined(EXHAUSTIVE_TEST_ORDER)
/**
* Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the
* full case we don't bother making k1 and k2 be small, we just want them to be
* nontrivial to get full test coverage for the exhaustive tests. We therefore
* (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda.
*/
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
*r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER;
*r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
}
#else
/** /**
* The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
* lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a, * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
@ -331,5 +365,6 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
secp256k1_scalar_add(r1, r1, a); secp256k1_scalar_add(r1, r1, a);
} }
#endif #endif
#endif
#endif #endif

15
src/scalar_low.h

@ -0,0 +1,15 @@
/**********************************************************************
* Copyright (c) 2015 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_SCALAR_REPR_
#define _SECP256K1_SCALAR_REPR_
#include <stdint.h>
/** A scalar modulo the group order of the secp256k1 curve. */
typedef uint32_t secp256k1_scalar;
#endif

114
src/scalar_low_impl.h

@ -0,0 +1,114 @@
/**********************************************************************
* Copyright (c) 2015 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
#define _SECP256K1_SCALAR_REPR_IMPL_H_
#include "scalar.h"
#include <string.h>
SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
return !(*a & 1);
}
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 unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
if (offset < 32)
return ((*a >> offset) & ((((uint32_t)1) << count) - 1));
else
return 0;
}
SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
return secp256k1_scalar_get_bits(a, offset, count);
}
SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; }
static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
*r = (*a + *b) % EXHAUSTIVE_TEST_ORDER;
return *r < *b;
}
static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
if (flag && bit < 32)
*r += (1 << bit);
#ifdef VERIFY
VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
#endif
}
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
int i;
*r = 0;
for (i = 0; i < 32; i++) {
*r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
}
/* just deny overflow, it basically always happens */
if (overflow) *overflow = 0;
}
static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
memset(bin, 0, 32);
bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a;
}
SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) {
return *a == 0;
}
static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) {
if (*a == 0) {
*r = 0;
} else {
*r = EXHAUSTIVE_TEST_ORDER - *a;
}
}
SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) {
return *a == 1;
}
static int secp256k1_scalar_is_high(const secp256k1_scalar *a) {
return *a > EXHAUSTIVE_TEST_ORDER / 2;
}
static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
if (flag) secp256k1_scalar_negate(r, r);
return flag ? -1 : 1;
}
static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
*r = (*a * *b) % EXHAUSTIVE_TEST_ORDER;
}
static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
int ret;
VERIFY_CHECK(n > 0);
VERIFY_CHECK(n < 16);
ret = *r & ((1 << n) - 1);
*r >>= n;
return ret;
}
static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
*r = (*a * *a) % EXHAUSTIVE_TEST_ORDER;
}
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
*r1 = *a;
*r2 = 0;
}
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
return *a == *b;
}
#endif

4
src/secp256k1.c

@ -359,16 +359,15 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
secp256k1_scalar_set_b32(&sec, seckey, &overflow); secp256k1_scalar_set_b32(&sec, seckey, &overflow);
/* Fail if the secret key is invalid. */ /* Fail if the secret key is invalid. */
if (!overflow && !secp256k1_scalar_is_zero(&sec)) { if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
unsigned char nonce32[32];
unsigned int count = 0; unsigned int count = 0;
secp256k1_scalar_set_b32(&msg, msg32, NULL); secp256k1_scalar_set_b32(&msg, msg32, NULL);
while (1) { while (1) {
unsigned char nonce32[32];
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
if (!ret) { if (!ret) {
break; break;
} }
secp256k1_scalar_set_b32(&non, nonce32, &overflow); secp256k1_scalar_set_b32(&non, nonce32, &overflow);
memset(nonce32, 0, 32);
if (!overflow && !secp256k1_scalar_is_zero(&non)) { if (!overflow && !secp256k1_scalar_is_zero(&non)) {
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) { if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
break; break;
@ -376,6 +375,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
} }
count++; count++;
} }
memset(nonce32, 0, 32);
secp256k1_scalar_clear(&msg); secp256k1_scalar_clear(&msg);
secp256k1_scalar_clear(&non); secp256k1_scalar_clear(&non);
secp256k1_scalar_clear(&sec); secp256k1_scalar_clear(&sec);

12
src/tests.c

@ -1733,18 +1733,18 @@ void run_field_inv_all_var(void) {
secp256k1_fe x[16], xi[16], xii[16]; secp256k1_fe x[16], xi[16], xii[16];
int i; int i;
/* Check it's safe to call for 0 elements */ /* Check it's safe to call for 0 elements */
secp256k1_fe_inv_all_var(0, xi, x); secp256k1_fe_inv_all_var(xi, x, 0);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
size_t j; size_t j;
size_t len = secp256k1_rand_int(15) + 1; size_t len = secp256k1_rand_int(15) + 1;
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
random_fe_non_zero(&x[j]); random_fe_non_zero(&x[j]);
} }
secp256k1_fe_inv_all_var(len, xi, x); secp256k1_fe_inv_all_var(xi, x, len);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
CHECK(check_fe_inverse(&x[j], &xi[j])); CHECK(check_fe_inverse(&x[j], &xi[j]));
} }
secp256k1_fe_inv_all_var(len, xii, xi); secp256k1_fe_inv_all_var(xii, xi, len);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
CHECK(check_fe_equal(&x[j], &xii[j])); CHECK(check_fe_equal(&x[j], &xii[j]));
} }
@ -1930,7 +1930,7 @@ void test_ge(void) {
zs[i] = gej[i].z; zs[i] = gej[i].z;
} }
} }
secp256k1_fe_inv_all_var(4 * runs + 1, zinv, zs); secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1);
free(zs); free(zs);
} }
@ -2050,8 +2050,8 @@ void test_ge(void) {
secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
} }
} }
secp256k1_ge_set_table_gej_var(4 * runs + 1, ge_set_table, gej, zr); secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1);
secp256k1_ge_set_all_gej_var(4 * runs + 1, ge_set_all, gej, &ctx->error_callback); secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback);
for (i = 0; i < 4 * runs + 1; i++) { for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s; secp256k1_fe s;
random_fe_non_zero(&s); random_fe_non_zero(&s);

329
src/tests_exhaustive.c

@ -0,0 +1,329 @@
/***********************************************************************
* 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.*
**********************************************************************/
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#undef USE_ECMULT_STATIC_PRECOMPUTATION
#ifndef EXHAUSTIVE_TEST_ORDER
/* see group_impl.h for allowable values */
#define EXHAUSTIVE_TEST_ORDER 13
#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */
#endif
#include "include/secp256k1.h"
#include "group.h"
#include "secp256k1.c"
#include "testrand_impl.h"
/** stolen from tests.c */
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
CHECK(a->infinity == b->infinity);
if (a->infinity) {
return;
}
CHECK(secp256k1_fe_equal_var(&a->x, &b->x));
CHECK(secp256k1_fe_equal_var(&a->y, &b->y));
}
void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
secp256k1_fe z2s;
secp256k1_fe u1, u2, s1, s2;
CHECK(a->infinity == b->infinity);
if (a->infinity) {
return;
}
/* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */
secp256k1_fe_sqr(&z2s, &b->z);
secp256k1_fe_mul(&u1, &a->x, &z2s);
u2 = b->x; secp256k1_fe_normalize_weak(&u2);
secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z);
s2 = b->y; secp256k1_fe_normalize_weak(&s2);
CHECK(secp256k1_fe_equal_var(&u1, &u2));
CHECK(secp256k1_fe_equal_var(&s1, &s2));
}
void random_fe(secp256k1_fe *x) {
unsigned char bin[32];
do {
secp256k1_rand256(bin);
if (secp256k1_fe_set_b32(x, bin)) {
return;
}
} while(1);
}
/** END stolen from tests.c */
int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
const unsigned char *key32, const unsigned char *algo16,
void *data, unsigned int attempt) {
secp256k1_scalar s;
int *idata = data;
(void)msg32;
(void)key32;
(void)algo16;
/* Some nonces cannot be used because they'd cause s and/or r to be zero.
* The signing function has retry logic here that just re-calls the nonce
* function with an increased `attempt`. So if attempt > 0 this means we
* need to change the nonce to avoid an infinite loop. */
if (attempt > 0) {
(*idata)++;
}
secp256k1_scalar_set_int(&s, *idata);
secp256k1_scalar_get_b32(nonce32, &s);
return 1;
}
#ifdef USE_ENDOMORPHISM
void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) {
int i;
for (i = 0; i < order; i++) {
secp256k1_ge res;
secp256k1_ge_mul_lambda(&res, &group[i]);
ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res);
}
}
#endif
void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
int i, j;
/* Sanity-check (and check infinity functions) */
CHECK(secp256k1_ge_is_infinity(&group[0]));
CHECK(secp256k1_gej_is_infinity(&groupj[0]));
for (i = 1; i < order; i++) {
CHECK(!secp256k1_ge_is_infinity(&group[i]));
CHECK(!secp256k1_gej_is_infinity(&groupj[i]));
}
/* Check all addition formulae */
for (j = 0; j < order; j++) {
secp256k1_fe fe_inv;
secp256k1_fe_inv(&fe_inv, &groupj[j].z);
for (i = 0; i < order; i++) {
secp256k1_ge zless_gej;
secp256k1_gej tmp;
/* add_var */
secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL);
ge_equals_gej(&group[(i + j) % order], &tmp);
/* add_ge */
if (j > 0) {
secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]);
ge_equals_gej(&group[(i + j) % order], &tmp);
}
/* add_ge_var */
secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL);
ge_equals_gej(&group[(i + j) % order], &tmp);
/* add_zinv_var */
zless_gej.infinity = groupj[j].infinity;
zless_gej.x = groupj[j].x;
zless_gej.y = groupj[j].y;
secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv);
ge_equals_gej(&group[(i + j) % order], &tmp);
}
}
/* Check doubling */
for (i = 0; i < order; i++) {
secp256k1_gej tmp;
if (i > 0) {
secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
ge_equals_gej(&group[(2 * i) % order], &tmp);
}
secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
ge_equals_gej(&group[(2 * i) % order], &tmp);
}
/* Check negation */
for (i = 1; i < order; i++) {
secp256k1_ge tmp;
secp256k1_gej tmpj;
secp256k1_ge_neg(&tmp, &group[i]);
ge_equals_ge(&group[order - i], &tmp);
secp256k1_gej_neg(&tmpj, &groupj[i]);
ge_equals_gej(&group[order - i], &tmpj);
}
}
void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
int i, j, r_log;
for (r_log = 1; r_log < order; r_log++) {
for (j = 0; j < order; j++) {
for (i = 0; i < order; i++) {
secp256k1_gej tmp;
secp256k1_scalar na, ng;
secp256k1_scalar_set_int(&na, i);
secp256k1_scalar_set_int(&ng, j);
secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng);
ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
if (i > 0) {
secp256k1_ecmult_const(&tmp, &group[i], &ng);
ge_equals_gej(&group[(i * j) % order], &tmp);
}
}
}
}
}
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
secp256k1_fe x;
unsigned char x_bin[32];
k %= EXHAUSTIVE_TEST_ORDER;
x = group[k].x;
secp256k1_fe_normalize(&x);
secp256k1_fe_get_b32(x_bin, &x);
secp256k1_scalar_set_b32(r, x_bin, NULL);
}
void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
int s, r, msg, key;
for (s = 1; s < order; s++) {
for (r = 1; r < order; r++) {
for (msg = 1; msg < order; msg++) {
for (key = 1; key < order; key++) {
secp256k1_ge nonconst_ge;
secp256k1_ecdsa_signature sig;
secp256k1_pubkey pk;
secp256k1_scalar sk_s, msg_s, r_s, s_s;
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
int k, should_verify;
unsigned char msg32[32];
secp256k1_scalar_set_int(&s_s, s);
secp256k1_scalar_set_int(&r_s, r);
secp256k1_scalar_set_int(&msg_s, msg);
secp256k1_scalar_set_int(&sk_s, key);
/* Verify by hand */
/* Run through every k value that gives us this r and check that *one* works.
* Note there could be none, there could be multiple, ECDSA is weird. */
should_verify = 0;
for (k = 0; k < order; k++) {
secp256k1_scalar check_x_s;
r_from_k(&check_x_s, group, k);
if (r_s == check_x_s) {
secp256k1_scalar_set_int(&s_times_k_s, k);
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
}
}
/* nb we have a "high s" rule */
should_verify &= !secp256k1_scalar_is_high(&s_s);
/* Verify by calling verify */
secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s);
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
secp256k1_pubkey_save(&pk, &nonconst_ge);
secp256k1_scalar_get_b32(msg32, &msg_s);
CHECK(should_verify ==
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
}
}
}
}
}
void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
int i, j, k;
/* Loop */
for (i = 1; i < order; i++) { /* message */
for (j = 1; j < order; j++) { /* key */
for (k = 1; k < order; k++) { /* nonce */
secp256k1_ecdsa_signature sig;
secp256k1_scalar sk, msg, r, s, expected_r;
unsigned char sk32[32], msg32[32];
secp256k1_scalar_set_int(&msg, i);
secp256k1_scalar_set_int(&sk, j);
secp256k1_scalar_get_b32(sk32, &sk);
secp256k1_scalar_get_b32(msg32, &msg);
secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
/* Note that we compute expected_r *after* signing -- this is important
* because our nonce-computing function function might change k during
* signing. */
r_from_k(&expected_r, group, k);
CHECK(r == expected_r);
CHECK((k * s) % order == (i + r * j) % order ||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
}
}
}
/* We would like to verify zero-knowledge here by counting how often every
* possible (s, r) tuple appears, but because the group order is larger
* than the field order, when coercing the x-values to scalar values, some
* appear more often than others, so we are actually not zero-knowledge.
* (This effect also appears in the real code, but the difference is on the
* order of 1/2^128th the field order, so the deviation is not useful to a
* computationally bounded attacker.)
*/
}
int main(void) {
int i;
secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
/* Build context */
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
/* TODO set z = 1, then do num_tests runs with random z values */
/* Generate the entire group */
secp256k1_gej_set_infinity(&groupj[0]);
secp256k1_ge_set_gej(&group[0], &groupj[0]);
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
/* Set a different random z-value for each Jacobian point */
secp256k1_fe z;
random_fe(&z);
secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
secp256k1_ge_set_gej(&group[i], &groupj[i]);
secp256k1_gej_rescale(&groupj[i], &z);
/* Verify against ecmult_gen */
{
secp256k1_scalar scalar_i;
secp256k1_gej generatedj;
secp256k1_ge generated;
secp256k1_scalar_set_int(&scalar_i, i);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i);
secp256k1_ge_set_gej(&generated, &generatedj);
CHECK(group[i].infinity == 0);
CHECK(generated.infinity == 0);
CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x));
CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y));
}
}
/* Run the tests */
#ifdef USE_ENDOMORPHISM
test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER);
#endif
test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
return 0;
}
Loading…
Cancel
Save