Merge pull request #105 from jonasnick/update-musig

MuSig state machine simplifictions, API improvements and taproot tweaking
This commit is contained in:
Andrew Poelstra 2020-11-30 16:11:34 +00:00 committed by GitHub
commit ff4714e641
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 633 additions and 412 deletions

View File

@ -22,9 +22,9 @@ env:
- WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes
- WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes
- WIDEMUL=int64 RECOVERY=yes - WIDEMUL=int64 RECOVERY=yes
- WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
- WIDEMUL=int128 - WIDEMUL=int128
- WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
- WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
- WIDEMUL=int128 ASM=x86_64 - WIDEMUL=int128 ASM=x86_64
- BIGNUM=no - BIGNUM=no
@ -33,10 +33,10 @@ env:
- BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
- CPPFLAGS=-DDETERMINISTIC - CPPFLAGS=-DDETERMINISTIC
- CFLAGS=-O0 CTIMETEST=no - CFLAGS=-O0 CTIMETEST=no
- CFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST=no - CFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST=no
- ECMULTGENPRECISION=2 - ECMULTGENPRECISION=2
- ECMULTGENPRECISION=8 - ECMULTGENPRECISION=8
- RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD= - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD=
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
@ -84,7 +84,7 @@ matrix:
- libc6-dbg:i386 - libc6-dbg:i386
# S390x build (big endian system) # S390x build (big endian system)
- compiler: gcc - compiler: gcc
env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST= env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST=
arch: s390x arch: s390x
# We use this to install macOS dependencies instead of the built in `homebrew` plugin, # We use this to install macOS dependencies instead of the built in `homebrew` plugin,

View File

