1
2
3
4
5
6 package secp256k1
7
8 import (
9 "bytes"
10 "errors"
11 "testing"
12 )
13
14
15
16 func TestParsePubKey(t *testing.T) {
17 tests := []struct {
18 name string
19 key string
20 err error
21 wantX string
22 wantY string
23 }{{
24 name: "uncompressed ok",
25 key: "04" +
26 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
27 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
28 err: nil,
29 wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
30 wantY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
31 }, {
32 name: "uncompressed x changed (not on curve)",
33 key: "04" +
34 "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
35 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
36 err: ErrPubKeyNotOnCurve,
37 }, {
38 name: "uncompressed y changed (not on curve)",
39 key: "04" +
40 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
41 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
42 err: ErrPubKeyNotOnCurve,
43 }, {
44 name: "uncompressed claims compressed",
45 key: "03" +
46 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
47 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
48 err: ErrPubKeyInvalidFormat,
49 }, {
50 name: "uncompressed as hybrid ok (ybit = 0)",
51 key: "06" +
52 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
53 "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
54 err: nil,
55 wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
56 wantY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
57 }, {
58 name: "uncompressed as hybrid ok (ybit = 1)",
59 key: "07" +
60 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
61 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
62 err: nil,
63 wantX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
64 wantY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
65 }, {
66 name: "uncompressed as hybrid wrong oddness",
67 key: "06" +
68 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
69 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
70 err: ErrPubKeyMismatchedOddness,
71 }, {
72 name: "compressed ok (ybit = 0)",
73 key: "02" +
74 "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
75 err: nil,
76 wantX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
77 wantY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032",
78 }, {
79 name: "compressed ok (ybit = 1)",
80 key: "03" +
81 "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
82 err: nil,
83 wantX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
84 wantY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f",
85 }, {
86 name: "compressed claims uncompressed (ybit = 0)",
87 key: "04" +
88 "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
89 err: ErrPubKeyInvalidFormat,
90 }, {
91 name: "compressed claims uncompressed (ybit = 1)",
92 key: "04" +
93 "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
94 err: ErrPubKeyInvalidFormat,
95 }, {
96 name: "compressed claims hybrid (ybit = 0)",
97 key: "06" +
98 "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
99 err: ErrPubKeyInvalidFormat,
100 }, {
101 name: "compressed claims hybrid (ybit = 1)",
102 key: "07" +
103 "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
104 err: ErrPubKeyInvalidFormat,
105 }, {
106 name: "compressed with invalid x coord (ybit = 0)",
107 key: "03" +
108 "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c",
109 err: ErrPubKeyNotOnCurve,
110 }, {
111 name: "compressed with invalid x coord (ybit = 1)",
112 key: "03" +
113 "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d",
114 err: ErrPubKeyNotOnCurve,
115 }, {
116 name: "empty",
117 key: "",
118 err: ErrPubKeyInvalidLen,
119 }, {
120 name: "wrong length",
121 key: "05",
122 err: ErrPubKeyInvalidLen,
123 }, {
124 name: "uncompressed x == p",
125 key: "04" +
126 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" +
127 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
128 err: ErrPubKeyXTooBig,
129 }, {
130
131
132 name: "uncompressed x > p (p + 1 -- aka 1)",
133 key: "04" +
134 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" +
135 "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441",
136 err: ErrPubKeyXTooBig,
137 }, {
138 name: "uncompressed y == p",
139 key: "04" +
140 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
141 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
142 err: ErrPubKeyYTooBig,
143 }, {
144
145
146 name: "uncompressed y > p (p + 1 -- aka 1)",
147 key: "04" +
148 "1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" +
149 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
150 err: ErrPubKeyYTooBig,
151 }, {
152 name: "compressed x == p (ybit = 0)",
153 key: "02" +
154 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
155 err: ErrPubKeyXTooBig,
156 }, {
157 name: "compressed x == p (ybit = 1)",
158 key: "03" +
159 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
160 err: ErrPubKeyXTooBig,
161 }, {
162
163
164 name: "compressed x > p (p + 2 -- aka 2) (ybit = 0)",
165 key: "02" +
166 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc31",
167 err: ErrPubKeyXTooBig,
168 }, {
169
170
171 name: "compressed x > p (p + 1 -- aka 1) (ybit = 1)",
172 key: "03" +
173 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
174 err: ErrPubKeyXTooBig,
175 }, {
176 name: "hybrid x == p (ybit = 1)",
177 key: "07" +
178 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" +
179 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
180 err: ErrPubKeyXTooBig,
181 }, {
182
183
184 name: "hybrid x > p (p + 1 -- aka 1) (ybit = 0)",
185 key: "06" +
186 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30" +
187 "bde70df51939b94c9c24979fa7dd04ebd9b3572da7802290438af2a681895441",
188 err: ErrPubKeyXTooBig,
189 }, {
190 name: "hybrid y == p (ybit = 0 when mod p)",
191 key: "06" +
192 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
193 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
194 err: ErrPubKeyYTooBig,
195 }, {
196
197
198 name: "hybrid y > p (p + 1 -- aka 1) (ybit = 1 when mod p)",
199 key: "07" +
200 "1fe1e5ef3fceb5c135ab7741333ce5a6e80d68167653f6b2b24bcbcfaaaff507" +
201 "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
202 err: ErrPubKeyYTooBig,
203 }}
204
205 for _, test := range tests {
206 pubKeyBytes := hexToBytes(test.key)
207 pubKey, err := ParsePubKey(pubKeyBytes)
208 if !errors.Is(err, test.err) {
209 t.Errorf("%s mismatched err -- got %v, want %v", test.name, err,
210 test.err)
211 continue
212 }
213 if err != nil {
214 continue
215 }
216
217
218
219 wantX, wantY := hexToFieldVal(test.wantX), hexToFieldVal(test.wantY)
220 if !pubKey.x.Equals(wantX) {
221 t.Errorf("%s: mismatched x coordinate -- got %v, want %v",
222 test.name, pubKey.x, wantX)
223 continue
224 }
225 if !pubKey.y.Equals(wantY) {
226 t.Errorf("%s: mismatched y coordinate -- got %v, want %v",
227 test.name, pubKey.y, wantY)
228 continue
229 }
230 }
231 }
232
233
234
235 func TestPubKeySerialize(t *testing.T) {
236 tests := []struct {
237 name string
238 pubX string
239 pubY string
240 compress bool
241 expected string
242 }{{
243 name: "uncompressed (ybit = 0)",
244 pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
245 pubY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
246 compress: false,
247 expected: "04" +
248 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
249 "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
250 }, {
251 name: "uncompressed (ybit = 1)",
252 pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
253 pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
254 compress: false,
255 expected: "04" +
256 "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
257 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
258 }, {
259
260
261
262 name: "uncompressed not on the curve due to x coord",
263 pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
264 pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
265 compress: false,
266 expected: "04" +
267 "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
268 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
269 }, {
270
271
272
273 name: "uncompressed not on the curve due to y coord",
274 pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
275 pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
276 compress: false,
277 expected: "04" +
278 "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
279 "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
280 }, {
281 name: "compressed (ybit = 0)",
282 pubX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
283 pubY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032",
284 compress: true,
285 expected: "02" +
286 "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d",
287 }, {
288 name: "compressed (ybit = 1)",
289 pubX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
290 pubY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f",
291 compress: true,
292 expected: "03" +
293 "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e",
294 }, {
295
296
297
298 name: "compressed not on curve (ybit = 0)",
299 pubX: "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c",
300 pubY: "0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032",
301 compress: true,
302 expected: "02" +
303 "ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4c",
304 }, {
305
306
307
308 name: "compressed not on curve (ybit = 1)",
309 pubX: "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d",
310 pubY: "499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f",
311 compress: true,
312 expected: "03" +
313 "2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448d",
314 }}
315
316 for _, test := range tests {
317
318 x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY)
319 pubKey := NewPublicKey(x, y)
320
321
322
323 var serialized []byte
324 if test.compress {
325 serialized = pubKey.SerializeCompressed()
326 } else {
327 serialized = pubKey.SerializeUncompressed()
328 }
329 expected := hexToBytes(test.expected)
330 if !bytes.Equal(serialized, expected) {
331 t.Errorf("%s: mismatched serialized public key -- got %x, want %x",
332 test.name, serialized, expected)
333 continue
334 }
335 }
336 }
337
338
339
340 func TestPublicKeyIsEqual(t *testing.T) {
341 pubKey1 := &PublicKey{
342 x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
343 y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"),
344 }
345 pubKey1Copy := &PublicKey{
346 x: *hexToFieldVal("2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
347 y: *hexToFieldVal("499dd7852849a38aa23ed9f306f07794063fe7904e0f347bc209fdddaf37691f"),
348 }
349 pubKey2 := &PublicKey{
350 x: *hexToFieldVal("ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"),
351 y: *hexToFieldVal("0890ff84d7999d878a57bee170e19ef4b4803b4bdede64503a6ac352b03c8032"),
352 }
353
354 if !pubKey1.IsEqual(pubKey1) {
355 t.Fatalf("bad self public key equality check: (%v, %v)", pubKey1.x,
356 pubKey1.y)
357 }
358 if !pubKey1.IsEqual(pubKey1Copy) {
359 t.Fatalf("bad public key equality check: (%v, %v) == (%v, %v)",
360 pubKey1.x, pubKey1.y, pubKey1Copy.x, pubKey1Copy.y)
361 }
362
363 if pubKey1.IsEqual(pubKey2) {
364 t.Fatalf("bad public key equality check: (%v, %v) != (%v, %v)",
365 pubKey1.x, pubKey1.y, pubKey2.x, pubKey2.y)
366 }
367 }
368
369
370
371 func TestPublicKeyAsJacobian(t *testing.T) {
372 tests := []struct {
373 name string
374 pubKey string
375 wantX string
376 wantY string
377
378 }{{
379 name: "public key for private key 0x01",
380 pubKey: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
381 wantX: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
382 wantY: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
383 }, {
384 name: "public for private key 0x03",
385 pubKey: "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
386 wantX: "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
387 wantY: "388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672",
388 }, {
389 name: "public for private key 0x06",
390 pubKey: "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556",
391 wantX: "fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556",
392 wantY: "ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297",
393 }}
394
395 for _, test := range tests {
396
397 pubKeyBytes := hexToBytes(test.pubKey)
398 wantX := hexToFieldVal(test.wantX)
399 wantY := hexToFieldVal(test.wantY)
400 pubKey, err := ParsePubKey(pubKeyBytes)
401 if err != nil {
402 t.Errorf("%s: failed to parse public key: %v", test.name, err)
403 continue
404 }
405
406
407
408 var point JacobianPoint
409 pubKey.AsJacobian(&point)
410 if !point.Z.IsOne() {
411 t.Errorf("%s: invalid Z coordinate -- got %v, want 1", test.name,
412 point.Z)
413 continue
414 }
415 if !point.X.Equals(wantX) {
416 t.Errorf("%s: invalid X coordinate - got %v, want %v", test.name,
417 point.X, wantX)
418 continue
419 }
420 if !point.Y.Equals(wantY) {
421 t.Errorf("%s: invalid Y coordinate - got %v, want %v", test.name,
422 point.Y, wantY)
423 continue
424 }
425 }
426 }
427
428
429
430 func TestPublicKeyIsOnCurve(t *testing.T) {
431 tests := []struct {
432 name string
433 pubX string
434 pubY string
435 want bool
436 }{{
437 name: "valid with even y",
438 pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
439 pubY: "4d1f1522047b33068bbb9b07d1e9f40564749b062b3fc0666479bc08a94be98c",
440 want: true,
441 }, {
442 name: "valid with odd y",
443 pubX: "11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
444 pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
445 want: true,
446 }, {
447 name: "invalid due to x coord",
448 pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
449 pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3",
450 want: false,
451 }, {
452 name: "invalid due to y coord",
453 pubX: "15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c",
454 pubY: "b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4",
455 want: false,
456 }}
457
458 for _, test := range tests {
459
460 x, y := hexToFieldVal(test.pubX), hexToFieldVal(test.pubY)
461 pubKey := NewPublicKey(x, y)
462
463 result := pubKey.IsOnCurve()
464 if result != test.want {
465 t.Errorf("%s: mismatched is on curve result -- got %v, want %v",
466 test.name, result, test.want)
467 continue
468 }
469 }
470 }
471
View as plain text