• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 10109696569

26 Jul 2024 10:20AM UTC coverage: 91.648% (+0.004%) from 91.644%
10109696569

push

github

web-flow
Merge pull request #4256 from randombit/jack/ec-group-debt

Define some deprecated EC_Group functions in terms of the new APIs

87247 of 95198 relevant lines covered (91.65%)

9303720.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.63
/src/tests/test_ec_group.cpp
1
/*
2
* (C) 2007 Falko Strenzke
3
*     2007 Manuel Hartl
4
*     2009,2015,2018,2021 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/data_src.h>
14
   #include <botan/ec_group.h>
15
   #include <botan/ec_point.h>
16
   #include <botan/hex.h>
17
   #include <botan/numthry.h>
18
   #include <botan/pk_keys.h>
19
   #include <botan/reducer.h>
20
   #include <botan/x509_key.h>
21
   #include <botan/internal/curve_nistp.h>
22
#endif
23

24
namespace Botan_Tests {
25

26
namespace {
27

28
#if defined(BOTAN_HAS_ECC_GROUP)
29

30
Botan::BigInt test_integer(Botan::RandomNumberGenerator& rng, size_t bits, const BigInt& max) {
645✔
31
   /*
32
   Produces integers with long runs of ones and zeros, for testing for
33
   carry handling problems.
34
   */
35
   Botan::BigInt x = 0;
645✔
36

37
   auto flip_prob = [](size_t i) -> double {
407,511✔
38
      if(i % 64 == 0) {
406,866✔
39
         return .5;
40
      }
41
      if(i % 32 == 0) {
400,416✔
42
         return .4;
43
      }
44
      if(i % 8 == 0) {
394,095✔
45
         return .05;
38,184✔
46
      }
47
      return .01;
48
   };
49

50
   bool active = (rng.next_byte() > 128) ? true : false;
645✔
51
   for(size_t i = 0; i != bits; ++i) {
407,511✔
52
      x <<= 1;
406,866✔
53
      x += static_cast<int>(active);
406,866✔
54

55
      const double prob = flip_prob(i);
406,866✔
56
      const double sample = double(rng.next_byte() % 100) / 100.0;  // biased
406,866✔
57

58
      if(sample < prob) {
406,866✔
59
         active = !active;
13,183✔
60
      }
61
   }
62

63
   if(max > 0) {
645✔
64
      while(x >= max) {
671✔
65
         const size_t b = x.bits() - 1;
26✔
66
         BOTAN_ASSERT(x.get_bit(b) == true, "Set");
52✔
67
         x.clear_bit(b);
26✔
68
      }
69
   }
70

71
   return x;
645✔
72
}
×
73

74
Botan::EC_Point create_random_point(Botan::RandomNumberGenerator& rng, const Botan::EC_Group& group) {
108✔
75
   const Botan::BigInt& p = group.get_p();
108✔
76
   const Botan::Modular_Reducer mod_p(p);
108✔
77

78
   for(;;) {
248✔
79
      const Botan::BigInt x = Botan::BigInt::random_integer(rng, 1, p);
178✔
80
      const Botan::BigInt x3 = mod_p.multiply(x, mod_p.square(x));
178✔
81
      const Botan::BigInt ax = mod_p.multiply(group.get_a(), x);
178✔
82
      const Botan::BigInt y = mod_p.reduce(x3 + ax + group.get_b());
356✔
83
      const Botan::BigInt sqrt_y = Botan::sqrt_modulo_prime(y, p);
178✔
84

85
      if(sqrt_y > 1) {
178✔
86
         BOTAN_ASSERT_EQUAL(mod_p.square(sqrt_y), y, "Square root is correct");
324✔
87
         return group.point(x, sqrt_y);
216✔
88
      }
89
   }
782✔
90
}
108✔
91

92
class ECC_Randomized_Tests final : public Test {
×
93
   public:
94
      std::vector<Test::Result> run() override;
95
};
96

