schnorr_batch_test.go raw
1 //go:build !js && !wasm && !tinygo && !wasm32
2
3 package p256k1
4
5 import (
6 "crypto/rand"
7 "fmt"
8 "testing"
9 )
10
11 func TestSchnorrBatchVerify(t *testing.T) {
12 // Generate test keys and signatures
13 testCases := []struct {
14 name string
15 count int
16 }{
17 {"empty batch", 0},
18 {"single signature", 1},
19 {"two signatures", 2},
20 {"five signatures", 5},
21 {"ten signatures", 10},
22 }
23
24 for _, tc := range testCases {
25 t.Run(tc.name, func(t *testing.T) {
26 items := make([]BatchSchnorrItem, tc.count)
27
28 for i := 0; i < tc.count; i++ {
29 // Generate random key pair
30 kp, err := KeyPairGenerate()
31 if err != nil {
32 t.Fatalf("failed to create keypair: %v", err)
33 }
34 defer kp.Clear()
35
36 xonly, err := kp.XOnlyPubkey()
37 if err != nil {
38 t.Fatalf("failed to get x-only pubkey: %v", err)
39 }
40
41 // Generate random message
42 msg := make([]byte, 32)
43 rand.Read(msg)
44
45 // Generate random aux data
46 auxRand := make([]byte, 32)
47 rand.Read(auxRand)
48
49 // Sign the message
50 var sig [64]byte
51 if err := SchnorrSign(sig[:], msg, kp, auxRand); err != nil {
52 t.Fatalf("failed to sign: %v", err)
53 }
54
55 // Verify individual signature works
56 if !SchnorrVerify(sig[:], msg, xonly) {
57 t.Fatalf("individual signature verification failed for signature %d", i)
58 }
59
60 items[i] = BatchSchnorrItem{
61 Pubkey: xonly,
62 Message: msg,
63 Signature: sig[:],
64 }
65 }
66
67 // Batch verify
68 if !SchnorrBatchVerify(items) {
69 t.Errorf("batch verification failed for %d valid signatures", tc.count)
70 }
71 })
72 }
73 }
74
75 func TestSchnorrBatchVerifyInvalid(t *testing.T) {
76 // Create a batch with one invalid signature
77 items := make([]BatchSchnorrItem, 5)
78
79 for i := 0; i < 5; i++ {
80 kp, err := KeyPairGenerate()
81 if err != nil {
82 t.Fatalf("failed to create keypair: %v", err)
83 }
84 defer kp.Clear()
85
86 xonly, err := kp.XOnlyPubkey()
87 if err != nil {
88 t.Fatalf("failed to get x-only pubkey: %v", err)
89 }
90
91 msg := make([]byte, 32)
92 rand.Read(msg)
93
94 auxRand := make([]byte, 32)
95 rand.Read(auxRand)
96
97 var sig [64]byte
98 if err := SchnorrSign(sig[:], msg, kp, auxRand); err != nil {
99 t.Fatalf("failed to sign: %v", err)
100 }
101
102 items[i] = BatchSchnorrItem{
103 Pubkey: xonly,
104 Message: msg,
105 Signature: sig[:],
106 }
107 }
108
109 // Corrupt one signature
110 items[2].Signature[0] ^= 0xFF
111
112 // Batch should fail
113 if SchnorrBatchVerify(items) {
114 t.Error("batch verification should fail with corrupted signature")
115 }
116
117 // Test fallback
118 valid, invalidIndices := SchnorrBatchVerifyWithFallback(items)
119 if valid {
120 t.Error("fallback should report failure")
121 }
122 if len(invalidIndices) != 1 || invalidIndices[0] != 2 {
123 t.Errorf("expected invalid index [2], got %v", invalidIndices)
124 }
125 }
126
127 func TestSchnorrBatchVerifyWrongMessage(t *testing.T) {
128 // Create signatures where one has wrong message
129 items := make([]BatchSchnorrItem, 3)
130
131 for i := 0; i < 3; i++ {
132 kp, err := KeyPairGenerate()
133 if err != nil {
134 t.Fatalf("failed to create keypair: %v", err)
135 }
136 defer kp.Clear()
137
138 xonly, err := kp.XOnlyPubkey()
139 if err != nil {
140 t.Fatalf("failed to get x-only pubkey: %v", err)
141 }
142
143 msg := make([]byte, 32)
144 rand.Read(msg)
145
146 auxRand := make([]byte, 32)
147 rand.Read(auxRand)
148
149 var sig [64]byte
150 if err := SchnorrSign(sig[:], msg, kp, auxRand); err != nil {
151 t.Fatalf("failed to sign: %v", err)
152 }
153
154 items[i] = BatchSchnorrItem{
155 Pubkey: xonly,
156 Message: msg,
157 Signature: sig[:],
158 }
159 }
160
161 // Modify message for one item
162 items[1].Message[0] ^= 0xFF
163
164 // Batch should fail
165 if SchnorrBatchVerify(items) {
166 t.Error("batch verification should fail with wrong message")
167 }
168 }
169
170 func BenchmarkSchnorrBatchVerify(b *testing.B) {
171 // Prepare batch of signatures
172 benchCounts := []int{1, 10, 100}
173
174 for _, count := range benchCounts {
175 items := make([]BatchSchnorrItem, count)
176
177 for i := 0; i < count; i++ {
178 kp, _ := KeyPairGenerate()
179 defer kp.Clear()
180 xonly, _ := kp.XOnlyPubkey()
181
182 msg := make([]byte, 32)
183 rand.Read(msg)
184
185 auxRand := make([]byte, 32)
186 rand.Read(auxRand)
187
188 var sig [64]byte
189 SchnorrSign(sig[:], msg, kp, auxRand)
190
191 items[i] = BatchSchnorrItem{
192 Pubkey: xonly,
193 Message: msg,
194 Signature: sig[:],
195 }
196 }
197
198 b.Run(fmt.Sprintf("batch_%03d", count), func(b *testing.B) {
199 b.ReportAllocs()
200 for i := 0; i < b.N; i++ {
201 SchnorrBatchVerify(items)
202 }
203 })
204
205 // Compare with individual verification
206 b.Run(fmt.Sprintf("individual_%03d", count), func(b *testing.B) {
207 b.ReportAllocs()
208 for i := 0; i < b.N; i++ {
209 for j := range items {
210 SchnorrVerify(items[j].Signature, items[j].Message, items[j].Pubkey)
211 }
212 }
213 })
214 }
215 }
216