diff --git a/.travis.yml b/.travis.yml index 9b7fe6f3..6826614e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,18 +17,19 @@ compiler: - gcc env: global: - - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no + - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no ECDSA_S2C=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no matrix: - WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int64 RECOVERY=yes - - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 - - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes + - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 ASM=x86_64 - BIGNUM=no - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes + - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes ECDSA_S2C=yes SCHNORRSIG=yes MUSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no - CPPFLAGS=-DDETERMINISTIC diff --git a/Makefile.am b/Makefile.am index 7309fa4a..434360f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -184,3 +184,8 @@ endif if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif + +if ENABLE_MODULE_ECDSA_S2C +include src/modules/ecdsa_s2c/Makefile.am.include +endif + diff --git a/configure.ac b/configure.ac index f4a341bc..27b245eb 100644 --- a/configure.ac +++ b/configure.ac @@ -161,6 +161,11 @@ AC_ARG_ENABLE(module_schnorrsig, [enable_module_schnorrsig=$enableval], [enable_module_schnorrsig=no]) +AC_ARG_ENABLE(module_ecdsa_s2c, + AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), + [enable_module_ecdsa_s2c=$enableval], + [enable_module_ecdsa_s2c=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -509,6 +514,10 @@ if test x"$enable_module_extrakeys" = x"yes"; then AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) fi +if test x"$enable_module_ecdsa_s2c" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDSA_S2C, 1, [Define this symbol to enable the ECDSA sign-to-contract module]) +fi + if test x"$use_external_asm" = x"yes"; then AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) fi @@ -532,6 +541,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) + AC_MSG_NOTICE([Building ECDSA sign-to-contract module: $enable_module_ecdsa_s2c]) AC_MSG_NOTICE([******]) @@ -565,6 +575,9 @@ else if test x"$enable_module_schnorrsig" = x"yes"; then AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_ecdsa_s2c" = x"yes"; then + AC_MSG_ERROR([ECDSA sign-to-contract module module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -601,6 +614,7 @@ AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDSA_S2C], [test x"$enable_module_ecdsa_s2c" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"]) @@ -625,6 +639,7 @@ echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" +echo " module ecdsa-s2c = $enable_module_ecdsa_s2c" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/contrib/travis.sh b/contrib/travis.sh index c667c151..fb3e4d0c 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -17,6 +17,7 @@ fi --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ecdsa-s2c="$ECDSA_S2C" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG"\ --with-valgrind="$WITH_VALGRIND" \ diff --git a/include/secp256k1_ecdsa_s2c.h b/include/secp256k1_ecdsa_s2c.h new file mode 100644 index 00000000..7f54e71f --- /dev/null +++ b/include/secp256k1_ecdsa_s2c.h @@ -0,0 +1,58 @@ +#ifndef SECP256K1_ECDSA_S2C_H +#define SECP256K1_ECDSA_S2C_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Data structure that holds a sign-to-contract ("s2c") opening information. + * Sign-to-contract allows a signer to commit to some data as part of a signature. It + * can be used as an Out-argument in certain signing functions. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_ecdsa_s2c_opening_serialize and secp256k1_ecdsa_s2c_opening_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_s2c_opening; + +/** Parse a sign-to-contract opening. + * + * Returns: 1 if the opening could be parsed + * 0 if the opening could not be parsed + * Args: ctx: a secp256k1 context object. + * Out: opening: pointer to an opening object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is unspecified. + * In: input33: pointer to 33-byte array with a serialized opening + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_parse( + const secp256k1_context* ctx, + secp256k1_ecdsa_s2c_opening* opening, + const unsigned char* input33 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a sign-to-contract opening into a byte sequence. + * + * Returns: 1 if the opening was successfully serialized. + * 0 if the opening could not be serialized + * Args: ctx: a secp256k1 context object + * Out: output33: pointer to a 33-byte array to place the serialized opening in + * In: opening: a pointer to an initialized `secp256k1_ecdsa_s2c_opening` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_s2c_opening_serialize( + const secp256k1_context* ctx, + unsigned char* output33, + const secp256k1_ecdsa_s2c_opening* opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDSA_S2C_H */ diff --git a/src/modules/ecdsa_s2c/Makefile.am.include b/src/modules/ecdsa_s2c/Makefile.am.include new file mode 100644 index 00000000..b4939a91 --- /dev/null +++ b/src/modules/ecdsa_s2c/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_ecdsa_s2c.h +noinst_HEADERS += src/modules/ecdsa_s2c/main_impl.h +noinst_HEADERS += src/modules/ecdsa_s2c/tests_impl.h diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h new file mode 100755 index 00000000..cf152359 --- /dev/null +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -0,0 +1,28 @@ +/********************************************************************** + * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_S2C_MAIN_H +#define SECP256K1_MODULE_ECDSA_S2C_MAIN_H + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdsa_s2c.h" + +int secp256k1_ecdsa_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* input33) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(input33 != NULL); + return secp256k1_ec_pubkey_parse(ctx, (secp256k1_pubkey*) opening, input33, 33); +} + +int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char* output33, const secp256k1_ecdsa_s2c_opening* opening) { + size_t out_len = 33; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output33 != NULL); + ARG_CHECK(opening != NULL); + return secp256k1_ec_pubkey_serialize(ctx, output33, &out_len, (const secp256k1_pubkey*) opening, SECP256K1_EC_COMPRESSED); +} + +#endif /* SECP256K1_ECDSA_S2C_MAIN_H */ diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h new file mode 100644 index 00000000..cd7e18f6 --- /dev/null +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -0,0 +1,76 @@ +/********************************************************************** + * Copyright (c) 2019-2020 Marko Bencun, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_S2C_TESTS_H +#define SECP256K1_MODULE_ECDSA_S2C_TESTS_H + +#include "include/secp256k1_ecdsa_s2c.h" + +void run_s2c_opening_test(void) { + int i = 0; + unsigned char output[33]; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + unsigned char input[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + secp256k1_ecdsa_s2c_opening opening; + int32_t ecount = 0; + + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + + /* First parsing, then serializing works */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1); + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1); + CHECK(ecount == 0); + + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, NULL, input) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1); + + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, NULL, &opening) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, NULL) == 0); + + CHECK(ecount == 4); + /* Invalid pubkey makes parsing fail */ + input[0] = 0; /* bad oddness bit */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 0); + input[0] = 2; + input[31] = 1; /* point not on the curve */ + CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 0); + CHECK(ecount == 4); /* neither of the above are API errors */ + + /* Try parsing and serializing a bunch of openings */ + for (i = 0; i < count; i++) { + /* This is expected to fail in about 50% of iterations because the + * points' x-coordinates are uniformly random */ + if (secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1) { + CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1); + CHECK(memcmp(output, input, sizeof(output)) == 0); + } + secp256k1_testrand256(&input[1]); + /* Set pubkey oddness tag to first bit of input[1] */ + input[0] = (input[1] & 1) + 2; + i++; + } + + secp256k1_context_destroy(none); +} + + +static void run_ecdsa_s2c_tests(void) { + run_s2c_opening_test(); +} + +#endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index 0c7a2575..a48a7371 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -786,6 +786,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/schnorrsig/main_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "modules/ecdsa_s2c/main_impl.h" +#endif + #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index bd6f7fd1..d0dc061a 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5697,6 +5697,10 @@ void run_ecdsa_openssl(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_S2C +# include "modules/ecdsa_s2c/tests_impl.h" +#endif + void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5998,6 +6002,11 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_ECDSA_S2C + /* ECDSA sign to contract */ + run_ecdsa_s2c_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test();