Skip to content

Commit

Permalink
Merge pull request #105 from jonasnick/update-musig
Browse files Browse the repository at this point in the history
MuSig state machine simplifictions, API improvements and taproot tweaking
  • Loading branch information
apoelstra committed Nov 30, 2020
2 parents 0d71b6c + 3fb4d6d commit ff4714e
Show file tree
Hide file tree
Showing 6 changed files with 628 additions and 407 deletions.
10 changes: 5 additions & 5 deletions .travis.yml
Expand Up @@ -22,9 +22,9 @@ env:
- 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=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 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 ASM=x86_64
- BIGNUM=no
Expand All @@ -33,10 +33,10 @@ env:
- BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
- CPPFLAGS=-DDETERMINISTIC
- 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=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:
fast_finish: true
include:
Expand Down Expand Up @@ -84,7 +84,7 @@ matrix:
- libc6-dbg:i386
# S390x build (big endian system)
- 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

# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
Expand Down
162 changes: 107 additions & 55 deletions include/secp256k1_musig.h
Expand Up @@ -20,18 +20,25 @@ extern "C" {
*/

/** Data structure containing auxiliary data generated in `pubkey_combine` and
* required for `session_*_initialize`.
* required for `session_*_init`.
* Fields:
* magic: Set during initialization in `pubkey_combine` in order to allow
* detecting an uninitialized object.
* pk_hash: The 32-byte hash of the original public keys
* is_negated: Whether the MuSig-aggregated point was negated when
* converting it to the combined xonly pubkey.
* magic: Set during initialization in `pubkey_combine` to allow
* detecting an uninitialized object.
* pk_hash: The 32-byte hash of the original public keys
* pk_parity: Whether the MuSig-aggregated point was negated when
* 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 {
uint64_t magic;
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;

/** Data structure containing data related to a signing session resulting in a single
Expand All @@ -45,49 +52,49 @@ typedef struct {
* structure.
*
* 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`
* combined_pk: MuSig-computed combined xonly public key
* 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_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
* is `false`, it may still be used for verification purposes.
* seckey: If `has_secret_data`, the signer's secret key
* secnonce: If `has_secret_data`, the signer's secret nonce
* nonce: If `has_secret_data`, the signer's public nonce
* nonce_commitments_hash: If `has_secret_data` and `nonce_commitments_hash_is_set`,
* the hash of all signers' commitments
* nonce_commitments_hash_is_set: If `has_secret_data`, whether the
* nonce_commitments_hash has been set
* nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all
* signers' commitments
* combined_nonce: If round >= 2, the summed combined public nonce
* combined_nonce_parity: If round >= 2, the parity of the Y coordinate of above
* nonce.
*/
typedef struct {
secp256k1_xonly_pubkey combined_pk;
uint64_t magic;
int round;
secp256k1_musig_pre_session pre_session;
secp256k1_xonly_pubkey combined_pk;
uint32_t n_signers;
secp256k1_pubkey combined_nonce;
int nonce_is_set;
int nonce_is_negated;
int is_msg_set;
unsigned char msg[32];
int msg_is_set;
int has_secret_data;
unsigned char seckey[32];
unsigned char secnonce[32];
secp256k1_pubkey nonce;
secp256k1_xonly_pubkey nonce;
int partial_nonce_parity;
unsigned char nonce_commitments_hash[32];
int nonce_commitments_hash_is_set;
secp256k1_xonly_pubkey combined_nonce;
int combined_nonce_parity;
} secp256k1_musig_session;

/** Data structure containing data on all signers in a single session.
*
* The workflow for this structure is as follows:
*
* 1. This structure is initialized with `musig_session_initialize` or
* `musig_session_initialize_verifier`, which set the `index` field, and zero out
* 1. This structure is initialized with `musig_session_init` or
* `musig_session_init_verifier`, which set the `index` field, and zero out
* all other fields. The public session is initialized with the signers'
* nonce_commitments.
*
Expand All @@ -112,7 +119,7 @@ typedef struct {
typedef struct {
int present;
uint32_t index;
secp256k1_pubkey nonce;
secp256k1_xonly_pubkey nonce;
unsigned char nonce_commitment[32];
} secp256k1_musig_session_signer_data;

Expand All @@ -129,7 +136,8 @@ typedef struct {
unsigned char data[32];
} 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
* Args: ctx: pointer to a context object initialized for verification
Expand All @@ -138,11 +146,11 @@ typedef struct {
* multiexponentiation. If NULL, an inefficient algorithm is used.
* 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
* `musig_session_initialize`.
* `musig_session_init` or `musig_pubkey_tweak_add`.
* In: pubkeys: input array of public keys to combine. The order is important;
* a different order will result in a different combined public
* 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(
const secp256k1_context* ctx,
Expand All @@ -153,6 +161,42 @@ SECP256K1_API int secp256k1_musig_pubkey_combine(
size_t n_pubkeys
) 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
*
* Returns: 1: session is successfully initialized
Expand All @@ -172,14 +216,16 @@ SECP256K1_API int secp256k1_musig_pubkey_combine(
* because it reduces nonce misuse resistance. If NULL, must be
* set with `musig_session_get_public_nonce`.
* combined_pk: the combined xonly public key of all signers (cannot be NULL)
* pre_session: pointer to a musig_pre_session struct from
* `musig_pubkey_combine` (cannot be NULL)
* pre_session: pointer to a musig_pre_session struct after initializing
* 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
* 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)
*/
SECP256K1_API int secp256k1_musig_session_initialize(
SECP256K1_API int secp256k1_musig_session_init(
const secp256k1_context* ctx,
secp256k1_musig_session *session,
secp256k1_musig_session_signer_data *signers,
Expand All @@ -193,28 +239,33 @@ SECP256K1_API int secp256k1_musig_session_initialize(
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);

/** 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
* 0: signer data is missing commitments or session isn't initialized
* for signing
* Args: ctx: pointer to a context object (cannot be NULL)
* session: the signing session to get the nonce from (cannot be NULL)
* 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)
* Out: nonce: the nonce (cannot be NULL)
* In: commitments: array of 32-byte nonce commitments (cannot be NULL)
* Out: nonce32: filled with a 32-byte public nonce which is supposed to be
* 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
* number of signers participating in the MuSig.
* 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(
const secp256k1_context* ctx,
secp256k1_musig_session *session,
secp256k1_musig_session_signer_data *signers,
secp256k1_pubkey *nonce,
unsigned char *nonce32,
const unsigned char *const *commitments,
size_t n_commitments,
const unsigned char *msg32
Expand All @@ -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
* `musig_pubkey_combine` (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
* `n_signers` (cannot be NULL)
* commitments: array of pointers to 32-byte nonce commitments. Array
* length must equal to `n_signers` (cannot be NULL)
* n_signers: length of signers and commitments array. Number of signers
* participating in the MuSig. Must be greater than 0 and at most
* 2^32 - 1.
*/
SECP256K1_API int secp256k1_musig_session_initialize_verifier(
SECP256K1_API int secp256k1_musig_session_init_verifier(
const secp256k1_context* ctx,
secp256k1_musig_session *session,
secp256k1_musig_session_signer_data *signers,
Expand All @@ -259,13 +310,13 @@ SECP256K1_API int secp256k1_musig_session_initialize_verifier(
* Args: ctx: pointer to a context object (cannot be NULL)
* signer: pointer to the signer data to update (cannot be NULL). Must have
* been used with `musig_session_get_public_nonce` or initialized
* with `musig_session_initialize_verifier`.
* In: nonce: signer's alleged public nonce (cannot be NULL)
* with `musig_session_init_verifier`.
* In: nonce32: signer's alleged public nonce (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce(
const secp256k1_context* ctx,
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);

/** Updates a session with the combined public nonce of all signers. The combined
Expand All @@ -281,8 +332,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce(
* (cannot be NULL)
* n_signers: the length of the signers array. Must be the total number of
* signers participating in the MuSig.
* Out: nonce_is_negated: a pointer to an integer that indicates if the combined
* public nonce had to be negated.
* Out: nonce_parity: if non-NULL, a pointer to an integer that indicates the
* parity of the combined public nonce. Used for adaptor
* signatures.
* adaptor: point to add to the combined public nonce. If NULL, nothing is
* added to the combined nonce.
*/
Expand All @@ -291,7 +343,7 @@ SECP256K1_API int secp256k1_musig_session_combine_nonces(
secp256k1_musig_session *session,
const secp256k1_musig_session_signer_data *signers,
size_t n_signers,
int *nonce_is_negated,
int *nonce_parity,
const secp256k1_pubkey *adaptor
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

Expand Down Expand Up @@ -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
* 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)
* session: initialized session for which the combined nonce has been
* computed (cannot be NULL)
Expand All @@ -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)
* sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot
* 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(
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 nonce_parity
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Extracts a secret adaptor from a MuSig, given all parties' partial
Expand All @@ -418,15 +470,15 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt(
* In: sig64: complete 2-of-2 signature (cannot be NULL)
* partial_sigs: array of partial signatures (cannot be NULL)
* 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(
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 nonce_parity
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

#ifdef __cplusplus
Expand Down

0 comments on commit ff4714e

Please sign in to comment.