97
std::vector<Test::Result> ECC_Randomized_Tests::run() {
1✔
98
   std::vector<Test::Result> results;
1✔
99
   for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
28✔
100
      Test::Result result("ECC randomized " + group_name);
27✔
101

102
      result.start_timer();
27✔
103

104
      auto group = Botan::EC_Group::from_name(group_name);
27✔
105

106
      const Botan::EC_Point pt = create_random_point(this->rng(), group);
27✔
107

108
      std::vector<Botan::BigInt> blind_ws;
27✔
109

110
      try {
27✔
111
         const size_t trials = (Test::run_long_tests() ? 10 : 3);
27✔
112
         for(size_t i = 0; i < trials; ++i) {
297✔
113
            const Botan::BigInt a = group.random_scalar(rng());
270✔
114
            const Botan::BigInt b = group.random_scalar(rng());
270✔
115
            const Botan::BigInt c = group.mod_order(a + b);
270✔
116

117
            const Botan::EC_Point P = pt * a;
270✔
118
            const Botan::EC_Point Q = pt * b;
270✔
119
            const Botan::EC_Point R = pt * c;
270✔
120

121
            Botan::EC_Point P1 = group.blinded_var_point_multiply(pt, a, this->rng(), blind_ws);
270✔
122
            Botan::EC_Point Q1 = group.blinded_var_point_multiply(pt, b, this->rng(), blind_ws);
270✔
123
            Botan::EC_Point R1 = group.blinded_var_point_multiply(pt, c, this->rng(), blind_ws);
270✔
124

125
            Botan::EC_Point A1 = P + Q;
270✔
126
            Botan::EC_Point A2 = Q + P;
270✔
127

128
            result.test_eq("p + q", A1, R);
270✔
129
            result.test_eq("q + p", A2, R);
270✔
130

131
            A1.force_affine();
270✔
132
            A2.force_affine();
270✔
133
            result.test_eq("p + q", A1, R);
270✔
134
            result.test_eq("q + p", A2, R);
270✔
135

136
            result.test_eq("p on the curve", P.on_the_curve(), true);
270✔
137
            result.test_eq("q on the curve", Q.on_the_curve(), true);
270✔
138
            result.test_eq("r on the curve", R.on_the_curve(), true);
270✔
139

140
            result.test_eq("P1", P1, P);
270✔
141
            result.test_eq("Q1", Q1, Q);
270✔
142
            result.test_eq("R1", R1, R);
270✔
143

144
            P1.force_affine();
270✔
145
            Q1.force_affine();
270✔
146
            R1.force_affine();
270✔
147
            result.test_eq("P1", P1, P);
270✔
148
            result.test_eq("Q1", Q1, Q);
270✔
149
            result.test_eq("R1", R1, R);
270✔
150
         }
1,080✔
151
      } catch(std::exception& e) {
×
152
         result.test_failure(group_name, e.what());
×
153
      }
×
154
      result.end_timer();
27✔
155
      results.push_back(result);
27✔
156
   }
27✔
157

158
   return results;
1✔
159
}
×
160

161
BOTAN_REGISTER_TEST("pubkey", "ecc_randomized", ECC_Randomized_Tests);
162

