aboutsummaryrefslogtreecommitdiffstats
path: root/schnorr_test.go
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-19 21:20:09 -0800
committerClawd <ai@clawd.bot>2026-02-19 21:20:09 -0800
commit6f3902d09504838e4e486825f073df971c412595 (patch)
tree030130cf57cfc629f36d715d40427f5dd3083d7f /schnorr_test.go
parent6c7f038d359e98172500252d320db9384c3f59d1 (diff)
Add Schnorr signatures (BIP-340 compatible)
Diffstat (limited to 'schnorr_test.go')
-rw-r--r--schnorr_test.go242
1 files changed, 242 insertions, 0 deletions
diff --git a/schnorr_test.go b/schnorr_test.go
new file mode 100644
index 0000000..acd709c
--- /dev/null
+++ b/schnorr_test.go
@@ -0,0 +1,242 @@
1package secp256k1
2
3import (
4 "bytes"
5 "encoding/hex"
6 "math/big"
7 "testing"
8)
9
10func TestTaggedHash(t *testing.T) {
11 // Just verify it produces 32 bytes
12 result := TaggedHash("test", []byte("hello"))
13 if len(result) != 32 {
14 t.Errorf("expected 32 bytes, got %d", len(result))
15 }
16
17 // Same inputs should produce same output
18 result2 := TaggedHash("test", []byte("hello"))
19 if !bytes.Equal(result, result2) {
20 t.Error("tagged hash should be deterministic")
21 }
22
23 // Different tag should produce different output
24 result3 := TaggedHash("other", []byte("hello"))
25 if bytes.Equal(result, result3) {
26 t.Error("different tags should produce different hashes")
27 }
28}
29
30func TestLiftX(t *testing.T) {
31 // Lift G.x should give us G (or its negation with even y)
32 p, err := liftX(Gx)
33 if err != nil {
34 t.Fatalf("failed to lift G.x: %v", err)
35 }
36
37 if !p.IsOnCurve() {
38 t.Error("lifted point should be on curve")
39 }
40
41 // x should match
42 if p.x.value.Cmp(Gx) != 0 {
43 t.Error("lifted x should match input")
44 }
45
46 // y should be even (BIP-340 convention)
47 if !hasEvenY(p) {
48 t.Error("lifted point should have even y")
49 }
50}
51
52func TestLiftXInvalid(t *testing.T) {
53 // x = 0 is not on the curve (0³ + 7 = 7, and 7 has no sqrt mod p)
54 _, err := liftX(big.NewInt(0))
55 if err == nil {
56 t.Error("x=0 should not be on curve")
57 }
58}
59
60func TestSignAndVerify(t *testing.T) {
61 priv, _ := GeneratePrivateKey()
62 pub := priv.PublicKey()
63 message := []byte("hello world")
64
65 sig, err := Sign(priv, message)
66 if err != nil {
67 t.Fatalf("signing failed: %v", err)
68 }
69
70 if !Verify(pub, message, sig) {
71 t.Error("signature should verify")
72 }
73}
74
75func TestSignatureIsDeteministic(t *testing.T) {
76 priv, _ := NewPrivateKeyFromHex("0000000000000000000000000000000000000000000000000000000000000001")
77 message := []byte("test message")
78
79 sig1, _ := Sign(priv, message)
80 sig2, _ := Sign(priv, message)
81
82 if sig1.R.Cmp(sig2.R) != 0 || sig1.S.Cmp(sig2.S) != 0 {
83 t.Error("BIP-340 signing should be deterministic")
84 }
85}
86
87func TestVerifyWrongMessage(t *testing.T) {
88 priv, _ := GeneratePrivateKey()
89 pub := priv.PublicKey()
90
91 sig, _ := Sign(priv, []byte("correct message"))
92
93 if Verify(pub, []byte("wrong message"), sig) {
94 t.Error("signature should not verify with wrong message")
95 }
96}
97
98func TestVerifyWrongPublicKey(t *testing.T) {
99 priv1, _ := GeneratePrivateKey()
100 priv2, _ := GeneratePrivateKey()
101 pub2 := priv2.PublicKey()
102 message := []byte("test")
103
104 sig, _ := Sign(priv1, message)
105
106 if Verify(pub2, message, sig) {
107 t.Error("signature should not verify with wrong public key")
108 }
109}
110
111func TestVerifyTamperedSignature(t *testing.T) {
112 priv, _ := GeneratePrivateKey()
113 pub := priv.PublicKey()
114 message := []byte("test")
115
116 sig, _ := Sign(priv, message)
117
118 // Tamper with s
119 tamperedSig := &Signature{
120 R: sig.R,
121 S: new(big.Int).Add(sig.S, big.NewInt(1)),
122 }
123
124 if Verify(pub, message, tamperedSig) {
125 t.Error("tampered signature should not verify")
126 }
127}
128
129func TestSignatureBytes(t *testing.T) {
130 priv, _ := GeneratePrivateKey()
131 message := []byte("test")
132
133 sig, _ := Sign(priv, message)
134 b := sig.Bytes()
135
136 if len(b) != 64 {
137 t.Errorf("signature should be 64 bytes, got %d", len(b))
138 }
139}
140
141func TestSignatureRoundTrip(t *testing.T) {
142 priv, _ := GeneratePrivateKey()
143 message := []byte("test")
144
145 sig1, _ := Sign(priv, message)
146 b := sig1.Bytes()
147 sig2, err := SignatureFromBytes(b)
148 if err != nil {
149 t.Fatalf("failed to parse signature: %v", err)
150 }
151
152 if sig1.R.Cmp(sig2.R) != 0 || sig1.S.Cmp(sig2.S) != 0 {
153 t.Error("signature should survive round-trip")
154 }
155}
156
157func TestSignatureFromBytesInvalid(t *testing.T) {
158 _, err := SignatureFromBytes(make([]byte, 63))
159 if err == nil {
160 t.Error("should reject wrong-length input")
161 }
162}
163
164// BIP-340 Test Vector 0
165func TestBIP340Vector0(t *testing.T) {
166 privHex := "0000000000000000000000000000000000000000000000000000000000000003"
167 msgHex := "0000000000000000000000000000000000000000000000000000000000000000"
168 expectedSigHex := "e907831f80848d1069a5371b402410364bdf1c5f8307b0084c55f1ce2dca821525f66a4a85ea8b71e482a74f382d2ce5ebeee8fdb2172f477df4900d310536c0"
169
170 priv, err := NewPrivateKeyFromHex(privHex)
171 if err != nil {
172 t.Fatalf("failed to parse private key: %v", err)
173 }
174
175 msg, _ := hex.DecodeString(msgHex)
176
177 sig, err := Sign(priv, msg)
178 if err != nil {
179 t.Fatalf("signing failed: %v", err)
180 }
181
182 sigHex := sig.Hex()
183 if sigHex != expectedSigHex {
184 t.Errorf("signature mismatch\ngot: %s\nwant: %s", sigHex, expectedSigHex)
185 }
186
187 // Also verify it
188 pub := priv.PublicKey()
189 if !Verify(pub, msg, sig) {
190 t.Error("BIP-340 test vector should verify")
191 }
192}
193
194// BIP-340 Test Vector 1
195func TestBIP340Vector1(t *testing.T) {
196 privHex := "b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfef"
197 msgHex := "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89"
198 auxHex := "0000000000000000000000000000000000000000000000000000000000000001"
199 expectedSigHex := "6896bd60eeae296db48a229ff71dfe071bde413e6d43f917dc8dcf8c78de33418906d11ac976abccb20b091292bff4ea897efcb639ea871cfa95f6de339e4b0a"
200
201 priv, err := NewPrivateKeyFromHex(privHex)
202 if err != nil {
203 t.Fatalf("failed to parse private key: %v", err)
204 }
205
206 msg, _ := hex.DecodeString(msgHex)
207 aux, _ := hex.DecodeString(auxHex)
208
209 sig, err := Sign(priv, msg, aux)
210 if err != nil {
211 t.Fatalf("signing failed: %v", err)
212 }
213
214 sigHex := sig.Hex()
215 if sigHex != expectedSigHex {
216 t.Errorf("signature mismatch\ngot: %s\nwant: %s", sigHex, expectedSigHex)
217 }
218
219 pub := priv.PublicKey()
220 if !Verify(pub, msg, sig) {
221 t.Error("BIP-340 test vector should verify")
222 }
223}
224
225func TestXOnlyBytes(t *testing.T) {
226 priv, _ := NewPrivateKeyFromHex("0000000000000000000000000000000000000000000000000000000000000001")
227 pub := priv.PublicKey()
228
229 xOnly := pub.XOnlyBytes()
230 if len(xOnly) != 32 {
231 t.Errorf("x-only pubkey should be 32 bytes, got %d", len(xOnly))
232 }
233
234 // Should match G.x
235 expectedX := make([]byte, 32)
236 gxBytes := Gx.Bytes()
237 copy(expectedX[32-len(gxBytes):], gxBytes)
238
239 if !bytes.Equal(xOnly, expectedX) {
240 t.Error("x-only bytes should match G.x")
241 }
242}