-
Notifications
You must be signed in to change notification settings - Fork 208
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 an experimental schnorr signature adaptor module #268
Changes from 1 commit
555abe5
e0b87db
ed3e2ae
9735b05
8c9e169
2aa074d
094b863
344620e
bf0b7f4
69a5471
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,16 +11,14 @@ extern "C" { | |
/** A pointer to a function to deterministically generate a nonce. | ||
* | ||
* Same as secp256k1_schnorrsig_nonce function with the exception of accepting an | ||
* additional adaptor point t argument. The adaptor point argument can protect | ||
* signature schemes with key-prefixed challenge hash inputs against reusing the | ||
* nonce when signing with different adaptor points. | ||
* additional adaptor point 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 being verified (will not be NULL) | ||
* key32: pointer to a 32-byte secret key (will not be NULL) | ||
* adaptor: the 33-byte serialized adaptor point (will not be NULL) | ||
* adaptor33: the 33-byte serialized adaptor point (will not be NULL) | ||
* xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32 | ||
* (will not be NULL) | ||
* algo: pointer to an array describing the signature | ||
|
@@ -35,24 +33,15 @@ typedef int (*secp256k1_adaptor_nonce_function_hardened)( | |
unsigned char *nonce32, | ||
const unsigned char *msg32, | ||
const unsigned char *key32, | ||
const unsigned char *adaptor, | ||
const unsigned char *adaptor33, | ||
const unsigned char *xonly_pk32, | ||
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. If the data pointer is NULL, | ||
* the nonce derivation procedure follows BIP-340 by setting the auxiliary | ||
* random data to zero. The algo argument must be non-NULL, otherwise the | ||
* function will fail and return 0. The hash will be tagged with algo. | ||
* Therefore, to create BIP-340 compliant signatures, algo must be set to | ||
* "BIP0340/nonce" and algolen to 13. | ||
*/ | ||
SECP256K1_API const secp256k1_adaptor_nonce_function_hardened secp256k1_adaptor_nonce_function_bip340; | ||
/** A Schnorr Adaptor nonce generation function. */ | ||
SECP256K1_API const secp256k1_adaptor_nonce_function_hardened secp256k1_nonce_function_schnorr_adaptor; | ||
|
||
/** Create a Schnorr adaptor signature. | ||
ZhePang marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to replace the term "adaptor signature" with "pre-signature" to be consistent with the literature (see #268 (comment))? |
||
* | ||
|
@@ -64,49 +53,49 @@ SECP256K1_API const secp256k1_adaptor_nonce_function_hardened secp256k1_adaptor_ | |
* signatures from being valid in multiple contexts by accident. | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
* Args: ctx: pointer to a context object (not secp256k1_context_static). | ||
* Out: presig65: pointer to a 65-byte array to store the serialized adaptor signature. | ||
* In: msg32: the 32-byte message being signed. | ||
* keypair: pointer to an initialized keypair. | ||
* adaptor: pointer to a 33-byte compressed adaptor point. | ||
* aux_rand32: 32 bytes of fresh randomness. While recommended to provide | ||
* this, it is only supplemental to security and can be NULL. A | ||
* NULL argument is treated the same as an all-zero one. See | ||
* BIP-340 "Default Signing" for a full explanation of this | ||
* argument and for guidance if randomness is expensive. | ||
* Args: ctx: pointer to a context object (not secp256k1_context_static). | ||
* Out: presig65: pointer to a 65-byte array to store the adaptor signature. | ||
* In: msg32: the 32-byte message being signed. | ||
* keypair: pointer to an initialized keypair. | ||
* adaptor33: pointer to a 33-byte compressed adaptor point. | ||
* aux_rand32: 32 bytes of fresh randomness. While recommended to provide | ||
* this, it is only supplemental to security and can be NULL. A | ||
* NULL argument is treated the same as an all-zero one. See | ||
* BIP-340 "Default Signing" for a full explanation of this | ||
* argument and for guidance if randomness is expensive. | ||
*/ | ||
SECP256K1_API int secp256k1_schnorr_adaptor_presign( | ||
const secp256k1_context *ctx, | ||
unsigned char *presig65, | ||
const unsigned char *msg32, | ||
const secp256k1_keypair *keypair, | ||
const unsigned char *adaptor, | ||
const unsigned char *adaptor33, | ||
const unsigned char *aux_rand32 | ||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); | ||
|
||
/** Extract an adaptor point T from the signature. | ||
/** Extract an adaptor point from the signature. | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
* Args: ctx: pointer to a context object. | ||
* Out: adaptor: pointer to a 33-byte array to store the compressed adaptor point. | ||
* In: presig65: pointer to a 65-byte serialized adaptor signature. | ||
* msg32: the 32-byte message being signed. | ||
* pubkey: pointer to an x-only public key to verify with (cannot be NULL) | ||
* Args: ctx: pointer to a context object. | ||
* Out: adaptor33: pointer to a 33-byte array to store the compressed adaptor point. | ||
* In: presig65: pointer to a 65-byte adaptor signature. | ||
* msg32: the 32-byte message being signed. | ||
* pubkey: pointer to an x-only public key to verify with | ||
*/ | ||
SECP256K1_API int secp256k1_schnorr_adaptor_extract( | ||
const secp256k1_context *ctx, | ||
unsigned char *adaptor, | ||
unsigned char *adaptor33, | ||
const unsigned char *presig65, | ||
const unsigned char *msg32, | ||
const secp256k1_xonly_pubkey *pubkey | ||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); | ||
|
||
/** Adapt an adaptor signature to a schnorr signature. | ||
/** Adapt an adaptor signature to result in a Schnorr signature. | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
* Args: ctx: pointer to a context object. | ||
* Out: sig64: pointer to a 64-byte array to store the adapted schnorr signature. | ||
* In: presig65: pointer to a 65-byte serialized adaptor signature. | ||
* Out: sig64: pointer to a 64-byte array to store the adapted Schnorr signature. | ||
* In: presig65: pointer to a 65-byte adaptor signature. | ||
* secadaptor: pointer to a 32-byte secadaptor. | ||
*/ | ||
SECP256K1_API int secp256k1_schnorr_adaptor_adapt( | ||
|
@@ -116,13 +105,13 @@ SECP256K1_API int secp256k1_schnorr_adaptor_adapt( | |
const unsigned char *secadaptor | ||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||
|
||
/** Extract the secadaptor from an adaptor signature and a schnorr signature. | ||
/** Extract the secadaptor from a valid adaptor signature and a Schnorr signature. | ||
* | ||
* Returns 1 on success, 0 on failure. | ||
* Args: ctx: pointer to a context object. | ||
* Out: secadaptor: pointer to a 32-byte array to store the secadaptor. | ||
* In: presig65: pointer to a 65-byte serialized adaptor signature. | ||
* sig64: pointer to a 64-byte adapted schnorr signature. | ||
* Args: ctx: pointer to a context object. | ||
* Out: secadaptor: pointer to a 32-byte array to store the secadaptor. | ||
* In: presig65: pointer to a 65-byte adaptor signature. | ||
* sig64: pointer to a 64-byte adapted Schnorr signature. | ||
*/ | ||
SECP256K1_API int secp256k1_schnorr_adaptor_extract_sec( | ||
const secp256k1_context *ctx, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ | |
|
||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying | ||
* SHA256 to SHA256("SchnorrAdaptor/nonce")||SHA256("SchnorrAdaptor/nonce"). */ | ||
static void secp256k1_adaptor_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) { | ||
static void secp256k1_nonce_function_schnorr_adaptor_sha256_tagged(secp256k1_sha256 *sha) { | ||
secp256k1_sha256_initialize(sha); | ||
sha->s[0] = 0xe268ac2aul; | ||
sha->s[1] = 0x3a221b84ul; | ||
|
@@ -29,7 +29,7 @@ static void secp256k1_adaptor_nonce_function_bip340_sha256_tagged(secp256k1_sha2 | |
|
||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying | ||
* SHA256 to SHA256("SchnorrAdaptor/aux")||SHA256("SchnorrAdaptor/aux"). */ | ||
static void secp256k1_adaptor_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) { | ||
static void secp256k1_nonce_function_schnorr_adaptor_sha256_tagged_aux(secp256k1_sha256 *sha) { | ||
secp256k1_sha256_initialize(sha); | ||
sha->s[0] = 0x50685e98ul; | ||
sha->s[1] = 0x6313905eul; | ||
|
@@ -47,7 +47,7 @@ static void secp256k1_adaptor_nonce_function_bip340_sha256_tagged_aux(secp256k1_ | |
* by using the correct tagged hash function. */ | ||
static const unsigned char adaptor_bip340_algo[20] = "SchnorrAdaptor/nonce"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
|
||
static int adaptor_nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *adaptor, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { | ||
static int nonce_function_schnorr_adaptor(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *adaptor33, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { | ||
secp256k1_sha256 sha; | ||
unsigned char masked_key[32]; | ||
int i; | ||
|
@@ -57,7 +57,7 @@ static int adaptor_nonce_function_bip340(unsigned char *nonce32, const unsigned | |
} | ||
|
||
if (data != NULL) { | ||
secp256k1_adaptor_nonce_function_bip340_sha256_tagged_aux(&sha); | ||
secp256k1_nonce_function_schnorr_adaptor_sha256_tagged_aux(&sha); | ||
secp256k1_sha256_write(&sha, data, 32); | ||
secp256k1_sha256_finalize(&sha, masked_key); | ||
for (i = 0; i < 32; i++) { | ||
|
@@ -77,27 +77,26 @@ static int adaptor_nonce_function_bip340(unsigned char *nonce32, const unsigned | |
} | ||
|
||
/* Tag the hash with algo which is important to avoid nonce reuse across | ||
* algorithms. If this nonce function is used in BIP-340 signing as defined | ||
* in the spec, an optimized tagging implementation is used. */ | ||
* algorithms. */ | ||
if (algolen == sizeof(adaptor_bip340_algo) | ||
&& secp256k1_memcmp_var(algo, adaptor_bip340_algo, algolen) == 0) { | ||
secp256k1_adaptor_nonce_function_bip340_sha256_tagged(&sha); | ||
secp256k1_nonce_function_schnorr_adaptor_sha256_tagged(&sha); | ||
} else { | ||
secp256k1_sha256_initialize_tagged(&sha, algo, algolen); | ||
} | ||
|
||
/* Hash masked-key||pk||msg using the tagged hash as per the spec */ | ||
/* Hash masked-key||adaptor33||msg using the tagged hash */ | ||
secp256k1_sha256_write(&sha, masked_key, 32); | ||
secp256k1_sha256_write(&sha, adaptor, 33); | ||
secp256k1_sha256_write(&sha, adaptor33, 33); | ||
secp256k1_sha256_write(&sha, xonly_pk32, 32); | ||
secp256k1_sha256_write(&sha, msg32, 32); | ||
secp256k1_sha256_finalize(&sha, nonce32); | ||
return 1; | ||
} | ||
|
||
const secp256k1_adaptor_nonce_function_hardened secp256k1_adaptor_nonce_function_bip340 = adaptor_nonce_function_bip340; | ||
const secp256k1_adaptor_nonce_function_hardened secp256k1_nonce_function_schnorr_adaptor = nonce_function_schnorr_adaptor; | ||
|
||
static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *ctx, unsigned char *presig65, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_adaptor_nonce_function_hardened noncefp, const unsigned char *adaptor, void *ndata) { | ||
static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *ctx, unsigned char *presig65, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_adaptor_nonce_function_hardened noncefp, const unsigned char *adaptor33, void *ndata) { | ||
secp256k1_scalar sk; | ||
secp256k1_scalar e; | ||
secp256k1_scalar k; | ||
|
@@ -119,10 +118,10 @@ static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *c | |
ARG_CHECK(presig65 != NULL); | ||
ARG_CHECK(msg32 != NULL); | ||
ARG_CHECK(keypair != NULL); | ||
ARG_CHECK(adaptor != NULL); | ||
ARG_CHECK(adaptor33 != NULL); | ||
|
||
if (noncefp == NULL) { | ||
noncefp = secp256k1_adaptor_nonce_function_bip340; | ||
noncefp = secp256k1_nonce_function_schnorr_adaptor; | ||
} | ||
|
||
ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair); | ||
|
@@ -136,7 +135,7 @@ static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *c | |
/* bytes_from_point(P) */ | ||
secp256k1_fe_get_b32(pk_buf, &pk.x); | ||
|
||
ret &= !!noncefp(nonce32, msg32, seckey, adaptor, pk_buf, adaptor_bip340_algo, sizeof(adaptor_bip340_algo), ndata); | ||
ret &= !!noncefp(nonce32, msg32, seckey, adaptor33, pk_buf, adaptor_bip340_algo, sizeof(adaptor_bip340_algo), ndata); | ||
/* k0 */ | ||
secp256k1_scalar_set_b32(&k, nonce32, NULL); | ||
ret &= !secp256k1_scalar_is_zero(&k); | ||
|
@@ -147,7 +146,7 @@ static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *c | |
secp256k1_ge_set_gej(&r, &rj); | ||
|
||
/* T = cpoint(T) */ | ||
ret &= !!secp256k1_eckey_pubkey_parse(&t, adaptor, 33); | ||
ret &= secp256k1_eckey_pubkey_parse(&t, adaptor33, 33); | ||
|
||
/* R' = k*G + T, can use gej_add_ge_var since r and t aren't secret */ | ||
secp256k1_gej_add_ge_var(&r0j, &rj, &t, NULL); | ||
|
@@ -161,7 +160,7 @@ static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *c | |
secp256k1_scalar_negate(&k, &k); | ||
} | ||
|
||
ret &= !!secp256k1_eckey_pubkey_serialize(&r0, presig65, &size, 1); | ||
ret &= secp256k1_eckey_pubkey_serialize(&r0, presig65, &size, 1); | ||
|
||
secp256k1_schnorrsig_challenge(&e, &presig65[1], msg32, msglen, pk_buf); | ||
secp256k1_scalar_mul(&e, &e, &sk); | ||
|
@@ -177,16 +176,15 @@ static int secp256k1_schnorr_adaptor_presign_internal(const secp256k1_context *c | |
return ret; | ||
} | ||
|
||
int secp256k1_schnorr_adaptor_presign(const secp256k1_context *ctx, unsigned char *presig65, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *adaptor, const unsigned char *aux_rand32) { | ||
int secp256k1_schnorr_adaptor_presign(const secp256k1_context *ctx, unsigned char *presig65, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *adaptor33, const unsigned char *aux_rand32) { | ||
/* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */ | ||
return secp256k1_schnorr_adaptor_presign_internal(ctx, presig65, msg32, keypair, secp256k1_adaptor_nonce_function_bip340, adaptor, (unsigned char*)aux_rand32); | ||
return secp256k1_schnorr_adaptor_presign_internal(ctx, presig65, msg32, keypair, secp256k1_nonce_function_schnorr_adaptor, adaptor33, (unsigned char*)aux_rand32); | ||
} | ||
|
||
int secp256k1_schnorr_adaptor_extract(const secp256k1_context *ctx, unsigned char *adaptor, const unsigned char *presig65, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) { | ||
int secp256k1_schnorr_adaptor_extract(const secp256k1_context *ctx, unsigned char *adaptor33, const unsigned char *presig65, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) { | ||
secp256k1_scalar s0; | ||
secp256k1_scalar e; | ||
secp256k1_gej rj; | ||
secp256k1_ge r; | ||
secp256k1_ge pk; | ||
secp256k1_gej pkj; | ||
secp256k1_ge r0; | ||
|
@@ -200,21 +198,21 @@ int secp256k1_schnorr_adaptor_extract(const secp256k1_context *ctx, unsigned cha | |
int ret = 1; | ||
|
||
VERIFY_CHECK(ctx != NULL); | ||
ARG_CHECK(adaptor != NULL); | ||
ARG_CHECK(adaptor33 != NULL); | ||
ARG_CHECK(presig65 != NULL); | ||
ARG_CHECK(msg32 != NULL); | ||
ARG_CHECK(pubkey != NULL); | ||
|
||
ZhePang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/* P */ | ||
ret &= !!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey); | ||
ret &= secp256k1_xonly_pubkey_load(ctx, &pk, pubkey); | ||
|
||
/* s0 */ | ||
secp256k1_scalar_set_b32(&s0, &presig65[33], &overflow); | ||
ret &= !overflow; | ||
|
||
/* R0 */ | ||
ret &= !!secp256k1_xonly_pubkey_parse(ctx, &pkr0, &presig65[1]); | ||
ret &= !!secp256k1_xonly_pubkey_load(ctx, &r0, &pkr0); | ||
ret &= secp256k1_xonly_pubkey_parse(ctx, &pkr0, &presig65[1]); | ||
ret &= secp256k1_xonly_pubkey_load(ctx, &r0, &pkr0); | ||
|
||
/* Compute e */ | ||
secp256k1_fe_get_b32(buf, &pk.x); | ||
|
@@ -226,8 +224,7 @@ int secp256k1_schnorr_adaptor_extract(const secp256k1_context *ctx, unsigned cha | |
secp256k1_ecmult(&rj, &pkj, &e, &s0); | ||
|
||
/* R */ | ||
secp256k1_ge_set_gej_var(&r, &rj); | ||
ret &= !secp256k1_ge_is_infinity(&r); | ||
ret &= !secp256k1_gej_is_infinity(&rj); | ||
|
||
/* T = R0 + (- R) */ | ||
secp256k1_gej_neg(&rj, &rj); | ||
|
@@ -240,9 +237,9 @@ int secp256k1_schnorr_adaptor_extract(const secp256k1_context *ctx, unsigned cha | |
ret = 0; | ||
} | ||
secp256k1_ge_set_gej(&t, &tj); | ||
ret &= !!secp256k1_eckey_pubkey_serialize(&t, adaptor, &size, 1); | ||
ret &= secp256k1_eckey_pubkey_serialize(&t, adaptor33, &size, 1); | ||
|
||
secp256k1_memczero(adaptor, 33, !ret); | ||
secp256k1_memczero(adaptor33, 33, !ret); | ||
|
||
return ret; | ||
} | ||
|
@@ -268,12 +265,10 @@ int secp256k1_schnorr_adaptor_adapt(const secp256k1_context *ctx, unsigned char | |
secp256k1_scalar_set_b32(&t, secadaptor, &overflow); | ||
ret &= !overflow; | ||
|
||
if (presig65[0] == SECP256K1_TAG_PUBKEY_EVEN) { | ||
secp256k1_scalar_add(&s, &s0, &t); | ||
} else if (presig65[0] == SECP256K1_TAG_PUBKEY_ODD) { | ||
if (presig65[0] == SECP256K1_TAG_PUBKEY_ODD) { | ||
secp256k1_scalar_negate(&t, &t); | ||
secp256k1_scalar_add(&s, &s0, &t); | ||
} | ||
secp256k1_scalar_add(&s, &s0, &t); | ||
|
||
memcpy(sig64, &presig65[1], 32); | ||
secp256k1_scalar_get_b32(&sig64[32], &s); | ||
|
@@ -307,11 +302,10 @@ int secp256k1_schnorr_adaptor_extract_sec(const secp256k1_context *ctx, unsigned | |
|
||
if (presig65[0] == SECP256K1_TAG_PUBKEY_EVEN) { | ||
secp256k1_scalar_negate(&s0, &s0); | ||
secp256k1_scalar_add(&t, &s, &s0); | ||
} else if (presig65[0] == SECP256K1_TAG_PUBKEY_ODD) { | ||
secp256k1_scalar_negate(&s, &s); | ||
secp256k1_scalar_add(&t, &s0, &s); | ||
} | ||
secp256k1_scalar_add(&t, &s0, &s); | ||
|
||
secp256k1_scalar_get_b32(secadaptor, &t); | ||
secp256k1_memczero(secadaptor, 32, !ret); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we could just remove the reference to BIP340, instead of the whole paragraph. Something like:
A Schnorr Adaptor nonce generation function.
If a data pointer is passed, it is assumed to be a pointer to 32 bytes of auxiliary random data. If the data pointer is NULL, the nonce derivation procedure sets the auxiliary random data to zero. The algo argument must be non-NULL, otherwise the function will fail and return 0. The hash will be tagged with algo. Therefore, algo must be set to "SchnorrAdaptor/nonce" and algolen to 20.