163
class NIST_Curve_Reduction_Tests final : public Test {
×
164
   public:
165
      typedef std::function<void(Botan::BigInt&, Botan::secure_vector<Botan::word>&)> reducer_fn;
166

167
      std::vector<Test::Result> run() override {
1✔
168
         std::vector<Test::Result> results;
1✔
169

170
         // Using lambdas here to avoid strange UbSan warning (#1370)
171

172
         results.push_back(random_redc_test(
2✔
173
            "P-384", Botan::prime_p384(), [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void {
129✔
174
               Botan::redc_p384(p, ws);
129✔
175
            }));
176
         results.push_back(random_redc_test(
2✔
177
            "P-256", Botan::prime_p256(), [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void {
129✔
178
               Botan::redc_p256(p, ws);
129✔
179
            }));
180
         results.push_back(random_redc_test(
2✔
181
            "P-224", Botan::prime_p224(), [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void {
129✔
182
               Botan::redc_p224(p, ws);
129✔
183
            }));
184
         results.push_back(random_redc_test(
2✔
185
            "P-192", Botan::prime_p192(), [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void {
129✔
186
               Botan::redc_p192(p, ws);
129✔
187
            }));
188
         results.push_back(random_redc_test(
2✔
189
            "P-521", Botan::prime_p521(), [](Botan::BigInt& p, Botan::secure_vector<Botan::word>& ws) -> void {
129✔
190
               Botan::redc_p521(p, ws);
129✔
191
            }));
192

193
         return results;
1✔
194
      }
×
195

196
      static Test::Result random_redc_test(const std::string& prime_name,
5✔
197
                                           const Botan::BigInt& p,
198
                                           const reducer_fn& redc_fn) {
199
         const Botan::BigInt p2 = p * p;
5✔
200
         const size_t p_bits = p.bits();
5✔
201

202
         Botan::Modular_Reducer p_redc(p);
5✔
203
         Botan::secure_vector<Botan::word> ws;
5✔
204

205
         auto rng = Test::new_rng("random_redc " + prime_name);
5✔
206

207
         Test::Result result("NIST " + prime_name + " reduction");
10✔
208
         result.start_timer();
5✔
209

210
         const size_t trials = (Test::run_long_tests() ? 128 : 16);
5✔
211

212
         for(size_t i = 0; i <= trials; ++i) {
650✔
213
            const Botan::BigInt x = test_integer(*rng, 2 * p_bits, p2);
645✔
214

215
            // TODO: time and report all three approaches
216
            const Botan::BigInt v1 = x % p;
645✔
217
            const Botan::BigInt v2 = p_redc.reduce(x);
645✔
218

219
            Botan::BigInt v3 = x;
645✔
220
            redc_fn(v3, ws);
645✔
221

222
            if(!result.test_eq("reference redc", v1, v2) || !result.test_eq("specialized redc", v2, v3)) {
1,290✔
223
               result.test_note("failing input" + Botan::hex_encode(Botan::BigInt::encode(x)));
×
224
            }
225
         }
2,580✔
226

227
         result.end_timer();
5✔
228

229
         return result;
5✔
230
      }
11✔
231
};
232

233
BOTAN_REGISTER_TEST("pubkey", "nist_redc", NIST_Curve_Reduction_Tests);
234

235
class EC_Group_Tests : public Test {
×
236
   public:
237
      std::vector<Test::Result> run() override {
1✔
238
         std::vector<Test::Result> results;
1✔
239

240
         for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
28✔
241
            Test::Result result("EC_Group " + group_name);
27✔
242

243
            const auto group = Botan::EC_Group::from_name(group_name);
27✔
244

245
            result.confirm("EC_Group is known", group.get_curve_oid().has_value());
54✔
246
            result.confirm("EC_Group is considered valid", group.verify_group(this->rng(), true));
54✔
247
            result.confirm("EC_Group is not considered explict encoding", !group.used_explicit_encoding());
54✔
248

249
            result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits());
27✔
250
            result.test_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes());
27✔
251

252
            result.test_eq("EC_Group has cofactor == 1", group.get_cofactor(), 1);
54✔
253

254
            const Botan::OID from_order = Botan::EC_Group::EC_group_identity_from_order(group.get_order());
27✔
255

256
            result.test_eq(
54✔
257
               "EC_group_identity_from_order works", from_order.to_string(), group.get_curve_oid().to_string());
54✔
258

259
            result.confirm("Same group is same", group == Botan::EC_Group::from_name(group_name));
54✔
260

261
            try {
27✔
262
               const Botan::EC_Group copy(group.get_curve_oid(),
27✔
263
                                          group.get_p(),
264
                                          group.get_a(),
265
                                          group.get_b(),
266
                                          group.get_g_x(),
267
                                          group.get_g_y(),
268
                                          group.get_order());
27✔
269

270
               result.confirm("Same group is same even with copy", group == copy);
38✔
271
            } catch(Botan::Invalid_Argument&) {}
27✔
272

273
            const auto group_der_oid = group.DER_encode();
27✔
274
            const Botan::EC_Group group_via_oid(group_der_oid);
27✔
275
            result.confirm("EC_Group via OID is not considered explict encoding",
54✔
276
                           !group_via_oid.used_explicit_encoding());
27✔
277

278
            const auto group_der_explicit = group.DER_encode(Botan::EC_Group_Encoding::Explicit);
27✔
279
            const Botan::EC_Group group_via_explicit(group_der_explicit);
27✔
280
            result.confirm("EC_Group via explicit DER is considered explict encoding",
54✔
281
                           group_via_explicit.used_explicit_encoding());
27✔
282

283
            const auto pt_mult_by_order = group.get_base_point() * group.get_order();
27✔
284
            result.confirm("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero());
54✔
285

286
            if(group.a_is_minus_3()) {
27✔
287
               result.test_eq("Group A equals -3", group.get_a(), group.get_p() - 3);
48✔
288
            } else {
289
               result.test_ne("Group " + group_name + " A does not equal -3", group.get_a(), group.get_p() - 3);
44✔
290
            }
291

292
            if(group.a_is_zero()) {
27✔
293
               result.test_eq("Group A is zero", group.get_a(), BigInt(0));
8✔
294
            } else {
295
               result.test_ne("Group " + group_name + " A does not equal zero", group.get_a(), BigInt(0));
69✔
296
            }
297

298
            // get a valid point
299
            Botan::EC_Point p = group.get_base_point() * this->rng().next_nonzero_byte();
54✔
300

301
            // get a copy
302
            Botan::EC_Point q = p;
27✔
303

304
            p.randomize_repr(this->rng());
27✔
305
            q.randomize_repr(this->rng());
27✔
306

307
            result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
81✔
308
            result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
81✔
309

310
            q.force_affine();
27✔
311

312
            result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
81✔
313
            result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
81✔
314

315
            test_ser_der(result, group);
27✔
316
            test_basic_math(result, group);
27✔
317
            test_point_swap(result, group);
27✔
318
            test_zeropoint(result, group);
27✔
319

320
            results.push_back(result);
27✔
321
         }
81✔
322

323
         return results;
1✔
324
      }
×
325

326
   private:
327
      void test_ser_der(Test::Result& result, const Botan::EC_Group& group) {
27✔
328
         // generate point
329
         const Botan::EC_Point pt = create_random_point(this->rng(), group);
27✔
330
         const Botan::EC_Point zero = group.zero_point();
27✔
331

332
         for(auto scheme : {Botan::EC_Point_Format::Uncompressed,
189✔
333
                            Botan::EC_Point_Format::Compressed,
334
                            Botan::EC_Point_Format::Hybrid}) {
108✔
335
            result.test_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)), pt);
162✔
336
            result.test_eq("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)), zero);
243✔
337
         }
338
      }
27✔
339

340
      static void test_basic_math(Test::Result& result, const Botan::EC_Group& group) {
27✔
341
         const Botan::EC_Point& G = group.get_base_point();
27✔
342

343
         Botan::EC_Point p1 = G * 2;
54✔
344
         p1 += G;
27✔
345

346
         result.test_eq("point addition", p1, G * 3);
81✔
347

348
         p1 -= G * 2;
54✔
349

350
         result.test_eq("point subtraction", p1, G);
27✔
351

352
         // The scalar multiplication algorithm relies on this being true:
353
         Botan::EC_Point zero_coords = group.point(0, 0);
54✔
354
         result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve());
54✔
355
      }
