Last active
July 3, 2023 15:34
-
-
Save Zenithar/ac72f1bc206f418e3c2b42de2dcc79e0 to your computer and use it in GitHub Desktop.
Schnorr signature aggregation with MuSig in Go.
This file contains hidden or 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
package main | |
import ( | |
"crypto/ecdsa" | |
"crypto/elliptic" | |
"crypto/rand" | |
"crypto/sha256" | |
"fmt" | |
"math/big" | |
"os" | |
) | |
func main() { | |
// https://eprint.iacr.org/2018/068 - Simple Schnorr Multi-Signatures with Applications to Bitcoin | |
// | |
// Inspired from https://medium.com/asecuritysite-when-bob-met-alice/multiple-signers-and-key-aggregation-with-schnorr-be0868133876 | |
// https://asecuritysite.com/schnorr/schnorr_test3 | |
// Use MuSig signature aggregation with P256 curve. | |
msg := []byte("Hello") | |
curve := elliptic.P256() | |
// X1 = x1 . G | |
x1, err := ecdsa.GenerateKey(curve, rand.Reader) | |
if err != nil { | |
panic(err) | |
} | |
X1 := x1.PublicKey | |
// R1 = r1 . G | |
r1, err := ecdsa.GenerateKey(curve, rand.Reader) | |
if err != nil { | |
panic(err) | |
} | |
R1 := r1.PublicKey | |
// X2 = x2 . G | |
x2, err := ecdsa.GenerateKey(curve, rand.Reader) | |
if err != nil { | |
panic(err) | |
} | |
X2 := x2.PublicKey | |
// R2 = r2 . G | |
r2, err := ecdsa.GenerateKey(curve, rand.Reader) | |
if err != nil { | |
panic(err) | |
} | |
R2 := r2.PublicKey | |
// X3 = x3 . G | |
x3, err := ecdsa.GenerateKey(curve, rand.Reader) | |
if err != nil { | |
panic(err) | |
} | |
X3 := x3.PublicKey | |
// R3 = r3 . G | |
r3, err := ecdsa.GenerateKey(curve, rand.Reader) | |
if err != nil { | |
panic(err) | |
} | |
R3 := r3.PublicKey | |
// L = H(X1 || X2 || X3) | |
h := sha256.New() | |
h.Write(X1.X.Bytes()) | |
h.Write(X1.Y.Bytes()) | |
h.Write(X2.X.Bytes()) | |
h.Write(X2.Y.Bytes()) | |
h.Write(X3.X.Bytes()) | |
h.Write(X3.Y.Bytes()) | |
L := h.Sum(nil) | |
// H1 = H( L || X1 ) . X1 | |
h.Reset() | |
h.Write(L) | |
h.Write(X1.X.Bytes()) | |
h.Write(X1.Y.Bytes()) | |
Xx1, Xy1 := curve.ScalarMult(X1.X, X1.Y, h.Sum(nil)) | |
// H2 = H( L || X2 ) . X2 | |
h.Reset() | |
h.Write(L) | |
h.Write(X2.X.Bytes()) | |
h.Write(X2.Y.Bytes()) | |
Xx2, Xy2 := curve.ScalarMult(X2.X, X2.Y, h.Sum(nil)) | |
// H3 = H( L || X3 ) . X3 | |
h.Reset() | |
h.Write(L) | |
h.Write(X3.X.Bytes()) | |
h.Write(X3.Y.Bytes()) | |
Xx3, Xy3 := curve.ScalarMult(X3.X, X3.Y, h.Sum(nil)) | |
// X = X1 + X2 + X3 | |
Xx, Xy := curve.Add(Xx1, Xy1, Xx2, Xy2) | |
Xx, Xy = curve.Add(Xx, Xy, Xx3, Xy3) | |
X := append(Xx.Bytes(), Xy.Bytes()...) | |
// R = R1 + R2 + R3 | |
Rx, Ry := curve.Add(R1.X, R1.Y, R2.X, R2.Y) | |
Rx, Ry = curve.Add(Rx, Ry, R3.X, R3.Y) | |
R := append(Rx.Bytes(), Ry.Bytes()...) | |
// H(X, R, m) | |
h.Reset() | |
h.Write(X) | |
h.Write(R) | |
h.Write(msg) | |
HXRm := big.NewInt(0).SetBytes(h.Sum(nil)) | |
// HLX1 = H(L, X1) | |
h.Reset() | |
h.Write(L) | |
h.Write(X1.X.Bytes()) | |
h.Write(X1.Y.Bytes()) | |
HLX1 := big.NewInt(0).SetBytes(h.Sum(nil)) | |
// HLX2 = H(L, X2) | |
h.Reset() | |
h.Write(L) | |
h.Write(X2.X.Bytes()) | |
h.Write(X2.Y.Bytes()) | |
HLX2 := big.NewInt(0).SetBytes(h.Sum(nil)) | |
// HLX3 = H(L, X3) | |
h.Reset() | |
h.Write(L) | |
h.Write(X3.X.Bytes()) | |
h.Write(X3.Y.Bytes()) | |
HLX3 := big.NewInt(0).SetBytes(h.Sum(nil)) | |
// Compute signatures | |
s1 := new(big.Int).Add(r1.D, new(big.Int).Mul(new(big.Int).Mul(HXRm, HLX1), x1.D)) | |
s2 := new(big.Int).Add(r2.D, new(big.Int).Mul(new(big.Int).Mul(HXRm, HLX2), x2.D)) | |
s3 := new(big.Int).Add(r3.D, new(big.Int).Mul(new(big.Int).Mul(HXRm, HLX3), x3.D)) | |
s := new(big.Int).Add(s1, s2) | |
s = s.Add(s, s3) | |
fmt.Fprintf(os.Stdout, "Bob Private key (x1) = %s\n", x1.D.Text(16)) | |
fmt.Fprintf(os.Stdout, "Bob Public key (X1) = (%s, %s)\n", X1.X.Text(16), X1.Y.Text(16)) | |
fmt.Fprintf(os.Stdout, "Alice Private key (x2) = %s\n", x2.D.Text(16)) | |
fmt.Fprintf(os.Stdout, "Alice Public key (X2) = (%s, %s)\n", X2.X.Text(16), X2.Y.Text(16)) | |
fmt.Fprintf(os.Stdout, "Charlie Private key (x3) = %s\n", x3.D.Text(16)) | |
fmt.Fprintf(os.Stdout, "Charlie Public key (X3) = (%s, %s)\n", X3.X.Text(16), X3.Y.Text(16)) | |
fmt.Fprintf(os.Stdout, "\nMerged Public Key X = (%s, %s)\n", Xx.Text(16), Xy.Text(16)) | |
fmt.Fprintf(os.Stdout, "\nBob s1 = %s\n", s1.Text(16)) | |
fmt.Fprintf(os.Stdout, "Alice s2 = %s\n", s2.Text(16)) | |
fmt.Fprintf(os.Stdout, "Charlie s3 = %s\n", s3.Text(16)) | |
fmt.Fprintf(os.Stdout, "\nMerged s = %x\n", s.Bytes()) | |
fmt.Fprintf(os.Stdout, "Merged R = %x\n", R) | |
// Verify signature | |
// sG = s . G | |
sGx, sGy := curve.ScalarBaseMult(s.Bytes()) | |
// H(X,R,m) | |
h.Reset() | |
h.Write(X) | |
h.Write(R) | |
h.Write(msg) | |
hRXm := h.Sum(nil) | |
// H(X,R,m) . X | |
Cx, Cy := curve.ScalarMult(Xx, Xy, hRXm) | |
// R + H(X,R,m) . X | |
rX, rY := curve.Add(Rx, Ry, Cx, Cy) | |
// s . G == R + H(X,R,m) . X | |
if sGx.Cmp(rX) == 0 && sGy.Cmp(rY) == 0 { | |
fmt.Fprintln(os.Stdout, "\nSignature verified") | |
} else { | |
fmt.Fprintln(os.Stdout, "\nSignature verification error") | |
} | |
} |
This file contains hidden or 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
Bob Private key (x1) = 7b0405c0f430e28b36c7ad6e9b41bb5f6361d7bfe6df54e99b363dfee9e89b42 | |
Bob Public key (X1) = (dbe75cf345f45b71103efbbbf1826c61010c21cbbb5e772bb691afc66933a0fe, 443e5bf138a74fbac23540a726135213579b7a5f2b7c1930eeed4e2430e6d7f4) | |
Alice Private key (x2) = de7d6db3d555ac908afac73d3c027a5bbca290fe41bcb25cc918aa0d5f62b58f | |
Alice Public key (X2) = (9b50304a3b3f8bd9b9b82a6a479324591d21746db417de8e258bbb829cb9bdb1, 9192c64a1612fca147d0362b9c3a6916f4bed9a48b2bf2e16beff984d7816347) | |
Charlie Private key (x3) = 399b8a393803036a9e9aae248e94da1389d2cd21da56174ae3a70bf1b7d80952 | |
Charlie Public key (X3) = (47755a59346a2077541eb864bc3f73d25d4ae0170d79a36834c36401f3858048, 7cb1f202a161d6f43f883501d57028d1c27d3a9954e86b9eb73efea1b895623d) | |
Merged Public Key X = (e37d2d5c4ae1859f75088c90ab18f5b132de868efa848e381b8929c18f124396, 940cd556f952aebfb01ce570a6cadc2fa5204161a29840b957136e17ebaf5a74) | |
Bob s1 = 5d31d8e8b2eb72935fef040a9e1fd8f2cc62f58abf13c7026dcbf9090948de9a879126d9dca3c28499a705e4112fc17c1752b802092a7c8d906da28b09fedb20f81376f335606ba1c84055c5c9cf254fc901ef38bcee364a4995be5e79b2d92 | |
Alice s2 = 889cfd39cb02cee45171032efee3708a39292ac7f97ddf1c9e330116facbc9f82a5ac1c17af2703a99021adce2835adb823425ffddb0824e90c5b970955aa605aedcaa9288bf0b309ec27afec691c0b9b334c8468213bc90038ce9e343c5407 | |
Charlie s3 = 237608e17e3a22b4537e44aeeac5c90f497f336c4155ec5c2198cad8d9cd441513671ea4a2a26f55b49ca0b9935ad7c4ac7e27e8a95fba96b9617ec98061c037ed14887af8987e7d4740d50fc5fcd2cf0f428bb14df8e8b5dd2db7e966a746c | |
Merged s = 10944df03fc28642c04de4be887c9128c4f0b53bef9e7927b2d97c4f8dde1eca7c553073ffa38a214e745c17a870df41c460505ea903ab972da94dac51fbb415e9404aa00b6b7f54fae43a5d4565db8d88b7943308cfadb902a50602b241f605 | |
Merged R = 9ff09a9cf32fbf54b32ccc23d6bcb3cea3d970c67217875e6404f338714519d1c9328b3035e75af12d631aac071534aa5047bdf36d8528ec5e9fadc8b274cf16 | |
Signature verified |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment