musig: Re-add adaptor signatures support

This commit is contained in:
mllwchrry
2026-02-13 14:03:05 +02:00
parent 248358f2bc
commit 8d443b8030
9 changed files with 401 additions and 21 deletions

View File

@@ -18,6 +18,7 @@ Therefore, users of the musig module must take great care to make sure of the fo
See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`.
3. Opaque data structures are never written to or read from directly.
Instead, only the provided accessor functions are used.
4. If adaptor signatures are used, all partial signatures are verified.
## Key Aggregation and (Taproot) Tweaking
@@ -52,3 +53,14 @@ Similarly, the API supports an alternative protocol flow where generating the ag
## Verification
A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7.
# Atomic Swaps
The signing API supports the production of "adaptor signatures", modified partial signatures
which are offset by an auxiliary secret known to one party. That is,
1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`.
2. When calling `secp256k1_musig_nonce_process`, the public adaptor `T` is provided as the `adaptor` argument.
3. The party who is going to extract the secret adaptor `t` later must verify all partial signatures.
4. Due to step 2, the signature output of `secp256k1_musig_partial_sig_agg` is a pre-signature and not a valid Schnorr signature. All parties involved extract this session's `nonce_parity` with `secp256k1_musig_nonce_parity`.
5. The party who knows `t` must "adapt" the pre-signature with `t` (and the `nonce_parity` using `secp256k1_musig_adapt` to complete the signature.
6. Any party who sees both the final signature and the pre-signature (and has the `nonce_parity`) can extract `t` with `secp256k1_musig_extract_adaptor`.

View File

@@ -139,7 +139,7 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr
/* Every signer creates a partial signature */
for (i = 0; i < N_SIGNERS; i++) {
/* Initialize the signing session by processing the aggregate nonce */
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache)) {
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache, NULL)) {
return 0;
}
/* partial_sign will clear the secnonce by setting it to 0. That's because

View File

@@ -16,7 +16,7 @@ extern "C" {
* v1.0.0. You can find an example demonstrating the musig module in
* examples/musig.c.
*
* The module also supports BIP 341 ("Taproot") public key tweaking.
* The module also supports BIP 341 ("Taproot") public key tweaking and adaptor signatures.
*
* It is recommended to read the documentation in this include file carefully.
* Further notes on API usage can be found in doc/musig.md
@@ -462,6 +462,11 @@ SECP256K1_API int secp256k1_musig_nonce_agg(
/** Takes the aggregate nonce and creates a session that is required for signing
* and verification of partial signatures.
*
* If the adaptor argument is non-NULL, then the output of
* musig_partial_sig_agg will be a pre-signature which is not a valid Schnorr
* signature. In order to create a valid signature, the pre-signature and the
* secret adaptor must be provided to `musig_adapt`.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: session: pointer to a struct to store the session
@@ -470,13 +475,17 @@ SECP256K1_API int secp256k1_musig_nonce_agg(
* msg32: the 32-byte message to sign
* keyagg_cache: pointer to the keyagg_cache that was used to create the
* aggregate (and potentially tweaked) pubkey
* adaptor: optional pointer to an adaptor point encoded as a public
* key if this signing session is part of an adaptor
* signature protocol (can be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_nonce_process(
const secp256k1_context *ctx,
secp256k1_musig_session *session,
const secp256k1_musig_aggnonce *aggnonce,
const unsigned char *msg32,
const secp256k1_musig_keyagg_cache *keyagg_cache
const secp256k1_musig_keyagg_cache *keyagg_cache,
const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Produces a partial signature
@@ -534,6 +543,7 @@ SECP256K1_API int secp256k1_musig_partial_sign(
* before aggregating it with `musig_nonce_agg` and using the result to
* create the `session` with `musig_nonce_process`.
*
* This function is essential when using protocols with adaptor signatures.
* It is not required to call this function in regular MuSig sessions, because
* if any partial signature does not verify, the final signature will not
* verify either, so the problem will be caught. However, this function
@@ -581,6 +591,77 @@ SECP256K1_API int secp256k1_musig_partial_sig_agg(
size_t n_sigs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Extracts the nonce_parity bit from a session
*
* This is used for adaptor signatures.
*
* Returns: 0 if the arguments are invalid, 1 otherwise
* Args: ctx: pointer to a context object
* Out: nonce_parity: pointer to an integer that indicates the parity
* of the aggregate public nonce. Used for adaptor
* signatures.
* In: session: pointer to the session that was created with
* musig_nonce_process
*/
SECP256K1_API int secp256k1_musig_nonce_parity(
const secp256k1_context *ctx,
int *nonce_parity,
const secp256k1_musig_session *session
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Creates a signature from a pre-signature and an adaptor.
*
* If the sec_adaptor32 argument is incorrect, the output signature will be
* invalid. This function does not verify the signature.
*
* Returns: 0 if the arguments are invalid, or pre_sig64 or sec_adaptor32 contain
* invalid (overflowing) values. 1 otherwise (which does NOT mean the
* signature or the adaptor are valid!)
* Args: ctx: pointer to a context object
* Out: sig64: 64-byte signature. This pointer may point to the same
* memory area as `pre_sig`.
* In: pre_sig64: 64-byte pre-signature
* sec_adaptor32: 32-byte secret adaptor to add to the pre-signature
* nonce_parity: the output of `musig_nonce_parity` called with the
* session used for producing the pre-signature
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_adapt(
const secp256k1_context *ctx,
unsigned char *sig64,
const unsigned char *pre_sig64,
const unsigned char *sec_adaptor32,
int nonce_parity
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Extracts a secret adaptor from a MuSig pre-signature and corresponding
* signature
*
* This function will not fail unless given grossly invalid data; if it is
* merely given signatures that do not verify, the returned value will be
* nonsense. It is therefore important that all data be verified at earlier
* steps of any protocol that uses this function. In particular, this includes
* verifying all partial signatures that were aggregated into pre_sig64.
*
* Returns: 0 if the arguments are NULL, or sig64 or pre_sig64 contain
* grossly invalid (overflowing) values. 1 otherwise (which does NOT
* mean the signatures or the adaptor are valid!)
* Args: ctx: pointer to a context object
* Out:sec_adaptor32: 32-byte secret adaptor
* In: sig64: complete, valid 64-byte signature
* pre_sig64: the pre-signature corresponding to sig64, i.e., the
* aggregate of partial signatures without the secret
* adaptor
* nonce_parity: the output of `musig_nonce_parity` called with the
* session used for producing sig64
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_adaptor(
const secp256k1_context *ctx,
unsigned char *sec_adaptor32,
const unsigned char *sig64,
const unsigned char *pre_sig64,
int nonce_parity
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
}
#endif

View File

@@ -207,7 +207,12 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
secp256k1_musig_keyagg_cache cache;
secp256k1_musig_session session;
secp256k1_musig_partial_sig partial_sig;
const secp256k1_musig_partial_sig *partial_sig_ptr[1];
unsigned char extra_input[32];
unsigned char sec_adaptor[32];
secp256k1_pubkey adaptor;
unsigned char pre_sig[64];
int nonce_parity;
pk_ptr[0] = &pk;
pubnonce_ptr[0] = &pubnonce;
@@ -216,14 +221,19 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
session_secrand[0] = session_secrand[0] + 1;
memcpy(extra_input, key, sizeof(extra_input));
extra_input[0] = extra_input[0] + 2;
memcpy(sec_adaptor, key, sizeof(sec_adaptor));
sec_adaptor[0] = extra_input[0] + 3;
partial_sig_ptr[0] = &partial_sig;
CHECK(secp256k1_keypair_create(ctx, &keypair, key));
CHECK(secp256k1_keypair_pub(ctx, &pk, &keypair));
CHECK(secp256k1_musig_pubkey_agg(ctx, &agg_pk, &cache, pk_ptr, 1));
CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor));
SECP256K1_CHECKMEM_UNDEFINE(key, 32);
SECP256K1_CHECKMEM_UNDEFINE(session_secrand, sizeof(session_secrand));
SECP256K1_CHECKMEM_UNDEFINE(extra_input, sizeof(extra_input));
SECP256K1_CHECKMEM_UNDEFINE(sec_adaptor, sizeof(sec_adaptor));
ret = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_secrand, key, &pk, msg, &cache, extra_input);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
@@ -234,7 +244,7 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
CHECK(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonce_ptr, 1));
/* Make sure that previous tests don't undefine msg. It's not used as a secret here. */
SECP256K1_CHECKMEM_DEFINE(msg, sizeof(msg));
CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache) == 1);
CHECK(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg, &cache, &adaptor) == 1);
ret = secp256k1_keypair_create(ctx, &keypair, key);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
@@ -242,6 +252,18 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) {
ret = secp256k1_musig_partial_sign(ctx, &partial_sig, &secnonce, &keypair, &cache, &session);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
SECP256K1_CHECKMEM_DEFINE(&partial_sig, sizeof(partial_sig));
CHECK(secp256k1_musig_partial_sig_agg(ctx, pre_sig, &session, partial_sig_ptr, 1));
SECP256K1_CHECKMEM_DEFINE(pre_sig, sizeof(pre_sig));
CHECK(secp256k1_musig_nonce_parity(ctx, &nonce_parity, &session));
ret = secp256k1_musig_adapt(ctx, sig, pre_sig, sec_adaptor, nonce_parity);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
ret = secp256k1_musig_extract_adaptor(ctx, sec_adaptor, sig, pre_sig, nonce_parity);
SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
CHECK(ret == 1);
}
#endif