27✔
356

357
      void test_point_swap(Test::Result& result, const Botan::EC_Group& group) {
27✔
358
         Botan::EC_Point a(create_random_point(this->rng(), group));
27✔
359
         Botan::EC_Point b(create_random_point(this->rng(), group));
27✔
360
         b *= Botan::BigInt(this->rng(), 20);
27✔
361

362
         Botan::EC_Point c(a);
27✔
363
         Botan::EC_Point d(b);
27✔
364

365
         d.swap(c);
27✔
366
         result.test_eq("swap correct", a, d);
27✔
367
         result.test_eq("swap correct", b, c);
27✔
368
      }
27✔
369

370
      static void test_zeropoint(Test::Result& result, const Botan::EC_Group& group) {
27✔
371
         Botan::EC_Point zero = group.zero_point();
27✔
372

373
         result.test_throws("Zero point throws", "Cannot convert zero point to affine", [&]() { zero.get_affine_x(); });
81✔
374
         result.test_throws("Zero point throws", "Cannot convert zero point to affine", [&]() { zero.get_affine_y(); });
81✔
375

376
         const Botan::EC_Point p1 = group.get_base_point() * 2;
54✔
377

378
         result.confirm("point is on the curve", p1.on_the_curve());
54✔
379
         result.confirm("point is not zero", !p1.is_zero());
54✔
380

381
         Botan::EC_Point p2 = p1;
27✔
382
         p2 -= p1;
27✔
383

384
         result.confirm("p - q with q = p results in zero", p2.is_zero());
54✔
385

386
         const Botan::EC_Point minus_p1 = -p1;
27✔
387
         result.confirm("point is on the curve", minus_p1.on_the_curve());
54✔
388
         const Botan::EC_Point shouldBeZero = p1 + minus_p1;
27✔
389
         result.confirm("point is on the curve", shouldBeZero.on_the_curve());
54✔
390
         result.confirm("point is zero", shouldBeZero.is_zero());
54✔
391

392
         result.test_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x());
81✔
393
         result.test_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y());
108✔
394

395
         result.confirm("zero point is zero", zero.is_zero());
54✔
396
         result.confirm("zero point is on the curve", zero.on_the_curve());
54✔
397
         result.test_eq("addition of zero does nothing", p1, p1 + zero);
54✔
398
         result.test_eq("addition of zero does nothing", p1, zero + p1);
54✔
399
         result.test_eq("addition of zero does nothing", p1, p1 - zero);
54✔
400
         result.confirm("zero times anything is the zero point", (zero * 39193).is_zero());
81✔
401

402
         for(auto scheme : {Botan::EC_Point_Format::Uncompressed,
189✔
403
                            Botan::EC_Point_Format::Compressed,
404
                            Botan::EC_Point_Format::Hybrid}) {
108✔
405
            const std::vector<uint8_t> v = zero.encode(scheme);
81✔
406
            result.test_eq("encoded/decode rt works", group.OS2ECP(v), zero);
162✔
407
         }
81✔
408
      }
27✔
409
};
410

