Skip to content

Commit

Permalink
add ecdsa_adaptor module
Browse files Browse the repository at this point in the history
Move R from adaptor_proof to adaptor_sig and fix args of _adapt

Add dleq proof and dleq verify

Move dleq things in own file and add adaptor sign

Add ecdsa_adaptor_sig_verify

Add ecdsa_adaptor_adapt

add ecdsa_adaptor_extract_secret

cleanup: stricter input validation

Cleanup a few more things

Fix adaptor_sig_verify: should compare scalars and not fe's

Clean up docs and reenable tests

Make ecdsa_adaptor_sig_verify compare full points instead of only x coords

Add gen2 to dleq challenge and nonce function

Update TODOs

Update docs

Fix dleq tag by removing trailing NUL bytes

Fix not checking return value of dleq_proof in ecdsa_adaptor_sign
  • Loading branch information
jonasnick authored and nkohen committed Sep 10, 2020
1 parent c076759 commit 62ee6da
Show file tree
Hide file tree
Showing 9 changed files with 800 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,8 @@ endif
if ENABLE_MODULE_SCHNORRSIG
include src/modules/schnorrsig/Makefile.am.include
endif

if ENABLE_MODULE_ECDSA_ADAPTOR
include src/modules/ecdsa_adaptor/Makefile.am.include
endif