View File

@@ -4,5 +4,6 @@ noinst_HEADERS += src/modules/musig/keyagg.h
noinst_HEADERS += src/modules/musig/keyagg_impl.h
noinst_HEADERS += src/modules/musig/session.h
noinst_HEADERS += src/modules/musig/session_impl.h
noinst_HEADERS += src/modules/musig/adaptor_impl.h
noinst_HEADERS += src/modules/musig/tests_impl.h
noinst_HEADERS += src/modules/musig/vectors.h

View File

@@ -0,0 +1,101 @@
/***********************************************************************
* Copyright (c) 2021 Jonas Nick *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#ifndef SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H
#define SECP256K1_MODULE_MUSIG_ADAPTOR_IMPL_H
#include <string.h>
#include "../../../include/secp256k1.h"
#include "../../../include/secp256k1_musig.h"
#include "session.h"
#include "../../scalar.h"
int secp256k1_musig_nonce_parity(const secp256k1_context* ctx, int *nonce_parity, const secp256k1_musig_session *session) {
secp256k1_musig_session_internal session_i;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(nonce_parity != NULL);
ARG_CHECK(session != NULL);
if (!secp256k1_musig_session_load(ctx, &session_i, session)) {
return 0;
}
*nonce_parity = session_i.fin_nonce_parity;
return 1;
}
int secp256k1_musig_adapt(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *pre_sig64, const unsigned char *sec_adaptor32, int nonce_parity) {
secp256k1_scalar s;
secp256k1_scalar t;
int overflow;
int ret = 1;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(pre_sig64 != NULL);
ARG_CHECK(sec_adaptor32 != NULL);
ARG_CHECK(nonce_parity == 0 || nonce_parity == 1);
secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow);
if (overflow) {
return 0;
}
secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow);
ret &= !overflow;
/* Determine if the secret adaptor should be negated.
*
* The musig_session stores the X-coordinate and the parity of the "final nonce"
* (r + t)*G, where r*G is the aggregate public nonce and t is the secret adaptor.
*
* Since a BIP340 signature requires an x-only public nonce, in the case where
* (r + t)*G has odd Y-coordinate (i.e. nonce_parity == 1), the x-only public nonce
* corresponding to the signature is actually (-r - t)*G. Thus adapting a
* pre-signature requires negating t in this case.
*/
if (nonce_parity) {
secp256k1_scalar_negate(&t, &t);
}
secp256k1_scalar_add(&s, &s, &t);
secp256k1_scalar_get_b32(&sig64[32], &s);
memmove(sig64, pre_sig64, 32);
secp256k1_scalar_clear(&t);
return ret;
}
int secp256k1_musig_extract_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const unsigned char *pre_sig64, int nonce_parity) {
secp256k1_scalar t;
secp256k1_scalar s;
int overflow;
int ret = 1;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sec_adaptor32 != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(pre_sig64 != NULL);
ARG_CHECK(nonce_parity == 0 || nonce_parity == 1);
secp256k1_scalar_set_b32(&t, &sig64[32], &overflow);
ret &= !overflow;
secp256k1_scalar_negate(&t, &t);
secp256k1_scalar_set_b32(&s, &pre_sig64[32], &overflow);
if (overflow) {
return 0;
}
secp256k1_scalar_add(&t, &t, &s);
if (!nonce_parity) {
secp256k1_scalar_negate(&t, &t);
}
secp256k1_scalar_get_b32(sec_adaptor32, &t);
secp256k1_scalar_clear(&t);
return ret;
}
#endif