411
BOTAN_REGISTER_TEST("pubkey", "ec_group", EC_Group_Tests);
412

413
Test::Result test_decoding_with_seed() {
1✔
414
   Test::Result result("ECC Unit");
1✔
415

416
   const auto secp384r1_with_seed = Botan::EC_Group::from_PEM(Test::read_data_file("x509/ecc/secp384r1_seed.pem"));
2✔
417

418
   result.confirm("decoding worked", secp384r1_with_seed.initialized());
2✔
419

420
   const auto secp384r1 = Botan::EC_Group::from_name("secp384r1");
1✔
421

422
   result.test_eq("P-384 prime", secp384r1_with_seed.get_p(), secp384r1.get_p());
1✔
423

424
   return result;
1✔
425
}
1✔
426

427
Test::Result test_coordinates() {
1✔
428
   Test::Result result("ECC Unit");
1✔
429

430
   const Botan::BigInt exp_affine_x("16984103820118642236896513183038186009872590470");
1✔
431
   const Botan::BigInt exp_affine_y("1373093393927139016463695321221277758035357890939");
1✔
432

433
   // precalculation
434
   const auto secp160r1 = Botan::EC_Group::from_name("secp160r1");
1✔
435
   const Botan::EC_Point& p_G = secp160r1.get_base_point();
1✔
436

437
   const Botan::EC_Point point_exp = secp160r1.point(exp_affine_x, exp_affine_y);
1✔
438
   result.confirm("Point is on the curve", point_exp.on_the_curve());
2✔
439

440
   const Botan::EC_Point p1 = p_G * 2;
2✔
441
   result.test_eq("Point affine x", p1.get_affine_x(), exp_affine_x);
2✔
442
   result.test_eq("Point affine y", p1.get_affine_y(), exp_affine_y);
2✔
443
   return result;
2✔
444
}
3✔
445

446
/**
447
Test point multiplication according to
448
--------
449
SEC 2: Test Vectors for SEC 1
450
Certicom Research
451
Working Draft
452
September, 1999
453
Version 0.3;
454
Section 2.1.2
455
--------
456
*/
457
Test::Result test_point_mult() {
1✔
458
   Test::Result result("ECC Unit");
1✔
459

460
   const auto secp160r1 = Botan::EC_Group::from_name("secp160r1");
1✔
461
   const Botan::EC_Point& p_G = secp160r1.get_base_point();
1✔
462

463
   Botan::BigInt d_U("0xaa374ffc3ce144e6b073307972cb6d57b2a4e982");
1✔
464
   Botan::EC_Point Q_U = d_U * p_G;
1✔
465

466
   result.test_eq("affine x", Q_U.get_affine_x(), Botan::BigInt("466448783855397898016055842232266600516272889280"));
3✔
467
   result.test_eq("affine y", Q_U.get_affine_y(), Botan::BigInt("1110706324081757720403272427311003102474457754220"));
3✔
468
   return result;
1✔
469
}
2✔
470

471
Test::Result test_point_negative() {
1✔
472
   Test::Result result("ECC Unit");
1✔
473

474
   const auto secp160r1 = Botan::EC_Group::from_name("secp160r1");
1✔
475
   const Botan::EC_Point& p_G = secp160r1.get_base_point();
1✔
476

477
   const Botan::EC_Point p1 = p_G * 2;
2✔
478

479
   result.test_eq("affine x", p1.get_affine_x(), Botan::BigInt("16984103820118642236896513183038186009872590470"));
3✔
480
   result.test_eq("affine y", p1.get_affine_y(), Botan::BigInt("1373093393927139016463695321221277758035357890939"));
3✔
481

482
   const Botan::EC_Point p1_neg = -p1;
1✔
483

484
   result.test_eq("affine x", p1_neg.get_affine_x(), p1.get_affine_x());
3✔
485
   result.test_eq("affine y", p1_neg.get_affine_y(), Botan::BigInt("88408243403763901739989511495005261618427168388"));
3✔
486
   return result;
1✔
487
}
1✔
488

489
Test::Result test_mult_point() {
1✔
490
   Test::Result result("ECC Unit");
1✔
491

492
   const auto secp160r1 = Botan::EC_Group::from_name("secp160r1");
1✔
493
   const Botan::EC_Point& p_G = secp160r1.get_base_point();
1✔
494

495
   const Botan::EC_Point& p0 = p_G;
1✔
496
   Botan::EC_Point p1 = p_G * 2;
2✔
497

498
   p1 *= p0.get_affine_x();
1✔
499

500
   const Botan::BigInt exp_mult_x(std::string("967697346845926834906555988570157345422864716250"));
1✔
501
   const Botan::BigInt exp_mult_y(std::string("512319768365374654866290830075237814703869061656"));
1✔
502
   Botan::EC_Point expected = secp160r1.point(exp_mult_x, exp_mult_y);
1✔
503

504
   result.test_eq("point mult", p1, expected);
1✔
505
   return result;
1✔
506
}
3✔
507