16 changes: 16 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ AC_ARG_ENABLE(module_schnorrsig,
[enable_module_schnorrsig=$enableval],
[enable_module_schnorrsig=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],
Expand Down Expand Up @@ -436,6 +442,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then
enable_module_extrakeys=yes
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

# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
# module to set enable_module_extrakeys=yes
if test x"$enable_module_extrakeys" = x"yes"; then
Expand All @@ -457,6 +467,7 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys])
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
AC_MSG_NOTICE([Building ecdsa-adaptor module: $enable_module_ecdsa_adaptor])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_ecdh" = x"yes"; then
Expand All @@ -468,6 +479,9 @@ else
if test x"$enable_module_schnorrsig" = x"yes"; then
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
AC_MSG_ERROR([ecdsa_adaptor 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
Expand All @@ -488,6 +502,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_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_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"])

Expand All @@ -509,6 +524,7 @@ echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor"
echo
echo " asm = $set_asm"
echo " bignum = $set_bignum"
Expand Down
131 changes: 131 additions & 0 deletions include/secp256k1_ecdsa_adaptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#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).
*
* Note that at this module is currently a work in progress. It's not secure
* nor stable. Let me repeat: IT IS EXTREMELY DANGEROUS AND RECKLESS TO USE
* THIS MODULE IN PRODUCTION. DON'T!
*
* This module passes a rudimentary test suite. But there are some things left
* TODO:
* - add API tests
* - add tests for the various overflow conditions
* - refactor adaptor verify to reuse code from secp256k1_ecdsa_verify()
* - test ecdsa_adaptor_sig_verify() more systematically. This is the most
* crucial function in this module. If it passes, we need to be sure that
* it is possible to compute the adaptor secret from the final ecdsa
* signature.
* - add ecdsa_adaptor_sign(), ecdsa_adaptor_adapt() and
* ecdsa_adaptor_extract_secret() to valgrind_ctime_test.c
* - allow using your own nonce function (noncefp, noncedata, synthetic
* nonces)
* - test module in travis
* - add comments to ease review
*/

/** Adaptor sign ("EncSign")
*
* Creates an adaptor signature along with a proof to verify the adaptor
* signature.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object, initialized for signing
* (cannot be NULL)
* Out: adaptor_sig65: pointer to 65 byte to store the returned signature
* (cannot be NULL)
* adaptor_proof97: pointer to 97 byte to store the adaptor proof (cannot be
* NULL)
* In: seckey32: pointer to 32 byte secret key corresponding to the
* pubkey (cannot be NULL)
* adaptor: pointer to the adaptor point (cannot be NULL)
* msg32: pointer to the 32-byte message to sign (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_sign(
const secp256k1_context* ctx,
unsigned char *adaptor_sig65,
unsigned char *adaptor_proof97,
unsigned char *seckey32,
const secp256k1_pubkey *adaptor,
const unsigned char *msg32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Adaptor verify ("EncVrfy")
*
* Verifies that the adaptor secret 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
* (cannot be NULL)
* In: adaptor_sig65: pointer to 65-byte signature to verify (cannot be NULL)
* pubkey: pointer to the public key (cannot be NULL)
* msg32: pointer to the 32-byte message (cannot be NULL)
* adaptor: pointer to the adaptor point (cannot be NULL)
* adaptor_proof97: pointer to 97-byte adaptor proof (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_sig_verify(
const secp256k1_context* ctx,
const unsigned char *adaptor_sig65,
const secp256k1_pubkey *pubkey,
const unsigned char *msg32,
const secp256k1_pubkey *adaptor,
const unsigned char *adaptor_proof97
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);

/** Adapt signature ("DecSig")
*
* Creates an ECDSA signature from an adaptor signature and an adaptor secret.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object (cannot be NULL)
* Out: sig: pointer to the ecdsa signature to create (cannot
* be NULL)
* In: adaptor_secret32: pointer to 32-byte byte adaptor secret of the adaptor
* point (cannot be NULL)
* adaptor_sig65: pointer to 65-byte byte adaptor sig (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_adapt(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *adaptor_secret32,
const unsigned char *adaptor_sig65
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Adaptor extract ("Rec")
*
* Extracts the adaptor secret from the complete signature and the adaptor
* signature.
*
* Returns: 1 on success, 0 on failure
* Args: ctx: a secp256k1 context object, initialized for signing
* (cannot be NULL)
* Out: adaptor_secret32: pointer to 32-byte adaptor secret of the adaptor point
* (cannot be NULL)
* In: sig: pointer to ecdsa signature to extract the adaptor_secret
* from (cannot be NULL)
* adaptor_sig: pointer to adaptor sig to extract the adaptor_secret
* from (cannot be NULL)
* adaptor: pointer to the adaptor point (cannot be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_extract_secret(
const secp256k1_context* ctx,
unsigned char *adaptor_secret32,
const secp256k1_ecdsa_signature *sig,
const unsigned char *adaptor_sig65,
const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

#ifdef __cplusplus
}
#endif

#endif /* SECP256K1_ADAPTOR_H */
4 changes: 4 additions & 0 deletions src/modules/ecdsa_adaptor/Makefile.am.include
Original file line number Diff line number Diff line change
@@ -0,0 +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
172 changes: 172 additions & 0 deletions src/modules/ecdsa_adaptor/dleq_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#ifndef _SECP256K1_DLEQ_IMPL_H_
#define _SECP256K1_DLEQ_IMPL_H_

/* Remove terminating NUL bytes */
static int algo16_len(const unsigned char *algo16) {
int algo16_len = 16;

/* Remove terminating null bytes */
while (algo16_len > 0 && !algo16[algo16_len - 1]) {
algo16_len--;
}
return algo16_len;
}

/* Modified bip340 nonce function */
static int nonce_function_dleq(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16) {
secp256k1_sha256 sha;

if (algo16 == NULL) {
return 0;
}
secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len(algo16));
secp256k1_sha256_write(&sha, key32, 32);
secp256k1_sha256_write(&sha, msg32, 32);
secp256k1_sha256_finalize(&sha, nonce32);
return 1;
}

static void secp256k1_dleq_serialize_point(unsigned char *buf33, const secp256k1_ge *p) {
secp256k1_fe x = p->x;
secp256k1_fe y = p->y;

secp256k1_fe_normalize(&y);
buf33[0] = secp256k1_fe_is_odd(&y);
secp256k1_fe_normalize(&x);
secp256k1_fe_get_b32(&buf33[1], &x);
}

static int secp256k1_dleq_deserialize_point(secp256k1_ge *p, const unsigned char *buf33) {
secp256k1_fe x;

if (!secp256k1_fe_set_b32(&x, &buf33[1])) {
return 0;
}
if (buf33[0] > 1) {
return 0;
}
secp256k1_ge_set_xo_var(p, &x, buf33[0]);
return 1;
}

/* TODO: Remove these debuggin functions */
static void print_buf(const unsigned char *buf, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
printf("%02X", buf[i]);
}
printf("\n");
}
static void print_scalar(const secp256k1_scalar *x) {
unsigned char buf32[32];
secp256k1_scalar_get_b32(buf32, x);
print_buf(buf32, 32);
}

