Skip to content

Commit

Permalink
Add Schnorr wizardry cheat sheet (#15)
Browse files Browse the repository at this point in the history
* Schnorr signatures
* Adaptor signatures
* Musig2
  • Loading branch information
t-bast authored Oct 22, 2021
1 parent fe37e8e commit fd76376
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ A collection of some in-depth articles about the Lightning Network:
* [Lightning transactions: from Zero to Hero](./lightning-txs.md)
* [Pinning attacks](./pinning-attacks.md)
* [Sphinx onion encryption: from Zero to Hero](./sphinx.md)
* [Schnorr Wizardry](./schnorr.md)
* [Spamming the Lightning Network](./spam-prevention.md)
* [Feature flags: from Zero to Hero](./feature-flags.md)
233 changes: 233 additions & 0 deletions schnorr.md
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
```

0 comments on commit fd76376

Please sign in to comment.