Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9069791
add ecdsa_adaptor module
jonasnick Apr 3, 2020
ba79603
Move R from adaptor_proof to adaptor_sig and fix args of _adapt
jonasnick Apr 4, 2020
20ce832
Add dleq proof and dleq verify
jonasnick Apr 4, 2020
05d2374
Move dleq things in own file and add adaptor sign
jonasnick Apr 4, 2020
d5000c1
Add ecdsa_adaptor_sig_verify
jonasnick Apr 4, 2020
15485af
Add ecdsa_adaptor_adapt
jonasnick Apr 4, 2020
279f1d5
add ecdsa_adaptor_extract_secret
jonasnick Apr 5, 2020
b8efd90
cleanup: stricter input validation
jonasnick Apr 5, 2020
14451dd
Cleanup a few more things
jonasnick Apr 5, 2020
15c73fc
Fix adaptor_sig_verify: should compare scalars and not fe's
jonasnick Apr 5, 2020
1221681
Clean up docs and reenable tests
jonasnick Apr 5, 2020
89f738d
Make ecdsa_adaptor_sig_verify compare full points instead of only x c…
jonasnick Apr 5, 2020
e7441bd
Add gen2 to dleq challenge and nonce function
jonasnick Apr 5, 2020
cf08619
Update TODOs
jonasnick Apr 5, 2020
39a8065
Update docs
jonasnick Apr 5, 2020
e58add3
Fix dleq tag by removing trailing NUL bytes
jonasnick Aug 3, 2020
ebb606e
Fix not checking return value of dleq_proof in ecdsa_adaptor_sign
jonasnick Aug 3, 2020
7c37bb4
Fix dleq tag for challenge hash too
jonasnick Aug 8, 2020
37a3452
Fix not checking the return value of secp256k1_ge_set_xo_var
jonasnick Sep 4, 2020
6c47844
Return 0 in ecdsa_adaptor_extract_secret if the sigs are unrelated
jonasnick Sep 4, 2020
8c0c32f
Fix merge
jesseposner Dec 7, 2020
c5beec2
Fix merge
jesseposner Dec 11, 2020
3246e15
Revise serialization to match latest spec
jesseposner Dec 11, 2020
a8ae212
Add spec test vector
jesseposner Dec 11, 2020
0e5c845
Remove cruft
jesseposner Dec 11, 2020
1c82382
Fix variable name
jesseposner Dec 23, 2020
9ecedf1
Add secp256k1_ecdsa_adaptor_adapt test
jesseposner Dec 23, 2020
420a946
Add secp256k1_ecdsa_adaptor_extract_secret test
jesseposner Dec 23, 2020
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
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,7 @@ 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
13 changes: 13 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ 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 @@ -447,6 +452,12 @@ if test x"$enable_module_extrakeys" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
fi

if test x"$enable_module_ecdsa_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
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
fi
Expand Down Expand Up @@ -489,6 +500,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 @@ -511,6 +523,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
126 changes: 126 additions & 0 deletions include/secp256k1_ecdsa_adaptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#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_sig162: pointer to 162 byte to store the returned signature
* (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_sig162,
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_sig162: pointer to 162-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)
*/
SECP256K1_API int secp256k1_ecdsa_adaptor_sig_verify(
const secp256k1_context* ctx,
const unsigned char *adaptor_sig162,
const secp256k1_pubkey *pubkey,
const unsigned char *msg32,
const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** 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_sig162: pointer to 162-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_sig162
) 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_sig162,
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
151 changes: 151 additions & 0 deletions src/modules/ecdsa_adaptor/dleq_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#ifndef _SECP256K1_DLEQ_IMPL_H_
#define _SECP256K1_DLEQ_IMPL_H_

/* Remove terminating NUL bytes */
static int algo33_len(const unsigned char *algo33) {
int algo33_len = 33;

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

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

if (algo33 == NULL) {
return 0;
}
secp256k1_sha256_initialize_tagged(&sha, algo33, algo33_len(algo33));
secp256k1_sha256_write(&sha, key32, 32);
secp256k1_sha256_write(&sha, msg32, 32);
secp256k1_sha256_finalize(&sha, nonce32);
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(secp256k1_ge *p) {
unsigned char buf33[33];
size_t size = 33;
secp256k1_eckey_pubkey_serialize(p, buf33, &size, 1);
print_buf(buf33, 33);
}

static void secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) {
unsigned char buf33[33];
size_t size = 33;
secp256k1_eckey_pubkey_serialize(p, buf33, &size, 1);
secp256k1_sha256_write(sha, buf33, 33);
}

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

secp256k1_sha256_initialize_tagged(&sha, algo33, algo33_len(algo33));
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, 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 *algo33, const secp256k1_scalar *sk, 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, algo33)) {
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, algo33, 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 *algo33, 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_hash(&e_expected, algo33, 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