static void print_ge(const secp256k1_ge *p) {
unsigned char buf33[33];
secp256k1_dleq_serialize_point(buf33, p);
print_buf(buf33, 33);
}

static void secp256k1_dleq_hash_point(secp256k1_sha256 *sha, const secp256k1_ge *p) {
unsigned char buf33[33];
secp256k1_dleq_serialize_point(buf33, p);
secp256k1_sha256_write(sha, buf33, 33);
}

static void secp256k1_dleq_challenge_hash(secp256k1_scalar *e, const unsigned char *algo16, const secp256k1_ge *gen2, const secp256k1_ge *r1, const secp256k1_ge *r2, const secp256k1_ge *p1, const secp256k1_ge *p2) {
secp256k1_sha256 sha;
unsigned char buf32[32];

secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len(algo16));
secp256k1_dleq_hash_point(&sha, gen2);
secp256k1_dleq_hash_point(&sha, r1);
secp256k1_dleq_hash_point(&sha, r2);
secp256k1_dleq_hash_point(&sha, p1);
secp256k1_dleq_hash_point(&sha, p2);
secp256k1_sha256_finalize(&sha, buf32);

secp256k1_scalar_set_b32(e, buf32, NULL);
}

/* p1 = x*G, p2 = x*gen2, constant time */
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);
}

/* TODO: allow signing a message by including it in the challenge hash */
static int secp256k1_dleq_proof(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar *s, secp256k1_scalar *e, const unsigned char *algo16, const secp256k1_scalar *sk, const secp256k1_ge *gen2) {
unsigned char nonce32[32];
unsigned char key32[32];
secp256k1_ge p1, p2;
secp256k1_sha256 sha;
secp256k1_gej r1j, r2j;
secp256k1_ge r1, r2;
unsigned char buf32[32];
secp256k1_scalar k;

secp256k1_dleq_pair(ecmult_gen_ctx, &p1, &p2, sk, gen2);

/* Everything that goes into the challenge hash must go into the nonce as well... */
secp256k1_sha256_initialize(&sha);
secp256k1_dleq_hash_point(&sha, gen2);
secp256k1_dleq_hash_point(&sha, &p1);
secp256k1_dleq_hash_point(&sha, &p2);
secp256k1_sha256_finalize(&sha, buf32);
secp256k1_scalar_get_b32(key32, sk);
if (!nonce_function_dleq(nonce32, buf32, key32, algo16)) {
return 0;
}
secp256k1_scalar_set_b32(&k, nonce32, NULL);
if (secp256k1_scalar_is_zero(&k)) {
return 0;
}

secp256k1_ecmult_gen(ecmult_gen_ctx, &r1j, &k);
secp256k1_ge_set_gej(&r1, &r1j);
secp256k1_ecmult_const(&r2j, gen2, &k, 256);
secp256k1_ge_set_gej(&r2, &r2j);

secp256k1_dleq_challenge_hash(e, algo16, gen2, &r1, &r2, &p1, &p2);
secp256k1_scalar_mul(s, e, sk);
secp256k1_scalar_add(s, s, &k);

secp256k1_scalar_clear(&k);
return 1;
}

static int secp256k1_dleq_verify(const secp256k1_ecmult_context *ecmult_ctx, const unsigned char *algo16, const secp256k1_scalar *s, const secp256k1_scalar *e, const secp256k1_ge *p1, const secp256k1_ge *gen2, const 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_hash(&e_expected, algo16, gen2, &r1, &r2, p1, p2);

secp256k1_scalar_add(&e_expected, &e_expected, &e_neg);
return secp256k1_scalar_is_zero(&e_expected);
}

#endif /* _SECP256K1_DLEQ_IMPL_H_ */
Loading

0 comments on commit 62ee6da

Please sign in to comment.