508
Test::Result test_mixed_points() {
1✔
509
   Test::Result result("ECC Unit");
1✔
510

511
   const auto secp256r1 = Botan::EC_Group::from_name("secp256r1");
1✔
512
   const auto secp384r1 = Botan::EC_Group::from_name("secp384r1");
1✔
513

514
   const Botan::EC_Point& G256 = secp256r1.get_base_point();
1✔
515
   const Botan::EC_Point& G384 = secp384r1.get_base_point();
1✔
516

517
   result.test_throws("Mixing points from different groups", [&] { Botan::EC_Point p = G256 + G384; });
3✔
518
   return result;
1✔
519
}
1✔
520

521
Test::Result test_basic_operations() {
1✔
522
   Test::Result result("ECC Unit");
1✔
523

524
   // precalculation
525
   const auto secp160r1 = Botan::EC_Group::from_name("secp160r1");
1✔
526
   const Botan::EC_Point& p_G = secp160r1.get_base_point();
1✔
527

528
   const Botan::EC_Point& p0 = p_G;
1✔
529
   const Botan::EC_Point p1 = p_G * 2;
2✔
530

531
   result.test_eq("p1 affine x", p1.get_affine_x(), Botan::BigInt("16984103820118642236896513183038186009872590470"));
3✔
532
   result.test_eq("p1 affine y", p1.get_affine_y(), Botan::BigInt("1373093393927139016463695321221277758035357890939"));
3✔
533

534
   const Botan::EC_Point simplePlus = p1 + p0;
1✔
535
   const Botan::EC_Point exp_simplePlus =
1✔
536
      secp160r1.point(Botan::BigInt("704859595002530890444080436569091156047721708633"),
2✔
537
                      Botan::BigInt("1147993098458695153857594941635310323215433166682"));
2✔
538

539
   result.test_eq("point addition", simplePlus, exp_simplePlus);
1✔
540

541
   const Botan::EC_Point simpleMinus = p1 - p0;
1✔
542
   result.test_eq("point subtraction", simpleMinus, p_G);
1✔
543

544
   const Botan::EC_Point simpleMult = p1 * 123456789;
2✔
545

546
   result.test_eq("point mult affine x",
3✔
547
                  simpleMult.get_affine_x(),
2✔
548
                  Botan::BigInt("43638877777452195295055270548491599621118743290"));
1✔
549
   result.test_eq("point mult affine y",
3✔
550
                  simpleMult.get_affine_y(),
2✔
551
                  Botan::BigInt("56841378500012376527163928510402662349220202981"));
1✔
552

553
   return result;
1✔
554
}
1✔
555

556
Test::Result test_enc_dec_compressed_160() {
1✔
557
   Test::Result result("ECC Unit");
1✔
558

559
   // Test for compressed conversion (02/03) 160bit
560
   const auto secp160r1 = Botan::EC_Group::from_name("secp160r1");
1✔
561
   const std::vector<uint8_t> G_comp = Botan::hex_decode("024A96B5688EF573284664698968C38BB913CBFC82");
1✔
562
   const Botan::EC_Point p = secp160r1.OS2ECP(G_comp);
1✔
563
   const std::vector<uint8_t> sv_result = p.encode(Botan::EC_Point_Format::Compressed);
1✔
564

565
   result.test_eq("result", sv_result, G_comp);
1✔
566
   return result;
2✔
567
}
2✔
568

569
Test::Result test_enc_dec_compressed_256() {
1✔
570
   Test::Result result("ECC Unit");
1✔
571

572
   const auto group = Botan::EC_Group::from_name("secp256r1");
1✔
573

574
   const std::string G_secp_comp = "036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296";
1✔
575
   const std::vector<uint8_t> sv_G_secp_comp = Botan::hex_decode(G_secp_comp);
1✔
576

577
   Botan::EC_Point p_G = group.OS2ECP(sv_G_secp_comp);
1✔
578
   std::vector<uint8_t> sv_result = p_G.encode(Botan::EC_Point_Format::Compressed);
1✔
579

580
   result.test_eq("compressed_256", sv_result, sv_G_secp_comp);
1✔
581
   return result;
2✔
582
}
2✔
583

