musig: add user documentation
This commit is contained in:
parent
0ad6b6036f
commit
ff16651273
@ -7,6 +7,10 @@
|
||||
* (https://eprint.iacr.org/2018/068.pdf). There's an example C source file in the
|
||||
* module's directory (src/modules/musig/example.c) that demonstrates how it can be
|
||||
* used.
|
||||
*
|
||||
* The documentation in this include file is for reference and may not be sufficient
|
||||
* for users to begin using the library. A full description of API usage can be found
|
||||
* in src/modules/musig/musig.md
|
||||
*/
|
||||
|
||||
/** Data structure containing data related to a signing session resulting in a single
|
||||
@ -15,22 +19,9 @@
|
||||
* This structure is not opaque, but it MUST NOT be copied or read or written to it
|
||||
* directly. A signer who is online throughout the whole process and can keep this
|
||||
* structure in memory can use the provided API functions for a safe standard
|
||||
* workflow.
|
||||
*
|
||||
* A signer who goes offline and needs to import/export or save/load this structure
|
||||
* **must** take measures prevent replay attacks wherein an old state is loaded and
|
||||
* the signing protocol forked from that point. One straightforward way to accomplish
|
||||
* this is to attach the output of a monotonic non-resettable counter (hardware
|
||||
* support is needed for this). Increment the counter before each output and
|
||||
* encrypt+sign the entire package. If a package is deserialized with an old counter
|
||||
* state or bad signature it should be rejected.
|
||||
*
|
||||
* Observe that an independent counter is needed for each concurrent signing session
|
||||
* such a device is involved in. To avoid fragility, it is therefore recommended that
|
||||
* any offline signer be usable for only a single session at once.
|
||||
*
|
||||
* Given access to such a counter, its output should be used as (or mixed into) the
|
||||
* session ID to ensure uniqueness.
|
||||
* workflow. See https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/
|
||||
* for more details about the risks associated with serializing or deserializing this
|
||||
* structure.
|
||||
*
|
||||
* Fields:
|
||||
* combined_pk: MuSig-computed combined public key
|
||||
|
199
src/modules/musig/musig.md
Normal file
199
src/modules/musig/musig.md
Normal file
@ -0,0 +1,199 @@
|
||||
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:
|
||||
|
||||
1. Each computes a nonce, or ephemeral keypair, `(k_i, R_i)`. Every signer
|
||||
communicates `H(R_i)` to every participant (both signers and auditors).
|
||||
2. Upon receipt of every `H(R_i)`, each signer communicates `R_i` to every
|
||||
participant. The recipients check that each `R_i` is consistent with the
|
||||
previously-communicated hash.
|
||||
3. Each signer computes a combined nonce
|
||||
`R = sum_i R_i`
|
||||
and shared challenge
|
||||
`e = H(R || P || m)`
|
||||
and partial signature
|
||||
`s_i = k_i + µ_i*x_i*e`
|
||||
where `x_i` is the secret key corresponding to `P_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
|
||||
|
||||
1. 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-pointer `nonce_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`
|
||||
2. The signer then communicates `H(R_i)` to all other signers, and receives
|
||||
commitments `H(R_j)` from all other signers `j`. These hashes are simply
|
||||
length-32 byte arrays which can be communicated however is communicated.
|
||||
3. Once all signers nonce commitments have been received, the signer records
|
||||
these commitments with the function `secp256k1_musig_session_get_public_nonce`.
|
||||
This function updates in place
|
||||
- the session state `session`
|
||||
- the array of signer data `signers`
|
||||
taking in as input the list of commitments `commitments` and outputting the
|
||||
signer's public nonce `R_i` in the out-pointer `nonce`.
|
||||
4. The signer then communicates `R_i` to all other signers, and receives `R_j`
|
||||
from each signer `j`. On receipt of a nonce `R_j` he calls the function
|
||||
`secp256k1_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 index `i`.
|
||||
These nonces `R_i` are secp256k1 public keys; they should be serialized using
|
||||
`secp256k1_ec_pubkey_serialize` and parsed with `secp256k1_ec_pubkey_parse`.
|
||||
5. 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 integer `nonce_is_negated` and has an auxiliary input
|
||||
`adaptor`. Both of these may be set to NULL for ordinary signing purposes.
|
||||
If the signer did not provide a message to `secp256k1_musig_session_initialize`,
|
||||
a message must be provided now by calling `secp256k1_musig_session_set_msg` which
|
||||
updates the session state in place.
|
||||
6. The signer computes a partial signature `s_i` using the function
|
||||
`secp256k1_musig_partial_sign` which takes the session state as input and
|
||||
partial signature as output.
|
||||
7. The signer then communicates the partial signature `s_i` to all other signers, or
|
||||
to a central coordinator. These partial signatures should be serialized using
|
||||
`musig_partial_signature_serialize` and parsed using `musig_partial_signature_parse`.
|
||||
8. 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.
|
||||
9. 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:
|
||||
|
||||
1. A signing session should be produced using `musig_session_initialize_verifier`
|
||||
rather than `musig_session_initialize`; this function takes no secret data or
|
||||
signer index.
|
||||
2. The participant receives nonce commitments, public nonces and partial signatures,
|
||||
but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce`
|
||||
and `secp256k1_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,
|
||||
1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`.
|
||||
2. When combining nonces, each party adds `T` to the total nonce used in the signature.
|
||||
3. The party who knows `t` must "adapt" their partial signature with `t` to complete the
|
||||
signature.
|
||||
4. 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.
|
||||
|
||||
1. Before the protocol begins, Bob chooses a 32-byte auxiliary secret `t` at random and
|
||||
computes a corresponding public point `T` by calling `secp256k1_ec_pubkey_create`.
|
||||
He communicates `T` to Alice.
|
||||
2. Together, the parties execute steps 1-4 of the signing protocol above.
|
||||
3. At step 5, when combining the two parties' public nonces, both parties call
|
||||
`secp256k1_musig_session_combine_nonces` with `adaptor` set to `T` and `nonce_is_negated`
|
||||
set to a non-NULL pointer to int.
|
||||
4. 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).
|
||||
|
||||
5. In Session B, Bob calls `secp256k1_musig_partial_sig_adapt` with his partial signature
|
||||
and `t`, to produce an adaptor signature. He can then call `secp256k1_musig_partial_sig_combine`
|
||||
with this adaptor signature and Alice's partial signature, to produce a complete
|
||||
signature for blockchain B.
|
||||
6. 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 outputs `t`, which until this point was only known to Bob.
|
||||
7. In Session A, Alice is now able to replicate Bob's action, calling
|
||||
`secp256k1_musig_partial_sig_adapt` with her own partial signature and `t`, 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user