From fd76376df7db42d3a0e4fd7e9c935e47480b10ef Mon Sep 17 00:00:00 2001 From: Bastien Teinturier <31281497+t-bast@users.noreply.github.com> Date: Fri, 22 Oct 2021 08:41:55 +0200 Subject: [PATCH] Add Schnorr wizardry cheat sheet (#15) * Schnorr signatures * Adaptor signatures * Musig2 --- README.md | 1 + schnorr.md | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 schnorr.md diff --git a/README.md b/README.md index 0e21b80..44e2c07 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/schnorr.md b/schnorr.md new file mode 100644 index 0000000..0a1ee54 --- /dev/null +++ b/schnorr.md @@ -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 +```