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