@ -20,18 +20,25 @@ extern "C" {
*/ */
/** Data structure containing auxiliary data generated in `pubkey_combine` and /** Data structure containing auxiliary data generated in `pubkey_combine` and
* required for `session_*_initialize`. * required for `session_*_init`.
* Fields: * Fields:
* magic: Set during initialization in `pubkey_combine` in order to allow * magic: Set during initialization in `pubkey_combine` to allow
* detecting an uninitialized object. * detecting an uninitialized object.
* pk_hash: The 32-byte hash of the original public keys * pk_hash: The 32-byte hash of the original public keys
* is_negated: Whether the MuSig-aggregated point was negated when * pk_parity: Whether the MuSig-aggregated point was negated when
* converting it to the combined xonly pubkey. * converting it to the combined xonly pubkey.
* is_tweaked: Whether the combined pubkey was tweaked
* tweak: If is_tweaked, array with the 32-byte tweak
* internal_key_parity: If is_tweaked, the parity of the combined pubkey
* before tweaking
*/ */
typedef struct { typedef struct {
uint64_t magic; uint64_t magic;
unsigned char pk_hash[32]; unsigned char pk_hash[32];
int is_negated; int pk_parity;
int is_tweaked;
unsigned char tweak[32];
int internal_key_parity;
} secp256k1_musig_pre_session; } secp256k1_musig_pre_session;
/** Data structure containing data related to a signing session resulting in a single /** Data structure containing data related to a signing session resulting in a single
@ -45,49 +52,49 @@ typedef struct {
* structure. * structure.
* *
* Fields: * Fields:
* combined_pk: MuSig-computed combined xonly public key * magic: Set in `musig_session_init` to allow detecting an
* uninitialized object.
* round: Current round of the session
* pre_session: Auxiliary data created in `pubkey_combine` * pre_session: Auxiliary data created in `pubkey_combine`
* combined_pk: MuSig-computed combined xonly public key
* n_signers: Number of signers * n_signers: Number of signers
* combined_nonce: Summed combined public nonce (undefined if `nonce_is_set` is false)
* nonce_is_set: Whether the above nonce has been set
* nonce_is_negated: If `nonce_is_set`, whether the above nonce was negated after
* summing the participants' nonces. Needed to ensure the nonce's y
* coordinate is even.
* msg: The 32-byte message (hash) to be signed * msg: The 32-byte message (hash) to be signed
* msg_is_set: Whether the above message has been set * is_msg_set: Whether the above message has been set
* has_secret_data: Whether this session object has a signers' secret data; if this * has_secret_data: Whether this session object has a signers' secret data; if this
* is `false`, it may still be used for verification purposes. * is `false`, it may still be used for verification purposes.
* seckey: If `has_secret_data`, the signer's secret key * seckey: If `has_secret_data`, the signer's secret key
* secnonce: If `has_secret_data`, the signer's secret nonce * secnonce: If `has_secret_data`, the signer's secret nonce
* nonce: If `has_secret_data`, the signer's public nonce * nonce: If `has_secret_data`, the signer's public nonce
* nonce_commitments_hash: If `has_secret_data` and `nonce_commitments_hash_is_set`, * nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all
* the hash of all signers' commitments * signers' commitments
* nonce_commitments_hash_is_set: If `has_secret_data`, whether the * combined_nonce: If round >= 2, the summed combined public nonce
* nonce_commitments_hash has been set * combined_nonce_parity: If round >= 2, the parity of the Y coordinate of above
* nonce.
*/ */
typedef struct { typedef struct {
secp256k1_xonly_pubkey combined_pk; uint64_t magic;
int round;
secp256k1_musig_pre_session pre_session; secp256k1_musig_pre_session pre_session;
secp256k1_xonly_pubkey combined_pk;
uint32_t n_signers; uint32_t n_signers;
secp256k1_pubkey combined_nonce; int is_msg_set;
int nonce_is_set;
int nonce_is_negated;
unsigned char msg[32]; unsigned char msg[32];
int msg_is_set;
int has_secret_data; int has_secret_data;
unsigned char seckey[32]; unsigned char seckey[32];
unsigned char secnonce[32]; unsigned char secnonce[32];
secp256k1_pubkey nonce; secp256k1_xonly_pubkey nonce;
int partial_nonce_parity;
unsigned char nonce_commitments_hash[32]; unsigned char nonce_commitments_hash[32];
int nonce_commitments_hash_is_set; secp256k1_xonly_pubkey combined_nonce;
int combined_nonce_parity;
} secp256k1_musig_session; } secp256k1_musig_session;
/** Data structure containing data on all signers in a single session. /** Data structure containing data on all signers in a single session.
* *
* The workflow for this structure is as follows: * The workflow for this structure is as follows:
* *
* 1. This structure is initialized with `musig_session_initialize` or * 1. This structure is initialized with `musig_session_init` or
* `musig_session_initialize_verifier`, which set the `index` field, and zero out * `musig_session_init_verifier`, which set the `index` field, and zero out
* all other fields. The public session is initialized with the signers' * all other fields. The public session is initialized with the signers'
* nonce_commitments. * nonce_commitments.
* *
@ -112,7 +119,7 @@ typedef struct {
typedef struct { typedef struct {
int present; int present;
uint32_t index; uint32_t index;
secp256k1_pubkey nonce; secp256k1_xonly_pubkey nonce;
unsigned char nonce_commitment[32]; unsigned char nonce_commitment[32];
} secp256k1_musig_session_signer_data; } secp256k1_musig_session_signer_data;
@ -129,7 +136,8 @@ typedef struct {
unsigned char data[32]; unsigned char data[32];
} secp256k1_musig_partial_signature; } secp256k1_musig_partial_signature;
/** Computes a combined public key and the hash of the given public keys /** Computes a combined public key and the hash of the given public keys.
* Different orders of `pubkeys` result in different `combined_pk`s.
* *
* Returns: 1 if the public keys were successfully combined, 0 otherwise * Returns: 1 if the public keys were successfully combined, 0 otherwise
* Args: ctx: pointer to a context object initialized for verification * Args: ctx: pointer to a context object initialized for verification
@ -138,11 +146,11 @@ typedef struct {
* multiexponentiation. If NULL, an inefficient algorithm is used. * multiexponentiation. If NULL, an inefficient algorithm is used.
* Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL)
* pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in
* `musig_session_initialize`. * `musig_session_init` or `musig_pubkey_tweak_add`.
* In: pubkeys: input array of public keys to combine. The order is important; * In: pubkeys: input array of public keys to combine. The order is important;
* a different order will result in a different combined public * a different order will result in a different combined public
* key (cannot be NULL) * key (cannot be NULL)
* n_pubkeys: length of pubkeys array * n_pubkeys: length of pubkeys array. Must be greater than 0.
*/ */
SECP256K1_API int secp256k1_musig_pubkey_combine( SECP256K1_API int secp256k1_musig_pubkey_combine(
const secp256k1_context* ctx, const secp256k1_context* ctx,
@ -153,6 +161,42 @@ SECP256K1_API int secp256k1_musig_pubkey_combine(
size_t n_pubkeys size_t n_pubkeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
/** Tweak an x-only public key by adding the generator multiplied with tweak32
* to it. The resulting output_pubkey with the given internal_pubkey and tweak
* passes `secp256k1_xonly_pubkey_tweak_test`.
*
* This function is only useful before initializing a signing session. If you
* are only computing a public key, but not intending to create a signature for
* it, you can just use `secp256k1_xonly_pubkey_tweak_add`. Can only be called
* once with a given pre_session.
*
* Returns: 0 if the arguments are invalid or the resulting public key would be
* invalid (only when the tweak is the negation of the corresponding
* secret key). 1 otherwise.
* Args: ctx: pointer to a context object initialized for verification
* (cannot be NULL)
* pre_session: pointer to a `musig_pre_session` struct initialized in
* `musig_pubkey_combine` (cannot be NULL)
* Out: output_pubkey: pointer to a public key to store the result. Will be set
* to an invalid value if this function returns 0 (cannot
* be NULL)
* In: internal_pubkey: pointer to the `combined_pk` from
* `musig_pubkey_combine` to which the tweak is applied.
* (cannot be NULL).
* tweak32: pointer to a 32-byte tweak. If the tweak is invalid
* according to secp256k1_ec_seckey_verify, this function
* returns 0. For uniformly random 32-byte arrays the
* chance of being invalid is negligible (around 1 in
* 2^128) (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add(
const secp256k1_context* ctx,
secp256k1_musig_pre_session *pre_session,
secp256k1_pubkey *output_pubkey,
const secp256k1_xonly_pubkey *internal_pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Initializes a signing session for a signer /** Initializes a signing session for a signer
* *
* Returns: 1: session is successfully initialized * Returns: 1: session is successfully initialized
@ -172,14 +216,16 @@ SECP256K1_API int secp256k1_musig_pubkey_combine(
* because it reduces nonce misuse resistance. If NULL, must be * because it reduces nonce misuse resistance. If NULL, must be
* set with `musig_session_get_public_nonce`. * set with `musig_session_get_public_nonce`.
* combined_pk: the combined xonly public key of all signers (cannot be NULL) * combined_pk: the combined xonly public key of all signers (cannot be NULL)
* pre_session: pointer to a musig_pre_session struct from * pre_session: pointer to a musig_pre_session struct after initializing
* `musig_pubkey_combine` (cannot be NULL) * it with `musig_pubkey_combine` and optionally provided to
* `musig_pubkey_tweak_add` (cannot be NULL).
* n_signers: length of signers array. Number of signers participating in * n_signers: length of signers array. Number of signers participating in
* the MuSig. Must be greater than 0 and at most 2^32 - 1. * the MuSig. Must be greater than 0 and at most 2^32 - 1.
* my_index: index of this signer in the signers array * my_index: index of this signer in the signers array. Must be less
* than `n_signers`.
* seckey: the signer's 32-byte secret key (cannot be NULL) * seckey: the signer's 32-byte secret key (cannot be NULL)
*/ */
SECP256K1_API int secp256k1_musig_session_initialize( SECP256K1_API int secp256k1_musig_session_init(
const secp256k1_context* ctx, const secp256k1_context* ctx,
secp256k1_musig_session *session, secp256k1_musig_session *session,
secp256k1_musig_session_signer_data *signers, secp256k1_musig_session_signer_data *signers,
@ -193,7 +239,10 @@ SECP256K1_API int secp256k1_musig_session_initialize(
const unsigned char *seckey const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11);
/** Gets the signer's public nonce given a list of all signers' data with commitments /** Gets the signer's public nonce given a list of all signers' data with
* commitments. Called by participating signers after
* `secp256k1_musig_session_init` and after all nonce commitments have
* been collected
* *
* Returns: 1: public nonce is written in nonce * Returns: 1: public nonce is written in nonce
* 0: signer data is missing commitments or session isn't initialized * 0: signer data is missing commitments or session isn't initialized
@ -201,20 +250,22 @@ SECP256K1_API int secp256k1_musig_session_initialize(
* Args: ctx: pointer to a context object (cannot be NULL) * Args: ctx: pointer to a context object (cannot be NULL)
* session: the signing session to get the nonce from (cannot be NULL) * session: the signing session to get the nonce from (cannot be NULL)
* signers: an array of signers' data initialized with * signers: an array of signers' data initialized with
* `musig_session_initialize`. Array length must equal to * `musig_session_init`. Array length must equal to
* `n_commitments` (cannot be NULL) * `n_commitments` (cannot be NULL)
* Out: nonce: the nonce (cannot be NULL) * Out: nonce32: filled with a 32-byte public nonce which is supposed to be
* In: commitments: array of 32-byte nonce commitments (cannot be NULL) * sent to the other signers and then used in `musig_set nonce`
* (cannot be NULL)
* In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL)
* n_commitments: the length of commitments and signers array. Must be the total * n_commitments: the length of commitments and signers array. Must be the total
* number of signers participating in the MuSig. * number of signers participating in the MuSig.
* msg32: the 32-byte message to be signed. Must be NULL if already * msg32: the 32-byte message to be signed. Must be NULL if already
* set with `musig_session_initialize` otherwise can not be NULL. * set with `musig_session_init` otherwise can not be NULL.
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce(
const secp256k1_context* ctx, const secp256k1_context* ctx,
secp256k1_musig_session *session, secp256k1_musig_session *session,
secp256k1_musig_session_signer_data *signers, secp256k1_musig_session_signer_data *signers,
secp256k1_pubkey *nonce, unsigned char *nonce32,
const unsigned char *const *commitments, const unsigned char *const *commitments,
size_t n_commitments, size_t n_commitments,
const unsigned char *msg32 const unsigned char *msg32
@ -234,13 +285,13 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi
* pre_session: pointer to a musig_pre_session struct from * pre_session: pointer to a musig_pre_session struct from
* `musig_pubkey_combine` (cannot be NULL) * `musig_pubkey_combine` (cannot be NULL)
* pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL)
* commitments: array of 32-byte nonce commitments. Array length must equal to * commitments: array of pointers to 32-byte nonce commitments. Array
* `n_signers` (cannot be NULL) * length must equal to `n_signers` (cannot be NULL)
* n_signers: length of signers and commitments array. Number of signers * n_signers: length of signers and commitments array. Number of signers
* participating in the MuSig. Must be greater than 0 and at most * participating in the MuSig. Must be greater than 0 and at most
* 2^32 - 1. * 2^32 - 1.
*/ */
SECP256K1_API int secp256k1_musig_session_initialize_verifier( SECP256K1_API int secp256k1_musig_session_init_verifier(
const secp256k1_context* ctx, const secp256k1_context* ctx,
secp256k1_musig_session *session, secp256k1_musig_session *session,
secp256k1_musig_session_signer_data *signers, secp256k1_musig_session_signer_data *signers,
@ -259,13 +310,13 @@ SECP256K1_API int secp256k1_musig_session_initialize_verifier(
* Args: ctx: pointer to a context object (cannot be NULL) * Args: ctx: pointer to a context object (cannot be NULL)
* signer: pointer to the signer data to update (cannot be NULL). Must have * signer: pointer to the signer data to update (cannot be NULL). Must have
* been used with `musig_session_get_public_nonce` or initialized * been used with `musig_session_get_public_nonce` or initialized
* with `musig_session_initialize_verifier`. * with `musig_session_init_verifier`.
* In: nonce: signer's alleged public nonce (cannot be NULL) * In: nonce32: signer's alleged public nonce (cannot be NULL)
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce(
const secp256k1_context* ctx, const secp256k1_context* ctx,
secp256k1_musig_session_signer_data *signer, secp256k1_musig_session_signer_data *signer,
const secp256k1_pubkey *nonce const unsigned char *nonce32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Updates a session with the combined public nonce of all signers. The combined /** Updates a session with the combined public nonce of all signers. The combined
@ -281,8 +332,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce(
* (cannot be NULL) * (cannot be NULL)
* n_signers: the length of the signers array. Must be the total number of * n_signers: the length of the signers array. Must be the total number of
* signers participating in the MuSig. * signers participating in the MuSig.
* Out: nonce_is_negated: a pointer to an integer that indicates if the combined * Out: nonce_parity: if non-NULL, a pointer to an integer that indicates the
* public nonce had to be negated. * parity of the combined public nonce. Used for adaptor
* signatures.
* adaptor: point to add to the combined public nonce. If NULL, nothing is * adaptor: point to add to the combined public nonce. If NULL, nothing is
* added to the combined nonce. * added to the combined nonce.
*/ */
@ -291,7 +343,7 @@ SECP256K1_API int secp256k1_musig_session_combine_nonces(
secp256k1_musig_session *session, secp256k1_musig_session *session,
const secp256k1_musig_session_signer_data *signers, const secp256k1_musig_session_signer_data *signers,
size_t n_signers, size_t n_signers,
int *nonce_is_negated, int *nonce_parity,
const secp256k1_pubkey *adaptor const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
@ -369,7 +421,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif
* *
* Returns: 1: all partial signatures have values in range. Does NOT mean the * Returns: 1: all partial signatures have values in range. Does NOT mean the
* resulting signature verifies. * resulting signature verifies.
* 0: some partial signature had s/r out of range * 0: some partial signature are missing or had s or r out of range
* Args: ctx: pointer to a context object (cannot be NULL) * Args: ctx: pointer to a context object (cannot be NULL)
* session: initialized session for which the combined nonce has been * session: initialized session for which the combined nonce has been
* computed (cannot be NULL) * computed (cannot be NULL)
@ -395,14 +447,14 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combi
* In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL) * In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL)
* sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot * sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot
* be NULL) * be NULL)
* nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces`
*/ */
SECP256K1_API int secp256k1_musig_partial_sig_adapt( SECP256K1_API int secp256k1_musig_partial_sig_adapt(
const secp256k1_context* ctx, const secp256k1_context* ctx,
secp256k1_musig_partial_signature *adaptor_sig, secp256k1_musig_partial_signature *adaptor_sig,
const secp256k1_musig_partial_signature *partial_sig, const secp256k1_musig_partial_signature *partial_sig,
const unsigned char *sec_adaptor32, const unsigned char *sec_adaptor32,
int nonce_is_negated int nonce_parity
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Extracts a secret adaptor from a MuSig, given all parties' partial /** Extracts a secret adaptor from a MuSig, given all parties' partial
@ -418,7 +470,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt(
* In: sig64: complete 2-of-2 signature (cannot be NULL) * In: sig64: complete 2-of-2 signature (cannot be NULL)
* partial_sigs: array of partial signatures (cannot be NULL) * partial_sigs: array of partial signatures (cannot be NULL)
* n_partial_sigs: number of elements in partial_sigs array * n_partial_sigs: number of elements in partial_sigs array
* nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` * nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces`
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor(
const secp256k1_context* ctx, const secp256k1_context* ctx,
@ -426,7 +478,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_ad
const unsigned char *sig64, const unsigned char *sig64,
const secp256k1_musig_partial_signature *partial_sigs, const secp256k1_musig_partial_signature *partial_sigs,
size_t n_partial_sigs, size_t n_partial_sigs,
int nonce_is_negated int nonce_parity
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -45,7 +45,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
unsigned char nonce_commitment[N_SIGNERS][32]; unsigned char nonce_commitment[N_SIGNERS][32];
const unsigned char *nonce_commitment_ptr[N_SIGNERS]; const unsigned char *nonce_commitment_ptr[N_SIGNERS];
secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS];
secp256k1_pubkey nonce[N_SIGNERS]; unsigned char nonce[N_SIGNERS][32];
int i, j; int i, j;
secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; secp256k1_musig_partial_signature partial_sig[N_SIGNERS];
@ -60,7 +60,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
return 0; return 0;
} }
/* Create random session ID. It is absolutely necessary that the session ID /* Create random session ID. It is absolutely necessary that the session ID
* is unique for every call of secp256k1_musig_session_initialize. Otherwise * is unique for every call of secp256k1_musig_session_init. Otherwise
* it's trivial for an attacker to extract the secret key! */ * it's trivial for an attacker to extract the secret key! */
frand = fopen("/dev/urandom", "r"); frand = fopen("/dev/urandom", "r");
if(frand == NULL) { if(frand == NULL) {
@ -72,7 +72,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
} }
fclose(frand); fclose(frand);
/* Initialize session */ /* Initialize session */
if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) {
return 0; return 0;
} }
nonce_commitment_ptr[i] = &nonce_commitment[i][0]; nonce_commitment_ptr[i] = &nonce_commitment[i][0];
@ -80,14 +80,14 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
/* Communication round 1: Exchange nonce commitments */ /* Communication round 1: Exchange nonce commitments */
for (i = 0; i < N_SIGNERS; i++) { for (i = 0; i < N_SIGNERS; i++) {
/* Set nonce commitments in the signer data and get the own public nonce */ /* Set nonce commitments in the signer data and get the own public nonce */
if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], &nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) { if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) {
return 0; return 0;
} }
} }
/* Communication round 2: Exchange nonces */ /* Communication round 2: Exchange nonces */
for (i = 0; i < N_SIGNERS; i++) { for (i = 0; i < N_SIGNERS; i++) {
for (j = 0; j < N_SIGNERS; j++) { for (j = 0; j < N_SIGNERS; j++) {
if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], &nonce[j])) { if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], nonce[j])) {
/* Signer j's nonce does not match the nonce commitment. In this case /* Signer j's nonce does not match the nonce commitment. In this case
* abort the protocol. If you make another attempt at finishing the * abort the protocol. If you make another attempt at finishing the
* protocol, create a new session (with a fresh session ID!). */ * protocol, create a new session (with a fresh session ID!). */

View File

@ -87,7 +87,6 @@ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256
return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]);
} }
static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) {
uint32_t i; uint32_t i;
for (i = 0; i < n_signers; i++) { for (i = 0; i < n_signers; i++) {
@ -103,7 +102,7 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat
secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; secp256k1_musig_pubkey_combine_ecmult_data ecmult_data;
secp256k1_gej pkj; secp256k1_gej pkj;
secp256k1_ge pkp; secp256k1_ge pkp;
int is_negated; int pk_parity;
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(combined_pk != NULL); ARG_CHECK(combined_pk != NULL);
@ -121,18 +120,46 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat
} }
secp256k1_ge_set_gej(&pkp, &pkj); secp256k1_ge_set_gej(&pkp, &pkj);
secp256k1_fe_normalize(&pkp.y); secp256k1_fe_normalize(&pkp.y);
is_negated = secp256k1_extrakeys_ge_even_y(&pkp); pk_parity = secp256k1_extrakeys_ge_even_y(&pkp);
secp256k1_xonly_pubkey_save(combined_pk, &pkp); secp256k1_xonly_pubkey_save(combined_pk, &pkp);
if (pre_session != NULL) { if (pre_session != NULL) {
pre_session->magic = pre_session_magic; pre_session->magic = pre_session_magic;
memcpy(pre_session->pk_hash, ecmult_data.ell, 32); memcpy(pre_session->pk_hash, ecmult_data.ell, 32);
pre_session->is_negated = is_negated; pre_session->pk_parity = pk_parity;
pre_session->is_tweaked = 0;
} }
return 1; return 1;
} }
int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
secp256k1_ge pk;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pre_session != NULL);
ARG_CHECK(pre_session->magic == pre_session_magic);
/* This function can only be called once because otherwise signing would not
* succeed */
ARG_CHECK(pre_session->is_tweaked == 0);
pre_session->internal_key_parity = pre_session->pk_parity;
if(!secp256k1_xonly_pubkey_tweak_add(ctx, output_pubkey, internal_pubkey, tweak32)) {
return 0;
}
memcpy(pre_session->tweak, tweak32, 32);
pre_session->is_tweaked = 1;
if (!secp256k1_pubkey_load(ctx, &pk, output_pubkey)) {
return 0;
}
pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk);
return 1;
}
static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL;
int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) {
unsigned char combined_ser[32]; unsigned char combined_ser[32];
int overflow; int overflow;
secp256k1_scalar secret; secp256k1_scalar secret;
@ -140,6 +167,8 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m
secp256k1_sha256 sha; secp256k1_sha256 sha;
secp256k1_gej pj; secp256k1_gej pj;
secp256k1_ge p; secp256k1_ge p;
unsigned char nonce_ser[32];
size_t nonce_ser_size = sizeof(nonce_ser);
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
@ -152,27 +181,24 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m
ARG_CHECK(pre_session->magic == pre_session_magic); ARG_CHECK(pre_session->magic == pre_session_magic);
ARG_CHECK(seckey != NULL); ARG_CHECK(seckey != NULL);
ARG_CHECK(n_signers > 0);
ARG_CHECK(n_signers <= UINT32_MAX);
ARG_CHECK(my_index < n_signers);
memset(session, 0, sizeof(*session)); memset(session, 0, sizeof(*session));
session->magic = session_magic;
if (msg32 != NULL) { if (msg32 != NULL) {
memcpy(session->msg, msg32, 32); memcpy(session->msg, msg32, 32);
session->msg_is_set = 1; session->is_msg_set = 1;
} else { } else {
session->msg_is_set = 0; session->is_msg_set = 0;
} }
memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk));
session->pre_session = *pre_session; session->pre_session = *pre_session;
session->nonce_is_set = 0;
session->has_secret_data = 1; session->has_secret_data = 1;
if (n_signers == 0 || my_index >= n_signers) {
return 0;
}
if (n_signers > UINT32_MAX) {
return 0;
}
session->n_signers = (uint32_t) n_signers; session->n_signers = (uint32_t) n_signers;
secp256k1_musig_signers_init(signers, session->n_signers); secp256k1_musig_signers_init(signers, session->n_signers);
session->nonce_commitments_hash_is_set = 0;
/* Compute secret key */ /* Compute secret key */
secp256k1_scalar_set_b32(&secret, seckey, &overflow); secp256k1_scalar_set_b32(&secret, seckey, &overflow);
@ -181,22 +207,33 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m
return 0; return 0;
} }
secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index);
/* Compute the signers public key point and determine if the secret needs to /* Compute the signer's public key point and determine if the secret is
* be negated before signing. If the signer's pubkey is negated XOR the * negated before signing. That happens if if the signer's pubkey has an odd
* MuSig-combined pubkey is negated the secret has to be negated. This can * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR
* be seen by looking at the secret key belonging to `combined_pk`. Let's * (if tweaked) the internal key has an odd Y coordinate.
* define *
* This can be seen by looking at the secret key belonging to `combined_pk`.
* Let's define
* P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key
* point x_i*G, mu_i is the i-th musig coefficient and |.| is a function * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function
* that normalizes a point to an even Y by negating if necessary similar to * that normalizes a point to an even Y by negating if necessary similar to
* secp256k1_extrakeys_ge_even_y. Then we have * secp256k1_extrakeys_ge_even_y. Then we have
* P := |P'| the combined xonly public key. Also, P = x*G where x = * P := |P'| + t*G where t is the tweak.
* sum_i(b_i*mu_i*x_i) and b_i = -1 if (P != |P'| XOR P_i != |P_i|) and 1 * And the combined xonly public key is
* otherwise. */ * |P| = x*G
* where x = sum_i(b_i*mu_i*x_i) + b'*t
* b' = -1 if P != |P|, 1 otherwise
* b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1
* otherwise.
*/
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret);
secp256k1_ge_set_gej(&p, &pj); secp256k1_ge_set_gej(&p, &pj);
secp256k1_fe_normalize(&p.y); secp256k1_fe_normalize(&p.y);
if (secp256k1_fe_is_odd(&p.y) != session->pre_session.is_negated) { if((secp256k1_fe_is_odd(&p.y)
+ session->pre_session.pk_parity
+ (session->pre_session.is_tweaked
&& session->pre_session.internal_key_parity))
% 2 == 1) {
secp256k1_scalar_negate(&secret, &secret); secp256k1_scalar_negate(&secret, &secret);
} }
secp256k1_scalar_mul(&secret, &secret, &mu); secp256k1_scalar_mul(&secret, &secret, &mu);
@ -205,7 +242,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m
/* Compute secret nonce */ /* Compute secret nonce */
secp256k1_sha256_initialize(&sha); secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, session_id32, 32); secp256k1_sha256_write(&sha, session_id32, 32);
if (session->msg_is_set) { if (session->is_msg_set) {
secp256k1_sha256_write(&sha, msg32, 32); secp256k1_sha256_write(&sha, msg32, 32);
} }
secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk); secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk);
@ -221,47 +258,49 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m
/* Compute public nonce and commitment */ /* Compute public nonce and commitment */
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret);
secp256k1_ge_set_gej(&p, &pj); secp256k1_ge_set_gej(&p, &pj);
secp256k1_pubkey_save(&session->nonce, &p); secp256k1_fe_normalize_var(&p.y);
session->partial_nonce_parity = secp256k1_extrakeys_ge_even_y(&p);
secp256k1_xonly_pubkey_save(&session->nonce, &p);
if (nonce_commitment32 != NULL) { secp256k1_sha256_initialize(&sha);
unsigned char commit[33]; secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce);
size_t commit_size = sizeof(commit); secp256k1_sha256_write(&sha, nonce_ser, nonce_ser_size);
secp256k1_sha256_initialize(&sha); secp256k1_sha256_finalize(&sha, nonce_commitment32);
secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, &session->nonce, SECP256K1_EC_COMPRESSED);
secp256k1_sha256_write(&sha, commit, commit_size);
secp256k1_sha256_finalize(&sha, nonce_commitment32);
}
session->round = 0;
secp256k1_scalar_clear(&secret); secp256k1_scalar_clear(&secret);
return 1; return 1;
} }
int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) { int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) {
secp256k1_sha256 sha; secp256k1_sha256 sha;
unsigned char nonce_commitments_hash[32]; unsigned char nonce_commitments_hash[32];
size_t i; size_t i;
unsigned char nonce_ser[32];
size_t nonce_ser_size = sizeof(nonce_ser);
(void) ctx; (void) ctx;
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(session != NULL); ARG_CHECK(session != NULL);
ARG_CHECK(session->magic == session_magic);
ARG_CHECK(signers != NULL); ARG_CHECK(signers != NULL);
ARG_CHECK(nonce != NULL); ARG_CHECK(nonce != NULL);
ARG_CHECK(commitments != NULL); ARG_CHECK(commitments != NULL);
/* If the message was not set during initialization it must be set now. */
ARG_CHECK(!(!session->msg_is_set && msg32 == NULL));
/* The message can only be set once. */
ARG_CHECK(!(session->msg_is_set && msg32 != NULL));
if (!session->has_secret_data || n_commitments != session->n_signers) { ARG_CHECK(session->round == 0);
return 0; /* If the message was not set during initialization it must be set now. */
} ARG_CHECK(!(!session->is_msg_set && msg32 == NULL));
/* The message can only be set once. */
ARG_CHECK(!(session->is_msg_set && msg32 != NULL));
ARG_CHECK(session->has_secret_data);
ARG_CHECK(n_commitments == session->n_signers);
for (i = 0; i < n_commitments; i++) { for (i = 0; i < n_commitments; i++) {
ARG_CHECK(commitments[i] != NULL); ARG_CHECK(commitments[i] != NULL);
} }
if (msg32 != NULL) { if (msg32 != NULL) {
memcpy(session->msg, msg32, 32); memcpy(session->msg, msg32, 32);
session->msg_is_set = 1; session->is_msg_set = 1;
} }
secp256k1_sha256_initialize(&sha); secp256k1_sha256_initialize(&sha);
for (i = 0; i < n_commitments; i++) { for (i = 0; i < n_commitments; i++) {
@ -269,19 +308,15 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2
secp256k1_sha256_write(&sha, commitments[i], 32); secp256k1_sha256_write(&sha, commitments[i], 32);
} }
secp256k1_sha256_finalize(&sha, nonce_commitments_hash); secp256k1_sha256_finalize(&sha, nonce_commitments_hash);
if (session->nonce_commitments_hash_is_set
&& memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) {
/* Abort if get_public_nonce has been called before with a different array of
* commitments. */
return 0;
}
memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32);
session->nonce_commitments_hash_is_set = 1;
memcpy(nonce, &session->nonce, sizeof(*nonce)); secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce);
memcpy(nonce, &nonce_ser, nonce_ser_size);
session->round = 1;
return 1; return 1;
} }
int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { int secp256k1_musig_session_init_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) {
size_t i; size_t i;
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
@ -294,9 +329,8 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se
ARG_CHECK(commitments != NULL); ARG_CHECK(commitments != NULL);
/* Check n_signers before checking commitments to allow testing the case where /* Check n_signers before checking commitments to allow testing the case where
* n_signers is big without allocating the space. */ * n_signers is big without allocating the space. */
if (n_signers > UINT32_MAX) { ARG_CHECK(n_signers > 0);
return 0; ARG_CHECK(n_signers <= UINT32_MAX);
}
for (i = 0; i < n_signers; i++) { for (i = 0; i < n_signers; i++) {
ARG_CHECK(commitments[i] != NULL); ARG_CHECK(commitments[i] != NULL);
} }
@ -304,50 +338,48 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se
memset(session, 0, sizeof(*session)); memset(session, 0, sizeof(*session));
session->magic = session_magic;
memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk));
session->pre_session = *pre_session; session->pre_session = *pre_session;
if (n_signers == 0) {
return 0;
}
session->n_signers = (uint32_t) n_signers; session->n_signers = (uint32_t) n_signers;
secp256k1_musig_signers_init(signers, session->n_signers); secp256k1_musig_signers_init(signers, session->n_signers);
session->pre_session = *pre_session; session->pre_session = *pre_session;
session->nonce_is_set = 0; session->is_msg_set = 1;
session->msg_is_set = 1;
memcpy(session->msg, msg32, 32); memcpy(session->msg, msg32, 32);
session->has_secret_data = 0; session->has_secret_data = 0;
session->nonce_commitments_hash_is_set = 0;
for (i = 0; i < n_signers; i++) { for (i = 0; i < n_signers; i++) {
memcpy(signers[i].nonce_commitment, commitments[i], 32); memcpy(signers[i].nonce_commitment, commitments[i], 32);
} }
session->round = 1;
return 1; return 1;
} }
int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const secp256k1_pubkey *nonce) { int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const unsigned char *nonce) {
unsigned char commit[33];
size_t commit_size = sizeof(commit);
secp256k1_sha256 sha; secp256k1_sha256 sha;
unsigned char commit[32];
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(signer != NULL); ARG_CHECK(signer != NULL);
ARG_CHECK(nonce != NULL); ARG_CHECK(nonce != NULL);
secp256k1_sha256_initialize(&sha); secp256k1_sha256_initialize(&sha);
secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, nonce, SECP256K1_EC_COMPRESSED); secp256k1_sha256_write(&sha, nonce, 32);
secp256k1_sha256_write(&sha, commit, commit_size);
secp256k1_sha256_finalize(&sha, commit); secp256k1_sha256_finalize(&sha, commit);
if (memcmp(commit, signer->nonce_commitment, 32) != 0) { if (memcmp(commit, signer->nonce_commitment, 32) != 0) {
return 0; return 0;
} }
memcpy(&signer->nonce, nonce, sizeof(*nonce)); memcpy(&signer->nonce, nonce, sizeof(*nonce));
if (!secp256k1_xonly_pubkey_parse(ctx, &signer->nonce, nonce)) {
return 0;
}
signer->present = 1; signer->present = 1;
return 1; return 1;
} }
int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_is_negated, const secp256k1_pubkey *adaptor) { int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_parity, const secp256k1_pubkey *adaptor) {
secp256k1_gej combined_noncej; secp256k1_gej combined_noncej;
secp256k1_ge combined_noncep; secp256k1_ge combined_noncep;
secp256k1_ge noncep; secp256k1_ge noncep;
@ -358,10 +390,10 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(session != NULL); ARG_CHECK(session != NULL);
ARG_CHECK(signers != NULL); ARG_CHECK(signers != NULL);
ARG_CHECK(session->magic == session_magic);
ARG_CHECK(session->round == 1);
ARG_CHECK(n_signers == session->n_signers);
if (n_signers != session->n_signers) {
return 0;
}
secp256k1_sha256_initialize(&sha); secp256k1_sha256_initialize(&sha);
secp256k1_gej_set_infinity(&combined_noncej); secp256k1_gej_set_infinity(&combined_noncej);
for (i = 0; i < n_signers; i++) { for (i = 0; i < n_signers; i++) {
@ -369,20 +401,17 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256
return 0; return 0;
} }
secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32); secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32);
secp256k1_pubkey_load(ctx, &noncep, &signers[i].nonce); secp256k1_xonly_pubkey_load(ctx, &noncep, &signers[i].nonce);
secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL);
} }
secp256k1_sha256_finalize(&sha, nonce_commitments_hash); secp256k1_sha256_finalize(&sha, nonce_commitments_hash);
/* Either the session is a verifier session or or the nonce_commitments_hash has /* If the signers' commitments changed between get_public_nonce and now we
* been set in `musig_session_get_public_nonce`. */ * have to abort because in that case they may have seen our nonce before
VERIFY_CHECK(!session->has_secret_data || session->nonce_commitments_hash_is_set); * creating their commitment. That can happen if the signer_data given to
* this function is different to the signer_data given to get_public_nonce.
* */
if (session->has_secret_data if (session->has_secret_data
&& memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) {
/* If the signers' commitments changed between get_public_nonce and now we
* have to abort because in that case they may have seen our nonce before
* creating their commitment. That can happen if the signer_data given to
* this function is different to the signer_data given to get_public_nonce.
* */
return 0; return 0;
} }
@ -391,19 +420,16 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256
secp256k1_pubkey_load(ctx, &noncep, adaptor); secp256k1_pubkey_load(ctx, &noncep, adaptor);
secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL);
} }
/* Negate nonce if Y coordinate is not square */
secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); secp256k1_ge_set_gej(&combined_noncep, &combined_noncej);
secp256k1_fe_normalize(&combined_noncep.y); secp256k1_fe_normalize_var(&combined_noncep.y);
if (!secp256k1_fe_is_odd(&combined_noncep.y)) { session->combined_nonce_parity = secp256k1_extrakeys_ge_even_y(&combined_noncep);
session->nonce_is_negated = 0; if (nonce_parity != NULL) {
} else { *nonce_parity = session->combined_nonce_parity;
session->nonce_is_negated = 1;
secp256k1_ge_neg(&combined_noncep, &combined_noncep);
} }
if (nonce_is_negated != NULL) { secp256k1_xonly_pubkey_save(&session->combined_nonce, &combined_noncep);
*nonce_is_negated = session->nonce_is_negated; session->round = 2;
}
secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep);
session->nonce_is_set = 1;
return 1; return 1;
} }
@ -424,27 +450,22 @@ int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp25
} }
/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ /* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */
static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { static void secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) {
unsigned char buf[32]; unsigned char buf[32];
secp256k1_ge rp; secp256k1_ge rp;
secp256k1_sha256 sha; secp256k1_sha256 sha;
VERIFY_CHECK(session->round >= 2);
secp256k1_schnorrsig_sha256_tagged(&sha); secp256k1_schnorrsig_sha256_tagged(&sha);
if (!session->nonce_is_set) { secp256k1_xonly_pubkey_load(ctx, &rp, &session->combined_nonce);
return 0;
}
secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce);
secp256k1_fe_get_b32(buf, &rp.x); secp256k1_fe_get_b32(buf, &rp.x);
secp256k1_sha256_write(&sha, buf, 32); secp256k1_sha256_write(&sha, buf, 32);
secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk); secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk);
secp256k1_sha256_write(&sha, buf, 32); secp256k1_sha256_write(&sha, buf, 32);
if (!session->msg_is_set) {
return 0;
}
secp256k1_sha256_write(&sha, session->msg, 32); secp256k1_sha256_write(&sha, session->msg, 32);
secp256k1_sha256_finalize(&sha, msghash); secp256k1_sha256_finalize(&sha, msghash);
return 1;
} }
int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) { int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) {
@ -456,15 +477,12 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
ARG_CHECK(partial_sig != NULL); ARG_CHECK(partial_sig != NULL);
ARG_CHECK(session != NULL); ARG_CHECK(session != NULL);
ARG_CHECK(session->magic == session_magic);
if (!session->nonce_is_set || !session->has_secret_data) { ARG_CHECK(session->round == 2);
return 0; ARG_CHECK(session->has_secret_data);
}
/* build message hash */ /* build message hash */
if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { secp256k1_musig_compute_messagehash(ctx, msghash, session);
return 0;
}
secp256k1_scalar_set_b32(&e, msghash, NULL); secp256k1_scalar_set_b32(&e, msghash, NULL);
secp256k1_scalar_set_b32(&sk, session->seckey, &overflow); secp256k1_scalar_set_b32(&sk, session->seckey, &overflow);
@ -479,7 +497,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m
secp256k1_scalar_clear(&k); secp256k1_scalar_clear(&k);
return 0; return 0;
} }
if (session->nonce_is_negated) { if (session->partial_nonce_parity != session->combined_nonce_parity) {
secp256k1_scalar_negate(&k, &k); secp256k1_scalar_negate(&k, &k);
} }
@ -503,10 +521,9 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp
ARG_CHECK(sig64 != NULL); ARG_CHECK(sig64 != NULL);
ARG_CHECK(partial_sigs != NULL); ARG_CHECK(partial_sigs != NULL);
ARG_CHECK(session != NULL); ARG_CHECK(session != NULL);
ARG_CHECK(session->magic == session_magic);
ARG_CHECK(session->round == 2);
if (!session->nonce_is_set) {
return 0;
}
if (n_sigs != session->n_signers) { if (n_sigs != session->n_signers) {
return 0; return 0;
} }
@ -522,7 +539,27 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp
secp256k1_scalar_add(&s, &s, &term); secp256k1_scalar_add(&s, &s, &term);
} }
secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); /* If there is a tweak then add (or subtract) `msghash` times `tweak` to `s`.*/
if (session->pre_session.is_tweaked) {
unsigned char msghash[32];
secp256k1_scalar e, scalar_tweak;
int overflow = 0;
secp256k1_musig_compute_messagehash(ctx, msghash, session);
secp256k1_scalar_set_b32(&e, msghash, NULL);
secp256k1_scalar_set_b32(&scalar_tweak, session->pre_session.tweak, &overflow);
if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) {
/* This mimics the behavior of secp256k1_ec_seckey_tweak_mul regarding
* overflow and tweak being 0. */
return 0;
}
if (session->pre_session.pk_parity) {
secp256k1_scalar_negate(&e, &e);
}
secp256k1_scalar_add(&s, &s, &e);
}
secp256k1_xonly_pubkey_load(ctx, &noncep, &session->combined_nonce);
VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y));
secp256k1_fe_normalize(&noncep.x); secp256k1_fe_normalize(&noncep.x);
secp256k1_fe_get_b32(&sig64[0], &noncep.x); secp256k1_fe_get_b32(&sig64[0], &noncep.x);
@ -548,17 +585,15 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
ARG_CHECK(signer != NULL); ARG_CHECK(signer != NULL);
ARG_CHECK(partial_sig != NULL); ARG_CHECK(partial_sig != NULL);
ARG_CHECK(pubkey != NULL); ARG_CHECK(pubkey != NULL);
ARG_CHECK(session->magic == session_magic);
ARG_CHECK(session->round == 2);
ARG_CHECK(signer->present);
if (!session->nonce_is_set || !signer->present) {
return 0;
}
secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow);
if (overflow) { if (overflow) {
return 0; return 0;
} }
if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { secp256k1_musig_compute_messagehash(ctx, msghash, session);
return 0;
}
secp256k1_scalar_set_b32(&e, msghash, NULL); secp256k1_scalar_set_b32(&e, msghash, NULL);
/* Multiplying the messagehash by the musig coefficient is equivalent /* Multiplying the messagehash by the musig coefficient is equivalent
@ -567,13 +602,18 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index); secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index);
secp256k1_scalar_mul(&e, &e, &mu); secp256k1_scalar_mul(&e, &e, &mu);
if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) {
return 0; return 0;
} }
/* If the MuSig-combined point is negated, the signers will sign for the
* negation of their individual xonly public key such that the combined /* If the MuSig-combined point has an odd Y coordinate, the signers will
* signature is valid for the MuSig aggregated xonly key. */ * sign for the negation of their individual xonly public key such that the
if (session->pre_session.is_negated) { * combined signature is valid for the MuSig aggregated xonly key. If the
* MuSig-combined point was tweaked then `e` is negated if the combined key
* has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/
if (session->pre_session.pk_parity
!= (session->pre_session.is_tweaked
&& session->pre_session.internal_key_parity)) {
secp256k1_scalar_negate(&e, &e); secp256k1_scalar_negate(&e, &e);
} }
@ -585,7 +625,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
secp256k1_gej_set_ge(&pkj, &pkp); secp256k1_gej_set_ge(&pkj, &pkp);
secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
if (!session->nonce_is_negated) { if (!session->combined_nonce_parity) {
secp256k1_ge_neg(&rp, &rp); secp256k1_ge_neg(&rp, &rp);
} }
secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL); secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL);
@ -593,7 +633,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
return secp256k1_gej_is_infinity(&rj); return secp256k1_gej_is_infinity(&rj);
} }
int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_is_negated) { int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_parity) {
secp256k1_scalar s; secp256k1_scalar s;
secp256k1_scalar t; secp256k1_scalar t;
int overflow; int overflow;
@ -614,7 +654,7 @@ int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_mu
return 0; return 0;
} }
if (nonce_is_negated) { if (nonce_parity) {
secp256k1_scalar_negate(&t, &t); secp256k1_scalar_negate(&t, &t);
} }
@ -624,7 +664,7 @@ int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_mu
return 1; return 1;
} }
int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_parity) {
secp256k1_scalar t; secp256k1_scalar t;
secp256k1_scalar s; secp256k1_scalar s;
int overflow; int overflow;
@ -651,7 +691,7 @@ int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigne
secp256k1_scalar_add(&t, &t, &s); secp256k1_scalar_add(&t, &t, &s);
} }
if (!nonce_is_negated) { if (!nonce_parity) {
secp256k1_scalar_negate(&t, &t); secp256k1_scalar_negate(&t, &t);
} }
secp256k1_scalar_get_b32(sec_adaptor32, &t); secp256k1_scalar_get_b32(sec_adaptor32, &t);

View File

@ -74,7 +74,7 @@ signature process, which is also a supported mode) acts as follows.
### Signing Participant ### Signing Participant
1. The signer starts the session by calling `secp256k1_musig_session_initialize`. 1. The signer starts the session by calling `secp256k1_musig_session_init`.
This function outputs This function outputs
- an initialized session state in the out-pointer `session` - an initialized session state in the out-pointer `session`
- an array of initialized signer data in the out-pointer `signers` - an array of initialized signer data in the out-pointer `signers`
@ -91,7 +91,7 @@ signature process, which is also a supported mode) acts as follows.
length-32 byte arrays which can be communicated however is communicated. length-32 byte arrays which can be communicated however is communicated.
3. Once all signers nonce commitments have been received, the signer records 3. Once all signers nonce commitments have been received, the signer records
these commitments with the function `secp256k1_musig_session_get_public_nonce`. these commitments with the function `secp256k1_musig_session_get_public_nonce`.
If the signer did not provide a message to `secp256k1_musig_session_initialize`, If the signer did not provide a message to `secp256k1_musig_session_init`,
a message must be provided now. a message must be provided now.
This function updates in place This function updates in place
- the session state `session` - the session state `session`
@ -133,8 +133,8 @@ A participant who wants to verify the signing process, i.e. check that nonce com
are consistent and partial signatures are correct without contributing a partial signature, are consistent and partial signatures are correct without contributing a partial signature,
may do so using the above instructions except for the following changes: may do so using the above instructions except for the following changes:
1. A signing session should be produced using `musig_session_initialize_verifier` 1. A signing session should be produced using `musig_session_init_verifier`
rather than `musig_session_initialize`; this function takes no secret data or rather than `musig_session_init`; this function takes no secret data or
signer index. signer index.
2. The participant receives nonce commitments, public nonces and partial signatures, 2. The participant receives nonce commitments, public nonces and partial signatures,
but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce` but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce`

View File

@ -31,7 +31,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) {
unsigned char session_id[2][32]; unsigned char session_id[2][32];
secp256k1_xonly_pubkey pk[2]; secp256k1_xonly_pubkey pk[2];
const unsigned char *ncs[2]; const unsigned char *ncs[2];
secp256k1_pubkey public_nonce[3]; unsigned char public_nonce[3][32];
secp256k1_musig_partial_signature partial_sig[2]; secp256k1_musig_partial_signature partial_sig[2];
unsigned char final_sig[64]; unsigned char final_sig[64];
@ -45,19 +45,19 @@ void musig_simple_test(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1);
CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
ncs[0] = nonce_commitment[0]; ncs[0] = nonce_commitment[0];
ncs[1] = nonce_commitment[1]; ncs[1] = nonce_commitment[1];
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], &public_nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], &public_nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], public_nonce[1]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], &public_nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], &public_nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], public_nonce[1]) == 1);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1);
@ -75,6 +75,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) {
void musig_api_tests(secp256k1_scratch_space *scratch) { void musig_api_tests(secp256k1_scratch_space *scratch) {
secp256k1_scratch_space *scratch_small; secp256k1_scratch_space *scratch_small;
secp256k1_musig_session session[2]; secp256k1_musig_session session[2];
secp256k1_musig_session session_uninitialized;
secp256k1_musig_session verifier_session; secp256k1_musig_session verifier_session;
secp256k1_musig_session_signer_data signer0[2]; secp256k1_musig_session_signer_data signer0[2];
secp256k1_musig_session_signer_data signer1[2]; secp256k1_musig_session_signer_data signer1[2];
@ -90,10 +91,9 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
unsigned char ones[32]; unsigned char ones[32];
unsigned char session_id[2][32]; unsigned char session_id[2][32];
unsigned char nonce_commitment[2][32]; unsigned char nonce_commitment[2][32];
int nonce_is_negated; int combined_nonce_parity;
const unsigned char *ncs[2]; const unsigned char *ncs[2];
unsigned char msg[32]; unsigned char msg[32];
unsigned char msghash[32];
secp256k1_xonly_pubkey combined_pk; secp256k1_xonly_pubkey combined_pk;
secp256k1_musig_pre_session pre_session; secp256k1_musig_pre_session pre_session;
secp256k1_musig_pre_session pre_session_uninitialized; secp256k1_musig_pre_session pre_session_uninitialized;
@ -118,10 +118,11 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
memset(ones, 0xff, 32); memset(ones, 0xff, 32);
/* Simulate pre_session being uninitialized by setting it to 0s. Actually providing /* Simulate structs being uninitialized by setting it to 0s. We don't want
* an unitialized pre_session object to a initialize_*_session would be undefined * to produce undefined behavior by actually providing uninitialized
* behavior */ * structs. */
memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized));
memset(&session_uninitialized, 0, sizeof(session_uninitialized));
secp256k1_testrand256(session_id[0]); secp256k1_testrand256(session_id[0]);
secp256k1_testrand256(session_id[1]); secp256k1_testrand256(session_id[1]);
@ -169,139 +170,188 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
/** Tweaking */
ecount = 0;
{
secp256k1_xonly_pubkey tmp_internal_pk = combined_pk;
secp256k1_pubkey tmp_output_pk;
secp256k1_musig_pre_session tmp_pre_session = pre_session;
CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1);
/* Reset pre_session */
tmp_pre_session = pre_session;
CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1);
CHECK(ecount == 2);
tmp_pre_session = pre_session;
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
CHECK(ecount == 3);
/* Uninitialized pre_session */
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &pre_session_uninitialized, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
CHECK(ecount == 4);
/* Using the same pre_session twice does not work */
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1);
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
CHECK(ecount == 5);
tmp_pre_session = pre_session;
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, NULL, &tmp_internal_pk, tweak) == 0);
CHECK(ecount == 6);
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, NULL, tweak) == 0);
CHECK(ecount == 7);
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, NULL) == 0);
CHECK(ecount == 8);
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, ones) == 0);
CHECK(ecount == 8);
}
/** Session creation **/ /** Session creation **/
ecount = 0; ecount = 0;
CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 4); CHECK(ecount == 4);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 5); CHECK(ecount == 5);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 6); CHECK(ecount == 6);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
CHECK(ecount == 6); CHECK(ecount == 6);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0);
CHECK(ecount == 7); CHECK(ecount == 7);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0);
CHECK(ecount == 8); CHECK(ecount == 8);
/* Uninitialized pre_session */ /* Uninitialized pre_session */
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0);
CHECK(ecount == 9); CHECK(ecount == 9);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0);
CHECK(ecount == 9); CHECK(ecount == 10);
/* If more than UINT32_MAX fits in a size_t, test that session_initialize /* If more than UINT32_MAX fits in a size_t, test that session_init
* rejects n_signers that high. */ * rejects n_signers that high. */
if (SIZE_MAX > UINT32_MAX) { if (SIZE_MAX > UINT32_MAX) {
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0);
} }
CHECK(ecount == 9); CHECK(ecount == 11);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0);
CHECK(ecount == 10); CHECK(ecount == 12);
/* secret key overflows */ /* secret key overflows */
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0);
CHECK(ecount == 10); CHECK(ecount == 12);
CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
ncs[0] = nonce_commitment[0]; ncs[0] = nonce_commitment[0];
ncs[1] = nonce_commitment[1]; ncs[1] = nonce_commitment[1];
ecount = 0; ecount = 0;
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1);
CHECK(ecount == 0); CHECK(ecount == 0);
CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(secp256k1_musig_session_init_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0);
CHECK(ecount == 4); CHECK(ecount == 4);
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0);
CHECK(ecount == 5);
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0);
CHECK(ecount == 5); CHECK(ecount == 5);
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0);
CHECK(ecount == 6);
if (SIZE_MAX > UINT32_MAX) { if (SIZE_MAX > UINT32_MAX) {
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0);
} }
CHECK(ecount == 5); CHECK(ecount == 7);
CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1);
CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0);
CHECK(secp256k1_musig_compute_messagehash(none, msghash, &session[0]) == 0);
/** Signing step 0 -- exchange nonce commitments */ /** Signing step 0 -- exchange nonce commitments */
ecount = 0; ecount = 0;
{ {
secp256k1_pubkey nonce; unsigned char nonce[32];
secp256k1_musig_session session_0_tmp;
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
/* Can obtain public nonce after commitments have been exchanged; still can't sign */ /* Can obtain public nonce after commitments have been exchanged; still can't sign */
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, nonce, ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 0); CHECK(secp256k1_musig_partial_sign(none, &session_0_tmp, &partial_sig[0]) == 0);
CHECK(ecount == 0); CHECK(ecount == 1);
} }
/** Signing step 1 -- exchange nonces */ /** Signing step 1 -- exchange nonces */
ecount = 0; ecount = 0;
{ {
secp256k1_pubkey public_nonce[3]; unsigned char public_nonce[3][32];
secp256k1_musig_session session_0_tmp;
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 2, NULL) == 1);
CHECK(ecount == 0); CHECK(ecount == 0);
CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2, NULL) == 0); /* Reset session */
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, public_nonce[0], ncs, 2, NULL) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], NULL, &public_nonce[0], ncs, 2, NULL) == 0); /* uninitialized session */
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, public_nonce[0], ncs, 2, NULL) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, NULL, ncs, 2, NULL) == 0); CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, public_nonce[0], ncs, 2, NULL) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], NULL, 2, NULL) == 0); CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0);
CHECK(ecount == 4); CHECK(ecount == 4);
/* Number of commitments and number of signers are different */ CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], NULL, 2, NULL) == 0);
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 1, NULL) == 0);
CHECK(ecount == 4);
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer0[0], &public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0);
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1);
CHECK(ecount == 4);
CHECK(secp256k1_musig_set_nonce(none, NULL, &public_nonce[0]) == 0);
CHECK(ecount == 5); CHECK(ecount == 5);
CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); /* Number of commitments and number of signers are different */
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 1, NULL) == 0);
CHECK(ecount == 6); CHECK(ecount == 6);
CHECK(secp256k1_musig_set_nonce(none, &signer1[0], &public_nonce[0]) == 1); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer1[1], &public_nonce[1]) == 1); CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], &public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], &public_nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(none, &signer0[0], public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[0]) == 0);
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1);
CHECK(ecount == 6);
CHECK(secp256k1_musig_set_nonce(none, NULL, public_nonce[0]) == 0);
CHECK(ecount == 7);
CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0);
CHECK(ecount == 8);
CHECK(secp256k1_musig_set_nonce(none, &signer1[0], public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &signer1[1], public_nonce[1]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], public_nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], public_nonce[1]) == 1);
ecount = 0; ecount = 0;
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &nonce_is_negated, &adaptor) == 0); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, &adaptor) == 1);
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &combined_nonce_parity, &adaptor) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], NULL, 2, &nonce_is_negated, &adaptor) == 0); /* Uninitialized session */
CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &combined_nonce_parity, &adaptor) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &combined_nonce_parity, &adaptor) == 0);
CHECK(ecount == 3);
/* Number of signers differs from number during intialization */ /* Number of signers differs from number during intialization */
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 1, &nonce_is_negated, &adaptor) == 0); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &combined_nonce_parity, &adaptor) == 0);
CHECK(ecount == 2); CHECK(ecount == 4);
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, NULL, &adaptor) == 1); CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1);
CHECK(ecount == 2); CHECK(ecount == 4);
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, NULL) == 1); memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, NULL) == 1);
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &combined_nonce_parity, &adaptor) == 1);
CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &nonce_is_negated, &adaptor) == 1); CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &combined_nonce_parity, &adaptor) == 1);
CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &nonce_is_negated, &adaptor) == 1); CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &combined_nonce_parity, &adaptor) == 1);
} }
/** Signing step 2 -- partial signatures */ /** Signing step 2 -- partial signatures */
@ -310,14 +360,17 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
CHECK(ecount == 0); CHECK(ecount == 0);
CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0); CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); /* Uninitialized session */
CHECK(secp256k1_musig_partial_sign(none, &session_uninitialized, &partial_sig[0]) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1);
CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1);
/* observer can't sign */ /* observer can't sign */
CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0);
CHECK(ecount == 2); CHECK(ecount == 4);
ecount = 0; ecount = 0;
CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1);
@ -344,14 +397,17 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0); CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); /* Unitialized session */
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session_uninitialized, &signer0[0], &partial_sig[0], &pk[0]) == 0);
CHECK(ecount == 4); CHECK(ecount == 4);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0);
CHECK(ecount == 5);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0);
CHECK(ecount == 5);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0);
CHECK(ecount == 5);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0);
CHECK(ecount == 6); CHECK(ecount == 6);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0);
CHECK(ecount == 6);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0);
CHECK(ecount == 7);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1);
@ -359,21 +415,21 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1);
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1); CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1);
CHECK(ecount == 6); CHECK(ecount == 7);
/** Adaptor signature verification */ /** Adaptor signature verification */
memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1]));
ecount = 0; ecount = 0;
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, nonce_is_negated) == 1); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, combined_nonce_parity) == 1);
CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0); CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, nonce_is_negated) == 0); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, combined_nonce_parity) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, nonce_is_negated) == 0); CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, combined_nonce_parity) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
/** Signing combining and verification */ /** Signing combining and verification */
@ -386,28 +442,31 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0); CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); /* Unitialized session */
CHECK(secp256k1_musig_partial_sig_combine(none, &session_uninitialized, final_sig, partial_sig_adapted, 2) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0);
CHECK(ecount == 4);
{ {
secp256k1_musig_partial_signature partial_sig_tmp[2]; secp256k1_musig_partial_signature partial_sig_tmp[2];
partial_sig_tmp[0] = partial_sig_adapted[0]; partial_sig_tmp[0] = partial_sig_adapted[0];
partial_sig_tmp[1] = partial_sig_overflow; partial_sig_tmp[1] = partial_sig_overflow;
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0); CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0);
} }
CHECK(ecount == 3); CHECK(ecount == 4);
/* Wrong number of partial sigs */ /* Wrong number of partial sigs */
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0); CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0);
CHECK(ecount == 3); CHECK(ecount == 4);
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1);
CHECK(ecount == 3); CHECK(ecount == 4);
CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1); CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1);
/** Secret adaptor can be extracted from signature */ /** Secret adaptor can be extracted from signature */
ecount = 0; ecount = 0;
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, nonce_is_negated) == 1); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, combined_nonce_parity) == 1);
CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0);
CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0); CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
@ -417,7 +476,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
unsigned char final_sig_tmp[64]; unsigned char final_sig_tmp[64];
memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp)); memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp));
memcpy(&final_sig_tmp[32], ones, 32); memcpy(&final_sig_tmp[32], ones, 32);
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, combined_nonce_parity) == 0);
} }
CHECK(ecount == 2); CHECK(ecount == 2);
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0);
@ -426,7 +485,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
secp256k1_musig_partial_signature partial_sig_tmp[2]; secp256k1_musig_partial_signature partial_sig_tmp[2];
partial_sig_tmp[0] = partial_sig[0]; partial_sig_tmp[0] = partial_sig[0];
partial_sig_tmp[1] = partial_sig_overflow; partial_sig_tmp[1] = partial_sig_overflow;
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, combined_nonce_parity) == 0);
} }
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1);
@ -446,7 +505,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
* ones and return the resulting messagehash. This should not result in a different * ones and return the resulting messagehash. This should not result in a different
* messagehash because the public keys of the signers are only used during session * messagehash because the public keys of the signers are only used during session
* initialization. */ * initialization. */
int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id) {
secp256k1_musig_session session; secp256k1_musig_session session;
secp256k1_musig_session session_tmp; secp256k1_musig_session session_tmp;
unsigned char nonce_commitment[32]; unsigned char nonce_commitment[32];
@ -456,26 +515,26 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256
secp256k1_xonly_pubkey pks_tmp[2]; secp256k1_xonly_pubkey pks_tmp[2];
secp256k1_xonly_pubkey combined_pk_tmp; secp256k1_xonly_pubkey combined_pk_tmp;
secp256k1_musig_pre_session pre_session_tmp; secp256k1_musig_pre_session pre_session_tmp;
secp256k1_pubkey nonce; unsigned char nonce[32];
/* Set up signers with different public keys */ /* Set up signers with different public keys */
secp256k1_testrand256(sk_dummy); secp256k1_testrand256(sk_dummy);
pks_tmp[0] = pks[0]; pks_tmp[0] = pks[0];
CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1);
CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1);
CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1);
CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1);
CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0);
/* Call get_public_nonce with different signers than the signers the session was /* Call get_public_nonce with different signers than the signers the session was
* initialized with. */ * initialized with. */
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, &nonce, nonce_commitments, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, nonce, nonce_commitments, 2, NULL) == 1);
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, &nonce, nonce_commitments, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, nonce, nonce_commitments, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1);
return secp256k1_musig_compute_messagehash(ctx, msghash, &session); secp256k1_musig_compute_messagehash(ctx, msghash, &session);
} }
/* Creates a new session (with a different session id) and tries to use that session /* Creates a new session (with a different session id) and tries to use that session
@ -483,24 +542,24 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256
* commitments of signers_other do not match the nonce commitments the new session * commitments of signers_other do not match the nonce commitments the new session
* was initialized with. If do_test is 0, the correct signers are being used and * was initialized with. If do_test is 0, the correct signers are being used and
* therefore the function should return 1. */ * therefore the function should return 1. */
int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) {
secp256k1_musig_session session; secp256k1_musig_session session;
secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data signers[2];
secp256k1_musig_session_signer_data *signers_to_use; secp256k1_musig_session_signer_data *signers_to_use;
unsigned char nonce_commitment[32]; unsigned char nonce_commitment[32];
unsigned char session_id[32]; unsigned char session_id[32];
secp256k1_pubkey nonce; unsigned char nonce[32];
const unsigned char *ncs[2]; const unsigned char *ncs[2];
/* Initialize new signers */ /* Initialize new signers */
secp256k1_testrand256(session_id); secp256k1_testrand256(session_id);
CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1);
ncs[0] = nonce_commitment_other; ncs[0] = nonce_commitment_other;
ncs[1] = nonce_commitment; ncs[1] = nonce_commitment;
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL); secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL);
if (do_test) { if (do_test) {
signers_to_use = signers_other; signers_to_use = signers_other;
@ -514,7 +573,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *
* parameters but without a message. Will test that the message must be * parameters but without a message. Will test that the message must be
* provided with `get_public_nonce`. * provided with `get_public_nonce`.
*/ */
void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) {
/* Create context for testing ARG_CHECKs by setting an illegal_callback. */ /* Create context for testing ARG_CHECKs by setting an illegal_callback. */
secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
int ecount = 0; int ecount = 0;
@ -522,71 +581,37 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo
secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data signers[2];
unsigned char nonce_commitment[32]; unsigned char nonce_commitment[32];
const unsigned char *ncs[2]; const unsigned char *ncs[2];
secp256k1_pubkey nonce; unsigned char nonce[32];
secp256k1_musig_partial_signature partial_sig; secp256k1_musig_partial_signature partial_sig;
secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount);
CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1);
ncs[0] = nonce_commitment_other; ncs[0] = nonce_commitment_other;
ncs[1] = nonce_commitment; ncs[1] = nonce_commitment;
/* Trying to get the nonce without providing a message fails. */ /* Trying to get the nonce without providing a message fails. */
CHECK(ecount == 0); CHECK(ecount == 0);
CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, &nonce, ncs, 2, NULL) == 0); CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, NULL) == 0);
CHECK(ecount == 1); CHECK(ecount == 1);
/* Providing a message should make get_public_nonce succeed. */ /* Providing a message should make get_public_nonce succeed. */
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, msg) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, msg) == 1);
/* Trying to set the message again fails. */ /* Trying to set the message again fails. */
CHECK(ecount == 1); CHECK(ecount == 1);
CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, &nonce, ncs, 2, msg) == 0); CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, msg) == 0);
CHECK(ecount == 2); CHECK(ecount == 2);
/* Check that it's working */ /* Check that it's working */
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1);
CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig)); CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig));
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1])); CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1]));
} secp256k1_context_destroy(ctx_tmp);
/* Recreates a session with the given session_id, signers, pk, msg etc. parameters
* and tries to verify and combine partial sigs. If do_combine is 0, the
* combine_nonces step is left out. In that case verify and combine should fail and
* this function should return 0. */
int musig_state_machine_missing_combine_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) {
secp256k1_musig_session session;
secp256k1_musig_session_signer_data signers[2];
unsigned char nonce_commitment[32];
const unsigned char *ncs[2];
secp256k1_pubkey nonce;
secp256k1_musig_partial_signature partial_sigs[2];
unsigned char sig[64];
int partial_verify, sig_combine;
CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1);
ncs[0] = nonce_commitment_other;
ncs[1] = nonce_commitment;
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1);
partial_sigs[0] = *partial_sig_other;
partial_sigs[1] = *partial_sig;
if (do_combine != 0) {
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1);
}
partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]);
sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, sig, partial_sigs, 2);
if (do_combine != 0) {
/* Return 1 if both succeeded */
return partial_verify && sig_combine;
}
/* Return 0 if both failed */
return partial_verify || sig_combine;
} }
void musig_state_machine_tests(secp256k1_scratch_space *scratch) { void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_VERIFY);
size_t i; size_t i;
secp256k1_musig_session session[2]; secp256k1_musig_session session[2];
secp256k1_musig_session_signer_data signers0[2]; secp256k1_musig_session_signer_data signers0[2];
@ -598,11 +623,16 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
secp256k1_xonly_pubkey pk[2]; secp256k1_xonly_pubkey pk[2];
secp256k1_xonly_pubkey combined_pk; secp256k1_xonly_pubkey combined_pk;
secp256k1_musig_pre_session pre_session; secp256k1_musig_pre_session pre_session;
secp256k1_pubkey nonce[2]; unsigned char nonce[2][32];
const unsigned char *ncs[2]; const unsigned char *ncs[2];
secp256k1_musig_partial_signature partial_sig[2]; secp256k1_musig_partial_signature partial_sig[2];
unsigned char sig[64];
unsigned char msghash1[32]; unsigned char msghash1[32];
unsigned char msghash2[32]; unsigned char msghash2[32];
int ecount;
secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount);
ecount = 0;
/* Run state machine with the same objects twice to test that it's allowed to /* Run state machine with the same objects twice to test that it's allowed to
* reinitialize session and session_signer_data. */ * reinitialize session and session_signer_data. */
@ -616,54 +646,63 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1);
CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
/* Can't combine nonces unless we're through round 1 already */
ecount = 0;
CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0);
CHECK(ecount == 1);
/* Set nonce commitments */ /* Set nonce commitments */
ncs[0] = nonce_commitment[0]; ncs[0] = nonce_commitment[0];
ncs[1] = nonce_commitment[1]; ncs[1] = nonce_commitment[1];
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1);
/* Changing a nonce commitment is not okay */ /* Calling the function again is not okay */
ncs[1] = (unsigned char*) "this isn't a nonce commitment..."; ecount = 0;
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 0); CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, nonce[0], ncs, 2, NULL) == 0);
/* Repeating with the same nonce commitments is okay */ CHECK(ecount == 1);
ncs[1] = nonce_commitment[1];
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1);
/* Get nonce for signer 1 */ /* Get nonce for signer 1 */
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1);
/* Set nonces */ /* Set nonces */
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1);
/* Can't set nonce that doesn't match nonce commitment */ /* Can't set nonce that doesn't match nonce commitment */
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[0]) == 0); CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[0]) == 0);
/* Set correct nonce */ /* Set correct nonce */
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1);
/* Combine nonces */ /* Combine nonces */
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1);
/* Not everyone is present from signer 1's view */ /* Not everyone is present from signer 1's view */
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0);
/* Make everyone present */ /* Make everyone present */
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1);
/* Can't combine nonces from signers of a different session */ /* Can't combine nonces from signers of a different session */
CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 1) == 0);
CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 0) == 1);
/* Partially sign */ /* Partially sign */
CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1);
/* Can't verify or sign until nonce is combined */ /* Can't verify, sign or combine signatures until nonce is combined */
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); ecount = 0;
CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 0); CHECK(secp256k1_musig_partial_sig_verify(ctx_tmp, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_musig_partial_sign(ctx_tmp, &session[1], &partial_sig[1]) == 0);
CHECK(ecount == 2);
memset(&partial_sig[1], 0, sizeof(partial_sig[1]));
CHECK(secp256k1_musig_partial_sig_combine(ctx_tmp, &session[1], sig, partial_sig, 2) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1);
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1);
/* messagehash should be the same as a session whose get_public_nonce was called /* messagehash should be the same as a session whose get_public_nonce was called
* with different signers (i.e. they diff in public keys). This is because the * with different signers (i.e. they diff in public keys). This is because the
* public keys of the signers is set in stone when initializing the session. */ * public keys of the signers is set in stone when initializing the session. */
CHECK(secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]) == 1); secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]);
CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, nonce[0], sk[1], session_id[1]);
CHECK(memcmp(msghash1, msghash2, 32) == 0); CHECK(memcmp(msghash1, msghash2, 32) == 0);
CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1);
@ -671,12 +710,9 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
/* Wrong signature */ /* Wrong signature */
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0);
/* Can't get the public nonce until msg is set */ /* Can't get the public nonce until msg is set */
musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], nonce[0], sk[1], session_id[1], msg);
/* Can't verify and combine partial sigs until nonces are combined */
CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0);
CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1);
} }
secp256k1_context_destroy(ctx_tmp);
} }
void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
@ -707,10 +743,10 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
unsigned char noncommit_b[2][32]; unsigned char noncommit_b[2][32];
const unsigned char *noncommit_a_ptr[2]; const unsigned char *noncommit_a_ptr[2];
const unsigned char *noncommit_b_ptr[2]; const unsigned char *noncommit_b_ptr[2];
secp256k1_pubkey pubnon_a[2]; unsigned char pubnon_a[2][32];
secp256k1_pubkey pubnon_b[2]; unsigned char pubnon_b[2][32];
int nonce_is_negated_a; int combined_nonce_parity_a;
int nonce_is_negated_b; int combined_nonce_parity_b;
secp256k1_musig_session_signer_data data_a[2]; secp256k1_musig_session_signer_data data_a[2];
secp256k1_musig_session_signer_data data_b[2]; secp256k1_musig_session_signer_data data_b[2];
@ -734,28 +770,28 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2));
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2));
CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0]));
CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1]));
noncommit_a_ptr[0] = noncommit_a[0]; noncommit_a_ptr[0] = noncommit_a[0];
noncommit_a_ptr[1] = noncommit_a[1]; noncommit_a_ptr[1] = noncommit_a[1];
CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0]));
CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1]));
noncommit_b_ptr[0] = noncommit_b[0]; noncommit_b_ptr[0] = noncommit_b[0];
noncommit_b_ptr[1] = noncommit_b[1]; noncommit_b_ptr[1] = noncommit_b[1];
/* Step 2: Exchange nonces */ /* Step 2: Exchange nonces */
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, &pubnon_a[0], noncommit_a_ptr, 2, NULL)); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, pubnon_a[0], noncommit_a_ptr, 2, NULL));
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, &pubnon_a[1], noncommit_a_ptr, 2, NULL)); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, pubnon_a[1], noncommit_a_ptr, 2, NULL));
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, &pubnon_b[0], noncommit_b_ptr, 2, NULL)); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, pubnon_b[0], noncommit_b_ptr, 2, NULL));
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, &pubnon_b[1], noncommit_b_ptr, 2, NULL)); CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, pubnon_b[1], noncommit_b_ptr, 2, NULL));
CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], &pubnon_a[0])); CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], pubnon_a[0]));
CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], &pubnon_a[1])); CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], pubnon_a[1]));
CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], &pubnon_b[0])); CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], pubnon_b[0]));
CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], &pubnon_b[1])); CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], pubnon_b[1]));
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &nonce_is_negated_a, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &combined_nonce_parity_a, &pub_adaptor));
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor));
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &nonce_is_negated_b, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &combined_nonce_parity_b, &pub_adaptor));
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor)); CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor));
/* Step 3: Signer 0 produces partial signatures for both chains. */ /* Step 3: Signer 0 produces partial signatures for both chains. */
@ -771,16 +807,16 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
/* Step 5: Signer 0 adapts its own partial signature and combines it with the /* Step 5: Signer 0 adapts its own partial signature and combines it with the
* partial signature from signer 1. This results in a complete signature which * partial signature from signer 1. This results in a complete signature which
* is broadcasted by signer 0 to take B-coins. */ * is broadcasted by signer 0 to take B-coins. */
CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, combined_nonce_parity_b));
memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1]));
CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1); CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1);
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1);
/* Step 6: Signer 1 extracts adaptor from the published signature, applies it to /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to
* other partial signature, and takes A-coins. */ * other partial signature, and takes A-coins. */
CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, combined_nonce_parity_b) == 1);
CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */
CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, combined_nonce_parity_a));
CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1]));
CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1); CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1);
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1);
@ -819,6 +855,97 @@ void sha256_tag_test(void) {
CHECK(memcmp(buf, buf2, 32) == 0); CHECK(memcmp(buf, buf2, 32) == 0);
} }
/* Attempts to create a signature for the combined public key using given secret
* keys and pre_session. */
void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_pre_session *pre_session) {
secp256k1_musig_session session[2];
secp256k1_musig_session_signer_data signers0[2];
secp256k1_musig_session_signer_data signers1[2];
secp256k1_xonly_pubkey pk[2];
unsigned char session_id[2][32];
unsigned char msg[32];
unsigned char nonce_commitment[2][32];
unsigned char nonce[2][32];
const unsigned char *ncs[2];
secp256k1_musig_partial_signature partial_sig[2];
unsigned char final_sig[64];
secp256k1_testrand256(session_id[0]);
secp256k1_testrand256(session_id[1]);
secp256k1_testrand256(msg);
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1);
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1);
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, 0, sk0) == 1);
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, 1, sk1) == 1);
/* Set nonce commitments */
ncs[0] = nonce_commitment[0];
ncs[1] = nonce_commitment[1];
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1);
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1);
/* Set nonces */
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1);
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1);
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1);
CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1);
CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1);
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1);
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1);
CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2));
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, combined_pubkey) == 1);
}
/* In this test we create a combined public key P and a commitment Q = P +
* hash(P, contract)*G. Then we test that we can sign for both public keys. In
* order to sign for Q we use the tweak32 argument of partial_sig_combine. */
void musig_tweak_test(secp256k1_scratch_space *scratch) {
unsigned char sk[2][32];
secp256k1_xonly_pubkey pk[2];
secp256k1_musig_pre_session pre_session_P;
secp256k1_musig_pre_session pre_session_Q;
secp256k1_xonly_pubkey P;
unsigned char P_serialized[32];
secp256k1_pubkey Q;
int Q_parity;
secp256k1_xonly_pubkey Q_xonly;
unsigned char Q_serialized[32];
secp256k1_sha256 sha;
unsigned char contract[32];
unsigned char ec_commit_tweak[32];
/* Setup */
secp256k1_testrand256(sk[0]);
secp256k1_testrand256(sk[1]);
secp256k1_testrand256(contract);
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk, 2) == 1);
CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1);
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, P_serialized, 32);
secp256k1_sha256_write(&sha, contract, 32);
secp256k1_sha256_finalize(&sha, ec_commit_tweak);
pre_session_Q = pre_session_P;
CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &pre_session_Q, &Q, &P, ec_commit_tweak) == 1);
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &Q_xonly, &Q_parity, &Q));
CHECK(secp256k1_xonly_pubkey_serialize(ctx, Q_serialized, &Q_xonly));
/* Check that musig_pubkey_tweak_add produces same result as
* xonly_pubkey_tweak_add. */
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, Q_serialized, Q_parity, &P, ec_commit_tweak) == 1);
/* Test signing for P */
musig_tweak_test_helper(&P, sk[0], sk[1], &pre_session_P);
/* Test signing for Q */
musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q);
}
void run_musig_tests(void) { void run_musig_tests(void) {
int i; int i;
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
@ -829,8 +956,10 @@ void run_musig_tests(void) {
musig_api_tests(scratch); musig_api_tests(scratch);
musig_state_machine_tests(scratch); musig_state_machine_tests(scratch);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
/* Run multiple times to ensure that the nonce is negated in some tests */ /* Run multiple times to ensure that pk and nonce have different y
* parities */
scriptless_atomic_swap(scratch); scriptless_atomic_swap(scratch);
musig_tweak_test(scratch);
} }
sha256_tag_test(); sha256_tag_test();