584
Test::Result test_enc_dec_uncompressed_112() {
1✔
585
   Test::Result result("ECC Unit");
1✔
586

587
   // Test for uncompressed conversion (04) 112bit
588

589
   // Curve is secp112r2
590

591
   const Botan::BigInt p("0xdb7c2abf62e35e668076bead208b");
1✔
592
   const Botan::BigInt a("0x6127C24C05F38A0AAAF65C0EF02C");
1✔
593
   const Botan::BigInt b("0x51DEF1815DB5ED74FCC34C85D709");
1✔
594

595
   const Botan::BigInt g_x("0x4BA30AB5E892B4E1649DD0928643");
1✔
596
   const Botan::BigInt g_y("0xADCD46F5882E3747DEF36E956E97");
1✔
597

598
   const Botan::BigInt order("0x36DF0AAFD8B8D7597CA10520D04B");
1✔
599
   const Botan::BigInt cofactor("4");  // !
1✔
600

601
   // This uses the deprecated constructor due to making use of cofactor > 1
602
   const Botan::EC_Group group(p, a, b, g_x, g_y, order, cofactor);
1✔
603

604
   const std::string G_secp_uncomp = "044BA30AB5E892B4E1649DD0928643ADCD46F5882E3747DEF36E956E97";
1✔
605
   const std::vector<uint8_t> sv_G_secp_uncomp = Botan::hex_decode(G_secp_uncomp);
1✔
606

607
   Botan::EC_Point p_G = group.OS2ECP(sv_G_secp_uncomp);
1✔
608
   std::vector<uint8_t> sv_result = p_G.encode(Botan::EC_Point_Format::Uncompressed);
1✔
609

610
   result.test_eq("uncompressed_112", sv_result, sv_G_secp_uncomp);
1✔
611
   return result;
1✔
612
}
9✔
613

614
Test::Result test_enc_dec_uncompressed_521() {
1✔
615
   Test::Result result("ECC Unit");
1✔
616

617
   // Test for uncompressed conversion(04) with big values(521 bit)
618

619
   const std::string G_secp_uncomp =
1✔
620
      "0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2ffA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650";
1✔
621

622
   const std::vector<uint8_t> sv_G_secp_uncomp = Botan::hex_decode(G_secp_uncomp);
1✔
623

624
   const auto group = Botan::EC_Group::from_name("secp521r1");
1✔
625

626
   Botan::EC_Point p_G = group.OS2ECP(sv_G_secp_uncomp);
1✔
627

628
   std::vector<uint8_t> sv_result = p_G.encode(Botan::EC_Point_Format::Uncompressed);
1✔
629

630
   result.test_eq("expected", sv_result, sv_G_secp_uncomp);
1✔
631
   return result;
1✔
632
}
2✔
633

634
Test::Result test_ecc_registration() {
1✔
635
   Test::Result result("ECC registration");
1✔
636

637
   // secp128r1
638
   const Botan::BigInt p("0xfffffffdffffffffffffffffffffffff");
1✔
639
   const Botan::BigInt a("0xfffffffdfffffffffffffffffffffffc");
1✔
640
   const Botan::BigInt b("0xe87579c11079f43dd824993c2cee5ed3");
1✔
641

642
   const Botan::BigInt g_x("0x161ff7528b899b2d0c28607ca52c5b86");
1✔
643
   const Botan::BigInt g_y("0xcf5ac8395bafeb13c02da292dded7a83");
1✔
644
   const Botan::BigInt order("0xfffffffe0000000075a30d1b9038a115");
1✔
645

646
   const Botan::OID oid("1.3.132.0.28");
1✔
647

648
   // Creating this object implicitly registers the curve for future use ...
649
   Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
1✔
650

651
   auto group = Botan::EC_Group::from_OID(oid);
1✔
652

653
   result.test_eq("Group registration worked", group.get_p(), p);
1✔
654

655
   return result;
2✔
656
}
7✔
657

658
Test::Result test_ec_group_from_params() {
1✔
659
   Test::Result result("EC_Group from params");
1✔
660

661
   Botan::EC_Group::clear_registered_curve_data();
1✔
662

663
   // secp256r1
664
   const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
1✔
665
   const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
1✔
666
   const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
1✔
667

668
   const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
1✔
669
   const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
1✔
670
   const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1✔
671

672
   const Botan::OID oid("1.2.840.10045.3.1.7");
1✔
673

674
   // This uses the deprecated constructor to verify we dedup even without an OID
675
   // This whole test can be removed once explicit curve support is removed
676
   Botan::EC_Group reg_group(p, a, b, g_x, g_y, order, 1);
2✔
677
   result.confirm("Group has correct OID", reg_group.get_curve_oid() == oid);
2✔
678

679
   return result;
2✔
680
}
7✔
681

