forked from bitcoin-core/secp256k1
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
9 changed files
with
800 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ */ |
Oops, something went wrong.