Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ecdsa_adaptor module #14

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,8 @@ endif
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif

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

12 changes: 12 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=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 @@ -492,6 +498,10 @@ if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi

if test x"$enable_module_ecdsa_adaptor" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDSA_ADAPTOR, 1, [Define this symbol to enable the ECDSA adaptor module])
fi

AC_C_BIGENDIAN()

if test x"$use_external_asm" = x"yes"; then
Expand Down Expand Up @@ -530,6 +540,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = 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 @@ -549,6 +560,7 @@ echo " with benchmarks = $use_benchmark"
echo " with coverage = $enable_coverage"
echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
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 */
13 changes: 13 additions & 0 deletions src/hash_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out
memcpy(out32, (const unsigned char*)out, 32);
}

/* Initializes a sha256 struct and writes the 64 byte string
* SHA256(tag)||SHA256(tag) into it. */
static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) {
unsigned char buf[32];
secp256k1_sha256_initialize(hash);
secp256k1_sha256_write(hash, tag, taglen);
secp256k1_sha256_finalize(hash, buf);

secp256k1_sha256_initialize(hash);
secp256k1_sha256_write(hash, buf, 32);
secp256k1_sha256_write(hash, buf, 32);
}

static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
size_t n;
unsigned char rkey[64];
Expand Down
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
171 changes: 171 additions & 0 deletions src/modules/ecdsa_adaptor/dleq_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#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;
}
return secp256k1_ge_set_xo_var(p, &x, buf33[0]);
}

/* 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