10 KiB
MuSig - Rogue-Key-Resistant Multisignatures Module
This module implements the MuSig [1] multisignature scheme. The majority of the module is an API designed to be used by signing or auditing participants in a multisignature scheme. This involves a somewhat complex state machine and significant effort has been taken to prevent accidental misuse of the API in ways that could lead to accidental signatures or loss of key material.
The resulting signatures are valid Schnorr signatures as described in [2].
Theory
In MuSig all signers contribute key material to a single signing key, using the equation
P = sum_i µ_i * P_i
where P_i
is the public key of the i
th signer and µ_i
is a so-called
MuSig coefficient computed according to the following equation
L = H(P_1 || P_2 || ... || P_n)
µ_i = H(L || i)
where H is a hash function modelled as a random oracle.
To produce a multisignature (s, R)
on a message m
using verification key
P
, signers act as follows:
- Each computes a nonce, or ephemeral keypair,
(k_i, R_i)
. Every signer communicatesH(R_i)
to every participant (both signers and auditors). - Upon receipt of every
H(R_i)
, each signer communicatesR_i
to every participant. The recipients check that eachR_i
is consistent with the previously-communicated hash. - Each signer computes a combined nonce
R = sum_i R_i
and shared challengee = H(R || P || m)
and partial signatures_i = k_i + µ_i*x_i*e
wherex_i
is the secret key corresponding toP_i
.
The complete signature is then the (s, R)
where s = sum_i s_i
and R = sum_i R_i
.
API Usage
The following sections describe use of our API, and are mirrored in code in src/modules/musig/example.c
.
It is essential to security that signers use a unique uniformly random nonce for all
signing sessions, and that they do not reuse these nonces even in the case that a
signing session fails to complete. To that end, all signing state is encapsulated
in the data structure secp256k1_musig_session
. The API does not expose any
functionality to serialize or deserialize this structure; it is designed to exist
only in memory.
Users who need to persist this structure must take additional security measures
which cannot be enforced by a C API. Some guidance is provided in the documentation
for this data structure in include/secp256k1_musig.h
.
Key Generation
To use MuSig, users must first compute their combined public key P
, which is
suitable for use on a blockchain or other public key repository. They do this
by calling secp256k1_musig_pubkey_combine
.
This function takes as input a list of public keys P_i
in the argument
pubkeys
. It outputs the combined public key P
in the out-pointer combined_pk
and hash L
in the out-pointer pk_hash32
, if this pointer is non-NULL.
Signing
A participant who wishes to sign a message (as opposed to observing/auditing the signature process, which is also a supported mode) acts as follows.
Signing Participant
- The signer starts the session by calling
secp256k1_musig_session_initialize
. This function outputs- an initialized session state in the out-pointer
session
- an array of initialized signer data in the out-pointer
signers
- a commitment
H(R_i)
to a nonce in the out-pointernonce_commitment32
It takes as input - a unique session ID
session_id32
- (optionally) a message to be signed
msg32
- the combined public key output from
secp256k1_musig_pubkey_combine
- the public key hash output from
secp256k1_musig_pubkey_combine
- the signer's index
i
my_index
- the signer's secret key
seckey
- an initialized session state in the out-pointer
- The signer then communicates
H(R_i)
to all other signers, and receives commitmentsH(R_j)
from all other signersj
. These hashes are simply length-32 byte arrays which can be communicated however is communicated. - Once all signers nonce commitments have been received, the signer records
these commitments with the function
secp256k1_musig_session_get_public_nonce
. If the signer did not provide a message tosecp256k1_musig_session_initialize
, a message must be provided now. This function updates in place- the session state
session
- the array of signer data
signers
taking in as input the list of commitmentscommitments
and outputting the signer's public nonceR_i
in the out-pointernonce
.
- the session state
- The signer then communicates
R_i
to all other signers, and receivesR_j
from each signerj
. On receipt of a nonceR_j
he calls the functionsecp256k1_musig_set_nonce
to record this fact. This function checks that the received nonce is consistent with the previously-received nonce and will return 0 in this case. The signer must also call this function with his own nonce and his own indexi
. These noncesR_i
are secp256k1 public keys; they should be serialized usingsecp256k1_ec_pubkey_serialize
and parsed withsecp256k1_ec_pubkey_parse
. - Once all nonces have been exchanged in this way, signers are able to compute
their partial signatures. They do so by calling
secp256k1_musig_session_combine_nonces
which updates in place- the session state
session
- the array of signer data
signers
It outputs an auxiliary integernonce_is_negated
and has an auxiliary inputadaptor
. Both of these may be set to NULL for ordinary signing purposes.
- the session state
- The signer computes a partial signature
s_i
using the functionsecp256k1_musig_partial_sign
which takes the session state as input and partial signature as output. - The signer then communicates the partial signature
s_i
to all other signers, or to a central coordinator. These partial signatures should be serialized usingmusig_partial_signature_serialize
and parsed usingmusig_partial_signature_parse
. - Each signer calls
secp256k1_musig_partial_sig_verify
on the other signers' partial signatures to verify their correctness. If only the validity of the final signature is important, not assigning blame, this step can be skipped. - Any signer, or central coordinator, may combine the partial signatures to obtain
a complete signature using
secp256k1_musig_partial_sig_combine
. This function takes a signing session and array of MuSig partial signatures, and outputs a single Schnorr signature.
Non-signing Participant
A participant who wants to verify the signing process, i.e. check that nonce commitments are consistent and partial signatures are correct without contributing a partial signature, may do so using the above instructions except for the following changes:
- A signing session should be produced using
musig_session_initialize_verifier
rather thanmusig_session_initialize
; this function takes no secret data or signer index. - The participant receives nonce commitments, public nonces and partial signatures,
but does not produce these values. Therefore
secp256k1_musig_session_get_public_nonce
andsecp256k1_musig_partial_sign
are not called.
Verifier
The final signature is simply a valid Schnorr signature using the combined public key. It
can be verified using the secp256k1_schnorrsig_verify
with the correct message and
public key output from secp256k1_musig_pubkey_combine
.
Atomic Swaps
The signing API supports the production of "adaptor signatures", modified partial signatures which are offset by an auxiliary secret known to one party. That is,
- One party generates a (secret) adaptor
t
with corresponding (public) adaptorT = t*G
. - When combining nonces, each party adds
T
to the total nonce used in the signature. - The party who knows
t
must "adapt" their partial signature witht
to complete the signature. - Any party who sees both the final signature and the original partial signatures
can compute
t
.
Using these adaptor signatures, two 2-of-2 MuSig signing protocols can be executed in parallel such that one party's partial signatures are made atomic. That is, when the other party learns one partial signature, she automatically learns the other. This has applications in cross-chain atomic swaps.
Such a protocol can be executed as follows. Consider two participants, Alice and Bob, who are simultaneously producing 2-of-2 multisignatures for two blockchains A and B. They act as follows.
- Before the protocol begins, Bob chooses a 32-byte auxiliary secret
t
at random and computes a corresponding public pointT
by callingsecp256k1_ec_pubkey_create
. He communicatesT
to Alice. - Together, the parties execute steps 1-4 of the signing protocol above.
- At step 5, when combining the two parties' public nonces, both parties call
secp256k1_musig_session_combine_nonces
withadaptor
set toT
andnonce_is_negated
set to a non-NULL pointer to int. - Steps 6 and 7 proceed as before. Step 8, verifying the partial signatures, is now essential to the security of the protocol and must not be omitted!
The above steps are executed identically for both signing sessions. However, step 9 will not work as before, since the partial signatures will not add up to a valid total signature. Additional steps must be taken, and it is at this point that the two signing sessions diverge. From here on we consider "Session A" which benefits Alice (e.g. which sends her coins) and "Session B" which benefits Bob (e.g. which sends him coins).
- In Session B, Bob calls
secp256k1_musig_partial_sig_adapt
with his partial signature andt
, to produce an adaptor signature. He can then callsecp256k1_musig_partial_sig_combine
with this adaptor signature and Alice's partial signature, to produce a complete signature for blockchain B. - Alice reads this signature from blockchain B. She calls
secp256k1_musig_extract_secret_adaptor
, passing the complete signature along with her and Bob's partial signatures from Session B. This function outputst
, which until this point was only known to Bob. - In Session A, Alice is now able to replicate Bob's action, calling
secp256k1_musig_partial_sig_adapt
with her own partial signature andt
, ultimately producing a complete signature on blockchain A.
[1] https://eprint.iacr.org/2018/068 [2] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki