State of
Signature Crypto
Overview
- n-of-n multisig (MuSig variants)
- MuSig2 usage patterns
- implementation status
- discussion about open questions in practice
- t-of-n threshold sig (FROST)
- signature aggregation
- half aggregation
- full aggregation
MuSig
MuSig Variants
- MuSig1 (deprecated)
- MuSig-DN (or alternatively GKMN'21 https://eprint.iacr.org/2021/1055.pdf )
- MuSig2
MuSig2:
High Level Explanation
- multiple parties (each having an individual pubkey) sign the same message under a *single* aggregate pubkey
- great for Taproot
- simple key setup: everybody can compute aggregate pubkey from individual pubkeys
- 2-round signing protocol
(all signers send two broadcast messages) - first round can be run before message to be signed (transaction) is known
- effectively one round
MuSig2:
2-of-2 example
\(X_1\)
\(X_2\)
\( R_1', R_1''\)
\( R_2', R_2''\)
\(s_1\)
\(s_2\)
\( R_i=R_i'(R_i'')^b\)
\( b = H_\textit{non}(\widetilde X,m, R_1'R_2', R_1''R_2'')\)
\( c = H_\textit{sig}(\widetilde X, R_1R_2, m) \)
\(\text{return}\ (R_1R_2,s_1+s_2)\)
\(\widetilde X=X_1^{\textcolor{#00c3ff}{}{a_1}}\cdot X_2^{\textcolor{#00c3ff}{}{a_2}}\)
\( a_i = H_\textit{agg}(X_i, \{\textit{pk}_1, \textit{pk}_2\})\)
MuSig2:
2-of-2 example
\(X_1\)
\(X_2\)
\( R_1', R_1'', m\)
\( R_2', R_2''\)
\(s_2\)
\(\text{return}\ (R_1R_2,s_1+s_2)\)
\(\widetilde X=X_1^{\textcolor{#00c3ff}{}{a_1}}\cdot X_2^{\textcolor{#00c3ff}{}{a_2}}\)
\( a_i = H_\textit{agg}(X_i, \{\textit{pk}_1, \textit{pk}_2\})\)
MuSig2:
2-of-2 example (precomp)
\(X_1\)
\(X_2\)
\( R_1', R_1''\)
\( R_2', R_2''\)
\(s_2, m\)
\(\text{return}\ (R_1R_2,s_1+s_2)\)
\(\widetilde X=X_1^{\textcolor{#00c3ff}{}{a_1}}\cdot X_2^{\textcolor{#00c3ff}{}{a_2}}\)
\( a_i = H_\textit{agg}(X_i, \{\textit{pk}_1, \textit{pk}_2\})\)
libsecp256k1-zkp implementation
- (draft) key agg spec: src/modules/musig/musig-spec.mediawiki
- Jonas' MuSig2 PR #131 (needs review)
- API is safe to use, if you
- provide fresh, uniform randomness to nonce gen fn (which returns a "secnonce")
- the secnonce structure is never copied or serialized.
- opaque data structures are never written to or read from directly.
Discussion
- (Hardware) Wallets
- need to keep state between rounds
- current workflow vs MuSig2 workflow
- sweet case:
Taproot(key = 2of2 MuSig,
script = 2of3 multisig)
- Key derivation and descriptors?
-
musig(xpub1/*, xpub2/*) vs
-
musig(xpub1, xpub2)/*
-
it would mean that to the outside world, the combined wallet is just an xpub.
-
-
- PSBT
Threshold
- t-of-n instead of n-of-n
- looks similar but key setup in fact much more involved
- no spontaneous key aggregation as in MuSig
- secure broadcast channel necessary
- key shares of other parties need to be backed up
- candidate scheme: FROST
- signing very similar to MuSig2
- looks solid
- there are bip340 compatible implementations
- ElementsProject/secp256k1-zkp/pull/138
- taurusgroup/multi-party-sig
- Tim is working on improvements
Cross-Input Signature Aggregation
Signature Aggregation
-
AggVerify((pk_1, m_1), ..., (pk_n, m_n), sig) -> {true, false}
-
Trivial solution:
sig = (sig_1, ..., sig_n)
-
Goal Nr 2: sig should be short
-
Note the different messages != multisignatures, MuSig, etc.
Schnorr Half Aggregation
-
Aggregate(sig_1, ..., sig_n) -> sig
-
|sig| ≈ 1/2 (|sig_1| + ... + |sig_n|)
- aggregation is non-interactive (can be done by block producers)
- proposed on Bitcoin mailing list ~2017, recent academic paper (Chalkias et al.)
Schnorr Full Aggregation
-
|sig| = |sig_1|
-
aggregation _is_ interactive
-
Tx-wide aggregation
-
can be combined with half aggregation
Consensus
- Requires new signature verification algorithm
- Only available update path: new SegWit version due to interactions with OP_SUCCESS
- Option: new SegWit version, only allow aggregation of key path spends
- But that's limiting
- Better: Move public keys out of Script
- instead of taproot merkle tree leafs being scripts
- leafs consist of both pubkey and script
- in order to spend need both sig for pubkey and script inputs
- Most advanced in that family of ideas: entroot (surprisingly elegant & simple update)
Savings for typical tx
| | bytes | weight units |
|-----------------------------------------------+-------+--------------|
| half aggregation | 20.6% | 7.6% |
| full aggregation | 26.1% | 9.6% |
| both | 33.6% | 12.4% |
| max (like infinite large full agged coinjoin) | 41.2% | 15.2% |
Discussion
- full aggregation: nonce generation similar to MuSig. Unless all other partial signatures come from trusted signers, no deterministic nonce generation, hence randomness (or non-repeating counter) required
- half aggregation:
- how to resubmit transactions after a reorgs (only a problem if used across transactions)?
- breaks adaptor sigs?
- more CPU/byte necessary to verify a block
- github.com/ElementsProject/cross-input-aggregation
- https://www.youtube.com/watch?v=Dns_9jaNPNk
State of libsecp256k1
libsecp256k1
- started by Pieter Wuille in 2013 to replace OpenSSL's ECDSA implementation in Bitcoin Core
- written in C (... C89)
- general-purpose library but written with Bitcoin Core as a main user in mind
- ECDSA and tweaking keys (e.g., for BIP32)
- modules for
- ECDSA with key recovery
- ECDH
- Schnorr sigs (experimental?!)
- extra keys (experimental?!)
Problems
- No clear rule what should be in libsecp256k1
- only stuff used by Core?
- counter-example: ECDH
- "things relevant to the wider Bitcoin ecosystem"
- should those be in forks like libsecp256k1-zkp?
- only stuff used by Core?
- No release ever
- experimental modules
- Lack of developers
- lots of abandoned PRs
- people think they can't contribute because they lack expertise in cryptography but that's just not true
CoreDev
By iamjon
CoreDev
- 364