View File

@@ -8,5 +8,6 @@
#include "keyagg_impl.h"
#include "session_impl.h"
#include "adaptor_impl.h"
#endif

View File

@@ -608,7 +608,7 @@ static void secp256k1_musig_nonce_process_internal(int *fin_nonce_parity, unsign
*fin_nonce_parity = secp256k1_fe_is_odd(&fin_nonce_pt.y);
}
int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache) {
int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_aggnonce *aggnonce, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const secp256k1_pubkey *adaptor) {
secp256k1_keyagg_cache_internal cache_i;
secp256k1_ge aggnonce_pts[2];
unsigned char fin_nonce[32];
@@ -630,6 +630,18 @@ int secp256k1_musig_nonce_process(const secp256k1_context* ctx, secp256k1_musig_
return 0;
}
/* Add public adaptor to nonce */
if (adaptor != NULL) {
secp256k1_ge adaptorp;
secp256k1_gej tmp;
if (!secp256k1_pubkey_load(ctx, &adaptorp, adaptor)) {
return 0;
}
secp256k1_gej_set_ge(&tmp, &aggnonce_pts[0]);
secp256k1_gej_add_ge_var(&tmp, &tmp, &adaptorp, NULL);
secp256k1_ge_set_gej(&aggnonce_pts[0], &tmp);
}
secp256k1_musig_nonce_process_internal(&session_i.fin_nonce_parity, fin_nonce, &session_i.noncecoef, aggnonce_pts, agg_pk32, msg32);
secp256k1_schnorrsig_challenge(&session_i.challenge, fin_nonce, msg32, 32, agg_pk32);

View File

@@ -34,7 +34,7 @@ static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_pubkey *p
return ret;
}
/* Just a simple (non-tweaked) 2-of-2 MuSig aggregate, sign, verify
/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig aggregate, sign, verify
* test. */
static void musig_simple_test(void) {
unsigned char sk[2][32];
@@ -74,7 +74,7 @@ static void musig_simple_test(void) {
CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1);
CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1);
for (i = 0; i < 2; i++) {
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[i], &secnonce[i], &keypair[i], &keyagg_cache, &session) == 1);
@@ -123,6 +123,7 @@ static void musig_api_tests(void) {
const secp256k1_musig_partial_sig *partial_sig_ptr[2];
secp256k1_musig_partial_sig invalid_partial_sig;
const secp256k1_musig_partial_sig *invalid_partial_sig_ptr[2];
unsigned char final_sig[64];
unsigned char pre_sig[64];
unsigned char buf[32];
unsigned char sk[2][32];
@@ -157,6 +158,10 @@ static void musig_api_tests(void) {
const secp256k1_pubkey *invalid_pk_ptr2[2];
const secp256k1_pubkey *invalid_pk_ptr3[3];
unsigned char tweak[32];
int nonce_parity;
unsigned char sec_adaptor[32];
unsigned char sec_adaptor1[32];
secp256k1_pubkey adaptor;
int i;
/** setup **/
@@ -174,8 +179,10 @@ static void musig_api_tests(void) {
memset(&invalid_pubnonce, 0, sizeof(invalid_pubnonce));
memset(&invalid_session, 0, sizeof(invalid_session));
testrand256(sec_adaptor);
testrand256(msg);
testrand256(tweak);
CHECK(secp256k1_ec_pubkey_create(CTX, &adaptor, sec_adaptor) == 1);
for (i = 0; i < 2; i++) {
pk_ptr[i] = &pk[i];
invalid_pk_ptr2[i] = &invalid_pk;
@@ -388,15 +395,17 @@ static void musig_api_tests(void) {
}
/** Process nonces **/
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1);
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1);
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, NULL, &aggnonce, msg, &keyagg_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, NULL, msg, &keyagg_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, (secp256k1_musig_aggnonce*) &invalid_pubnonce, msg, &keyagg_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, NULL, &keyagg_cache, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, NULL, &adaptor));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &invalid_keyagg_cache, &adaptor));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, NULL) == 1);
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, (secp256k1_pubkey *)&invalid_pk));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, &keyagg_cache, &adaptor) == 1);
memcpy(&secnonce_tmp, &secnonce[0], sizeof(secnonce_tmp));
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce_tmp, &keypair[0], &keyagg_cache, &session) == 1);
@@ -481,6 +490,40 @@ static void musig_api_tests(void) {
CHECK_ILLEGAL(CTX, secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 0));
CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 1) == 1);
CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig, &session, partial_sig_ptr, 2) == 1);
/** Adaptor signature verification */
CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity, &session) == 1);
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, NULL, &session));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, NULL));
CHECK_ILLEGAL(CTX, secp256k1_musig_nonce_parity(CTX, &nonce_parity, &invalid_session));
CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1);
CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, NULL, pre_sig, sec_adaptor, 0));
CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, NULL, sec_adaptor, 0));
CHECK(secp256k1_musig_adapt(CTX, final_sig, max64, sec_adaptor, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, NULL, 0));
CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, max64, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, 2));
/* sig and pre_sig argument point to the same location */
memcpy(final_sig, pre_sig, sizeof(final_sig));
CHECK(secp256k1_musig_adapt(CTX, final_sig, final_sig, sec_adaptor, nonce_parity) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1);
CHECK(secp256k1_musig_adapt(CTX, final_sig, pre_sig, sec_adaptor, nonce_parity) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig, msg, sizeof(msg), &agg_pk) == 1);
/** Secret adaptor can be extracted from signature */
CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, nonce_parity) == 1);
CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) == 0);
/* wrong nonce parity */
CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, !nonce_parity) == 1);
CHECK(secp256k1_memcmp_var(sec_adaptor, sec_adaptor1, 32) != 0);
CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, NULL, final_sig, pre_sig, 0));
CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, NULL, pre_sig, 0));
CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, max64, pre_sig, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, NULL, 0));
CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, max64, 0) == 0);
CHECK_ILLEGAL(CTX, secp256k1_musig_extract_adaptor(CTX, sec_adaptor1, final_sig, pre_sig, 2));
}
static void musig_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
@@ -548,6 +591,111 @@ static void musig_nonce_test(void) {
}
}
static void scriptless_atomic_swap(void) {
/* Throughout this test "a" and "b" refer to two hypothetical blockchains,
* while the indices 0 and 1 refer to the two signers. Here signer 0 is
* sending a-coins to signer 1, while signer 1 is sending b-coins to signer
* 0. Signer 0 produces the adaptor signatures. */
unsigned char pre_sig_a[64];
unsigned char final_sig_a[64];
unsigned char pre_sig_b[64];
unsigned char final_sig_b[64];
secp256k1_musig_partial_sig partial_sig_a[2];
const secp256k1_musig_partial_sig *partial_sig_a_ptr[2];
secp256k1_musig_partial_sig partial_sig_b[2];
const secp256k1_musig_partial_sig *partial_sig_b_ptr[2];
unsigned char sec_adaptor[32];
unsigned char sec_adaptor_extracted[32];
secp256k1_pubkey pub_adaptor;
unsigned char sk_a[2][32];
unsigned char sk_b[2][32];
secp256k1_keypair keypair_a[2];
secp256k1_keypair keypair_b[2];
secp256k1_pubkey pk_a[2];
const secp256k1_pubkey *pk_a_ptr[2];
secp256k1_pubkey pk_b[2];
const secp256k1_pubkey *pk_b_ptr[2];
secp256k1_musig_keyagg_cache keyagg_cache_a;
secp256k1_musig_keyagg_cache keyagg_cache_b;
secp256k1_xonly_pubkey agg_pk_a;
secp256k1_xonly_pubkey agg_pk_b;
secp256k1_musig_secnonce secnonce_a[2];
secp256k1_musig_secnonce secnonce_b[2];
secp256k1_musig_pubnonce pubnonce_a[2];
secp256k1_musig_pubnonce pubnonce_b[2];
const secp256k1_musig_pubnonce *pubnonce_ptr_a[2];
const secp256k1_musig_pubnonce *pubnonce_ptr_b[2];
secp256k1_musig_aggnonce aggnonce_a;
secp256k1_musig_aggnonce aggnonce_b;
secp256k1_musig_session session_a, session_b;
int nonce_parity_a;
int nonce_parity_b;
unsigned char seed_a[2][32] = { "a0", "a1" };
unsigned char seed_b[2][32] = { "b0", "b1" };
const unsigned char msg32_a[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'a'};
const unsigned char msg32_b[32] = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 'b', 'l', 'o', 'c', 'k', 'c', 'h', 'a', 'i', 'n', ' ', 'b'};
int i;
/* Step 1: key setup */
for (i = 0; i < 2; i++) {
pk_a_ptr[i] = &pk_a[i];
pk_b_ptr[i] = &pk_b[i];
pubnonce_ptr_a[i] = &pubnonce_a[i];
pubnonce_ptr_b[i] = &pubnonce_b[i];
partial_sig_a_ptr[i] = &partial_sig_a[i];
partial_sig_b_ptr[i] = &partial_sig_b[i];
testrand256(sk_a[i]);
testrand256(sk_b[i]);
CHECK(create_keypair_and_pk(&keypair_a[i], &pk_a[i], sk_a[i]) == 1);
CHECK(create_keypair_and_pk(&keypair_b[i], &pk_b[i], sk_b[i]) == 1);
}
testrand256(sec_adaptor);
CHECK(secp256k1_ec_pubkey_create(CTX, &pub_adaptor, sec_adaptor) == 1);
CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk_a, &keyagg_cache_a, pk_a_ptr, 2) == 1);
CHECK(secp256k1_musig_pubkey_agg(CTX, &agg_pk_b, &keyagg_cache_b, pk_b_ptr, 2) == 1);
CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[0], &pubnonce_a[0], seed_a[0], sk_a[0], &pk_a[0], NULL, NULL, NULL) == 1);
CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_a[1], &pubnonce_a[1], seed_a[1], sk_a[1], &pk_a[1], NULL, NULL, NULL) == 1);
CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[0], &pubnonce_b[0], seed_b[0], sk_b[0], &pk_b[0], NULL, NULL, NULL) == 1);
CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce_b[1], &pubnonce_b[1], seed_b[1], sk_b[1], &pk_b[1], NULL, NULL, NULL) == 1);
/* Step 2: Exchange nonces */
CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_a, pubnonce_ptr_a, 2) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session_a, &aggnonce_a, msg32_a, &keyagg_cache_a, &pub_adaptor) == 1);
CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_a, &session_a) == 1);
CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce_b, pubnonce_ptr_b, 2) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session_b, &aggnonce_b, msg32_b, &keyagg_cache_b, &pub_adaptor) == 1);
CHECK(secp256k1_musig_nonce_parity(CTX, &nonce_parity_b, &session_b) == 1);
/* Step 3: Signer 0 produces partial signatures for both chains. */
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[0], &secnonce_a[0], &keypair_a[0], &keyagg_cache_a, &session_a) == 1);
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[0], &secnonce_b[0], &keypair_b[0], &keyagg_cache_b, &session_b) == 1);
/* Step 4: Signer 1 receives partial signatures, verifies them and creates a
* partial signature to send B-coins to signer 0. */
CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_a[0], &pubnonce_a[0], &pk_a[0], &keyagg_cache_a, &session_a) == 1);
CHECK(secp256k1_musig_partial_sig_verify(CTX, &partial_sig_b[0], &pubnonce_b[0], &pk_b[0], &keyagg_cache_b, &session_b) == 1);
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_b[1], &secnonce_b[1], &keypair_b[1], &keyagg_cache_b, &session_b) == 1);
/* Step 5: Signer 0 aggregates its own partial signature with the partial
* signature from signer 1 and adapts it. This results in a complete
* signature which is broadcasted by signer 0 to take B-coins. */
CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_b, &session_b, partial_sig_b_ptr, 2) == 1);
CHECK(secp256k1_musig_adapt(CTX, final_sig_b, pre_sig_b, sec_adaptor, nonce_parity_b) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_b, msg32_b, sizeof(msg32_b), &agg_pk_b) == 1);
/* Step 6: Signer 1 signs, extracts adaptor from the published signature,
* and adapts the signature to take A-coins. */
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig_a[1], &secnonce_a[1], &keypair_a[1], &keyagg_cache_a, &session_a) == 1);
CHECK(secp256k1_musig_partial_sig_agg(CTX, pre_sig_a, &session_a, partial_sig_a_ptr, 2) == 1);
CHECK(secp256k1_musig_extract_adaptor(CTX, sec_adaptor_extracted, final_sig_b, pre_sig_b, nonce_parity_b) == 1);
CHECK(secp256k1_memcmp_var(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */
CHECK(secp256k1_musig_adapt(CTX, final_sig_a, pre_sig_a, sec_adaptor_extracted, nonce_parity_a) == 1);
CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1);
}
static void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) {
secp256k1_sha256 sha;
secp256k1_sha256_initialize_tagged(&sha, tag, taglen);
@@ -616,7 +764,7 @@ static void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const
CHECK(secp256k1_musig_nonce_gen(CTX, &secnonce[1], &pubnonce[1], session_secrand[1], sk1, &pk[1], NULL, NULL, NULL) == 1);
CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, 2) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, msg, keyagg_cache, NULL) == 1);
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[0], &secnonce[0], &keypair[0], keyagg_cache, &session) == 1);
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig[1], &secnonce[1], &keypair[1], keyagg_cache, &session) == 1);
@@ -882,7 +1030,7 @@ static void musig_test_vectors_signverify(void) {
CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL));
CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, vector->aggnonces[c->aggnonce_index]));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL));
CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0])));
musig_test_set_secnonce(&secnonce, vector->secnonces[0], &pubkey);
@@ -922,8 +1070,9 @@ static void musig_test_vectors_signverify(void) {
if (!expected) {
continue;
}
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL));
CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk));
CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[0], sizeof(vector->pubkeys[0])));
musig_test_set_secnonce(&secnonce, vector->secnonces[c->secnonce_index], &pubkey);
expected = c->error != MUSIG_SECNONCE;
@@ -955,7 +1104,7 @@ static void musig_test_vectors_signverify(void) {
CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, NULL, c->key_indices_len, c->key_indices, 0, NULL, NULL));
CHECK(secp256k1_musig_nonce_agg(CTX, &aggnonce, pubnonce_ptr, c->nonce_indices_len) == 1);
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msgs[c->msg_index], &keyagg_cache, NULL));
CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, vector->pubkeys[c->signer_index], sizeof(vector->pubkeys[0])));
@@ -1010,7 +1159,7 @@ static void musig_test_vectors_tweak(void) {
CHECK(secp256k1_keypair_create(CTX, &keypair, vector->sk));
CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, NULL, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL));
CHECK(secp256k1_musig_partial_sign(CTX, &partial_sig, &secnonce, &keypair, &keyagg_cache, &session));
CHECK(secp256k1_musig_partial_sig_serialize(CTX, partial_sig32, &partial_sig));
@@ -1046,7 +1195,7 @@ static void musig_test_vectors_sigagg(void) {
CHECK(musig_vectors_keyagg_and_tweak(&error, &keyagg_cache, agg_pk32, vector->pubkeys, vector->tweaks, c->key_indices_len, c->key_indices, c->tweak_indices_len, c->tweak_indices, c->is_xonly));
CHECK(secp256k1_musig_aggnonce_parse(CTX, &aggnonce, c->aggnonce));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache));
CHECK(secp256k1_musig_nonce_process(CTX, &session, &aggnonce, vector->msg, &keyagg_cache, NULL));
for (j = 0; j < c->psig_indices_len; j++) {
CHECK(secp256k1_musig_partial_sig_parse(CTX, &partial_sig[j], vector->psigs[c->psig_indices[j]]));
partial_sig_ptr[j] = &partial_sig[j];
@@ -1127,6 +1276,7 @@ static void run_musig_tests(void) {
for (i = 0; i < COUNT; i++) {
/* Run multiple times to ensure that pk and nonce have different y
* parities */
scriptless_atomic_swap();
musig_tweak_test();
}
sha256_tag_test();