1 /*
2 * (C) 2007 Falko Strenzke
3 * 2007 Manuel Hartl
4 * 2009,2015,2018 Jack Lloyd
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8
9 #include "tests.h"
10
11 #if defined(BOTAN_HAS_ECC_GROUP)
12 #include <botan/bigint.h>
13 #include <botan/numthry.h>
14 #include <botan/curve_nistp.h>
15 #include <botan/pk_keys.h>
16 #include <botan/point_gfp.h>
17 #include <botan/ec_group.h>
18 #include <botan/reducer.h>
19 #include <botan/hex.h>
20 #include <botan/data_src.h>
21 #include <botan/x509_key.h>
22 #endif
23
24 namespace Botan_Tests {
25
26 namespace {
27
28 #if defined(BOTAN_HAS_ECC_GROUP)
29
test_integer(Botan::RandomNumberGenerator & rng,size_t bits,BigInt max)30 Botan::BigInt test_integer(Botan::RandomNumberGenerator& rng, size_t bits, BigInt max)
31 {
32 /*
33 Produces integers with long runs of ones and zeros, for testing for
34 carry handling problems.
35 */
36 Botan::BigInt x = 0;
37
38 auto flip_prob = [](size_t i) -> double
39 {
40 if(i % 64 == 0)
41 {
42 return .5;
43 }
44 if(i % 32 == 0)
45 {
46 return .4;
47 }
48 if(i % 8 == 0)
49 {
50 return .05;
51 }
52 return .01;
53 };
54
55 bool active = (rng.next_byte() > 128) ? true : false;
56 for(size_t i = 0; i != bits; ++i)
57 {
58 x <<= 1;
59 x += static_cast<int>(active);
60
61 const double prob = flip_prob(i);
62 const double sample = double(rng.next_byte() % 100) / 100.0; // biased
63
64 if(sample < prob)
65 {
66 active = !active;
67 }
68 }
69
70 if(max > 0)
71 {
72 while(x >= max)
73 {
74 const size_t b = x.bits() - 1;
75 BOTAN_ASSERT(x.get_bit(b) == true, "Set");
76 x.clear_bit(b);
77 }
78 }
79
80 return x;
81 }
82
create_random_point(Botan::RandomNumberGenerator & rng,const Botan::EC_Group & group)83 Botan::PointGFp create_random_point(Botan::RandomNumberGenerator& rng,
84 const Botan::EC_Group& group)
85 {
86 const Botan::BigInt& p = group.get_p();
87 const Botan::Modular_Reducer mod_p(p);
88
89 for(;;)
90 {
91 const Botan::BigInt x = Botan::BigInt::random_integer(rng, 1, p);
92 const Botan::BigInt x3 = mod_p.multiply(x, mod_p.square(x));
93 const Botan::BigInt ax = mod_p.multiply(group.get_a(), x);
94 const Botan::BigInt y = mod_p.reduce(x3 + ax + group.get_b());
95 const Botan::BigInt sqrt_y = ressol(y, p);
96
97 if(sqrt_y > 1)
98 {
99 BOTAN_ASSERT_EQUAL(mod_p.square(sqrt_y), y, "Square root is correct");
100 return group.point(x, sqrt_y);
101 }
102 }
103 }
104
105 class ECC_Randomized_Tests final : public Test
106 {
107 public:
108 std::vector<Test::Result> run() override;
109 };
110
run()111 std::vector<Test::Result> ECC_Randomized_Tests::run()
112 {
113 std::vector<Test::Result> results;
114 for(const std::string& group_name : Botan::EC_Group::known_named_groups())
115 {
116 Test::Result result("ECC randomized " + group_name);
117
118 result.start_timer();
119
120 Botan::EC_Group group(group_name);
121
122 const Botan::PointGFp pt = create_random_point(Test::rng(), group);
123 const Botan::BigInt& group_order = group.get_order();
124
125 std::vector<Botan::BigInt> blind_ws;
126
127 try
128 {
129 const size_t trials = (Test::run_long_tests() ? 10 : 3);
130 for(size_t i = 0; i < trials; ++i)
131 {
132 const Botan::BigInt a = Botan::BigInt::random_integer(Test::rng(), 2, group_order);
133 const Botan::BigInt b = Botan::BigInt::random_integer(Test::rng(), 2, group_order);
134 const Botan::BigInt c = a + b;
135
136 const Botan::PointGFp P = pt * a;
137 const Botan::PointGFp Q = pt * b;
138 const Botan::PointGFp R = pt * c;
139
140 Botan::PointGFp P1 = group.blinded_var_point_multiply(pt, a, Test::rng(), blind_ws);
141 Botan::PointGFp Q1 = group.blinded_var_point_multiply(pt, b, Test::rng(), blind_ws);
142 Botan::PointGFp R1 = group.blinded_var_point_multiply(pt, c, Test::rng(), blind_ws);
143
144 Botan::PointGFp A1 = P + Q;
145 Botan::PointGFp A2 = Q + P;
146
147 result.test_eq("p + q", A1, R);
148 result.test_eq("q + p", A2, R);
149
150 A1.force_affine();
151 A2.force_affine();
152 result.test_eq("p + q", A1, R);
153 result.test_eq("q + p", A2, R);
154
155 result.test_eq("p on the curve", P.on_the_curve(), true);
156 result.test_eq("q on the curve", Q.on_the_curve(), true);
157 result.test_eq("r on the curve", R.on_the_curve(), true);
158
159 result.test_eq("P1", P1, P);
160 result.test_eq("Q1", Q1, Q);
161 result.test_eq("R1", R1, R);
162
163 P1.force_affine();
164 Q1.force_affine();
165 R1.force_affine();
166 result.test_eq("P1", P1, P);
167 result.test_eq("Q1", Q1, Q);
168 result.test_eq("R1", R1, R);
169 }
170 }
171 catch(std::exception& e)
172 {
173 result.test_failure(group_name, e.what());
174 }
175 result.end_timer();
176 results.push_back(result);
177 }
178
179 return results;
180 }
181
182 BOTAN_REGISTER_TEST("pubkey", "ecc_randomized", ECC_Randomized_Tests);
183
184 class NIST_Curve_Reduction_Tests final : public Test
185 {
186 public:
187 typedef std::function<void (Botan::BigInt&, Botan::secure_vector<Botan::word>&)> reducer_fn;
188
run()189 std::vector<Test::Result> run() override
190 {
191 std::vector<Test::Result> results;
192
193 // Using lambdas here to avoid strange UbSan warning (#1370)
194
195 results.push_back(random_redc_test("P-384", Botan::prime_p384(),
196 [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
197 {
198 Botan::redc_p384(p, ws);
199 }));
200 results.push_back(random_redc_test("P-256", Botan::prime_p256(),
201 [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
202 {
203 Botan::redc_p256(p, ws);
204 }));
205 results.push_back(random_redc_test("P-224", Botan::prime_p224(),
206 [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
207 {
208 Botan::redc_p224(p, ws);
209 }));
210 results.push_back(random_redc_test("P-192", Botan::prime_p192(),
211 [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
212 {
213 Botan::redc_p192(p, ws);
214 }));
215 results.push_back(random_redc_test("P-521", Botan::prime_p521(),
216 [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void
217 {
218 Botan::redc_p521(p, ws);
219 }));
220
221 return results;
222 }
223
random_redc_test(const std::string & prime_name,const Botan::BigInt & p,reducer_fn redc_fn)224 Test::Result random_redc_test(const std::string& prime_name,
225 const Botan::BigInt& p,
226 reducer_fn redc_fn)
227 {
228 const Botan::BigInt p2 = p * p;
229 const size_t p_bits = p.bits();
230
231 Botan::Modular_Reducer p_redc(p);
232 Botan::secure_vector<Botan::word> ws;
233
234 Test::Result result("NIST " + prime_name + " reduction");
235 result.start_timer();
236
237 const size_t trials = (Test::run_long_tests() ? 128 : 16);
238
239 for(size_t i = 0; i <= trials; ++i)
240 {
241 const Botan::BigInt x = test_integer(Test::rng(), 2 * p_bits, p2);
242
243 // TODO: time and report all three approaches
244 const Botan::BigInt v1 = x % p;
245 const Botan::BigInt v2 = p_redc.reduce(x);
246
247 Botan::BigInt v3 = x;
248 redc_fn(v3, ws);
249
250 if(!result.test_eq("reference redc", v1, v2) ||
251 !result.test_eq("specialized redc", v2, v3))
252 {
253 result.test_note("failing input" + Botan::hex_encode(Botan::BigInt::encode(x)));
254 }
255 }
256
257 result.end_timer();
258
259 return result;
260 }
261 };
262
263 BOTAN_REGISTER_TEST("pubkey", "nist_redc", NIST_Curve_Reduction_Tests);
264
265 class EC_Group_Tests : public Test
266 {
267 public:
run()268 std::vector<Test::Result> run() override
269 {
270 std::vector<Test::Result> results;
271
272 for(const std::string& group_name : Botan::EC_Group::known_named_groups())
273 {
274 Test::Result result("EC_Group " + group_name);
275
276 const Botan::OID oid = Botan::OID::from_string(group_name);
277
278 const Botan::EC_Group group(oid);
279
280 result.confirm("EC_Group is known", !group.get_curve_oid().empty());
281 result.confirm("EC_Group is considered valid", group.verify_group(Test::rng(), true));
282
283 result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits());
284 result.test_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes());
285
286 result.confirm("Same group is same", group == Botan::EC_Group(group_name));
287
288 const Botan::EC_Group copy(group.get_p(), group.get_a(), group.get_b(),
289 group.get_g_x(), group.get_g_y(),
290 group.get_order(), group.get_cofactor());
291
292 result.confirm("Same group is same even with copy", group == copy);
293
294 const auto pt_mult_by_order = group.get_base_point() * group.get_order();
295 result.confirm("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero());
296
297 if(group.a_is_minus_3())
298 result.test_eq("Group A equals -3", group.get_a(), group.get_p() - 3);
299 else
300 result.test_ne("Group " + group_name + " A does not equal -3", group.get_a(), group.get_p() - 3);
301
302 if(group.a_is_zero())
303 result.test_eq("Group A is zero", group.get_a(), BigInt(0));
304 else
305 result.test_ne("Group " + group_name + " A does not equal zero", group.get_a(), BigInt(0));
306
307 // get a valid point
308 Botan::PointGFp p = group.get_base_point() * Test::rng().next_nonzero_byte();
309
310 // get a copy
311 Botan::PointGFp q = p;
312
313 p.randomize_repr(Test::rng());
314 q.randomize_repr(Test::rng());
315
316 result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
317 result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
318
319 q.force_affine();
320
321 result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
322 result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
323
324 test_ser_der(result, group);
325 test_basic_math(result, group);
326 test_point_swap(result, group);
327 test_zeropoint(result, group);
328
329 results.push_back(result);
330 }
331
332 return results;
333 }
334 private:
335
test_ser_der(Test::Result & result,const Botan::EC_Group & group)336 void test_ser_der(Test::Result& result, const Botan::EC_Group& group)
337 {
338 // generate point
339 const Botan::PointGFp pt = create_random_point(Test::rng(), group);
340 const Botan::PointGFp zero = group.zero_point();
341
342 for(auto scheme : { Botan::PointGFp::UNCOMPRESSED,
343 Botan::PointGFp::COMPRESSED,
344 Botan::PointGFp::HYBRID })
345 {
346 result.test_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)), pt);
347 result.test_eq("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)), zero);
348 }
349 }
350
test_basic_math(Test::Result & result,const Botan::EC_Group & group)351 void test_basic_math(Test::Result& result, const Botan::EC_Group& group)
352 {
353 const Botan::PointGFp& G = group.get_base_point();
354
355 Botan::PointGFp p1 = G * 2;
356 p1 += G;
357
358 result.test_eq("point addition", p1, G * 3);
359
360 p1 -= G * 2;
361
362 result.test_eq("point subtraction", p1, G);
363
364 // The scalar multiplication algorithm relies on this being true:
365 Botan::PointGFp zero_coords = group.point(0, 0);
366 result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve());
367 }
368
test_point_swap(Test::Result & result,const Botan::EC_Group & group)369 void test_point_swap(Test::Result& result, const Botan::EC_Group& group)
370 {
371 Botan::PointGFp a(create_random_point(Test::rng(), group));
372 Botan::PointGFp b(create_random_point(Test::rng(), group));
373 b *= Botan::BigInt(Test::rng(), 20);
374
375 Botan::PointGFp c(a);
376 Botan::PointGFp d(b);
377
378 d.swap(c);
379 result.test_eq("swap correct", a, d);
380 result.test_eq("swap correct", b, c);
381 }
382
test_zeropoint(Test::Result & result,const Botan::EC_Group & group)383 void test_zeropoint(Test::Result& result, const Botan::EC_Group& group)
384 {
385 Botan::PointGFp zero = group.zero_point();
386
387 result.test_throws("Zero point throws", "Cannot convert zero point to affine",
388 [&]() { zero.get_affine_x(); });
389 result.test_throws("Zero point throws", "Cannot convert zero point to affine",
390 [&]() { zero.get_affine_y(); });
391
392 const Botan::PointGFp p1 = group.get_base_point() * 2;
393
394 result.confirm("point is on the curve", p1.on_the_curve());
395 result.confirm("point is not zero", !p1.is_zero());
396
397 Botan::PointGFp p2 = p1;
398 p2 -= p1;
399
400 result.confirm("p - q with q = p results in zero", p2.is_zero());
401
402 const Botan::PointGFp minus_p1 = -p1;
403 result.confirm("point is on the curve", minus_p1.on_the_curve());
404 const Botan::PointGFp shouldBeZero = p1 + minus_p1;
405 result.confirm("point is on the curve", shouldBeZero.on_the_curve());
406 result.confirm("point is zero", shouldBeZero.is_zero());
407
408 result.test_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x());
409 result.test_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y());
410
411 result.confirm("zero point is zero", zero.is_zero());
412 result.confirm("zero point is on the curve", zero.on_the_curve());
413 result.test_eq("addition of zero does nothing", p1, p1 + zero);
414 result.test_eq("addition of zero does nothing", p1, zero + p1);
415 result.test_eq("addition of zero does nothing", p1, p1 - zero);
416 result.confirm("zero times anything is the zero point", (zero * 39193).is_zero());
417
418 for(auto scheme : { Botan::PointGFp::UNCOMPRESSED,
419 Botan::PointGFp::COMPRESSED,
420 Botan::PointGFp::HYBRID })
421 {
422 const std::vector<uint8_t> v = zero.encode(scheme);
423 result.test_eq("encoded/decode rt works", group.OS2ECP(v), zero);
424 }
425 }
426
427
428 };
429
430 BOTAN_REGISTER_TEST("pubkey", "ec_group", EC_Group_Tests);
431
test_decoding_with_seed()432 Test::Result test_decoding_with_seed()
433 {
434 Test::Result result("ECC Unit");
435
436 Botan::EC_Group secp384r1_with_seed(
437 Test::read_data_file("x509/ecc/secp384r1_seed.pem"));
438
439 result.confirm("decoding worked", secp384r1_with_seed.initialized());
440
441 Botan::EC_Group secp384r1("secp384r1");
442
443 result.test_eq("P-384 prime", secp384r1_with_seed.get_p(), secp384r1.get_p());
444
445 return result;
446 }
447
test_coordinates()448 Test::Result test_coordinates()
449 {
450 Test::Result result("ECC Unit");
451
452 const Botan::BigInt exp_affine_x("16984103820118642236896513183038186009872590470");
453 const Botan::BigInt exp_affine_y("1373093393927139016463695321221277758035357890939");
454
455 // precalculation
456 const Botan::EC_Group secp160r1("secp160r1");
457 const Botan::PointGFp& p_G = secp160r1.get_base_point();
458
459 const Botan::PointGFp point_exp = secp160r1.point(exp_affine_x, exp_affine_y);
460 result.confirm("Point is on the curve", point_exp.on_the_curve());
461
462 const Botan::PointGFp p1 = p_G * 2;
463 result.test_eq("Point affine x", p1.get_affine_x(), exp_affine_x);
464 result.test_eq("Point affine y", p1.get_affine_y(), exp_affine_y);
465 return result;
466 }
467
468
469 /**
470 Test point multiplication according to
471 --------
472 SEC 2: Test Vectors for SEC 1
473 Certicom Research
474 Working Draft
475 September, 1999
476 Version 0.3;
477 Section 2.1.2
478 --------
479 */
test_point_mult()480 Test::Result test_point_mult()
481 {
482 Test::Result result("ECC Unit");
483
484 Botan::EC_Group secp160r1("secp160r1");
485 const Botan::PointGFp& p_G = secp160r1.get_base_point();
486
487 Botan::BigInt d_U("0xaa374ffc3ce144e6b073307972cb6d57b2a4e982");
488 Botan::PointGFp Q_U = d_U * p_G;
489
490 result.test_eq("affine x", Q_U.get_affine_x(), Botan::BigInt("466448783855397898016055842232266600516272889280"));
491 result.test_eq("affine y", Q_U.get_affine_y(), Botan::BigInt("1110706324081757720403272427311003102474457754220"));
492 return result;
493 }
494
test_point_negative()495 Test::Result test_point_negative()
496 {
497 Test::Result result("ECC Unit");
498
499 Botan::EC_Group secp160r1("secp160r1");
500 const Botan::PointGFp& p_G = secp160r1.get_base_point();
501
502 const Botan::PointGFp p1 = p_G * 2;
503
504 result.test_eq("affine x", p1.get_affine_x(), Botan::BigInt("16984103820118642236896513183038186009872590470"));
505 result.test_eq("affine y", p1.get_affine_y(), Botan::BigInt("1373093393927139016463695321221277758035357890939"));
506
507 const Botan::PointGFp p1_neg = -p1;
508
509 result.test_eq("affine x", p1_neg.get_affine_x(), p1.get_affine_x());
510 result.test_eq("affine y", p1_neg.get_affine_y(), Botan::BigInt("88408243403763901739989511495005261618427168388"));
511 return result;
512 }
513
test_mult_point()514 Test::Result test_mult_point()
515 {
516 Test::Result result("ECC Unit");
517
518 Botan::EC_Group secp160r1("secp160r1");
519 const Botan::PointGFp& p_G = secp160r1.get_base_point();
520
521 Botan::PointGFp p0 = p_G;
522 Botan::PointGFp p1 = p_G * 2;
523
524 p1 *= p0.get_affine_x();
525
526 const Botan::BigInt exp_mult_x(std::string("967697346845926834906555988570157345422864716250"));
527 const Botan::BigInt exp_mult_y(std::string("512319768365374654866290830075237814703869061656"));
528 Botan::PointGFp expected = secp160r1.point(exp_mult_x, exp_mult_y);
529
530 result.test_eq("point mult", p1, expected);
531 return result;
532 }
533
test_mixed_points()534 Test::Result test_mixed_points()
535 {
536 Test::Result result("ECC Unit");
537
538 Botan::EC_Group secp256r1("secp256r1");
539 Botan::EC_Group secp384r1("secp384r1");
540
541 const Botan::PointGFp& G256 = secp256r1.get_base_point();
542 const Botan::PointGFp& G384 = secp384r1.get_base_point();
543
544 result.test_throws("Mixing points from different groups",
545 [&] { Botan::PointGFp p = G256 + G384; });
546 return result;
547 }
548
test_basic_operations()549 Test::Result test_basic_operations()
550 {
551 Test::Result result("ECC Unit");
552
553 // precalculation
554 Botan::EC_Group secp160r1("secp160r1");
555 const Botan::PointGFp& p_G = secp160r1.get_base_point();
556
557 const Botan::PointGFp p0 = p_G;
558 const Botan::PointGFp p1 = p_G * 2;
559
560 result.test_eq("p1 affine x", p1.get_affine_x(), Botan::BigInt("16984103820118642236896513183038186009872590470"));
561 result.test_eq("p1 affine y", p1.get_affine_y(), Botan::BigInt("1373093393927139016463695321221277758035357890939"));
562
563 const Botan::PointGFp simplePlus = p1 + p0;
564 const Botan::PointGFp exp_simplePlus = secp160r1.point(Botan::BigInt("704859595002530890444080436569091156047721708633"),
565 Botan::BigInt("1147993098458695153857594941635310323215433166682"));
566
567 result.test_eq("point addition", simplePlus, exp_simplePlus);
568
569 const Botan::PointGFp simpleMinus = p1 - p0;
570 result.test_eq("point subtraction", simpleMinus, p_G);
571
572 const Botan::PointGFp simpleMult = p1 * 123456789;
573
574 result.test_eq("point mult affine x", simpleMult.get_affine_x(),
575 Botan::BigInt("43638877777452195295055270548491599621118743290"));
576 result.test_eq("point mult affine y", simpleMult.get_affine_y(),
577 Botan::BigInt("56841378500012376527163928510402662349220202981"));
578
579 return result;
580 }
581
test_enc_dec_compressed_160()582 Test::Result test_enc_dec_compressed_160()
583 {
584 Test::Result result("ECC Unit");
585
586 // Test for compressed conversion (02/03) 160bit
587 Botan::EC_Group secp160r1("secp160r1");
588 const std::vector<uint8_t> G_comp = Botan::hex_decode("024A96B5688EF573284664698968C38BB913CBFC82");
589 const Botan::PointGFp p = secp160r1.OS2ECP(G_comp);
590 const std::vector<uint8_t> sv_result = p.encode(Botan::PointGFp::COMPRESSED);
591
592 result.test_eq("result", sv_result, G_comp);
593 return result;
594 }
595
test_enc_dec_compressed_256()596 Test::Result test_enc_dec_compressed_256()
597 {
598 Test::Result result("ECC Unit");
599
600 Botan::EC_Group group("secp256r1");
601
602 const std::string G_secp_comp = "036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296";
603 const std::vector<uint8_t> sv_G_secp_comp = Botan::hex_decode(G_secp_comp);
604
605 Botan::PointGFp p_G = group.OS2ECP(sv_G_secp_comp);
606 std::vector<uint8_t> sv_result = p_G.encode(Botan::PointGFp::COMPRESSED);
607
608 result.test_eq("compressed_256", sv_result, sv_G_secp_comp);
609 return result;
610 }
611
612
test_enc_dec_uncompressed_112()613 Test::Result test_enc_dec_uncompressed_112()
614 {
615 Test::Result result("ECC Unit");
616
617 // Test for uncompressed conversion (04) 112bit
618
619 // Curve is secp112r2
620
621 const Botan::BigInt p("0xdb7c2abf62e35e668076bead208b");
622 const Botan::BigInt a("0x6127C24C05F38A0AAAF65C0EF02C");
623 const Botan::BigInt b("0x51DEF1815DB5ED74FCC34C85D709");
624
625 const Botan::BigInt g_x("0x4BA30AB5E892B4E1649DD0928643");
626 const Botan::BigInt g_y("0xADCD46F5882E3747DEF36E956E97");
627
628 const Botan::BigInt order("0x36DF0AAFD8B8D7597CA10520D04B");
629 const Botan::BigInt cofactor("4"); // !
630
631 const Botan::EC_Group group(p, a, b, g_x, g_y, order, cofactor);
632
633 const std::string G_secp_uncomp = "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97";
634 const std::vector<uint8_t> sv_G_secp_uncomp = Botan::hex_decode(G_secp_uncomp);
635
636 Botan::PointGFp p_G = group.OS2ECP(sv_G_secp_uncomp);
637 std::vector<uint8_t> sv_result = p_G.encode(Botan::PointGFp::UNCOMPRESSED);
638
639 result.test_eq("uncompressed_112", sv_result, sv_G_secp_uncomp);
640 return result;
641 }
642
test_enc_dec_uncompressed_521()643 Test::Result test_enc_dec_uncompressed_521()
644 {
645 Test::Result result("ECC Unit");
646
647 // Test for uncompressed conversion(04) with big values(521 bit)
648
649 const std::string G_secp_uncomp =
650 "0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2ffA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650";
651
652 const std::vector<uint8_t> sv_G_secp_uncomp = Botan::hex_decode(G_secp_uncomp);
653
654 Botan::EC_Group group("secp521r1");
655
656 Botan::PointGFp p_G = group.OS2ECP(sv_G_secp_uncomp);
657
658 std::vector<uint8_t> sv_result = p_G.encode(Botan::PointGFp::UNCOMPRESSED);
659
660 result.test_eq("expected", sv_result, sv_G_secp_uncomp);
661 return result;
662 }
663
test_ecc_registration()664 Test::Result test_ecc_registration()
665 {
666 Test::Result result("ECC registration");
667
668 // secp112r1
669 const Botan::BigInt p("0xDB7C2ABF62E35E668076BEAD208B");
670 const Botan::BigInt a("0xDB7C2ABF62E35E668076BEAD2088");
671 const Botan::BigInt b("0x659EF8BA043916EEDE8911702B22");
672
673 const Botan::BigInt g_x("0x09487239995A5EE76B55F9C2F098");
674 const Botan::BigInt g_y("0xA89CE5AF8724C0A23E0E0FF77500");
675 const Botan::BigInt order("0xDB7C2ABF62E35E7628DFAC6561C5");
676
677 const Botan::OID oid("1.3.132.0.6");
678
679 // Creating this object implicitly registers the curve for future use ...
680 Botan::EC_Group reg_group(p, a, b, g_x, g_y, order, 1, oid);
681
682 Botan::EC_Group group(oid);
683
684 result.test_eq("Group registration worked", group.get_p(), p);
685
686 return result;
687 }
688
689 class ECC_Unit_Tests final : public Test
690 {
691 public:
run()692 std::vector<Test::Result> run() override
693 {
694 std::vector<Test::Result> results;
695
696 results.push_back(test_coordinates());
697 results.push_back(test_decoding_with_seed());
698 results.push_back(test_point_mult());
699 results.push_back(test_point_negative());
700 results.push_back(test_mult_point());
701 results.push_back(test_mixed_points());
702 results.push_back(test_basic_operations());
703 results.push_back(test_enc_dec_compressed_160());
704 results.push_back(test_enc_dec_compressed_256());
705 results.push_back(test_enc_dec_uncompressed_112());
706 results.push_back(test_enc_dec_uncompressed_521());
707 results.push_back(test_ecc_registration());
708
709 return results;
710 }
711 };
712
713 BOTAN_REGISTER_TEST("pubkey", "ecc_unit", ECC_Unit_Tests);
714
715 #if defined(BOTAN_HAS_ECDSA)
716
717 class ECC_Invalid_Key_Tests final : public Text_Based_Test
718 {
719 public:
ECC_Invalid_Key_Tests()720 ECC_Invalid_Key_Tests() :
721 Text_Based_Test("pubkey/ecc_invalid.vec", "SubjectPublicKey") {}
722
clear_between_callbacks() const723 bool clear_between_callbacks() const override
724 {
725 return false;
726 }
727
run_one_test(const std::string &,const VarMap & vars)728 Test::Result run_one_test(const std::string&, const VarMap& vars) override
729 {
730 Test::Result result("ECC invalid keys");
731
732 const std::string encoded = vars.get_req_str("SubjectPublicKey");
733 Botan::DataSource_Memory key_data(Botan::hex_decode(encoded));
734
735 try
736 {
737 std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(key_data));
738 result.test_eq("public key fails check", key->check_key(Test::rng(), false), false);
739 }
740 catch(Botan::Decoding_Error&)
741 {
742 result.test_success("Decoding invalid ECC key results in decoding error exception");
743 }
744
745 return result;
746 }
747 };
748
749 BOTAN_REGISTER_TEST("pubkey", "ecc_invalid", ECC_Invalid_Key_Tests);
750
751 #endif
752
753 #endif
754
755 }
756
757 }
758