p8k_test.mx raw
1 // Package p8k tests exercise the signer's decision surface without any
2 // network or spawning: key/secret parsing and validation, the exact BIP-340
3 // vector-0 derivation and deterministic signature, request/response shape
4 // (lengths and rejection mapping), Zero(), and ECDH symmetry/hashing.
5 //
6 // The vectors are the first rows of the vendored BIP-340 test vector CSV
7 // (src/crypto/secp256k1/testdata/bip0340.csv), so a derivation or signing
8 // regression fails here against the specification, not against the signer's
9 // own output.
10 package p8k
11
12 import (
13 "crypto/sha256"
14 "encoding/hex"
15 "testing"
16 )
17
18 // BIP-340 vector 0: secret key, matching x-only public key, and the
19 // deterministic signature over an all-zero 32-byte message with zero aux.
20 const (
21 p8kVecSk0 = "0000000000000000000000000000000000000000000000000000000000000003"
22 p8kVecPub0 = "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"
23 p8kVecSig0 = "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"
24 )
25
26 // p8kHex decodes a hex fixture, failing the test when the fixture itself is
27 // malformed.
28 func p8kHex(t *testing.T, s string) (b []byte) {
29 dec, err := hex.DecodeString([]byte(s))
30 if err != nil {
31 t.Fatalf("fixture %q is not hex: %s", s, err.Error())
32 return
33 }
34 return dec
35 }
36
37 // p8kBytes returns n bytes all set to fill.
38 func p8kBytes(n int32, fill byte) (b []byte) {
39 b = []byte{:n}
40 for i := 0; i < n; i++ {
41 b[i] = fill
42 }
43 return
44 }
45
46 // p8kCopy returns an independent copy of src.
47 func p8kCopy(src []byte) (dst []byte) {
48 dst = []byte{:len(src)}
49 for i := 0; i < len(src); i++ {
50 dst[i] = src[i]
51 }
52 return
53 }
54
55 // p8kEq compares two byte strings without bytes.Equal (whose needles can be
56 // missed in long haystacks under this toolchain).
57 func p8kEq(a, b []byte) (ok bool) {
58 if len(a) != len(b) {
59 return
60 }
61 for i := 0; i < len(a); i++ {
62 if a[i] != b[i] {
63 return
64 }
65 }
66 ok = true
67 return
68 }
69
70 func TestNewAndMustNew(t *testing.T) {
71 var err error
72 var s *Signer
73 s, err = New()
74 if err != nil || s == nil {
75 t.Fatal("New must return a live signer")
76 }
77 if MustNew() == nil {
78 t.Fatal("MustNew must return a live signer")
79 }
80 // A fresh signer has neither key loaded.
81 if s.Sec() != nil {
82 t.Fatal("a fresh signer must expose no secret")
83 }
84 if s.Pub() != nil {
85 t.Fatal("a fresh signer must expose no public key")
86 }
87 }
88
89 func TestInitSecRoundTrip(t *testing.T) {
90 var err error
91 sk := p8kHex(t, p8kVecSk0)
92 wantPub := p8kHex(t, p8kVecPub0)
93 s := MustNew()
94 err = s.InitSec(sk)
95 if err != nil {
96 t.Fatalf("InitSec(valid): %s", err.Error())
97 }
98 if !p8kEq(s.Sec(), sk) {
99 t.Fatal("Sec must round-trip the secret key")
100 }
101 if !p8kEq(s.Pub(), wantPub) {
102 t.Fatal("InitSec must derive the BIP-340 public key")
103 }
104 // Sec returns a copy, not a view: mutating it must not change the signer.
105 got := s.Sec()
106 if len(got) == 0 {
107 t.Fatal("Sec returned nothing to mutate")
108 return
109 }
110 got[0] = got[0] + 1
111 if p8kEq(s.Sec(), got) {
112 t.Fatal("Sec must return an independent copy")
113 }
114 }
115
116 func TestInitSecRejects(t *testing.T) {
117 var err error
118 s := MustNew()
119 err = s.InitSec([]byte("short"))
120 if err == nil {
121 t.Fatal("InitSec must reject a non-32-byte key")
122 }
123 err = s.InitSec([]byte{})
124 if err == nil {
125 t.Fatal("InitSec must reject an empty key")
126 }
127 err = s.InitSec(p8kBytes(33, 0x01))
128 if err == nil {
129 t.Fatal("InitSec must reject a 33-byte key")
130 }
131 // The all-zero scalar is outside [1, n-1].
132 err = s.InitSec(p8kBytes(32, 0))
133 if err == nil {
134 t.Fatal("InitSec must reject the zero scalar")
135 }
136 if s.Sec() != nil || s.Pub() != nil {
137 t.Fatal("a rejected InitSec must leave no keys")
138 }
139 }
140
141 func TestInitPubRoundTripAndRejects(t *testing.T) {
142 var err error
143 pub := p8kHex(t, p8kVecPub0)
144 s := MustNew()
145 err = s.InitPub(pub)
146 if err != nil {
147 t.Fatalf("InitPub(valid): %s", err.Error())
148 }
149 if !p8kEq(s.Pub(), pub) {
150 t.Fatal("Pub must round-trip the public key")
151 }
152 if s.Sec() != nil {
153 t.Fatal("InitPub alone must not expose a secret")
154 }
155 err = s.InitPub(p8kBytes(31, 0x02))
156 if err == nil {
157 t.Fatal("InitPub must reject a short key")
158 }
159 err = s.InitPub(p8kBytes(33, 0x02))
160 if err == nil {
161 t.Fatal("InitPub must reject a long key")
162 }
163 // An x coordinate at or above the field size has no curve point.
164 err = s.InitPub(p8kBytes(32, 0xFF))
165 if err == nil {
166 t.Fatal("InitPub must reject an out-of-field x coordinate")
167 }
168 }
169
170 func TestInitPubClearsSecret(t *testing.T) {
171 var err error
172 sk := p8kHex(t, p8kVecSk0)
173 pub := p8kHex(t, p8kVecPub0)
174 s := MustNew()
175 err = s.InitSec(sk)
176 if err != nil {
177 t.Fatalf("InitSec(valid): %s", err.Error())
178 }
179 err = s.InitPub(pub)
180 if err != nil {
181 t.Fatalf("InitPub(valid): %s", err.Error())
182 }
183 if s.Sec() != nil {
184 t.Fatal("InitPub must drop a previously loaded secret")
185 }
186 if !p8kEq(s.Pub(), pub) {
187 t.Fatal("InitPub must keep the loaded public key")
188 }
189 }
190
191 func TestSignDeterministicVector(t *testing.T) {
192 var err error
193 var sig []byte
194 var ok bool
195 sk := p8kHex(t, p8kVecSk0)
196 pub := p8kHex(t, p8kVecPub0)
197 wantSig := p8kHex(t, p8kVecSig0)
198 msg := p8kBytes(32, 0)
199
200 s := MustNew()
201 err = s.InitSec(sk)
202 if err != nil {
203 t.Fatalf("InitSec(valid): %s", err.Error())
204 }
205 sig, err = s.Sign(msg)
206 if err != nil {
207 t.Fatalf("Sign(vector 0 message): %s", err.Error())
208 }
209 if !p8kEq(sig, wantSig) {
210 t.Fatal("Sign must reproduce the BIP-340 vector-0 signature (zero aux)")
211 }
212
213 v := MustNew()
214 err = v.InitPub(pub)
215 if err != nil {
216 t.Fatalf("InitPub(valid): %s", err.Error())
217 }
218 ok, err = v.Verify(msg, sig)
219 if err != nil {
220 t.Fatalf("Verify(vector 0): %s", err.Error())
221 }
222 if !ok {
223 t.Fatal("the vector-0 signature must verify")
224 }
225
226 // A different message must not verify against the same signature.
227 other := p8kBytes(32, 0x01)
228 ok, err = v.Verify(other, sig)
229 if err != nil {
230 t.Fatalf("Verify(other message): %s", err.Error())
231 }
232 if ok {
233 t.Fatal("a signature must not verify against a different message")
234 }
235
236 // A corrupted signature must not verify.
237 corrupt := p8kCopy(sig)
238 corrupt[0] = corrupt[0] + 1
239 ok, err = v.Verify(msg, corrupt)
240 if err != nil {
241 t.Fatalf("Verify(corrupted signature): %s", err.Error())
242 }
243 if ok {
244 t.Fatal("a corrupted signature must not verify")
245 }
246 }
247
248 func TestSignVerifyRejections(t *testing.T) {
249 var err error
250 var ok bool
251 msg := p8kBytes(32, 0x22)
252 s := MustNew()
253 _, err = s.Sign(msg)
254 if err == nil {
255 t.Fatal("Sign without a secret key must fail")
256 }
257 nokey := MustNew()
258 _, err = nokey.Verify(msg, p8kBytes(64, 0))
259 if err == nil {
260 t.Fatal("Verify without a public key must fail")
261 }
262 err = s.InitSec(p8kHex(t, p8kVecSk0))
263 if err != nil {
264 t.Fatalf("InitSec(valid): %s", err.Error())
265 }
266 _, err = s.Sign(p8kBytes(31, 0))
267 if err == nil {
268 t.Fatal("Sign must reject a short message")
269 }
270 _, err = s.Sign(p8kBytes(33, 0))
271 if err == nil {
272 t.Fatal("Sign must reject a long message")
273 }
274
275 v := MustNew()
276 err = v.InitPub(p8kHex(t, p8kVecPub0))
277 if err != nil {
278 t.Fatalf("InitPub(valid): %s", err.Error())
279 }
280 _, err = v.Verify(p8kBytes(31, 0), p8kBytes(64, 0))
281 if err == nil {
282 t.Fatal("Verify must reject a short message")
283 }
284 _, err = v.Verify(msg, p8kBytes(63, 0))
285 if err == nil {
286 t.Fatal("Verify must reject a short signature")
287 }
288 _, err = v.Verify(msg, p8kBytes(65, 0))
289 if err == nil {
290 t.Fatal("Verify must reject a long signature")
291 }
292 ok, err = v.Verify(msg, p8kBytes(64, 0))
293 if err != nil {
294 t.Fatalf("Verify(all-zero signature): %s", err.Error())
295 }
296 if ok {
297 t.Fatal("an all-zero signature must not verify")
298 }
299 }
300
301 func TestGenerateAndZero(t *testing.T) {
302 var err error
303 var sig []byte
304 var ok bool
305 s := MustNew()
306 err = s.Generate()
307 if err != nil {
308 t.Fatalf("Generate: %s", err.Error())
309 }
310 sec := s.Sec()
311 pub := s.Pub()
312 if len(sec) != 32 {
313 t.Fatalf("generated secret length = %d", int32(len(sec)))
314 }
315 if len(pub) != 32 {
316 t.Fatalf("generated public length = %d", int32(len(pub)))
317 }
318 msg := p8kBytes(32, 0x11)
319 sig, err = s.Sign(msg)
320 if err != nil {
321 t.Fatalf("Sign after Generate: %s", err.Error())
322 }
323 if len(sig) != 64 {
324 t.Fatalf("signature length = %d", int32(len(sig)))
325 }
326 ok, err = s.Verify(msg, sig)
327 if err != nil {
328 t.Fatalf("Verify after Generate: %s", err.Error())
329 }
330 if !ok {
331 t.Fatal("a generated key must verify its own signature")
332 }
333
334 s.Zero()
335 if s.Sec() != nil {
336 t.Fatal("Zero must clear the secret")
337 }
338 if s.Pub() != nil {
339 t.Fatal("Zero must clear the public key")
340 }
341 _, err = s.Sign(msg)
342 if err == nil {
343 t.Fatal("Sign after Zero must fail")
344 }
345 _, err = s.Verify(msg, sig)
346 if err == nil {
347 t.Fatal("Verify after Zero must fail")
348 }
349 _, err = s.ECDHRaw(pub)
350 if err == nil {
351 t.Fatal("ECDHRaw after Zero must fail")
352 }
353 }
354
355 func TestECDH(t *testing.T) {
356 var err error
357 var raw []byte
358 var hashed []byte
359 var peer []byte
360 a := MustNew()
361 err = a.InitSec(p8kHex(t, p8kVecSk0))
362 if err != nil {
363 t.Fatalf("InitSec(valid): %s", err.Error())
364 }
365 b := MustNew()
366 err = b.Generate()
367 if err != nil {
368 t.Fatalf("Generate: %s", err.Error())
369 }
370
371 fresh := MustNew()
372 _, err = fresh.ECDHRaw(b.Pub())
373 if err == nil {
374 t.Fatal("ECDHRaw without a secret key must fail")
375 }
376 _, err = fresh.ECDH(b.Pub())
377 if err == nil {
378 t.Fatal("ECDH without a secret key must fail")
379 }
380
381 _, err = a.ECDHRaw(p8kBytes(31, 0))
382 if err == nil {
383 t.Fatal("ECDHRaw must reject a short pubkey")
384 }
385 _, err = a.ECDHRaw(p8kBytes(33, 0))
386 if err == nil {
387 t.Fatal("ECDHRaw must reject a long pubkey")
388 }
389 // An x coordinate at or above the field prime is not a curve point:
390 // BIP-340 fails it, and secp256k1.LiftX now refuses it instead of lifting a
391 // non-canonical value (p8k only checks the 32-byte length, so this is the
392 // stdlib's verdict propagating through ECDHRaw/ECDH).
393 _, err = a.ECDHRaw(p8kBytes(32, 0xFF))
394 if err == nil {
395 t.Fatal("ECDHRaw must reject an out-of-field x coordinate")
396 }
397 _, err = a.ECDH(p8kBytes(32, 0xFF))
398 if err == nil {
399 t.Fatal("ECDH must reject an out-of-field x coordinate")
400 }
401 _, err = a.ECDH(p8kBytes(31, 0))
402 if err == nil {
403 t.Fatal("ECDH must propagate a rejected pubkey")
404 }
405 // x = 0 has no curve point (7 is a non-residue mod p), so a well-formed
406 // 32-byte all-zero pubkey must surface ECDH's own failure, not a length
407 // error.
408 _, err = a.ECDHRaw(p8kBytes(32, 0))
409 if err == nil {
410 t.Fatal("ECDHRaw must report an x coordinate with no curve point")
411 }
412
413 raw, err = a.ECDHRaw(b.Pub())
414 if err != nil {
415 t.Fatalf("ECDHRaw: %s", err.Error())
416 }
417 if len(raw) != 32 {
418 t.Fatalf("ECDHRaw length = %d", int32(len(raw)))
419 }
420 peer, err = b.ECDHRaw(a.Pub())
421 if err != nil {
422 t.Fatalf("ECDHRaw(peer): %s", err.Error())
423 }
424 if !p8kEq(raw, peer) {
425 t.Fatal("ECDH must be symmetric")
426 }
427
428 hashed, err = a.ECDH(b.Pub())
429 if err != nil {
430 t.Fatalf("ECDH: %s", err.Error())
431 }
432 h := sha256.Sum256(raw)
433 if !p8kEq(hashed, h[:]) {
434 t.Fatal("ECDH must be sha256 of ECDHRaw")
435 }
436 }
437