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 an experimental schnorr signature adaptor module #268

Closed
3 changes: 2 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ fi
if test x"$enable_module_schnorr_adaptor" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_SCHNORR_ADAPTOR, 1, [Define thsi symbol to enable the Schnorr adaptor module])
enable_module_extrakeys=yes
enable_module_schnorrsig=yes
fi

if test x"$enable_module_ellswift" = x"yes"; then
Expand Down Expand Up @@ -567,7 +568,7 @@ else
AC_MSG_ERROR([ARM32 assembly optimization is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_schnorr_adaptor" = x"yes"; then
AC_MSG_ERROR([schnorr adaptor signatures module is experimental. Use --enable-experimental to allow.])
AC_MSG_ERROR([Schnorr adaptor signatures module is experimental. Use --enable-experimental to allow.])
fi
fi

Expand Down
73 changes: 31 additions & 42 deletions include/secp256k1_schnorr_adaptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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. */
Copy link
Contributor

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.

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
Copy link
Contributor

Choose a reason for hiding this comment

The 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))?

*
Expand All @@ -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(
Expand All @@ -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,
Expand Down
62 changes: 28 additions & 34 deletions src/modules/schnorr_adaptor/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: adaptor_bip340_algo -> adaptor_algo


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;
Expand All @@ -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++) {
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading