From 654cd633f509db3100ce99acd84f47db594ff9a6 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Thu, 4 Mar 2021 23:38:48 -0800 Subject: [PATCH 1/5] ecdsa_adaptor: initialize project This commit adds the foundational configuration and building scripts and an initial structure for the project. --- Makefile.am | 3 +++ README.md | 1 + configure.ac | 15 +++++++++++++++ include/secp256k1_ecdsa_adaptor.h | 19 +++++++++++++++++++ src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/main_impl.h | 12 ++++++++++++ src/secp256k1.c | 4 ++++ 7 files changed, 55 insertions(+) create mode 100644 include/secp256k1_ecdsa_adaptor.h create mode 100644 src/modules/ecdsa_adaptor/Makefile.am.include create mode 100644 src/modules/ecdsa_adaptor/main_impl.h diff --git a/Makefile.am b/Makefile.am index 434360f8..796be6e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -189,3 +189,6 @@ if ENABLE_MODULE_ECDSA_S2C include src/modules/ecdsa_s2c/Makefile.am.include endif +if ENABLE_MODULE_ECDSA_ADAPTOR +include src/modules/ecdsa_adaptor/Makefile.am.include +endif diff --git a/README.md b/README.md index 9918678e..43dc4238 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Features: * Suitable for embedded systems. * Optional module for public key recovery. * Optional module for ECDH key exchange. +* Optional module for ECDSA adaptor signatures (experimental). Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable. diff --git a/configure.ac b/configure.ac index 64942d1f..17e52078 100644 --- a/configure.ac +++ b/configure.ac @@ -180,6 +180,11 @@ AC_ARG_ENABLE(module_ecdsa_s2c, [enable_module_ecdsa_s2c=$enableval], [enable_module_ecdsa_s2c=no]) +AC_ARG_ENABLE(module_ecdsa-adaptor, + AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]), + [enable_module_ecdsa_adaptor=$enableval], + [enable_module_ecdsa_adaptor=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], @@ -580,6 +585,10 @@ if test x"$use_reduced_surjection_proof_size" = x"yes"; then AC_DEFINE(USE_REDUCED_SURJECTION_PROOF_SIZE, 1, [Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, disabling parsing and verification]) fi +if test x"$enable_module_ecdsa_adaptor" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDSA_ADAPTOR, 1, [Define this symbol to enable the ECDSA adaptor module]) +fi + ### ### Check for --enable-experimental if necessary ### @@ -596,6 +605,7 @@ if test x"$enable_experimental" = x"yes"; then 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([Building ECDSA adaptor signatures module: $enable_module_ecdsa_adaptor]) AC_MSG_NOTICE([******]) @@ -632,6 +642,9 @@ else 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"$enable_module_ecdsa_adaptor" = x"yes"; then + AC_MSG_ERROR([ecdsa adaptor signatures 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 @@ -673,6 +686,7 @@ AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x" 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([ENABLE_MODULE_ECDSA_ADAPTOR], [test x"$enable_module_ecdsa_adaptor" = 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"]) @@ -698,6 +712,7 @@ 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 " module ecdsa-adaptor = $enable_module_ecdsa_adaptor" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h new file mode 100644 index 00000000..60da16a4 --- /dev/null +++ b/include/secp256k1_ecdsa_adaptor.h @@ -0,0 +1,19 @@ +#ifndef SECP256K1_ECDSA_ADAPTOR_H +#define SECP256K1_ECDSA_ADAPTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements single signer ECDSA adaptor signatures following + * "One-Time Verifiably Encrypted Signatures A.K.A. Adaptor Signatures" by + * Lloyd Fournier + * (https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-November/002316.html + * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ECDSA_ADAPTOR_H */ diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include new file mode 100644 index 00000000..17766fed --- /dev/null +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -0,0 +1 @@ +include_HEADERS += include/secp256k1_ecdsa_adaptor.h diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h new file mode 100644 index 00000000..79b98fee --- /dev/null +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -0,0 +1,12 @@ +/********************************************************************** + * Copyright (c) 2020-2021 Jonas Nick, Jesse Posner * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H +#define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H + +#include "include/secp256k1_ecdsa_adaptor.h" + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 0ccbaf2e..32cc3e12 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -831,6 +831,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdsa_s2c/main_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +# include "modules/ecdsa_adaptor/main_impl.h" +#endif + #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/main_impl.h" #endif From d8f336564fe1255752c7e454d998beaa25f945c1 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 00:00:13 -0800 Subject: [PATCH 2/5] ecdsa_adaptor: add nonce function and tags This commit adds a nonce function that will be used by default for ECDSA adaptor signatures. This nonce function is similar to secp256k1_nonce_function_hardened except it uses the compressed 33-byte encoding for the pubkey argument. We need 33 bytes instead of 32 because, unlike with BIP-340, an ECDSA X-coordinate alone is not sufficient to disambiguate the Y-coordinate. --- include/secp256k1_ecdsa_adaptor.h | 34 ++++++++ src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/main_impl.h | 78 +++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index 60da16a4..7bc43277 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -12,6 +12,40 @@ extern "C" { * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). */ +/** A pointer to a function to deterministically generate a nonce. + * + * Same as secp256k1_nonce_function_hardened with the exception of using the + * compressed 33-byte encoding for the pubkey argument. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to + * return an error. + * Out: nonce32: pointer to a 32-byte array to be filled by the function + * In: msg32: the 32-byte message hash being verified + * key32: pointer to a 32-byte secret key + * pk33: the 33-byte serialized pubkey corresponding to key32 + * algo: pointer to an array describing the signature algorithm + * algolen: the length of the algo array + * data: arbitrary data pointer that is passed through + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the key, the pubkey, the algorithm description, and data. + */ +typedef int (*secp256k1_nonce_function_hardened_ecdsa_adaptor)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *pk33, + const unsigned char *algo, + size_t algolen, + void *data +); + +/** A modified BIP-340 nonce generation function. If a data pointer is passed, it is + * assumed to be a pointer to 32 bytes of auxiliary random data as defined in BIP-340. + * The hash will be tagged with algo after removing all terminating null bytes. + */ +SECP256K1_API extern const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor; + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include index 17766fed..31c881a3 100644 --- a/src/modules/ecdsa_adaptor/Makefile.am.include +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -1 +1,2 @@ include_HEADERS += include/secp256k1_ecdsa_adaptor.h +noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 79b98fee..e7426f59 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -9,4 +9,82 @@ #include "include/secp256k1_ecdsa_adaptor.h" +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ +static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x791dae43ul; + sha->s[1] = 0xe52d3b44ul; + sha->s[2] = 0x37f9edeaul; + sha->s[3] = 0x9bfd2ab1ul; + sha->s[4] = 0xcfb0f44dul; + sha->s[5] = 0xccf1d880ul; + sha->s[6] = 0xd18f2c13ul; + sha->s[7] = 0xa37b9024ul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("ECDSAadaptor/aux")||SHA256("ECDSAadaptor/aux"). */ +static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0xd14c7bd9ul; + sha->s[1] = 0x095d35e6ul; + sha->s[2] = 0xb8490a88ul; + sha->s[3] = 0xfb00ef74ul; + sha->s[4] = 0x0baa488ful; + sha->s[5] = 0x69366693ul; + sha->s[6] = 0x1c81c5baul; + sha->s[7] = 0xc33b296aul; + + sha->bytes = 64; +} + +/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ +static const unsigned char ecdsa_adaptor_algo[16] = "ECDSAadaptor/non"; + +/* Modified BIP-340 nonce function */ +static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *pk33, const unsigned char *algo, size_t algolen, void *data) { + secp256k1_sha256 sha; + unsigned char masked_key[32]; + int i; + + if (algo == NULL) { + return 0; + } + + if (data != NULL) { + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha); + secp256k1_sha256_write(&sha, data, 32); + secp256k1_sha256_finalize(&sha, masked_key); + for (i = 0; i < 32; i++) { + masked_key[i] ^= key32[i]; + } + } + + /* Tag the hash with algo which is important to avoid nonce reuse across + * algorithims. An optimized tagging implementation is used if the default + * tag is provided. */ + if (algolen == sizeof(ecdsa_adaptor_algo) + && secp256k1_memcmp_var(algo, ecdsa_adaptor_algo, algolen) == 0) { + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha); + } else { + secp256k1_sha256_initialize_tagged(&sha, algo, algolen); + } + + /* Hash (masked-)key||pk||msg using the tagged hash as per BIP-340 */ + if (data != NULL) { + secp256k1_sha256_write(&sha, masked_key, 32); + } else { + secp256k1_sha256_write(&sha, key32, 32); + } + secp256k1_sha256_write(&sha, pk33, 33); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + +const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor = nonce_function_ecdsa_adaptor; + #endif From b508e5dd9b1f6f4f9e552056a1fe898fffc0a450 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 00:04:03 -0800 Subject: [PATCH 3/5] ecdsa_adaptor: add support for proof of discrete logarithm equality This commit adds proving and verification functions for discrete logarithm equality. From the spec (https://github.com/discreetlogcontracts/dlcspecs/pull/114): "As part of the ECDSA adaptor signature a proof of discrete logarithm equality must be provided. This is a proof that the discrete logarithm of some X to the standard base G is the same as the discrete logarithm of some Z to the base Y. This proof can be constructed by using equality composition on two Sigma protocols proving knowledge of the discrete logarithm between both pairs of points. In other words the prover proves knowledge of a such that X = a * G and b such that Z = b * Y and that a = b. We make the resulting Sigma protocol non-interactive by applying the Fiat-Shamir transformation with SHA256 as the challenge hash." --- src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/dleq_impl.h | 158 ++++++++++++++++++ src/modules/ecdsa_adaptor/main_impl.h | 4 + 3 files changed, 163 insertions(+) create mode 100644 src/modules/ecdsa_adaptor/dleq_impl.h diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include index 31c881a3..d48d028c 100644 --- a/src/modules/ecdsa_adaptor/Makefile.am.include +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -1,2 +1,3 @@ include_HEADERS += include/secp256k1_ecdsa_adaptor.h noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h +noinst_HEADERS += src/modules/ecdsa_adaptor/dleq_impl.h diff --git a/src/modules/ecdsa_adaptor/dleq_impl.h b/src/modules/ecdsa_adaptor/dleq_impl.h new file mode 100644 index 00000000..da764ee7 --- /dev/null +++ b/src/modules/ecdsa_adaptor/dleq_impl.h @@ -0,0 +1,158 @@ +#ifndef SECP256K1_DLEQ_IMPL_H +#define SECP256K1_DLEQ_IMPL_H + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("DLEQ")||SHA256("DLEQ"). */ +static void secp256k1_nonce_function_dleq_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x8cc4beacul; + sha->s[1] = 0x2e011f3ful; + sha->s[2] = 0x355c75fbul; + sha->s[3] = 0x3ba6a2c5ul; + sha->s[4] = 0xe96f3aeful; + sha->s[5] = 0x180530fdul; + sha->s[6] = 0x94582499ul; + sha->s[7] = 0x577fd564ul; + + sha->bytes = 64; +} + +/* algo argument for nonce_function_ecdsa_adaptor to derive the nonce using a tagged hash function. */ +static const unsigned char dleq_algo[4] = "DLEQ"; + +static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) { + unsigned char buf[33]; + size_t size = 33; + + if (!secp256k1_eckey_pubkey_serialize(p, buf, &size, 1)) { + return 0; + } + + secp256k1_sha256_write(sha, buf, size); + return 1; +} + +static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *sk32, const unsigned char *gen2_33, const unsigned char *p1_33, const unsigned char *p2_33, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_sha256 sha; + unsigned char buf[32]; + unsigned char nonce[32]; + size_t size = 33; + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_ecdsa_adaptor; + } + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, p1_33, size); + secp256k1_sha256_write(&sha, p2_33, size); + secp256k1_sha256_finalize(&sha, buf); + + if (!noncefp(nonce, buf, sk32, gen2_33, dleq_algo, sizeof(dleq_algo), ndata)) { + return 0; + } + secp256k1_scalar_set_b32(k, nonce, NULL); + if (secp256k1_scalar_is_zero(k)) { + return 0; + } + + return 1; +} + +/* Generates a challenge as defined in the DLC Specification at + * https://github.com/discreetlogcontracts/dlcspecs */ +static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *gen2, secp256k1_ge *r1, secp256k1_ge *r2, secp256k1_ge *p1, secp256k1_ge *p2) { + unsigned char buf[32]; + secp256k1_sha256 sha; + + secp256k1_nonce_function_dleq_sha256_tagged(&sha); + secp256k1_dleq_hash_point(&sha, p1); + secp256k1_dleq_hash_point(&sha, gen2); + secp256k1_dleq_hash_point(&sha, p2); + secp256k1_dleq_hash_point(&sha, r1); + secp256k1_dleq_hash_point(&sha, r2); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(e, buf, NULL); +} + +/* P1 = x*G, P2 = x*Y */ +static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *p1, secp256k1_ge *p2, const secp256k1_scalar *sk, const secp256k1_ge *gen2) { + secp256k1_gej p1j, p2j; + + secp256k1_ecmult_gen(ecmult_gen_ctx, &p1j, sk); + secp256k1_ge_set_gej(p1, &p1j); + secp256k1_ecmult_const(&p2j, gen2, sk, 256); + secp256k1_ge_set_gej(p2, &p2j); +} + +/* Generates a proof that the discrete logarithm of P1 to the secp256k1 base G is the + * same as the discrete logarithm of P2 to the base Y */ +static int secp256k1_dleq_prove(const secp256k1_context* ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *sk, secp256k1_ge *gen2, secp256k1_ge *p1, secp256k1_ge *p2, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_ge r1, r2; + secp256k1_scalar k = { 0 }; + unsigned char sk32[32]; + unsigned char gen2_33[33]; + unsigned char p1_33[33]; + unsigned char p2_33[33]; + int ret = 1; + size_t pubkey_size = 33; + + secp256k1_scalar_get_b32(sk32, sk); + if (!secp256k1_eckey_pubkey_serialize(gen2, gen2_33, &pubkey_size, 1)) { + return 0; + } + if (!secp256k1_eckey_pubkey_serialize(p1, p1_33, &pubkey_size, 1)) { + return 0; + } + if (!secp256k1_eckey_pubkey_serialize(p2, p2_33, &pubkey_size, 1)) { + return 0; + } + + ret &= secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, noncefp, ndata); + /* R1 = k*G, R2 = k*Y */ + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &r1, &r2, &k, gen2); + /* We declassify the non-secret values r1 and r2 to allow using them as + * branch points. */ + secp256k1_declassify(ctx, &r1, sizeof(r1)); + secp256k1_declassify(ctx, &r2, sizeof(r2)); + + /* e = tagged hash(p1, gen2, p2, r1, r2) */ + /* s = k + e * sk */ + secp256k1_dleq_challenge(e, gen2, &r1, &r2, p1, p2); + secp256k1_scalar_mul(s, e, sk); + secp256k1_scalar_add(s, s, &k); + + secp256k1_scalar_clear(&k); + return ret; +} + +static int secp256k1_dleq_verify(const secp256k1_ecmult_context *ecmult_ctx, const secp256k1_scalar *s, const secp256k1_scalar *e, secp256k1_ge *p1, secp256k1_ge *gen2, secp256k1_ge *p2) { + secp256k1_scalar e_neg; + secp256k1_scalar e_expected; + secp256k1_gej gen2j; + secp256k1_gej p1j, p2j; + secp256k1_gej r1j, r2j; + secp256k1_ge r1, r2; + secp256k1_gej tmpj; + + secp256k1_gej_set_ge(&p1j, p1); + secp256k1_gej_set_ge(&p2j, p2); + + secp256k1_scalar_negate(&e_neg, e); + /* R1 = s*G - e*P1 */ + secp256k1_ecmult(ecmult_ctx, &r1j, &p1j, &e_neg, s); + /* R2 = s*gen2 - e*P2 */ + secp256k1_ecmult(ecmult_ctx, &tmpj, &p2j, &e_neg, &secp256k1_scalar_zero); + secp256k1_gej_set_ge(&gen2j, gen2); + secp256k1_ecmult(ecmult_ctx, &r2j, &gen2j, s, &secp256k1_scalar_zero); + secp256k1_gej_add_var(&r2j, &r2j, &tmpj, NULL); + + secp256k1_ge_set_gej(&r1, &r1j); + secp256k1_ge_set_gej(&r2, &r2j); + secp256k1_dleq_challenge(&e_expected, gen2, &r1, &r2, p1, p2); + + secp256k1_scalar_add(&e_expected, &e_expected, &e_neg); + return secp256k1_scalar_is_zero(&e_expected); +} + +#endif diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index e7426f59..005baf0e 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_ECDSA_ADAPTOR_MAIN_H #include "include/secp256k1_ecdsa_adaptor.h" +#include "modules/ecdsa_adaptor/dleq_impl.h" /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ @@ -69,6 +70,9 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c if (algolen == sizeof(ecdsa_adaptor_algo) && secp256k1_memcmp_var(algo, ecdsa_adaptor_algo, algolen) == 0) { secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha); + } else if (algolen == sizeof(dleq_algo) + && secp256k1_memcmp_var(algo, dleq_algo, algolen) == 0) { + secp256k1_nonce_function_dleq_sha256_tagged(&sha); } else { secp256k1_sha256_initialize_tagged(&sha, algo, algolen); } From 6955af5ca8930aa674e5fdbc4343e722b25e0ca8 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 00:10:05 -0800 Subject: [PATCH 4/5] ecdsa_adaptor: add ECDSA adaptor signature APIs This commit adds the ECDSA adaptor signature APIs: - Encrypted Signing Creates an adaptor signature, which includes a proof to verify the adaptor signature. - Encryption Verification Verifies that the adaptor decryption key can be extracted from the adaptor signature and the completed ECDSA signature. - Signature Decryption Derives an ECDSA signature from an adaptor signature and an adaptor decryption key. - Key Recovery Extracts the adaptor decryption key from the complete signature and the adaptor signature. --- include/secp256k1_ecdsa_adaptor.h | 111 ++++++++++- src/modules/ecdsa_adaptor/main_impl.h | 271 ++++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_ecdsa_adaptor.h b/include/secp256k1_ecdsa_adaptor.h index 7bc43277..64784a4f 100644 --- a/include/secp256k1_ecdsa_adaptor.h +++ b/include/secp256k1_ecdsa_adaptor.h @@ -10,7 +10,22 @@ extern "C" { * Lloyd Fournier * (https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-November/002316.html * and https://github.com/LLFourn/one-time-VES/blob/master/main.pdf). -*/ + * + * WARNING! DANGER AHEAD! + * As mentioned in Lloyd Fournier's paper, the adaptor signature leaks the + * Elliptic-curve Diffie–Hellman (ECDH) key between the signing key and the + * encryption key. This is not a problem for ECDSA adaptor signatures + * themselves, but may result in a complete loss of security when they are + * composed with other schemes. More specifically, let us refer to the + * signer's public key as X = x*G, and to the encryption key as Y = y*G. + * Given X, Y and the adaptor signature, it is trivial to compute Y^x = X^y. + * + * A defense is to not reuse the signing key of ECDSA adaptor signatures in + * protocols that rely on the hardness of the CDH problem, e.g., Diffie-Hellman + * key exchange and ElGamal encryption. In general, it is a well-established + * cryptographic practice to seperate keys for different purposes whenever + * possible. + */ /** A pointer to a function to deterministically generate a nonce. * @@ -46,6 +61,100 @@ typedef int (*secp256k1_nonce_function_hardened_ecdsa_adaptor)( */ SECP256K1_API extern const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor; +/** Encrypted Signing + * + * Creates an adaptor signature, which includes a proof to verify the adaptor + * signature. + * WARNING: Make sure you have read and understood the WARNING at the top of + * this file and applied the suggested countermeasures. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object, initialized for signing + * Out: adaptor_sig162: pointer to 162 byte to store the returned signature + * In: seckey32: pointer to 32 byte secret key that will be used for + * signing + * enckey: pointer to the encryption public key + * msg32: pointer to the 32-byte message hash to sign + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_ecdsa_adaptor is used + * ndata: pointer to arbitrary data used by the nonce generation + * function (can be NULL). If it is non-NULL and + * secp256k1_nonce_function_ecdsa_adaptor is used, then + * ndata must be a pointer to 32-byte auxiliary randomness + * as per BIP-340. + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_encrypt( + const secp256k1_context* ctx, + unsigned char *adaptor_sig162, + unsigned char *seckey32, + const secp256k1_pubkey *enckey, + const unsigned char *msg32, + secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Encryption Verification + * + * Verifies that the adaptor decryption key can be extracted from the adaptor signature + * and the completed ECDSA signature. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object, initialized for verification + * In: adaptor_sig162: pointer to 162-byte signature to verify + * pubkey: pointer to the public key corresponding to the secret key + * used for signing + * msg32: pointer to the 32-byte message hash being verified + * enckey: pointer to the adaptor encryption public key + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_verify( + const secp256k1_context* ctx, + const unsigned char *adaptor_sig162, + const secp256k1_pubkey *pubkey, + const unsigned char *msg32, + const secp256k1_pubkey *enckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Signature Decryption + * + * Derives an ECDSA signature from an adaptor signature and an adaptor decryption key. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to the ECDSA signature to create + * In: deckey32: pointer to 32-byte decryption secret key for the adaptor + * encryption public key + * adaptor_sig162: pointer to 162-byte adaptor sig + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_decrypt( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *deckey32, + const unsigned char *adaptor_sig162 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decryption Key Recovery + * + * Extracts the adaptor decryption key from the complete signature and the adaptor + * signature. + * + * Returns: 1 on success, 0 on failure + * Args: ctx: a secp256k1 context object, initialized for signing + * Out: deckey32: pointer to 32-byte adaptor decryption key for the adaptor + * encryption public key + * In: sig: pointer to ECDSA signature to recover the adaptor decryption + * key from + * adaptor_sig162: pointer to adaptor signature to recover the adaptor + * decryption key from + * enckey: pointer to the adaptor encryption public key + */ +SECP256K1_API int secp256k1_ecdsa_adaptor_recover( + const secp256k1_context* ctx, + unsigned char *deckey32, + const secp256k1_ecdsa_signature *sig, + const unsigned char *adaptor_sig162, + const secp256k1_pubkey *enckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + #ifdef __cplusplus } #endif diff --git a/src/modules/ecdsa_adaptor/main_impl.h b/src/modules/ecdsa_adaptor/main_impl.h index 005baf0e..18e6132d 100644 --- a/src/modules/ecdsa_adaptor/main_impl.h +++ b/src/modules/ecdsa_adaptor/main_impl.h @@ -10,6 +10,61 @@ #include "include/secp256k1_ecdsa_adaptor.h" #include "modules/ecdsa_adaptor/dleq_impl.h" +/* (R, R', s', dleq_proof) */ +static int secp256k1_ecdsa_adaptor_sig_serialize(unsigned char *adaptor_sig162, secp256k1_ge *r, secp256k1_ge *rp, const secp256k1_scalar *sp, const secp256k1_scalar *dleq_proof_e, const secp256k1_scalar *dleq_proof_s) { + size_t size = 33; + + if (!secp256k1_eckey_pubkey_serialize(r, adaptor_sig162, &size, 1)) { + return 0; + } + if (!secp256k1_eckey_pubkey_serialize(rp, &adaptor_sig162[33], &size, 1)) { + return 0; + } + secp256k1_scalar_get_b32(&adaptor_sig162[66], sp); + secp256k1_scalar_get_b32(&adaptor_sig162[98], dleq_proof_e); + secp256k1_scalar_get_b32(&adaptor_sig162[130], dleq_proof_s); + + return 1; +} + +static int secp256k1_ecdsa_adaptor_sig_deserialize(secp256k1_ge *r, secp256k1_scalar *sigr, secp256k1_ge *rp, secp256k1_scalar *sp, secp256k1_scalar *dleq_proof_e, secp256k1_scalar *dleq_proof_s, const unsigned char *adaptor_sig162) { + /* If r is deserialized, require that a sigr is provided to receive + * the X-coordinate */ + VERIFY_CHECK((r == NULL) || (r != NULL && sigr != NULL)); + if (r != NULL) { + if (!secp256k1_eckey_pubkey_parse(r, &adaptor_sig162[0], 33)) { + return 0; + } + } + if (sigr != NULL) { + secp256k1_scalar_set_b32(sigr, &adaptor_sig162[1], NULL); + if (secp256k1_scalar_is_zero(sigr)) { + return 0; + } + } + if (rp != NULL) { + if (!secp256k1_eckey_pubkey_parse(rp, &adaptor_sig162[33], 33)) { + return 0; + } + } + if (sp != NULL) { + if (!secp256k1_scalar_set_b32_seckey(sp, &adaptor_sig162[66])) { + return 0; + } + } + if (dleq_proof_e != NULL) { + secp256k1_scalar_set_b32(dleq_proof_e, &adaptor_sig162[98], NULL); + } + if (dleq_proof_s != NULL) { + int overflow; + secp256k1_scalar_set_b32(dleq_proof_s, &adaptor_sig162[130], &overflow); + if (overflow) { + return 0; + } + } + return 1; +} + /* Initializes SHA256 with fixed midstate. This midstate was computed by applying * SHA256 to SHA256("ECDSAadaptor/non")||SHA256("ECDSAadaptor/non"). */ static void secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(secp256k1_sha256 *sha) { @@ -91,4 +146,220 @@ static int nonce_function_ecdsa_adaptor(unsigned char *nonce32, const unsigned c const secp256k1_nonce_function_hardened_ecdsa_adaptor secp256k1_nonce_function_ecdsa_adaptor = nonce_function_ecdsa_adaptor; +int secp256k1_ecdsa_adaptor_encrypt(const secp256k1_context* ctx, unsigned char *adaptor_sig162, unsigned char *seckey32, const secp256k1_pubkey *enckey, const unsigned char *msg32, secp256k1_nonce_function_hardened_ecdsa_adaptor noncefp, void *ndata) { + secp256k1_scalar k; + secp256k1_gej rj, rpj; + secp256k1_ge r, rp; + secp256k1_ge enckey_ge; + secp256k1_scalar dleq_proof_s; + secp256k1_scalar dleq_proof_e; + secp256k1_scalar sk; + secp256k1_scalar msg; + secp256k1_scalar sp; + secp256k1_scalar sigr; + secp256k1_scalar n; + unsigned char nonce32[32] = { 0 }; + unsigned char buf33[33]; + size_t size = 33; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(enckey != NULL); + ARG_CHECK(msg32 != NULL); + + secp256k1_scalar_clear(&dleq_proof_e); + secp256k1_scalar_clear(&dleq_proof_s); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_ecdsa_adaptor; + } + + ret &= secp256k1_pubkey_load(ctx, &enckey_ge, enckey); + ret &= secp256k1_eckey_pubkey_serialize(&enckey_ge, buf33, &size, 1); + ret &= !!noncefp(nonce32, msg32, seckey32, buf33, ecdsa_adaptor_algo, sizeof(ecdsa_adaptor_algo), ndata); + secp256k1_scalar_set_b32(&k, nonce32, NULL); + ret &= !secp256k1_scalar_is_zero(&k); + secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); + + /* R' := k*G */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rpj, &k); + secp256k1_ge_set_gej(&rp, &rpj); + /* R = k*Y; */ + secp256k1_ecmult_const(&rj, &enckey_ge, &k, 256); + secp256k1_ge_set_gej(&r, &rj); + /* We declassify the non-secret values rp and r to allow using them + * as branch points. */ + secp256k1_declassify(ctx, &rp, sizeof(rp)); + secp256k1_declassify(ctx, &r, sizeof(r)); + + /* dleq_proof = DLEQ_prove(k, (R', Y, R)) */ + ret &= secp256k1_dleq_prove(ctx, &dleq_proof_s, &dleq_proof_e, &k, &enckey_ge, &rp, &r, noncefp, ndata); + + ret &= secp256k1_scalar_set_b32_seckey(&sk, seckey32); + secp256k1_scalar_cmov(&sk, &secp256k1_scalar_one, !ret); + secp256k1_scalar_set_b32(&msg, msg32, NULL); + secp256k1_fe_normalize(&r.x); + secp256k1_fe_get_b32(buf33, &r.x); + secp256k1_scalar_set_b32(&sigr, buf33, NULL); + ret &= !secp256k1_scalar_is_zero(&sigr); + /* s' = k⁻¹(m + R.x * x) */ + secp256k1_scalar_mul(&n, &sigr, &sk); + secp256k1_scalar_add(&n, &n, &msg); + secp256k1_scalar_inverse(&sp, &k); + secp256k1_scalar_mul(&sp, &sp, &n); + ret &= !secp256k1_scalar_is_zero(&sp); + + /* return (R, R', s', dleq_proof) */ + ret &= secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig162, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s); + + secp256k1_memczero(adaptor_sig162, 162, !ret); + secp256k1_scalar_clear(&n); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&sk); + + return ret; +} + +int secp256k1_ecdsa_adaptor_verify(const secp256k1_context* ctx, const unsigned char *adaptor_sig162, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_pubkey *enckey) { + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_scalar msg; + secp256k1_ge pubkey_ge; + secp256k1_ge r, rp; + secp256k1_scalar sp; + secp256k1_scalar sigr; + secp256k1_ge enckey_ge; + secp256k1_gej derived_rp; + secp256k1_scalar sn, u1, u2; + secp256k1_gej pubkeyj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(enckey != NULL); + + if (!secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)) { + return 0; + } + if (!secp256k1_pubkey_load(ctx, &enckey_ge, enckey)) { + return 0; + } + /* DLEQ_verify((R', Y, R), dleq_proof) */ + if(!secp256k1_dleq_verify(&ctx->ecmult_ctx, &dleq_proof_s, &dleq_proof_e, &rp, &enckey_ge, &r)) { + return 0; + } + secp256k1_scalar_set_b32(&msg, msg32, NULL); + if (!secp256k1_pubkey_load(ctx, &pubkey_ge, pubkey)) { + return 0; + } + + /* return R' == s'⁻¹(m * G + R.x * X) */ + secp256k1_scalar_inverse_var(&sn, &sp); + secp256k1_scalar_mul(&u1, &sn, &msg); + secp256k1_scalar_mul(&u2, &sn, &sigr); + secp256k1_gej_set_ge(&pubkeyj, &pubkey_ge); + secp256k1_ecmult(&ctx->ecmult_ctx, &derived_rp, &pubkeyj, &u2, &u1); + if (secp256k1_gej_is_infinity(&derived_rp)) { + return 0; + } + secp256k1_gej_neg(&derived_rp, &derived_rp); + secp256k1_gej_add_ge_var(&derived_rp, &derived_rp, &rp, NULL); + return secp256k1_gej_is_infinity(&derived_rp); +} + +int secp256k1_ecdsa_adaptor_decrypt(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sig, const unsigned char *deckey32, const unsigned char *adaptor_sig162) { + secp256k1_scalar deckey; + secp256k1_scalar sp; + secp256k1_scalar s; + secp256k1_scalar sigr; + int overflow; + int high; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(deckey32 != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + + secp256k1_scalar_clear(&sp); + secp256k1_scalar_set_b32(&deckey, deckey32, &overflow); + ret &= !overflow; + ret &= secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, &sp, NULL, NULL, adaptor_sig162); + ret &= !secp256k1_scalar_is_zero(&deckey); + secp256k1_scalar_inverse(&s, &deckey); + /* s = s' * y⁻¹ */ + secp256k1_scalar_mul(&s, &s, &sp); + high = secp256k1_scalar_is_high(&s); + secp256k1_scalar_cond_negate(&s, high); + secp256k1_ecdsa_signature_save(sig, &sigr, &s); + + secp256k1_memczero(&sig->data[0], 64, !ret); + secp256k1_scalar_clear(&deckey); + secp256k1_scalar_clear(&sp); + secp256k1_scalar_clear(&s); + + return ret; +} + +int secp256k1_ecdsa_adaptor_recover(const secp256k1_context* ctx, unsigned char *deckey32, const secp256k1_ecdsa_signature *sig, const unsigned char *adaptor_sig162, const secp256k1_pubkey *enckey) { + secp256k1_scalar sp, adaptor_sigr; + secp256k1_scalar s, r; + secp256k1_scalar deckey; + secp256k1_ge enckey_expected_ge; + secp256k1_gej enckey_expected_gej; + unsigned char enckey33[33]; + unsigned char enckey_expected33[33]; + size_t size = 33; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(deckey32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(adaptor_sig162 != NULL); + ARG_CHECK(enckey != NULL); + + if (!secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &adaptor_sigr, NULL, &sp, NULL, NULL, adaptor_sig162)) { + return 0; + } + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + /* Check that we're not looking at some unrelated signature */ + ret &= secp256k1_scalar_eq(&adaptor_sigr, &r); + /* y = s⁻¹ * s' */ + ret &= !secp256k1_scalar_is_zero(&s); + secp256k1_scalar_inverse(&deckey, &s); + secp256k1_scalar_mul(&deckey, &deckey, &sp); + + /* Deal with ECDSA malleability */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &enckey_expected_gej, &deckey); + secp256k1_ge_set_gej(&enckey_expected_ge, &enckey_expected_gej); + /* We declassify non-secret enckey_expected_ge to allow using it as a + * branch point. */ + secp256k1_declassify(ctx, &enckey_expected_ge, sizeof(enckey_expected_ge)); + if (!secp256k1_eckey_pubkey_serialize(&enckey_expected_ge, enckey_expected33, &size, SECP256K1_EC_COMPRESSED)) { + return 0; + } + if (!secp256k1_ec_pubkey_serialize(ctx, enckey33, &size, enckey, SECP256K1_EC_COMPRESSED)) { + return 0; + } + if (secp256k1_memcmp_var(&enckey_expected33[1], &enckey33[1], 32) != 0) { + return 0; + } + if (enckey_expected33[0] != enckey33[0]) { + /* try Y_implied == -Y */ + secp256k1_scalar_negate(&deckey, &deckey); + } + secp256k1_scalar_get_b32(deckey32, &deckey); + + secp256k1_scalar_clear(&deckey); + secp256k1_scalar_clear(&sp); + secp256k1_scalar_clear(&s); + + return ret; +} + #endif From b0ffa923199c45f717adf3fb003bcef796259032 Mon Sep 17 00:00:00 2001 From: Jesse Posner Date: Fri, 5 Mar 2021 01:03:43 -0800 Subject: [PATCH 5/5] ecdsa_adaptor: add tests This commit adds test coverage including Cirrus scripts, Valgrind constant time tests for secret data, API tests, nonce function tests, and test vectors from the spec. --- .cirrus.yml | 11 +- ci/cirrus.sh | 2 +- src/modules/ecdsa_adaptor/Makefile.am.include | 1 + src/modules/ecdsa_adaptor/tests_impl.h | 1221 +++++++++++++++++ src/tests.c | 8 + src/valgrind_ctime_test.c | 42 + 6 files changed, 1281 insertions(+), 4 deletions(-) create mode 100644 src/modules/ecdsa_adaptor/tests_impl.h diff --git a/.cirrus.yml b/.cirrus.yml index 151227da..28a5d323 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -17,6 +17,7 @@ env: RANGEPROOF: no WHITELIST: no MUSIG: no + ECDSAADAPTOR: no EXPERIMENTAL: no CTIMETEST: yes BENCH: yes @@ -59,13 +60,13 @@ task: memory: 1G matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} - env: {WIDEMUL: int128} - env: {WIDEMUL: int128, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes} + - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} - env: {WIDEMUL: int128, ASM: x86_64} - env: {BIGNUM: no} - - env: {BIGNUM: no, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes} + - env: {BIGNUM: no, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes, ECDSA_S2C: yes, RANGEPROOF: yes, WHITELIST: yes, GENERATOR: yes, MUSIG: yes, ECDSAADAPTOR: yes} - env: {BIGNUM: no, STATICPRECOMPUTATION: no} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} @@ -85,6 +86,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes CTIMETEST: no - env: { ECMULTGENPRECISION: 2 } - env: { ECMULTGENPRECISION: 8 } @@ -101,6 +103,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes EXTRAFLAGS: "--disable-openssl-tests" BUILD: matrix: @@ -130,6 +133,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes matrix: - env: CC: i686-linux-gnu-gcc @@ -227,6 +231,7 @@ task: WHITELIST: yes GENERATOR: yes MUSIG: yes + ECDSAADAPTOR: yes CTIMETEST: no << : *MERGE_BASE test_script: diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 63a337f4..785a522a 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -19,7 +19,7 @@ valgrind --version || true --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"\ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" --enable-module-ecdsa-adaptor="$ECDSAADAPTOR" \ --with-valgrind="$WITH_VALGRIND" \ --host="$HOST" $EXTRAFLAGS diff --git a/src/modules/ecdsa_adaptor/Makefile.am.include b/src/modules/ecdsa_adaptor/Makefile.am.include index d48d028c..e855a17a 100644 --- a/src/modules/ecdsa_adaptor/Makefile.am.include +++ b/src/modules/ecdsa_adaptor/Makefile.am.include @@ -1,3 +1,4 @@ include_HEADERS += include/secp256k1_ecdsa_adaptor.h noinst_HEADERS += src/modules/ecdsa_adaptor/main_impl.h noinst_HEADERS += src/modules/ecdsa_adaptor/dleq_impl.h +noinst_HEADERS += src/modules/ecdsa_adaptor/tests_impl.h diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h new file mode 100644 index 00000000..5a12bb74 --- /dev/null +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -0,0 +1,1221 @@ +#ifndef SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H +#define SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H + +#include "include/secp256k1_ecdsa_adaptor.h" + +void rand_scalar(secp256k1_scalar *scalar) { + unsigned char buf32[32]; + secp256k1_testrand256(buf32); + secp256k1_scalar_set_b32(scalar, buf32, NULL); +} + +void rand_point(secp256k1_ge *point) { + secp256k1_scalar x; + secp256k1_gej pointj; + rand_scalar(&x); + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pointj, &x); + secp256k1_ge_set_gej(point, &pointj); +} + +void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { + secp256k1_scalar k1, k2; + + CHECK(secp256k1_dleq_nonce(&k1, args[0], args[1], args[2], args[3], NULL, args[4]) == 1); + secp256k1_testrand_flip(args[n_flip], n_bytes); + CHECK(secp256k1_dleq_nonce(&k2, args[0], args[1], args[2], args[3], NULL, args[4]) == 1); + CHECK(secp256k1_scalar_eq(&k1, &k2) == 0); +} + +void dleq_tests(void) { + secp256k1_scalar s, e, sk, k; + secp256k1_ge gen2, p1, p2; + unsigned char *args[5]; + unsigned char sk32[32]; + unsigned char gen2_33[33]; + unsigned char p1_33[33]; + unsigned char p2_33[33]; + unsigned char aux_rand[32]; + int i; + size_t pubkey_size = 33; + + rand_point(&gen2); + rand_scalar(&sk); + secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &p1, &p2, &sk, &gen2); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &gen2, &p1, &p2, NULL, NULL) == 1); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p1, &gen2, &p2) == 1); + + { + secp256k1_scalar tmp; + secp256k1_scalar_set_int(&tmp, 1); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &tmp, &e, &p1, &gen2, &p2) == 0); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &tmp, &p1, &gen2, &p2) == 0); + } + { + secp256k1_ge p_tmp; + rand_point(&p_tmp); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p_tmp, &gen2, &p2) == 0); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p1, &p_tmp, &p2) == 0); + CHECK(secp256k1_dleq_verify(&ctx->ecmult_ctx, &s, &e, &p1, &gen2, &p_tmp) == 0); + } + { + secp256k1_ge p_inf; + secp256k1_ge_set_infinity(&p_inf); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &p_inf, &p1, &p2, NULL, NULL) == 0); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &gen2, &p_inf, &p2, NULL, NULL) == 0); + CHECK(secp256k1_dleq_prove(ctx, &s, &e, &sk, &gen2, &p1, &p_inf, NULL, NULL) == 0); + } + + /* Nonce tests */ + secp256k1_scalar_get_b32(sk32, &sk); + CHECK(secp256k1_eckey_pubkey_serialize(&gen2, gen2_33, &pubkey_size, 1)); + CHECK(secp256k1_eckey_pubkey_serialize(&p1, p1_33, &pubkey_size, 1)); + CHECK(secp256k1_eckey_pubkey_serialize(&p2, p2_33, &pubkey_size, 1)); + CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); + + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, sk32, sizeof(sk32)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, gen2_33, sizeof(gen2_33)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, p1_33, sizeof(p1_33)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, p2_33, sizeof(p2_33)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = sk32; + args[1] = gen2_33; + args[2] = p1_33; + args[3] = p2_33; + args[4] = aux_rand; + for (i = 0; i < count; i++) { + dleq_nonce_bitflip(args, 0, sizeof(sk32)); + dleq_nonce_bitflip(args, 1, sizeof(gen2_33)); + dleq_nonce_bitflip(args, 2, sizeof(p1_33)); + /* Flip p2 */ + dleq_nonce_bitflip(args, 3, sizeof(p2_33)); + /* Flip p2 again */ + dleq_nonce_bitflip(args, 3, sizeof(p2_33)); + dleq_nonce_bitflip(args, 4, sizeof(aux_rand)); + } + + /* NULL aux_rand argument is allowed. */ + CHECK(secp256k1_dleq_nonce(&k, sk32, gen2_33, p1_33, p2_33, NULL, NULL) == 1); +} + +void rand_flip_bit(unsigned char *array, size_t n) { + array[secp256k1_testrand_int(n)] ^= 1 << secp256k1_testrand_int(8); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the adaptor signature is valid for the public and encryption keys. */ +void test_ecdsa_adaptor_spec_vectors_check_verify(const unsigned char *adaptor_sig162, const unsigned char *msg32, const unsigned char *pubkey33, const unsigned char *encryption_key33, int expected) { + secp256k1_pubkey pubkey; + secp256k1_ge pubkey_ge; + secp256k1_pubkey encryption_key; + secp256k1_ge encryption_key_ge; + + CHECK(secp256k1_eckey_pubkey_parse(&encryption_key_ge, encryption_key33, 33) == 1); + secp256k1_pubkey_save(&encryption_key, &encryption_key_ge); + CHECK(secp256k1_eckey_pubkey_parse(&pubkey_ge, pubkey33, 33) == 1); + secp256k1_pubkey_save(&pubkey, &pubkey_ge); + + CHECK(expected == secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig162, &pubkey, msg32, &encryption_key)); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the signature can be decrypted from the adaptor signature and the decryption key. */ +void test_ecdsa_adaptor_spec_vectors_check_decrypt(const unsigned char *adaptor_sig162, const unsigned char *decryption_key32, const unsigned char *signature64, int expected) { + unsigned char signature[64]; + secp256k1_ecdsa_signature s; + + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &s, decryption_key32, adaptor_sig162) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &s) == 1); + + CHECK(expected == !(secp256k1_memcmp_var(signature, signature64, 64))); +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks that the decryption key can be recovered from the adaptor signature, encryption key, and the signature. */ +void test_ecdsa_adaptor_spec_vectors_check_recover(const unsigned char *adaptor_sig162, const unsigned char *encryption_key33, const unsigned char *decryption_key32, const unsigned char *signature64, int expected) { + unsigned char deckey32[32] = { 0 }; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey encryption_key; + secp256k1_ge encryption_key_ge; + + CHECK(secp256k1_eckey_pubkey_parse(&encryption_key_ge, encryption_key33, 33) == 1); + secp256k1_pubkey_save(&encryption_key, &encryption_key_ge); + + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature64) == 1); + CHECK(expected == secp256k1_ecdsa_adaptor_recover(ctx, deckey32, &sig, adaptor_sig162, &encryption_key)); + if (decryption_key32 != NULL) { + CHECK(expected == !(secp256k1_memcmp_var(deckey32, decryption_key32, 32))); + } +} + +/* Helper function for test_ecdsa_adaptor_spec_vectors + * Checks deserialization and serialization. */ +void test_ecdsa_adaptor_spec_vectors_check_serialization(const unsigned char *adaptor_sig162, int expected) { + unsigned char buf[162]; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_ge r, rp; + secp256k1_scalar sp; + secp256k1_scalar sigr; + + CHECK(expected == secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig162)); + if (expected == 1) { + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(buf, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 1); + CHECK(secp256k1_memcmp_var(buf, adaptor_sig162, 162) == 0); + } +} + +/* Test vectors according to ECDSA adaptor signature spec. See + * https://github.com/discreetlogcontracts/dlcspecs/blob/596a177375932a47306f07e7385f398f52519a83/test/ecdsa_adaptor.json. */ +void test_ecdsa_adaptor_spec_vectors(void) { + { + /* Test vector 0 */ + /* kind: verification test */ + /* plain valid adaptor signature */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, + 0x8a, 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, + 0x12, 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, + 0x7a, 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, + 0x73, 0x02, 0x23, 0xf3, 0x25, 0x04, 0x2f, 0xce, + 0x53, 0x5d, 0x04, 0x0f, 0xee, 0x52, 0xec, 0x13, + 0x23, 0x1b, 0xf7, 0x09, 0xcc, 0xd8, 0x42, 0x33, + 0xc6, 0x94, 0x4b, 0x90, 0x31, 0x7e, 0x62, 0x52, + 0x8b, 0x25, 0x27, 0xdf, 0xf9, 0xd6, 0x59, 0xa9, + 0x6d, 0xb4, 0xc9, 0x9f, 0x97, 0x50, 0x16, 0x83, + 0x08, 0x63, 0x3c, 0x18, 0x67, 0xb7, 0x0f, 0x3a, + 0x18, 0xfb, 0x0f, 0x45, 0x39, 0xa1, 0xae, 0xce, + 0xdc, 0xd1, 0xfc, 0x01, 0x48, 0xfc, 0x22, 0xf3, + 0x6b, 0x63, 0x03, 0x08, 0x3e, 0xce, 0x3f, 0x87, + 0x2b, 0x18, 0xe3, 0x5d, 0x36, 0x8b, 0x39, 0x58, + 0xef, 0xe5, 0xfb, 0x08, 0x1f, 0x77, 0x16, 0x73, + 0x6c, 0xcb, 0x59, 0x8d, 0x26, 0x9a, 0xa3, 0x08, + 0x4d, 0x57, 0xe1, 0x85, 0x5e, 0x1e, 0xa9, 0xa4, + 0x5e, 0xfc, 0x10, 0x46, 0x3b, 0xbf, 0x32, 0xae, + 0x37, 0x80, 0x29, 0xf5, 0x76, 0x3c, 0xeb, 0x40, + 0x17, 0x3f + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0xc2, 0x66, 0x2c, 0x97, 0x48, 0x8b, 0x07, + 0xb6, 0xe8, 0x19, 0x12, 0x4b, 0x89, 0x89, 0x84, + 0x92, 0x06, 0x33, 0x4a, 0x4c, 0x2f, 0xbd, 0xf6, + 0x91, 0xf7, 0xb3, 0x4d, 0x2b, 0x16, 0xe9, 0xc2, + 0x93 + }; + const unsigned char decryption_key[32] = { + 0x0b, 0x2a, 0xba, 0x63, 0xb8, 0x85, 0xa0, 0xf0, + 0xe9, 0x6f, 0xa0, 0xf3, 0x03, 0x92, 0x0c, 0x7f, + 0xb7, 0x43, 0x1d, 0xdf, 0xa9, 0x43, 0x76, 0xad, + 0x94, 0xd9, 0x69, 0xfb, 0xf4, 0x10, 0x9d, 0xc8 + }; + const unsigned char signature[64] = { + 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, 0x8a, + 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, 0x12, + 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, 0x7a, + 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, 0x73, + 0x29, 0xe8, 0x0e, 0x0e, 0xe6, 0x0e, 0x57, 0xaf, + 0x3e, 0x62, 0x5b, 0xba, 0xe1, 0x67, 0x2b, 0x1e, + 0xca, 0xa5, 0x8e, 0xff, 0xe6, 0x13, 0x42, 0x6b, + 0x02, 0x4f, 0xa1, 0x62, 0x1d, 0x90, 0x33, 0x94 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 1); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 1 */ + /* verification test */ + /* the decrypted signature is high so it must be negated first + * AND the extracted decryption key must be negated */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x60, 0x35, 0xc8, 0x98, 0x60, 0xec, 0x62, + 0xad, 0x15, 0x3f, 0x69, 0xb5, 0xb3, 0x07, 0x7b, + 0xcd, 0x08, 0xfb, 0xb0, 0xd2, 0x8d, 0xc7, 0xf7, + 0xf6, 0xdf, 0x4a, 0x05, 0xcc, 0xa3, 0x54, 0x55, + 0xbe, 0x03, 0x70, 0x43, 0xb6, 0x3c, 0x56, 0xf6, + 0x31, 0x7d, 0x99, 0x28, 0xe8, 0xf9, 0x10, 0x07, + 0x33, 0x57, 0x48, 0xc4, 0x98, 0x24, 0x22, 0x0d, + 0xb1, 0x4a, 0xd1, 0x0d, 0x80, 0xa5, 0xd0, 0x0a, + 0x96, 0x54, 0xaf, 0x09, 0x96, 0xc1, 0x82, 0x4c, + 0x64, 0xc9, 0x0b, 0x95, 0x1b, 0xb2, 0x73, 0x4a, + 0xae, 0xcf, 0x78, 0xd4, 0xb3, 0x61, 0x31, 0xa4, + 0x72, 0x38, 0xc3, 0xfa, 0x2b, 0xa2, 0x5e, 0x2c, + 0xed, 0x54, 0x25, 0x5b, 0x06, 0xdf, 0x69, 0x6d, + 0xe1, 0x48, 0x3c, 0x37, 0x67, 0x24, 0x2a, 0x37, + 0x28, 0x82, 0x6e, 0x05, 0xf7, 0x9e, 0x39, 0x81, + 0xe1, 0x25, 0x53, 0x35, 0x5b, 0xba, 0x8a, 0x01, + 0x31, 0xcd, 0x37, 0x0e, 0x63, 0xe3, 0xda, 0x73, + 0x10, 0x6f, 0x63, 0x85, 0x76, 0xa5, 0xaa, 0xb0, + 0xea, 0x6d, 0x45, 0xc0, 0x42, 0x57, 0x4c, 0x0c, + 0x8d, 0x0b, 0x14, 0xb8, 0xc7, 0xc0, 0x1c, 0xfe, + 0x90, 0x72 + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0x4e, 0xee, 0x18, 0xbe, 0x9a, 0x5a, 0x52, + 0x24, 0x00, 0x0f, 0x91, 0x6c, 0x80, 0xb3, 0x93, + 0x44, 0x79, 0x89, 0xe7, 0x19, 0x4b, 0xc0, 0xb0, + 0xf1, 0xad, 0x7a, 0x03, 0x36, 0x97, 0x02, 0xbb, + 0x51 + }; + const unsigned char decryption_key[32] = { + 0xdb, 0x2d, 0xeb, 0xdd, 0xb0, 0x02, 0x47, 0x3a, + 0x00, 0x1d, 0xd7, 0x0b, 0x06, 0xf6, 0xc9, 0x7b, + 0xdc, 0xd1, 0xc4, 0x6b, 0xa1, 0x00, 0x12, 0x37, + 0xfe, 0x0e, 0xe1, 0xae, 0xff, 0xb2, 0xb6, 0xc4 + }; + const unsigned char signature[64] = { + 0x60, 0x35, 0xc8, 0x98, 0x60, 0xec, 0x62, 0xad, + 0x15, 0x3f, 0x69, 0xb5, 0xb3, 0x07, 0x7b, 0xcd, + 0x08, 0xfb, 0xb0, 0xd2, 0x8d, 0xc7, 0xf7, 0xf6, + 0xdf, 0x4a, 0x05, 0xcc, 0xa3, 0x54, 0x55, 0xbe, + 0x4c, 0xea, 0xcf, 0x92, 0x15, 0x46, 0xc0, 0x3d, + 0xd1, 0xbe, 0x59, 0x67, 0x23, 0xad, 0x1e, 0x76, + 0x91, 0xbd, 0xac, 0x73, 0xd8, 0x8c, 0xc3, 0x6c, + 0x42, 0x1c, 0x5e, 0x7f, 0x08, 0x38, 0x43, 0x05 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 1); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 2 */ + /* verification test */ + /* proof is wrong */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xf9, 0x4d, 0xca, 0x20, 0x6d, 0x75, 0x82, + 0xc0, 0x15, 0xfb, 0x9b, 0xff, 0xe4, 0xe4, 0x3b, + 0x14, 0x59, 0x1b, 0x30, 0xef, 0x7d, 0x2b, 0x46, + 0x4d, 0x10, 0x3e, 0xc5, 0xe1, 0x16, 0x59, 0x5d, + 0xba, 0x03, 0x12, 0x7f, 0x8a, 0xc3, 0x53, 0x3d, + 0x24, 0x92, 0x80, 0x33, 0x24, 0x74, 0x33, 0x90, + 0x00, 0x92, 0x2e, 0xb6, 0xa5, 0x8e, 0x3b, 0x9b, + 0xf4, 0xfc, 0x7e, 0x01, 0xe4, 0xb4, 0xdf, 0x2b, + 0x7a, 0x41, 0x00, 0xa1, 0xe0, 0x89, 0xf1, 0x6e, + 0x5d, 0x70, 0xbb, 0x89, 0xf9, 0x61, 0x51, 0x6f, + 0x1d, 0xe0, 0x68, 0x4c, 0xc7, 0x9d, 0xb9, 0x78, + 0x49, 0x5d, 0xf2, 0xf3, 0x99, 0xb0, 0xd0, 0x1e, + 0xd7, 0x24, 0x0f, 0xa6, 0xe3, 0x25, 0x2a, 0xed, + 0xb5, 0x8b, 0xdc, 0x6b, 0x58, 0x77, 0xb0, 0xc6, + 0x02, 0x62, 0x8a, 0x23, 0x5d, 0xd1, 0xcc, 0xae, + 0xbd, 0xdd, 0xcb, 0xe9, 0x61, 0x98, 0xc0, 0xc2, + 0x1b, 0xea, 0xd7, 0xb0, 0x5f, 0x42, 0x3b, 0x67, + 0x3d, 0x14, 0xd2, 0x06, 0xfa, 0x15, 0x07, 0xb2, + 0xdb, 0xe2, 0x72, 0x2a, 0xf7, 0x92, 0xb8, 0xc2, + 0x66, 0xfc, 0x25, 0xa2, 0xd9, 0x01, 0xd7, 0xe2, + 0xc3, 0x35 + }; + const unsigned char message_hash[32] = { + 0x81, 0x31, 0xe6, 0xf4, 0xb4, 0x57, 0x54, 0xf2, + 0xc9, 0x0b, 0xd0, 0x66, 0x88, 0xce, 0xea, 0xbc, + 0x0c, 0x45, 0x05, 0x54, 0x60, 0x72, 0x99, 0x28, + 0xb4, 0xee, 0xcf, 0x11, 0x02, 0x6a, 0x9e, 0x2d + }; + const unsigned char pubkey[33] = { + 0x03, 0x5b, 0xe5, 0xe9, 0x47, 0x82, 0x09, 0x67, + 0x4a, 0x96, 0xe6, 0x0f, 0x1f, 0x03, 0x7f, 0x61, + 0x76, 0x54, 0x0f, 0xd0, 0x01, 0xfa, 0x1d, 0x64, + 0x69, 0x47, 0x70, 0xc5, 0x6a, 0x77, 0x09, 0xc4, + 0x2c + }; + const unsigned char encryption_key[33] = { + 0x02, 0x14, 0xcc, 0xb7, 0x56, 0x24, 0x9a, 0xd6, + 0xe7, 0x33, 0xc8, 0x02, 0x85, 0xea, 0x7a, 0xc2, + 0xee, 0x12, 0xff, 0xeb, 0xbc, 0xee, 0x4e, 0x55, + 0x6e, 0x68, 0x10, 0x79, 0x3a, 0x60, 0xc4, 0x5a, + 0xd4 + }; + const unsigned char decryption_key[32] = { + 0x1d, 0xfc, 0xfc, 0x08, 0x80, 0xe7, 0x25, 0x09, + 0x76, 0x8a, 0xb4, 0x6f, 0x25, 0x45, 0xb3, 0x31, + 0x68, 0xb8, 0xb8, 0xdf, 0x8e, 0x4f, 0x5f, 0xeb, + 0x50, 0x59, 0xaa, 0x37, 0x50, 0xee, 0x59, 0xd0 + }; + const unsigned char signature[64] = { + 0x42, 0x4d, 0x14, 0xa5, 0x47, 0x1c, 0x04, 0x8a, + 0xb8, 0x7b, 0x3b, 0x83, 0xf6, 0x08, 0x5d, 0x12, + 0x5d, 0x58, 0x64, 0x24, 0x9a, 0xe4, 0x29, 0x7a, + 0x57, 0xc8, 0x4e, 0x74, 0x71, 0x0b, 0xb6, 0x73, + 0x29, 0xe8, 0x0e, 0x0e, 0xe6, 0x0e, 0x57, 0xaf, + 0x3e, 0x62, 0x5b, 0xba, 0xe1, 0x67, 0x2b, 0x1e, + 0xca, 0xa5, 0x8e, 0xff, 0xe6, 0x13, 0x42, 0x6b, + 0x02, 0x4f, 0xa1, 0x62, 0x1d, 0x90, 0x33, 0x94 + }; + test_ecdsa_adaptor_spec_vectors_check_verify(adaptor_sig, message_hash, pubkey, encryption_key, 0); + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 0); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 0); + } + { + /* Test vector 3 */ + /* recovery test */ + /* plain recovery */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xf2, 0xdb, 0x6e, 0x9e, 0xd3, 0x30, 0x92, + 0xcc, 0x0b, 0x89, 0x8f, 0xd6, 0xb2, 0x82, 0xe9, + 0x9b, 0xda, 0xec, 0xcb, 0x3d, 0xe8, 0x5c, 0x2d, + 0x25, 0x12, 0xd8, 0xd5, 0x07, 0xf9, 0xab, 0xab, + 0x29, 0x02, 0x10, 0xc0, 0x1b, 0x5b, 0xed, 0x70, + 0x94, 0xa1, 0x26, 0x64, 0xae, 0xaa, 0xb3, 0x40, + 0x2d, 0x87, 0x09, 0xa8, 0xf3, 0x62, 0xb1, 0x40, + 0x32, 0x8d, 0x1b, 0x36, 0xdd, 0x7c, 0xb4, 0x20, + 0xd0, 0x2f, 0xb6, 0x6b, 0x12, 0x30, 0xd6, 0x1c, + 0x16, 0xd0, 0xcd, 0x0a, 0x2a, 0x02, 0x24, 0x6d, + 0x5a, 0xc7, 0x84, 0x8d, 0xcd, 0x6f, 0x04, 0xfe, + 0x62, 0x70, 0x53, 0xcd, 0x3c, 0x70, 0x15, 0xa7, + 0xd4, 0xaa, 0x6a, 0xc2, 0xb0, 0x43, 0x47, 0x34, + 0x8b, 0xd6, 0x7d, 0xa4, 0x3b, 0xe8, 0x72, 0x25, + 0x15, 0xd9, 0x9a, 0x79, 0x85, 0xfb, 0xfa, 0x66, + 0xf0, 0x36, 0x5c, 0x70, 0x1d, 0xe7, 0x6f, 0xf0, + 0x40, 0x0d, 0xff, 0xdc, 0x9f, 0xa8, 0x4d, 0xdd, + 0xf4, 0x13, 0xa7, 0x29, 0x82, 0x3b, 0x16, 0xaf, + 0x60, 0xaa, 0x63, 0x61, 0xbc, 0x32, 0xe7, 0xcf, + 0xd6, 0x70, 0x1e, 0x32, 0x95, 0x7c, 0x72, 0xac, + 0xe6, 0x7b + }; + const unsigned char encryption_key[33] = { + 0x02, 0x7e, 0xe4, 0xf8, 0x99, 0xbc, 0x9c, 0x5f, + 0x2b, 0x62, 0x6f, 0xa1, 0xa9, 0xb3, 0x7c, 0xe2, + 0x91, 0xc0, 0x38, 0x8b, 0x52, 0x27, 0xe9, 0x0b, + 0x0f, 0xd8, 0xf4, 0xfa, 0x57, 0x61, 0x64, 0xed, + 0xe7 + }; + const unsigned char decryption_key[32] = { + 0x9c, 0xf3, 0xea, 0x9b, 0xe5, 0x94, 0x36, 0x6b, + 0x78, 0xc4, 0x57, 0x16, 0x29, 0x08, 0xaf, 0x3c, + 0x2e, 0xa1, 0x77, 0x05, 0x81, 0x77, 0xe9, 0xc6, + 0xbf, 0x99, 0x04, 0x79, 0x27, 0x77, 0x3a, 0x06 + }; + const unsigned char signature[64] = { + 0xf2, 0xdb, 0x6e, 0x9e, 0xd3, 0x30, 0x92, 0xcc, + 0x0b, 0x89, 0x8f, 0xd6, 0xb2, 0x82, 0xe9, 0x9b, + 0xda, 0xec, 0xcb, 0x3d, 0xe8, 0x5c, 0x2d, 0x25, + 0x12, 0xd8, 0xd5, 0x07, 0xf9, 0xab, 0xab, 0x29, + 0x21, 0x81, 0x1f, 0xe7, 0xb5, 0x3b, 0xec, 0xf3, + 0xb7, 0xaf, 0xfa, 0x94, 0x42, 0xab, 0xaa, 0x93, + 0xc0, 0xab, 0x8a, 0x8e, 0x45, 0xcd, 0x7e, 0xe2, + 0xea, 0x8d, 0x25, 0x8b, 0xfc, 0x25, 0xd4, 0x64 + }; + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 1); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 4 */ + /* recovery test */ + /* the R value of the signature does not match */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xaa, 0x86, 0xd7, 0x80, 0x59, 0xa9, 0x10, + 0x59, 0xc2, 0x9e, 0xc1, 0xa7, 0x57, 0xc4, 0xdc, + 0x02, 0x9f, 0xf6, 0x36, 0xa1, 0xe6, 0xc1, 0x14, + 0x2f, 0xef, 0xe1, 0xe9, 0xd7, 0x33, 0x96, 0x17, + 0xc0, 0x03, 0xa8, 0x15, 0x3e, 0x50, 0xc0, 0xc8, + 0x57, 0x4a, 0x38, 0xd3, 0x89, 0xe6, 0x1b, 0xbb, + 0x0b, 0x58, 0x15, 0x16, 0x9e, 0x06, 0x09, 0x24, + 0xe4, 0xb5, 0xf2, 0xe7, 0x8f, 0xf1, 0x3a, 0xa7, + 0xad, 0x85, 0x8e, 0x0c, 0x27, 0xc4, 0xb9, 0xee, + 0xd9, 0xd6, 0x05, 0x21, 0xb3, 0xf5, 0x4f, 0xf8, + 0x3c, 0xa4, 0x77, 0x4b, 0xe5, 0xfb, 0x3a, 0x68, + 0x0f, 0x82, 0x0a, 0x35, 0xe8, 0x84, 0x0f, 0x4a, + 0xaf, 0x2d, 0xe8, 0x8e, 0x7c, 0x5c, 0xff, 0x38, + 0xa3, 0x7b, 0x78, 0x72, 0x59, 0x04, 0xef, 0x97, + 0xbb, 0x82, 0x34, 0x13, 0x28, 0xd5, 0x59, 0x87, + 0x01, 0x9b, 0xd3, 0x8a, 0xe1, 0x74, 0x5e, 0x3e, + 0xfe, 0x0f, 0x8e, 0xa8, 0xbd, 0xfe, 0xde, 0x0d, + 0x37, 0x8f, 0xc1, 0xf9, 0x6e, 0x94, 0x4a, 0x75, + 0x05, 0x24, 0x9f, 0x41, 0xe9, 0x37, 0x81, 0x50, + 0x9e, 0xe0, 0xba, 0xde, 0x77, 0x29, 0x0d, 0x39, + 0xcd, 0x12 + }; + const unsigned char encryption_key[33] = { + 0x03, 0x51, 0x76, 0xd2, 0x41, 0x29, 0x74, 0x1b, + 0x0f, 0xca, 0xa5, 0xfd, 0x67, 0x50, 0x72, 0x7c, + 0xe3, 0x08, 0x60, 0x44, 0x7e, 0x0a, 0x92, 0xc9, + 0xeb, 0xeb, 0xde, 0xb7, 0xc3, 0xf9, 0x39, 0x95, + 0xed + }; + const unsigned char signature[64] = { + 0xf7, 0xf7, 0xfe, 0x6b, 0xd0, 0x56, 0xfc, 0x4a, + 0xbd, 0x70, 0xd3, 0x35, 0xf7, 0x2d, 0x0a, 0xa1, + 0xe8, 0x40, 0x6b, 0xba, 0x68, 0xf3, 0xe5, 0x79, + 0xe4, 0x78, 0x94, 0x75, 0x32, 0x35, 0x64, 0xa4, + 0x52, 0xc4, 0x61, 0x76, 0xc7, 0xfb, 0x40, 0xaa, + 0x37, 0xd5, 0x65, 0x13, 0x41, 0xf5, 0x56, 0x97, + 0xda, 0xb2, 0x7d, 0x84, 0xa2, 0x13, 0xb3, 0x0c, + 0x93, 0x01, 0x1a, 0x77, 0x90, 0xba, 0xce, 0x8c + }; + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, NULL, signature, 0); + } + { + /* Test vector 5 */ + /* recovery test */ + /* recovery from high s signature */ + const unsigned char adaptor_sig[162] = { + 0x03, 0x2c, 0x63, 0x7c, 0xd7, 0x97, 0xdd, 0x8c, + 0x2c, 0xe2, 0x61, 0x90, 0x7e, 0xd4, 0x3e, 0x82, + 0xd6, 0xd1, 0xa4, 0x8c, 0xba, 0xbb, 0xbe, 0xce, + 0x80, 0x11, 0x33, 0xdd, 0x8d, 0x70, 0xa0, 0x1b, + 0x14, 0x03, 0xeb, 0x61, 0x5a, 0x3e, 0x59, 0xb1, + 0xcb, 0xbf, 0x4f, 0x87, 0xac, 0xaf, 0x64, 0x5b, + 0xe1, 0xed, 0xa3, 0x2a, 0x06, 0x66, 0x11, 0xf3, + 0x5d, 0xd5, 0x55, 0x78, 0x02, 0x80, 0x2b, 0x14, + 0xb1, 0x9c, 0x81, 0xc0, 0x4c, 0x3f, 0xef, 0xac, + 0x57, 0x83, 0xb2, 0x07, 0x7b, 0xd4, 0x3f, 0xa0, + 0xa3, 0x9a, 0xb8, 0xa6, 0x4d, 0x4d, 0x78, 0x33, + 0x2a, 0x5d, 0x62, 0x1e, 0xa2, 0x3e, 0xca, 0x46, + 0xbc, 0x01, 0x10, 0x11, 0xab, 0x82, 0xdd, 0xa6, + 0xde, 0xb8, 0x56, 0x99, 0xf5, 0x08, 0x74, 0x4d, + 0x70, 0xd4, 0x13, 0x4b, 0xea, 0x03, 0xf7, 0x84, + 0xd2, 0x85, 0xb5, 0xc6, 0xc1, 0x5a, 0x56, 0xe4, + 0xe1, 0xfa, 0xb4, 0xbc, 0x35, 0x6a, 0xbb, 0xde, + 0xbb, 0x3b, 0x8f, 0xe1, 0xe5, 0x5e, 0x6d, 0xd6, + 0xd2, 0xa9, 0xea, 0x45, 0x7e, 0x91, 0xb2, 0xe6, + 0x64, 0x2f, 0xae, 0x69, 0xf9, 0xdb, 0xb5, 0x25, + 0x88, 0x54 + }; + const unsigned char encryption_key[33] = { + 0x02, 0x04, 0x25, 0x37, 0xe9, 0x13, 0xad, 0x74, + 0xc4, 0xbb, 0xd8, 0xda, 0x96, 0x07, 0xad, 0x3b, + 0x9c, 0xb2, 0x97, 0xd0, 0x8e, 0x01, 0x4a, 0xfc, + 0x51, 0x13, 0x30, 0x83, 0xf1, 0xbd, 0x68, 0x7a, + 0x62 + }; + const unsigned char decryption_key[32] = { + 0x32, 0x47, 0x19, 0xb5, 0x1f, 0xf2, 0x47, 0x4c, + 0x94, 0x38, 0xeb, 0x76, 0x49, 0x4b, 0x0d, 0xc0, + 0xbc, 0xce, 0xeb, 0x52, 0x9f, 0x0a, 0x54, 0x28, + 0xfd, 0x19, 0x8a, 0xd8, 0xf8, 0x86, 0xe9, 0x9c + }; + const unsigned char signature[64] = { + 0x2c, 0x63, 0x7c, 0xd7, 0x97, 0xdd, 0x8c, 0x2c, + 0xe2, 0x61, 0x90, 0x7e, 0xd4, 0x3e, 0x82, 0xd6, + 0xd1, 0xa4, 0x8c, 0xba, 0xbb, 0xbe, 0xce, 0x80, + 0x11, 0x33, 0xdd, 0x8d, 0x70, 0xa0, 0x1b, 0x14, + 0xb5, 0xf2, 0x43, 0x21, 0xf5, 0x50, 0xb7, 0xb9, + 0xdd, 0x06, 0xee, 0x4f, 0xcf, 0xd8, 0x2b, 0xda, + 0xd8, 0xb1, 0x42, 0xff, 0x93, 0xa7, 0x90, 0xcc, + 0x4d, 0x9f, 0x79, 0x62, 0xb3, 0x8c, 0x6a, 0x3b + }; + test_ecdsa_adaptor_spec_vectors_check_decrypt(adaptor_sig, decryption_key, signature, 0); + test_ecdsa_adaptor_spec_vectors_check_recover(adaptor_sig, encryption_key, decryption_key, signature, 1); + } + { + /* Test vector 6 */ + /* serialization test */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 7 */ + /* serialization test */ + /* R can be above curve order */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xff, 0xff, 0xff, 0xff, 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, 0xfc, + 0x2c, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 8 */ + /* serialization test */ + /* R_a can be above curve order */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xff, 0xff, 0xff, 0xff, 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, + 0xfc, 0x2c, 0xd6, 0x19, 0xac, 0xb1, 0xcb, 0x75, + 0xc1, 0xa5, 0x94, 0x6f, 0xba, 0xf7, 0x16, 0xd2, + 0x27, 0x19, 0x9a, 0x64, 0x79, 0xa6, 0x78, 0xd1, + 0x0a, 0x6d, 0x95, 0x51, 0x2d, 0x67, 0x4f, 0xb7, + 0x70, 0x3d, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 1); + } + { + /* Test vector 9 */ + /* serialization test */ + /* s_a cannot be zero */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 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, 0x00, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 0); + } + { + /* Test vector 10 */ + /* serialization test */ + /* s_a too high */ + const unsigned char adaptor_sig[162] = { + 0x03, 0xe6, 0xd5, 0x1d, 0xa7, 0xbc, 0x2b, 0xf2, + 0x4c, 0xf9, 0xdf, 0xd9, 0xac, 0xc6, 0xc4, 0xf0, + 0xa3, 0xe7, 0x4d, 0x8a, 0x62, 0x73, 0xee, 0x5a, + 0x57, 0x3e, 0xd6, 0x81, 0x8e, 0x30, 0x95, 0xb6, + 0x09, 0x03, 0xf3, 0x3b, 0xc9, 0x8f, 0x9d, 0x2e, + 0xa3, 0x51, 0x1f, 0x2e, 0x24, 0xf3, 0x35, 0x85, + 0x57, 0xc8, 0x15, 0xab, 0xd7, 0x71, 0x3c, 0x93, + 0x18, 0xaf, 0x9f, 0x4d, 0xfa, 0xb4, 0x44, 0x18, + 0x98, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, + 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, + 0x41, 0x41, 0x85, 0xb5, 0x89, 0x80, 0xb8, 0xe6, + 0xc5, 0x4b, 0xd2, 0x06, 0x16, 0xbd, 0xb9, 0x46, + 0x1d, 0xcc, 0xd8, 0xee, 0xbb, 0x7d, 0x7e, 0x7c, + 0x83, 0xa9, 0x14, 0x52, 0xcc, 0x20, 0xed, 0xf5, + 0x3b, 0xe5, 0xb0, 0xfe, 0x0d, 0xb4, 0x4d, 0xdd, + 0xaa, 0xaf, 0xbe, 0x73, 0x76, 0x78, 0xc6, 0x84, + 0xb6, 0xe8, 0x9b, 0x9b, 0x4b, 0x67, 0x9b, 0x18, + 0x55, 0xaa, 0x6e, 0xd6, 0x44, 0x49, 0x8b, 0x89, + 0xc9, 0x18 + }; + test_ecdsa_adaptor_spec_vectors_check_serialization(adaptor_sig, 0); + } +} + +/* Nonce function that returns constant 0 */ +static int ecdsa_adaptor_nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int ecdsa_adaptor_nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0, 32); + return 1; +} + +/* Nonce function that sets nonce to 0xFF...0xFF */ +static int ecdsa_adaptor_nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *encryption_key33, const unsigned char *algo, size_t algolen, void *data) { + (void) msg32; + (void) key32; + (void) encryption_key33; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0xFF, 32); + return 1; +} + +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function + */ +void nonce_function_ecdsa_adaptor_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t algolen) { + unsigned char nonces[2][32]; + CHECK(nonce_function_ecdsa_adaptor(nonces[0], args[0], args[1], args[2], args[3], algolen, args[4]) == 1); + secp256k1_testrand_flip(args[n_flip], n_bytes); + CHECK(nonce_function_ecdsa_adaptor(nonces[1], args[0], args[1], args[2], args[3], algolen, args[4]) == 1); + CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); +} + +/* Tests for the equality of two sha256 structs. This function only produces a + * correct result if an integer multiple of 64 many bytes have been written + * into the hash functions. */ +void ecdsa_adaptor_test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + CHECK(sha1->bytes == sha2->bytes); + CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); +} + +void run_nonce_function_ecdsa_adaptor_tests(void) { + unsigned char tag[16] = "ECDSAadaptor/non"; + unsigned char aux_tag[16] = "ECDSAadaptor/aux"; + unsigned char algo[16] = "ECDSAadaptor/non"; + size_t algolen = sizeof(algo); + unsigned char dleq_tag[4] = "DLEQ"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char nonce[32]; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char pk[33]; + unsigned char aux_rand[32]; + unsigned char *args[5]; + int i; + + /* Check that hash initialized by + * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged(&sha_optimized); + ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); + secp256k1_nonce_function_ecdsa_adaptor_sha256_tagged_aux(&sha_optimized); + ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_nonce_function_dleq_sha256_tagged_aux has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, dleq_tag, sizeof(dleq_tag)); + secp256k1_nonce_function_dleq_sha256_tagged(&sha_optimized); + ecdsa_adaptor_test_sha256_eq(&sha, &sha_optimized); + + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, msg, sizeof(msg)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, key, sizeof(key)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, pk, sizeof(pk)); + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, aux_rand, sizeof(aux_rand)); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = msg; + args[1] = key; + args[2] = pk; + args[3] = algo; + args[4] = aux_rand; + for (i = 0; i < count; i++) { + nonce_function_ecdsa_adaptor_bitflip(args, 0, sizeof(msg), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 1, sizeof(key), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 2, sizeof(pk), algolen); + /* Flip algo special case "ECDSAadaptor/non" */ + nonce_function_ecdsa_adaptor_bitflip(args, 3, sizeof(algo), algolen); + /* Flip algo again */ + nonce_function_ecdsa_adaptor_bitflip(args, 3, sizeof(algo), algolen); + nonce_function_ecdsa_adaptor_bitflip(args, 4, sizeof(aux_rand), algolen); + } + + /* NULL algo is disallowed */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, NULL, 0, NULL) == 0); + /* Empty algo is fine */ + memset(algo, 0x00, algolen); + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); + /* Other algo is fine */ + memset(algo, 0xFF, algolen); + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); + /* dleq algo is fine */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, dleq_algo, sizeof(dleq_algo), NULL) == 1); + + /* Different algolen gives different nonce */ + for (i = 0; i < count; i++) { + unsigned char nonce2[32]; + uint32_t offset = secp256k1_testrand_int(algolen - 1); + size_t algolen_tmp = (algolen + offset) % algolen; + + CHECK(nonce_function_ecdsa_adaptor(nonce2, msg, key, pk, algo, algolen_tmp, NULL) == 1); + CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); + } + + /* NULL aux_rand argument is allowed. */ + CHECK(nonce_function_ecdsa_adaptor(nonce, msg, key, pk, algo, algolen, NULL) == 1); +} + +void test_ecdsa_adaptor_api(void) { + secp256k1_pubkey pubkey; + secp256k1_pubkey enckey; + secp256k1_pubkey zero_pk; + secp256k1_ecdsa_signature sig; + unsigned char sk[32]; + unsigned char msg[32]; + unsigned char asig[162]; + unsigned char deckey[32]; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_testrand256(sk); + secp256k1_testrand256(msg); + secp256k1_testrand256(deckey); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, sk) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &enckey, deckey) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_encrypt(none, asig, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_encrypt(vrfy, asig, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, msg, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, NULL, sk, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, NULL, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, NULL, &enckey, msg, NULL, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, NULL, msg, NULL, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &zero_pk, msg, NULL, NULL) == 0); + CHECK(ecount == 7); + + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_encrypt(sign, asig, sk, &enckey, msg, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(none, asig, &pubkey, msg, &enckey) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(sign, asig, &pubkey, msg, &enckey) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, msg, &enckey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, NULL, &pubkey, msg, &enckey) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, NULL, &enckey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, msg, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, NULL, msg, &enckey) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &zero_pk, msg, &enckey) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_ecdsa_adaptor_verify(vrfy, asig, &pubkey, msg, &zero_pk) == 0); + CHECK(ecount == 8); + + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_decrypt(none, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(sign, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(vrfy, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, NULL, deckey, asig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, NULL, asig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, deckey, NULL) == 0); + CHECK(ecount == 3); + + ecount = 0; + CHECK(secp256k1_ecdsa_adaptor_decrypt(both, &sig, deckey, asig) == 1); + CHECK(secp256k1_ecdsa_adaptor_recover(none, deckey, &sig, asig, &enckey) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_adaptor_recover(vrfy, deckey, &sig, asig, &enckey) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &enckey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, NULL, &sig, asig, &enckey) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, NULL, asig, &enckey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, NULL, &enckey) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_adaptor_recover(sign, deckey, &sig, asig, &zero_pk) == 0); + CHECK(ecount == 7); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +void adaptor_tests(void) { + unsigned char seckey[32]; + secp256k1_pubkey pubkey; + unsigned char msg[32]; + unsigned char deckey[32]; + secp256k1_pubkey enckey; + unsigned char adaptor_sig[162]; + secp256k1_ecdsa_signature sig; + unsigned char zeros162[162] = { 0 }; + unsigned char zeros64[64] = { 0 }; + unsigned char big[32]; + + secp256k1_testrand256(seckey); + secp256k1_testrand256(msg); + secp256k1_testrand256(deckey); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &enckey, deckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, NULL, NULL) == 1); + + { + /* Test overflowing seckey */ + memset(big, 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, big, &enckey, msg, NULL, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); + + /* Test different nonce functions */ + memset(adaptor_sig, 1, sizeof(adaptor_sig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_failing, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); + memset(&adaptor_sig, 1, sizeof(adaptor_sig)); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_0, NULL) == 0); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) == 0); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, seckey, &enckey, msg, ecdsa_adaptor_nonce_function_overflowing, NULL) == 1); + CHECK(secp256k1_memcmp_var(adaptor_sig, zeros162, sizeof(adaptor_sig)) != 0); + } + { + /* Test adaptor_sig_serialize roundtrip */ + secp256k1_ge r, rp; + secp256k1_scalar sigr; + secp256k1_scalar sp; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + secp256k1_ge p_inf; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); + + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 1); + CHECK(secp256k1_memcmp_var(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)) == 0); + + /* Test adaptor_sig_serialize points at infinity */ + secp256k1_ge_set_infinity(&p_inf); + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &p_inf, &rp, &sp, &dleq_proof_e, &dleq_proof_s) == 0); + CHECK(secp256k1_ecdsa_adaptor_sig_serialize(adaptor_sig_tmp, &r, &p_inf, &sp, &dleq_proof_e, &dleq_proof_s) == 0); + } + { + /* Test adaptor_sig_deserialize */ + secp256k1_ge r, rp; + secp256k1_scalar sigr; + secp256k1_scalar sp; + secp256k1_scalar dleq_proof_s, dleq_proof_e; + unsigned char adaptor_sig_tmp[162]; + + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, &rp, &sp, &dleq_proof_e, &dleq_proof_s, adaptor_sig) == 1); + + /* r */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[0], 0xFF, 33); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(&r, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* sigr */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[1], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 1); + memset(&adaptor_sig_tmp[1], 0, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* rp */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, &rp, NULL, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[33], 0xFF, 33); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, &rp, NULL, NULL, NULL, adaptor_sig_tmp) == 0); + + /* sp */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, &sp, NULL, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[66], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, &sp, NULL, NULL, adaptor_sig_tmp) == 0); + + /* dleq_proof_e */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, &dleq_proof_e, NULL, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[98], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, &dleq_proof_e, NULL, adaptor_sig_tmp) == 1); + + /* dleq_proof_s */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, NULL, &dleq_proof_s, adaptor_sig) == 1); + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[130], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, NULL, NULL, NULL, NULL, &dleq_proof_s, adaptor_sig_tmp) == 0); + } + + /* Test adaptor_sig_verify */ + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg, &enckey) == 1); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &enckey, msg, &enckey) == 0); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg, &pubkey) == 0); + { + unsigned char adaptor_sig_tmp[65]; + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + rand_flip_bit(&adaptor_sig_tmp[1], sizeof(adaptor_sig_tmp) - 1); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig_tmp, &pubkey, msg, &enckey) == 0); + } + { + unsigned char msg_tmp[32]; + memcpy(msg_tmp, msg, sizeof(msg_tmp)); + rand_flip_bit(msg_tmp, sizeof(msg_tmp)); + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig, &pubkey, msg_tmp, &enckey) == 0); + } + { + /* Verification must check that the derived R' is not equal to the point at + * infinity before negating it. R' is derived as follows: + * + * R' == s'⁻¹(m * G + R.x * X) + * + * When the base point, G, is multiplied by the subgroup order, q, the + * result is the point at infinity, 0: + * + * q * G = 0 + * + * Thus, if we set s' equal to R.x, m equal to (q - 1) * R.x, and X equal to + * G, then our derived R' will be 0: + * + * R' = R.x⁻¹((q - 1 * R.x) * G + R.x * G) = q * G = 0 */ + + /* t := q - 1 */ + const unsigned char target[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 + }; + unsigned char seckey_tmp[32] = { 0 }; + unsigned char msg_tmp[32]; + unsigned char adaptor_sig_tmp[162]; + secp256k1_pubkey pubkey_tmp; + secp256k1_scalar sigr, t, m; + + /* m := t * sigr */ + CHECK(secp256k1_ecdsa_adaptor_sig_deserialize(NULL, &sigr, NULL, NULL, NULL, NULL, adaptor_sig) == 1); + secp256k1_scalar_set_b32(&t, target, NULL); + secp256k1_scalar_mul(&m, &t, &sigr); + secp256k1_scalar_get_b32(msg_tmp, &m); + + /* X := G */ + seckey_tmp[31] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_tmp, seckey_tmp) == 1); + + /* sp := sigr */ + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memcpy(&adaptor_sig_tmp[66], &adaptor_sig_tmp[1], 32); + + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, adaptor_sig_tmp, &pubkey_tmp, msg_tmp, &enckey) == 0); + } + + /* Test decryption */ + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &sig, deckey, adaptor_sig) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); + + { + /* Test overflowing decryption key */ + secp256k1_ecdsa_signature s; + memset(big, 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &s, big, adaptor_sig) == 0); + CHECK(secp256k1_memcmp_var(&s.data[0], zeros64, sizeof(&s.data[0])) == 0); + } + { + /* Test key recover */ + secp256k1_ecdsa_signature sig_tmp; + unsigned char decryption_key_tmp[32]; + unsigned char adaptor_sig_tmp[162]; + const unsigned char order_le[32] = { + 0x41, 0x41, 0x36, 0xd0, 0x8c, 0x5e, 0xd2, 0xbf, + 0x3b, 0xa0, 0x48, 0xaf, 0xe6, 0xdc, 0xae, 0xba, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig, adaptor_sig, &enckey) == 1); + CHECK(secp256k1_memcmp_var(deckey, decryption_key_tmp, sizeof(deckey)) == 0); + + /* Test failed sp deserialization */ + memcpy(adaptor_sig_tmp, adaptor_sig, sizeof(adaptor_sig_tmp)); + memset(&adaptor_sig_tmp[66], 0xFF, 32); + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig, adaptor_sig_tmp, &enckey) == 0); + + /* Test failed enckey_expected serialization */ + memcpy(sig_tmp.data, sig.data, 32); + memcpy(&sig_tmp.data[32], order_le, 32); + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, decryption_key_tmp, &sig_tmp, adaptor_sig, &enckey) == 0); + } +} + +void multi_hop_lock_tests(void) { + unsigned char seckey_a[32]; + unsigned char seckey_b[32]; + unsigned char pop[32]; + unsigned char tx_ab[32]; + unsigned char tx_bc[32]; + unsigned char buf[32]; + unsigned char asig_ab[162]; + unsigned char asig_bc[162]; + secp256k1_pubkey pubkey_pop; + secp256k1_pubkey pubkey_a, pubkey_b; + secp256k1_pubkey l, r; + secp256k1_ge l_ge, r_ge; + secp256k1_scalar t1, t2, tp; + secp256k1_scalar deckey; + secp256k1_ecdsa_signature sig_ab, sig_bc; + + secp256k1_testrand256(seckey_a); + secp256k1_testrand256(seckey_b); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_a, seckey_a)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_b, seckey_b)); + + /* Carol setup */ + /* Proof of payment */ + secp256k1_testrand256(pop); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey_pop, pop)); + + /* Alice setup */ + secp256k1_testrand256(tx_ab); + rand_scalar(&t1); + rand_scalar(&t2); + secp256k1_scalar_add(&tp, &t1, &t2); + /* Left lock */ + secp256k1_pubkey_load(ctx, &l_ge, &pubkey_pop); + CHECK(secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &l_ge, &t1)); + secp256k1_pubkey_save(&l, &l_ge); + /* Right lock */ + secp256k1_pubkey_load(ctx, &r_ge, &pubkey_pop); + CHECK(secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &r_ge, &tp)); + secp256k1_pubkey_save(&r, &r_ge); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, asig_ab, seckey_a, &l, tx_ab, NULL, NULL)); + + /* Bob setup */ + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, asig_ab, &pubkey_a, tx_ab, &l)); + secp256k1_testrand256(tx_bc); + CHECK(secp256k1_ecdsa_adaptor_encrypt(ctx, asig_bc, seckey_b, &r, tx_bc, NULL, NULL)); + + /* Carol decrypt */ + CHECK(secp256k1_ecdsa_adaptor_verify(ctx, asig_bc, &pubkey_b, tx_bc, &r)); + secp256k1_scalar_set_b32(&deckey, pop, NULL); + secp256k1_scalar_add(&deckey, &deckey, &tp); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &sig_bc, buf, asig_bc)); + CHECK(secp256k1_ecdsa_verify(ctx, &sig_bc, tx_bc, &pubkey_b)); + + /* Bob recover and decrypt */ + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, buf, &sig_bc, asig_bc, &r)); + secp256k1_scalar_set_b32(&deckey, buf, NULL); + secp256k1_scalar_negate(&t2, &t2); + secp256k1_scalar_add(&deckey, &deckey, &t2); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_ecdsa_adaptor_decrypt(ctx, &sig_ab, buf, asig_ab)); + CHECK(secp256k1_ecdsa_verify(ctx, &sig_ab, tx_ab, &pubkey_a)); + + /* Alice recover and derive proof of payment */ + CHECK(secp256k1_ecdsa_adaptor_recover(ctx, buf, &sig_ab, asig_ab, &l)); + secp256k1_scalar_set_b32(&deckey, buf, NULL); + secp256k1_scalar_negate(&t1, &t1); + secp256k1_scalar_add(&deckey, &deckey, &t1); + secp256k1_scalar_get_b32(buf, &deckey); + CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0); +} + +void run_ecdsa_adaptor_tests(void) { + int i; + run_nonce_function_ecdsa_adaptor_tests(); + + test_ecdsa_adaptor_api(); + test_ecdsa_adaptor_spec_vectors(); + for (i = 0; i < count; i++) { + dleq_tests(); + } + for (i = 0; i < count; i++) { + adaptor_tests(); + } + for (i = 0; i < count; i++) { + multi_hop_lock_tests(); + } +} + +#endif /* SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H */ diff --git a/src/tests.c b/src/tests.c index 1208d0c9..12ac3b75 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5652,6 +5652,10 @@ void run_ecdsa_openssl(void) { # include "modules/ecdsa_s2c/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +# include "modules/ecdsa_adaptor/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)]; @@ -5966,6 +5970,10 @@ int main(int argc, char **argv) { run_ecdsa_s2c_tests(); #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + run_ecdsa_adaptor_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index f7081dbd..b153f2f1 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -31,6 +31,10 @@ #include "include/secp256k1_ecdsa_s2c.h" #endif +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR +#include "include/secp256k1_ecdsa_adaptor.h" +#endif + void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -199,4 +203,42 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { CHECK(ret == 1); } #endif + +#ifdef ENABLE_MODULE_ECDSA_ADAPTOR + { + unsigned char adaptor_sig[162]; + unsigned char deckey[32]; + unsigned char expected_deckey[32]; + secp256k1_pubkey enckey; + + for (i = 0; i < 32; i++) { + deckey[i] = i + 2; + } + + ret = secp256k1_ec_pubkey_create(ctx, &enckey, deckey); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + ret = secp256k1_ecdsa_adaptor_encrypt(ctx, adaptor_sig, key, &enckey, msg, NULL, NULL); + VALGRIND_MAKE_MEM_DEFINED(adaptor_sig, sizeof(adaptor_sig)); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(deckey, 32); + ret = secp256k1_ecdsa_adaptor_decrypt(ctx, &signature, deckey, adaptor_sig); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(&signature, 32); + ret = secp256k1_ecdsa_adaptor_recover(ctx, expected_deckey, &signature, adaptor_sig, &enckey); + VALGRIND_MAKE_MEM_DEFINED(expected_deckey, sizeof(expected_deckey)); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_DEFINED(deckey, sizeof(deckey)); + ret = secp256k1_memcmp_var(deckey, expected_deckey, sizeof(expected_deckey)); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 0); + } +#endif }