682
Test::Result test_ec_group_bad_registration() {
1✔
683
   Test::Result result("EC_Group registering non-match");
1✔
684

685
   Botan::EC_Group::clear_registered_curve_data();
1✔
686

687
   // secp256r1 params except with a bad B param
688
   const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
1✔
689
   const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
1✔
690
   const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604C");
1✔
691

692
   const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
1✔
693
   const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
1✔
694
   const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1✔
695

696
   const Botan::OID oid("1.2.840.10045.3.1.7");
1✔
697

698
   try {
1✔
699
      Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
1✔
700
      result.test_failure("Should have failed");
×
701
   } catch(Botan::Invalid_Argument&) {
1✔
702
      result.test_success("Got expected exception");
1✔
703
   }
1✔
704

705
   return result;
1✔
706
}
7✔
707

708
Test::Result test_ec_group_duplicate_orders() {
1✔
709
   Test::Result result("EC_Group with duplicate group order");
1✔
710

711
   Botan::EC_Group::clear_registered_curve_data();
1✔
712

713
   // secp256r1
714
   const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
1✔
715
   const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
1✔
716
   const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
1✔
717

718
   const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
1✔
719
   const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
1✔
720
   const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1✔
721

722
   const Botan::OID oid("1.3.6.1.4.1.25258.100.0");  // some other random OID
1✔
723

724
   Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
1✔
725
   result.test_success("Registration success");
1✔
726
   result.confirm("Group has correct OID", reg_group.get_curve_oid() == oid);
2✔
727

728
   // We can now get it by OID:
729
   const auto hc_group = Botan::EC_Group::from_OID(oid);
1✔
730
   result.confirm("Group has correct OID", hc_group.get_curve_oid() == oid);
2✔
731

732
   // Existing secp256r1 unmodified:
733
   const Botan::OID secp160r1("1.2.840.10045.3.1.7");
1✔
734
   const auto other_group = Botan::EC_Group::from_OID(secp160r1);
1✔
735
   result.confirm("Group has correct OID", other_group.get_curve_oid() == secp160r1);
2✔
736

737
   return result;
2✔
738
}
7✔
739

740
class ECC_Unit_Tests final : public Test {
×
741
   public:
742
      std::vector<Test::Result> run() override {
1✔
743
         std::vector<Test::Result> results;
1✔
744

745
         results.push_back(test_coordinates());
2✔
746
         results.push_back(test_decoding_with_seed());
2✔
747
         results.push_back(test_point_mult());
2✔
748
         results.push_back(test_point_negative());
2✔
749
         results.push_back(test_mult_point());
2✔
750
         results.push_back(test_mixed_points());
2✔
751
         results.push_back(test_basic_operations());
2✔
752
         results.push_back(test_enc_dec_compressed_160());
2✔
753
         results.push_back(test_enc_dec_compressed_256());
2✔
754
         results.push_back(test_enc_dec_uncompressed_112());
2✔
755
         results.push_back(test_enc_dec_uncompressed_521());
2✔
756
         results.push_back(test_ecc_registration());
2✔
757
         results.push_back(test_ec_group_from_params());
2✔
758
         results.push_back(test_ec_group_bad_registration());
2✔
759
         results.push_back(test_ec_group_duplicate_orders());
2✔
760

761
         return results;
1✔
762
      }
×
763
};
764

765
BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "ecc_unit", ECC_Unit_Tests);
766

767
   #if defined(BOTAN_HAS_ECDSA)
768

769
class ECC_Invalid_Key_Tests final : public Text_Based_Test {
×
770
   public:
771
      ECC_Invalid_Key_Tests() : Text_Based_Test("pubkey/ecc_invalid.vec", "SubjectPublicKey") {}
2✔
772

773
      bool clear_between_callbacks() const override { return false; }
5✔
774

775
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
5✔
776
         Test::Result result("ECC invalid keys");
5✔
777

778
         const std::string encoded = vars.get_req_str("SubjectPublicKey");
5✔
779
         Botan::DataSource_Memory key_data(Botan::hex_decode(encoded));
10✔
780

781
         try {
5✔
782
            auto key = Botan::X509::load_key(key_data);
5✔
783
            result.test_eq("public key fails check", key->check_key(this->rng(), false), false);
×
784
         } catch(Botan::Decoding_Error&) {
5✔
785
            result.test_success("Decoding invalid ECC key results in decoding error exception");
5✔
786
         }
5✔
787

788
         return result;
5✔
789
      }
5✔
790
};
791

792
BOTAN_REGISTER_TEST("pubkey", "ecc_invalid", ECC_Invalid_Key_Tests);
793

794
   #endif
795

796
#endif
797

798
}  // namespace
799

800
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc