-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Schnorr wizardry cheat sheet (#15)
* Schnorr signatures * Adaptor signatures * Musig2
- Loading branch information
Showing
2 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
# Schnorr Wizardry | ||
|
||
Bitcoin added support for schnorr signatures with the Taproot update, which activated at block 709 632. | ||
In this article, we dive into how it works and some of the advanced schemes it unlocks. | ||
|
||
## Table of Contents | ||
|
||
* [Schnorr signatures](#schnorr-signatures) | ||
* [Linearity of Schnorr signatures](#linearity-of-schnorr-signatures) | ||
* [Adaptor signatures](#adaptor-signatures) | ||
* [Musig2](#musig2) | ||
* [Musig2 adaptor signatures](#musig2-adaptor-signatures) | ||
|
||
## Schnorr signatures | ||
|
||
Schnorr signatures are specified in [BIP 340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). | ||
Ignoring many details described in the BIP, at a high level the signing algorithm works as follows: | ||
|
||
```text | ||
P = k*G -> signer's public key | ||
m -> message to sign | ||
Sign: | ||
r = {0;1}^256 -> random nonce | ||
R = r*G | ||
e = H(R || P || m) | ||
s = r + e*k | ||
(s, R) -> message signature | ||
Verify: | ||
e = H(R || P || m) | ||
R' = s*G - e*P | ||
If R = R', the signature is valid | ||
``` | ||
|
||
## Linearity of Schnorr signatures | ||
|
||
A very interesting property of schnorr signatures compared to ECDSA signatures is that they make it easy to combine signatures for a given message if we slightly change the signing algorithm. | ||
|
||
```text | ||
A = a*G -> Alice's public key | ||
B = b*G -> Bob's public key | ||
P = A + B -> combined public key | ||
m -> message to sign | ||
Sign: | ||
ra = {0;1}^256 -> random nonce generated by Alice | ||
RA = ra*G -> public partial nonce sent by Alice to Bob | ||
rb = {0;1}^256 -> random nonce generated by Bob | ||
RB = rb*G -> public partial nonce sent by Bob to Alice | ||
R = RA + RB -> public nonce | ||
e = H(R || P || m) | ||
sA = ra + e*a -> partial signature produced by Alice | ||
sB = rb + e*b -> partial signature produced by Bob | ||
s = sA + sB | ||
(s, R) -> combined message signature | ||
Verify: | ||
e = H(R || P || m) | ||
R' = s*G - e*P | ||
If R = R', the signature is valid | ||
``` | ||
|
||
You should notice that the verification algorithm hasn't changed. | ||
This means that the verifier doesn't even know that multiple signers were involved: the signature looks like it comes from a standard single signer. | ||
|
||
:warning: This signing scheme is not secure when Alice or Bob is malicious. We will describe a secure signing scheme in the Musig2 section below. | ||
|
||
## Adaptor signatures | ||
|
||
The linearity of schnorr signatures also allows revealing a secret through a signature. | ||
|
||
```text | ||
P = k*G -> signer's public key | ||
T = t*G -> tweak (t is the secret that will be revealed) | ||
m -> message to sign | ||
Sign: | ||
r = {0;1}^256 | ||
R = r*G | ||
e = H(R + T || P || m) -> notice that we tweak the nonce here with T | ||
s = r + e*k -> but we don't tweak it here with t | ||
(s, R, T) -> adaptor signature: will automatically reveal t when a valid signature for nonce R + T is produced | ||
Verify: | ||
e = H(R + T || P || m) | ||
R' = s*G - e*P | ||
If R = R', the adaptor signature is valid | ||
Note that (s, R) or (s, R + T) are not valid schnorr signatures | ||
Complete: | ||
s' = s + t | ||
R' = R + T | ||
(s', R') -> valid schnorr signature | ||
Extract: | ||
e' = H(R' || P || m) | ||
R'' = s'*G - e'*P | ||
If R'' = R', the signature is valid | ||
t = s' - s | ||
The verifier has learnt t through the schnorr signature | ||
``` | ||
|
||
NB: for this to work, the verifier must ensure that the nonce `R + T` is fixed beforehand. | ||
Otherwise the signer may create a valid signature for an unrelated nonce, which would not reveal the secret `t`. | ||
This is most useful when combined with Musig2, where participants commit to the nonce before signing. | ||
|
||
Adaptor signatures can also be used in the opposite way. | ||
If the signer doesn't know the secret `t`, it can produce an adaptor signature. | ||
Then another participant that knows `t` can convert that adaptor signature to a valid signature. | ||
|
||
## Musig2 | ||
|
||
[Musig2](https://eprint.iacr.org/2020/1261.pdf) is a secure scheme for combining multiple signatures into a single schnorr signature. | ||
It only needs two rounds of communications between participants, and the first round can be done ahead of time (independently of the message to sign). | ||
The novel idea in that scheme is to use multiple nonces for each participant and combine them in a smart way to produce the final nonce. | ||
This results in an elegant scheme that looks very similar to standard schnorr signing. | ||
|
||
```text | ||
PA = pa*G -> public key of participant A | ||
PB = pb*G -> public key of participant B | ||
L = sorted(PA, PB) -> sorted list of participants public keys | ||
P = H(H(L) || PA)*PA + PB -> combined public key | ||
NonceGenA (run by participant A): | ||
ra1 = {0;1}^256 | ||
ra2 = {0;1}^256 | ||
RA1 = ra1*G | ||
RA2 = ra2*G | ||
NonceGenB (run by participant B): | ||
rb1 = {0;1}^256 | ||
rb2 = {0;1}^256 | ||
RB1 = rb1*G | ||
RB2 = rb2*G | ||
NonceExchange (communication round 1): | ||
Participant A sends RA1, RA2 to participant B | ||
Participant B sends RB1, RB2 to participant A | ||
SignA (run by participant A): | ||
x = H(P || RA1 + RB1 || RA2 + RB2 || m) | ||
R = RA1 + RB1 + x*(RA2 + RB2) | ||
ra = ra1 + x*ra2 | ||
e = H(R || P || m) | ||
sa = ra + e*H(L || PA)*pa | ||
(sa, R) -> participant A's partial signature | ||
SignB (run by participant B): | ||
x = H(P || RA1 + RB1 || RA2 + RB2 || m) | ||
R = RA1 + RB1 + x*(RA2 + RB2) | ||
rb = rb1 + x*rb2 | ||
e = H(R || P || m) | ||
sb = rb + e*H(L || PB)*pb | ||
(sb, R) -> participant B's partial signature | ||
Combine (communication round 2): | ||
s = sa + sb | ||
(s, R) -> valid schnorr signature for public key P | ||
Verify: | ||
e = H(R || P || m) | ||
R' = s*G - e*P | ||
= (sa + sb)*G - e*P | ||
= (ra + e*H(L || PA)*pa)*G + (rb + e*H(L || PB)*pb)*G - e*P | ||
= (ra + rb)*G + e*(H(L || PA)*pa + H(L || PB)*pb)*G - e*P | ||
= R + e*P - e*P | ||
= R | ||
-> this is a valid schnorr signature | ||
``` | ||
|
||
NB: we used only two participants to simplify the example, but Musig2 works with any number of participants. | ||
|
||
## Musig2 adaptor signatures | ||
|
||
Musig2 can be combined with adaptor signatures: | ||
|
||
```text | ||
# The first round (pre-computing nonces) is vanilla Musig2 | ||
PA = pa*G -> public key of participant A | ||
PB = pb*G -> public key of participant B | ||
L = sorted(PA, PB) -> sorted list of participants public keys | ||
P = H(H(L) || PA)*PA + PB -> combined public key | ||
NonceGenA (run by participant A): | ||
ra1 = {0;1}^256 | ||
ra2 = {0;1}^256 | ||
RA1 = ra1*G | ||
RA2 = ra2*G | ||
NonceGenB (run by participant B): | ||
rb1 = {0;1}^256 | ||
rb2 = {0;1}^256 | ||
RB1 = rb1*G | ||
RB2 = rb2*G | ||
NonceExchange (communication round 1): | ||
Participant A sends RA1, RA2 to participant B | ||
Participant B sends RB1, RB2 to participant A | ||
# The second round simply tweaks the combined nonce with the secret | ||
T = t*G -> secret t known only by B | ||
SignA (run by participant A): | ||
x = H(P || RA1 + RB1 + T || RA2 + RB2 || m) | ||
R = RA1 + RB1 + x*(RA2 + RB2) | ||
ra = ra1 + x*ra2 | ||
e = H(R + T || P || m) | ||
sa = ra + e*H(L || PA)*pa | ||
(sa, R + T) -> participant A's partial signature | ||
SignB (run by participant B): | ||
x = H(P || RA1 + RB1 + T || RA2 + RB2 || m) | ||
R = RA1 + RB1 + x*(RA2 + RB2) | ||
rb = rb1 + x*rb2 | ||
e = H(R + T || P || m) | ||
sb = rb + e*H(L || PB)*pb | ||
(sb, R, T) -> participant B's adaptor signature | ||
Participant A can verify participant B's adaptor signature before sending its partial signature: | ||
(sa + sb)*G must be equal to R + H(R + T || P || m)*P | ||
-> NB: it's not a valid schnorr signature, notice the mismatch between R (outside of the hash) and R + T (inside the hash) | ||
Complete (run by participant B): | ||
s = sa + sb + t | ||
R' = R + T | ||
(s, R') -> valid schnorr signature for public key P and nonce R + T | ||
Extract (run by participant A): | ||
t = s - sa - sb | ||
``` |