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

randombit / botan / 12805544433

16 Jan 2025 09:08AM UTC coverage: 90.876% (-0.4%) from 91.245%
12805544433

Pull #4540

github

web-flow
Merge cc1ceff51 into 9b798efbb
Pull Request #4540: PKCS #11 Version 3.2 Support

93425 of 102805 relevant lines covered (90.88%)

11409241.89 hits per line

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

94.91
/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/hex.h>
16
   #include <botan/numthry.h>
17
   #include <botan/pk_keys.h>
18
   #include <botan/reducer.h>
19
   #include <botan/x509_key.h>
20
#endif
21

22
namespace Botan_Tests {
23

24
namespace {
25

26
#if defined(BOTAN_HAS_ECC_GROUP)
27

28
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
29

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

37
   auto flip_prob = [](size_t i) -> double {
154,040✔
38
      if(i % 64 == 0) {
153,480✔
39
         return .5;
40
      }
41
      if(i % 32 == 0) {
150,980✔
42
         return .4;
43
      }
44
      if(i % 8 == 0) {
148,560✔
45
         return .05;
14,360✔
46
      }
47
      return .01;
48
   };
49

50
   bool active = (rng.next_byte() > 128) ? true : false;
560✔
51
   for(size_t i = 0; i != bits; ++i) {
154,040✔
52
      x <<= 1;
153,480✔
53
      x += static_cast<int>(active);
153,480✔
54

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

58
      if(sample < prob) {
153,480✔
59
         active = !active;
5,061✔
60
      }
61
   }
62

63
   if(x == 0) {
560✔
64
      // EC_Scalar rejects zero as an input, if we hit this case instead
65
      // test with a completely randomized scalar
66
      return BigInt::random_integer(rng, 1, max);
×
67
   }
68

69
   if(max > 0) {
560✔
70
      while(x >= max) {
643✔
71
         const size_t b = x.bits() - 1;
83✔
72
         BOTAN_ASSERT(x.get_bit(b) == true, "Set");
166✔
73
         x.clear_bit(b);
83✔
74
      }
75
   }
76

77
   return x;
560✔
78
}
560✔
79

80
Botan::EC_Point create_random_point(Botan::RandomNumberGenerator& rng, const Botan::EC_Group& group) {
112✔
81
   const Botan::BigInt& p = group.get_p();
112✔
82
   const Botan::Modular_Reducer mod_p(p);
112✔
83

84
   for(;;) {
328✔
85
      const Botan::BigInt x = Botan::BigInt::random_integer(rng, 1, p);
220✔
86
      const Botan::BigInt x3 = mod_p.multiply(x, mod_p.square(x));
220✔
87
      const Botan::BigInt ax = mod_p.multiply(group.get_a(), x);
220✔
88
      const Botan::BigInt y = mod_p.reduce(x3 + ax + group.get_b());
440✔
89
      const Botan::BigInt sqrt_y = Botan::sqrt_modulo_prime(y, p);
220✔
90

91
      if(sqrt_y > 1) {
220✔
92
         BOTAN_ASSERT_EQUAL(mod_p.square(sqrt_y), y, "Square root is correct");
336✔
93
         return group.point(x, sqrt_y);
224✔
94
      }
95
   }
988✔
96
}
112✔
97

98
class ECC_Randomized_Tests final : public Test {
×
99
   public:
100
      std::vector<Test::Result> run() override;
101
};
102

103
std::vector<Test::Result> ECC_Randomized_Tests::run() {
1✔
104
   std::vector<Test::Result> results;
1✔
105
   for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
29✔
106
      Test::Result result("ECC randomized " + group_name);
28✔
107

108
      result.start_timer();
28✔
109

110
      auto group = Botan::EC_Group::from_name(group_name);
28✔
111

112
      const Botan::EC_Point pt = create_random_point(this->rng(), group);
28✔
113

114
      std::vector<Botan::BigInt> blind_ws;
28✔
115

116
      try {
28✔
117
         const size_t trials = (Test::run_long_tests() ? 10 : 3);
28✔
118
         for(size_t i = 0; i < trials; ++i) {
308✔
119
            const Botan::BigInt a = test_integer(rng(), group.get_order_bits(), group.get_order());
280✔
120
            const Botan::BigInt b = test_integer(rng(), group.get_order_bits(), group.get_order());
280✔
121
            const Botan::BigInt c = group.mod_order(a + b);
280✔
122

123
            const Botan::EC_Point P = pt * a;
280✔
124
            const Botan::EC_Point Q = pt * b;
280✔
125
            const Botan::EC_Point R = pt * c;
280✔
126

127
            Botan::EC_Point P1 = group.blinded_var_point_multiply(pt, a, this->rng(), blind_ws);
280✔
128
            Botan::EC_Point Q1 = group.blinded_var_point_multiply(pt, b, this->rng(), blind_ws);
280✔
129
            Botan::EC_Point R1 = group.blinded_var_point_multiply(pt, c, this->rng(), blind_ws);
280✔
130

131
            Botan::EC_Point A1 = P + Q;
280✔
132
            Botan::EC_Point A2 = Q + P;
280✔
133

134
            result.test_eq("p + q", A1, R);
280✔
135
            result.test_eq("q + p", A2, R);
280✔
136

137
            A1.force_affine();
280✔
138
            A2.force_affine();
280✔
139
            result.test_eq("p + q", A1, R);
280✔
140
            result.test_eq("q + p", A2, R);
280✔
141

142
            result.test_eq("p on the curve", P.on_the_curve(), true);
280✔
143
            result.test_eq("q on the curve", Q.on_the_curve(), true);
280✔
144
            result.test_eq("r on the curve", R.on_the_curve(), true);
280✔
145

146
            result.test_eq("P1", P1, P);
280✔
147
            result.test_eq("Q1", Q1, Q);
280✔
148
            result.test_eq("R1", R1, R);
280✔
149

150
            P1.force_affine();
280✔
151
            Q1.force_affine();
280✔
152
            R1.force_affine();
280✔
153
            result.test_eq("P1", P1, P);
280✔
154
            result.test_eq("Q1", Q1, Q);
280✔
155
            result.test_eq("R1", R1, R);
280✔
156
         }
1,120✔
157
      } catch(std::exception& e) {
×
158
         result.test_failure(group_name, e.what());
×
159
      }
×
160
      result.end_timer();
28✔
161
      results.push_back(result);
28✔
162
   }
28✔
163

164
   return results;
1✔
165
}
×
166

167
BOTAN_REGISTER_TEST("pubkey", "ecc_randomized", ECC_Randomized_Tests);
168

169
   #endif
170

171
class EC_Group_Tests : public Test {
×
172
   public:
173
      std::vector<Test::Result> run() override {
1✔
174
         std::vector<Test::Result> results;
1✔
175

176
         for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
29✔
177
            Test::Result result("EC_Group " + group_name);
28✔
178

179
            result.start_timer();
28✔
180

181
            const auto group = Botan::EC_Group::from_name(group_name);
28✔
182

183
            result.confirm("EC_Group is known", group.get_curve_oid().has_value());
56✔
184
            result.confirm("EC_Group is considered valid", group.verify_group(this->rng(), true));
56✔
185
            result.confirm("EC_Group is not considered explict encoding", !group.used_explicit_encoding());
56✔
186

187
            result.test_eq("EC_Group has correct bit size", group.get_p().bits(), group.get_p_bits());
28✔
188
            result.test_eq("EC_Group has byte size", group.get_p().bytes(), group.get_p_bytes());
28✔
189

190
            result.test_eq("EC_Group has cofactor == 1", group.get_cofactor(), 1);
56✔
191

192
            const Botan::OID from_order = Botan::EC_Group::EC_group_identity_from_order(group.get_order());
28✔
193

194
            result.test_eq(
56✔
195
               "EC_group_identity_from_order works", from_order.to_string(), group.get_curve_oid().to_string());
56✔
196

197
            result.confirm("Same group is same", group == Botan::EC_Group::from_name(group_name));
56✔
198

199
            try {
28✔
200
               const Botan::EC_Group copy(group.get_curve_oid(),
28✔
201
                                          group.get_p(),
202
                                          group.get_a(),
203
                                          group.get_b(),
204
                                          group.get_g_x(),
205
                                          group.get_g_y(),
206
                                          group.get_order());
28✔
207

208
               result.confirm("Same group is same even with copy", group == copy);
40✔
209
            } catch(Botan::Invalid_Argument&) {}
28✔
210

211
            const auto group_der_oid = group.DER_encode();
28✔
212
            const Botan::EC_Group group_via_oid(group_der_oid);
28✔
213
            result.confirm("EC_Group via OID is not considered explict encoding",
56✔
214
                           !group_via_oid.used_explicit_encoding());
28✔
215

216
            const auto group_der_explicit = group.DER_encode(Botan::EC_Group_Encoding::Explicit);
28✔
217
            const Botan::EC_Group group_via_explicit(group_der_explicit);
28✔
218
            result.confirm("EC_Group via explicit DER is considered explict encoding",
56✔
219
                           group_via_explicit.used_explicit_encoding());
28✔
220

221
            if(group.a_is_minus_3()) {
28✔
222
               result.test_eq("Group A equals -3", group.get_a(), group.get_p() - 3);
51✔
223
            } else {
224
               result.test_ne("Group " + group_name + " A does not equal -3", group.get_a(), group.get_p() - 3);
44✔
225
            }
226

227
            if(group.a_is_zero()) {
28✔
228
               result.test_eq("Group A is zero", group.get_a(), BigInt(0));
8✔
229
            } else {
230
               result.test_ne("Group " + group_name + " A does not equal zero", group.get_a(), BigInt(0));
72✔
231
            }
232

233
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
234
            const auto pt_mult_by_order = group.get_base_point() * group.get_order();
28✔
235
            result.confirm("Multiplying point by the order results in zero point", pt_mult_by_order.is_zero());
56✔
236

237
            // get a valid point
238
            Botan::EC_Point p = group.get_base_point() * this->rng().next_nonzero_byte();
28✔
239

240
            // get a copy
241
            Botan::EC_Point q = p;
28✔
242

243
            p.randomize_repr(this->rng());
28✔
244
            q.randomize_repr(this->rng());
28✔
245

246
            result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
84✔
247
            result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
84✔
248

249
            q.force_affine();
28✔
250

251
            result.test_eq("affine x after copy", p.get_affine_x(), q.get_affine_x());
84✔
252
            result.test_eq("affine y after copy", p.get_affine_y(), q.get_affine_y());
84✔
253

254
            test_ser_der(result, group);
28✔
255
            test_basic_math(result, group);
28✔
256
            test_point_swap(result, group);
28✔
257
            test_zeropoint(result, group);
28✔
258
   #endif
259

260
            result.end_timer();
28✔
261

262
            results.push_back(result);
28✔
263
         }
84✔
264

265
         return results;
1✔
266
      }
×
267

268
   private:
269
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
270

271
      void test_ser_der(Test::Result& result, const Botan::EC_Group& group) {
28✔
272
         // generate point
273
         const Botan::EC_Point pt = create_random_point(this->rng(), group);
28✔
274
         const Botan::EC_Point zero = group.zero_point();
28✔
275

276
         for(auto scheme : {Botan::EC_Point_Format::Uncompressed,
196✔
277
                            Botan::EC_Point_Format::Compressed,
278
                            Botan::EC_Point_Format::Hybrid}) {
112✔
279
            result.test_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)), pt);
168✔
280
            result.test_eq("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)), zero);
252✔
281
         }
282
      }
28✔
283

284
      static void test_basic_math(Test::Result& result, const Botan::EC_Group& group) {
28✔
285
         const Botan::EC_Point& G = group.get_base_point();
28✔
286

287
         Botan::EC_Point p1 = G * 2;
56✔
288
         p1 += G;
28✔
289

290
         result.test_eq("point addition", p1, G * 3);
84✔
291

292
         p1 -= G * 2;
56✔
293

294
         result.test_eq("point subtraction", p1, G);
28✔
295

296
         // The scalar multiplication algorithm relies on this being true:
297
         try {
28✔
298
            Botan::EC_Point zero_coords = group.point(0, 0);
56✔
299
            result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve());
×
300
         } catch(Botan::Exception&) {
28✔
301
            result.test_success("point (0,0) is rejected");
28✔
302
         }
28✔
303
      }
28✔
304

305
      void test_point_swap(Test::Result& result, const Botan::EC_Group& group) {
28✔
306
         Botan::EC_Point a(create_random_point(this->rng(), group));
28✔
307
         Botan::EC_Point b(create_random_point(this->rng(), group));
28✔
308
         b *= Botan::BigInt(this->rng(), 20);
28✔
309

310
         Botan::EC_Point c(a);
28✔
311
         Botan::EC_Point d(b);
28✔
312

313
         d.swap(c);
28✔
314
         result.test_eq("swap correct", a, d);
28✔
315
         result.test_eq("swap correct", b, c);
28✔
316
      }
28✔
317

318
      static void test_zeropoint(Test::Result& result, const Botan::EC_Group& group) {
28✔
319
         Botan::EC_Point zero = group.zero_point();
28✔
320

321
         result.test_throws("Zero point throws", "Cannot convert zero point to affine", [&]() { zero.get_affine_x(); });
84✔
322
         result.test_throws("Zero point throws", "Cannot convert zero point to affine", [&]() { zero.get_affine_y(); });
84✔
323

324
         const Botan::EC_Point p1 = group.get_base_point() * 2;
28✔
325

326
         result.confirm("point is on the curve", p1.on_the_curve());
56✔
327
         result.confirm("point is not zero", !p1.is_zero());
56✔
328

329
         Botan::EC_Point p2 = p1;
28✔
330
         p2 -= p1;
28✔
331

332
         result.confirm("p - q with q = p results in zero", p2.is_zero());
56✔
333

334
         const Botan::EC_Point minus_p1 = -p1;
28✔
335
         result.confirm("point is on the curve", minus_p1.on_the_curve());
56✔
336
         const Botan::EC_Point shouldBeZero = p1 + minus_p1;
28✔
337
         result.confirm("point is on the curve", shouldBeZero.on_the_curve());
56✔
338
         result.confirm("point is zero", shouldBeZero.is_zero());
56✔
339

340
         result.test_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x());
84✔
341
         result.test_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y());
112✔
342

343
         result.confirm("zero point is zero", zero.is_zero());
56✔
344
         result.confirm("zero point is on the curve", zero.on_the_curve());
56✔
345
         result.test_eq("addition of zero does nothing", p1, p1 + zero);
56✔
346
         result.test_eq("addition of zero does nothing", p1, zero + p1);
56✔
347
         result.test_eq("addition of zero does nothing", p1, p1 - zero);
56✔
348
         result.confirm("zero times anything is the zero point", (zero * 39193).is_zero());
84✔
349

350
         for(auto scheme : {Botan::EC_Point_Format::Uncompressed,
196✔
351
                            Botan::EC_Point_Format::Compressed,
352
                            Botan::EC_Point_Format::Hybrid}) {
112✔
353
            const std::vector<uint8_t> v = zero.encode(scheme);
84✔
354
            result.test_eq("encoded/decode rt works", group.OS2ECP(v), zero);
168✔
355
         }
84✔
356
      }
28✔
357
   #endif
358
};
359

360
BOTAN_REGISTER_TEST("pubkey", "ec_group", EC_Group_Tests);
361

362
Test::Result test_decoding_with_seed() {
1✔
363
   Test::Result result("ECC Unit");
1✔
364

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

367
   result.confirm("decoding worked", secp384r1_with_seed.initialized());
2✔
368

369
   const auto secp384r1 = Botan::EC_Group::from_name("secp384r1");
1✔
370

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

373
   return result;
1✔
374
}
1✔
375

376
Test::Result test_mixed_points() {
1✔
377
   Test::Result result("ECC Unit");
1✔
378

379
   const auto secp256r1 = Botan::EC_Group::from_name("secp256r1");
1✔
380
   const auto secp384r1 = Botan::EC_Group::from_name("secp384r1");
1✔
381

382
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
383
   const Botan::EC_Point& G256 = secp256r1.get_base_point();
1✔
384
   const Botan::EC_Point& G384 = secp384r1.get_base_point();
1✔
385

386
   result.test_throws("Mixing points from different groups", [&] { Botan::EC_Point p = G256 + G384; });
3✔
387
   #endif
388

389
   const auto p1 = Botan::EC_AffinePoint::generator(secp256r1);
1✔
390
   const auto p2 = Botan::EC_AffinePoint::generator(secp384r1);
1✔
391
   result.test_throws("Mixing points from different groups", [&] { auto p3 = p1.add(p2); });
3✔
392

393
   return result;
1✔
394
}
1✔
395

396
Test::Result test_ecc_registration() {
1✔
397
   Test::Result result("ECC registration");
1✔
398

399
   // numsp256d1
400
   const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
1✔
401
   const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
1✔
402
   const Botan::BigInt b("0x25581");
1✔
403
   const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
1✔
404

405
   const Botan::BigInt g_x("0x01");
1✔
406
   const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
1✔
407

408
   const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
1✔
409

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

413
   auto group = Botan::EC_Group::from_OID(oid);
1✔
414

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

417
   return result;
2✔
418
}
7✔
419

420
Test::Result test_ec_group_from_params() {
1✔
421
   Test::Result result("EC_Group from params");
1✔
422

423
   Botan::EC_Group::clear_registered_curve_data();
1✔
424

425
   // secp256r1
426
   const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
1✔
427
   const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
1✔
428
   const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
1✔
429

430
   const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
1✔
431
   const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
1✔
432
   const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1✔
433

434
   const Botan::OID oid("1.2.840.10045.3.1.7");
1✔
435

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

441
   return result;
2✔
442
}
7✔
443

444
Test::Result test_ec_group_bad_registration() {
1✔
445
   Test::Result result("EC_Group registering non-match");
1✔
446

447
   Botan::EC_Group::clear_registered_curve_data();
1✔
448

449
   // secp256r1 params except with a bad B param
450
   const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
1✔
451
   const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
1✔
452
   const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604C");
1✔
453

454
   const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
1✔
455
   const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
1✔
456
   const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1✔
457

458
   const Botan::OID oid("1.2.840.10045.3.1.7");
1✔
459

460
   try {
1✔
461
      Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
1✔
462
      result.test_failure("Should have failed");
×
463
   } catch(Botan::Invalid_Argument&) {
1✔
464
      result.test_success("Got expected exception");
1✔
465
   }
1✔
466

467
   return result;
1✔
468
}
7✔
469

470
Test::Result test_ec_group_duplicate_orders() {
1✔
471
   Test::Result result("EC_Group with duplicate group order");
1✔
472

473
   Botan::EC_Group::clear_registered_curve_data();
1✔
474

475
   // secp256r1
476
   const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
1✔
477
   const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
1✔
478
   const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
1✔
479

480
   const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
1✔
481
   const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
1✔
482
   const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1✔
483

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

486
   Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
1✔
487
   result.test_success("Registration success");
1✔
488
   result.confirm("Group has correct OID", reg_group.get_curve_oid() == oid);
2✔
489

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

494
   // Existing secp256r1 unmodified:
495
   const Botan::OID secp160r1("1.2.840.10045.3.1.7");
1✔
496
   const auto other_group = Botan::EC_Group::from_OID(secp160r1);
1✔
497
   result.confirm("Group has correct OID", other_group.get_curve_oid() == secp160r1);
2✔
498

499
   return result;
2✔
500
}
7✔
501

502
class ECC_Unit_Tests final : public Test {
×
503
   public:
504
      std::vector<Test::Result> run() override {
1✔
505
         std::vector<Test::Result> results;
1✔
506

507
         results.push_back(test_decoding_with_seed());
2✔
508
         results.push_back(test_mixed_points());
2✔
509
         results.push_back(test_ecc_registration());
2✔
510
         results.push_back(test_ec_group_from_params());
2✔
511
         results.push_back(test_ec_group_bad_registration());
2✔
512
         results.push_back(test_ec_group_duplicate_orders());
2✔
513

514
         return results;
1✔
515
      }
×
516
};
517

518
BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "ecc_unit", ECC_Unit_Tests);
519

520
class EC_PointEnc_Tests final : public Test {
×
521
   public:
522
      std::vector<Test::Result> run() override {
1✔
523
         std::vector<Test::Result> results;
1✔
524

525
         auto& rng = Test::rng();
1✔
526

527
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
29✔
528
            const auto group = Botan::EC_Group::from_name(group_id);
28✔
529

530
            Result result("EC_AffinePoint encoding " + group_id);
28✔
531

532
            result.start_timer();
28✔
533

534
            std::vector<Botan::BigInt> ws;
28✔
535

536
            for(size_t trial = 0; trial != 100; ++trial) {
2,828✔
537
               const auto scalar = Botan::EC_Scalar::random(group, rng);
2,800✔
538
               const auto pt = Botan::EC_AffinePoint::g_mul(scalar, rng, ws);
2,800✔
539

540
               const auto pt_u = pt.serialize_uncompressed();
2,800✔
541
               result.test_eq("Expected uncompressed header", static_cast<size_t>(pt_u[0]), 0x04);
2,800✔
542
               const size_t fe_bytes = (pt_u.size() - 1) / 2;
2,800✔
543
               const auto pt_c = pt.serialize_compressed();
2,800✔
544

545
               result.test_eq("Expected compressed size", pt_c.size(), 1 + fe_bytes);
2,800✔
546
               const uint8_t expected_c_header = (pt_u[pt_u.size() - 1] % 2 == 0) ? 0x02 : 0x03;
2,800✔
547
               result.confirm("Expected compressed header", pt_c[0] == expected_c_header);
5,600✔
548

549
               result.test_eq(
5,600✔
550
                  "Expected compressed x", std::span{pt_c}.subspan(1), std::span{pt_u}.subspan(1, fe_bytes));
551

552
               if(auto d_pt_u = Botan::EC_AffinePoint::deserialize(group, pt_u)) {
2,800✔
553
                  result.test_eq(
5,600✔
554
                     "Deserializing uncompressed returned correct point", d_pt_u->serialize_uncompressed(), pt_u);
5,600✔
555
               } else {
556
                  result.test_failure("Failed to deserialize uncompressed point");
×
557
               }
×
558

559
               if(auto d_pt_c = Botan::EC_AffinePoint::deserialize(group, pt_c)) {
2,800✔
560
                  result.test_eq(
5,600✔
561
                     "Deserializing compressed returned correct point", d_pt_c->serialize_uncompressed(), pt_u);
5,600✔
562
               } else {
563
                  result.test_failure("Failed to deserialize compressed point");
×
564
               }
×
565

566
               const auto neg_pt_c = [&]() {
2,800✔
567
                  auto x = pt_c;
2,800✔
568
                  x[0] ^= 0x01;
2,800✔
569
                  return x;
2,800✔
570
               }();
2,800✔
571

572
               if(auto d_neg_pt_c = Botan::EC_AffinePoint::deserialize(group, neg_pt_c)) {
2,800✔
573
                  result.test_eq("Deserializing compressed with inverted header returned negated point",
5,600✔
574
                                 d_neg_pt_c->serialize_uncompressed(),
5,600✔
575
                                 pt.negate().serialize_uncompressed());
5,600✔
576
               } else {
577
                  result.test_failure("Failed to deserialize compressed point");
×
578
               }
2,800✔
579
            }
2,800✔
580

581
            result.end_timer();
28✔
582

583
            results.push_back(result);
28✔
584
         }
28✔
585

586
         return results;
1✔
587
      }
2,800✔
588
};
589

590
BOTAN_REGISTER_TEST("pcurves", "ec_point_enc", EC_PointEnc_Tests);
591

592
class EC_Point_Arithmetic_Tests final : public Test {
×
593
   public:
594
      std::vector<Test::Result> run() override {
1✔
595
         std::vector<Test::Result> results;
1✔
596

597
         auto& rng = Test::rng();
1✔
598

599
         std::vector<Botan::BigInt> ws;
1✔
600

601
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
29✔
602
            const auto group = Botan::EC_Group::from_name(group_id);
28✔
603

604
            Result result("EC_AffinePoint arithmetic " + group_id);
28✔
605

606
            result.start_timer();
28✔
607

608
            const auto one = Botan::EC_Scalar::one(group);
28✔
609
            const auto zero = one - one;
28✔
610
            const auto g = Botan::EC_AffinePoint::generator(group);
28✔
611
            const auto g_bytes = g.serialize_uncompressed();
28✔
612

613
            const auto id = Botan::EC_AffinePoint::g_mul(zero, rng, ws);
28✔
614
            result.confirm("g*zero is point at identity", id.is_identity());
56✔
615

616
            const auto id2 = id.add(id);
28✔
617
            result.confirm("identity plus itself is identity", id2.is_identity());
56✔
618

619
            const auto g_one = Botan::EC_AffinePoint::g_mul(one, rng, ws);
28✔
620
            result.test_eq("g*one == generator", g_one.serialize_uncompressed(), g_bytes);
56✔
621

622
            const auto g_plus_id = g_one.add(id);
28✔
623
            result.test_eq("g + id == g", g_plus_id.serialize_uncompressed(), g_bytes);
56✔
624

625
            const auto id_plus_g = id.add(g_one);
28✔
626
            result.test_eq("id + g == g", id_plus_g.serialize_uncompressed(), g_bytes);
56✔
627

628
            const auto g_neg_one = Botan::EC_AffinePoint::g_mul(one.negate(), rng, ws);
28✔
629

630
            const auto id_from_g = g_one.add(g_neg_one);
28✔
631
            result.confirm("g - g is identity", id_from_g.is_identity());
56✔
632

633
            const auto g_two = Botan::EC_AffinePoint::g_mul(one + one, rng, ws);
28✔
634
            const auto g_plus_g = g_one.add(g_one);
28✔
635
            result.test_eq("2*g == g+g", g_two.serialize_uncompressed(), g_plus_g.serialize_uncompressed());
84✔
636

637
            result.confirm("Scalar::zero is zero", zero.is_zero());
56✔
638
            result.confirm("(zero+zero) is zero", (zero + zero).is_zero());
56✔
639
            result.confirm("(zero*zero) is zero", (zero * zero).is_zero());
56✔
640
            result.confirm("(zero-zero) is zero", (zero - zero).is_zero());
56✔
641

642
            const auto neg_zero = zero.negate();
28✔
643
            result.confirm("zero.negate() is zero", neg_zero.is_zero());
56✔
644

645
            result.confirm("(zero+nz) is zero", (zero + neg_zero).is_zero());
56✔
646
            result.confirm("(nz+nz) is zero", (neg_zero + neg_zero).is_zero());
56✔
647
            result.confirm("(nz+zero) is zero", (neg_zero + zero).is_zero());
56✔
648

649
            result.confirm("Scalar::one is not zero", !one.is_zero());
56✔
650
            result.confirm("(one-one) is zero", (one - one).is_zero());
56✔
651
            result.confirm("(one+one.negate()) is zero", (one + one.negate()).is_zero());
84✔
652
            result.confirm("(one.negate()+one) is zero", (one.negate() + one).is_zero());
84✔
653

654
            for(size_t i = 0; i != 16; ++i) {
476✔
655
               const auto pt = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng, ws);
448✔
656

657
               const auto a = Botan::EC_Scalar::random(group, rng);
448✔
658
               const auto b = Botan::EC_Scalar::random(group, rng);
448✔
659
               const auto c = a + b;
448✔
660

661
               const auto Pa = pt.mul(a, rng, ws);
448✔
662
               const auto Pb = pt.mul(b, rng, ws);
448✔
663
               const auto Pc = pt.mul(c, rng, ws);
448✔
664

665
               const auto Pc_bytes = Pc.serialize_uncompressed();
448✔
666

667
               const auto Pab = Pa.add(Pb);
448✔
668
               result.test_eq("Pa + Pb == Pc", Pab.serialize_uncompressed(), Pc_bytes);
896✔
669

670
               const auto Pba = Pb.add(Pa);
448✔
671
               result.test_eq("Pb + Pa == Pc", Pba.serialize_uncompressed(), Pc_bytes);
896✔
672
            }
896✔
673

674
            for(size_t i = 0; i != 64; ++i) {
1,820✔
675
               auto h = [&]() {
5,376✔
676
                  const auto s = [&]() {
5,376✔
677
                     if(i == 0) {
1,792✔
678
                        // Test the identity case
679
                        return Botan::EC_Scalar(zero);
28✔
680
                     } else if(i <= 32) {
1,764✔
681
                        // Test cases where the two points have a linear relation
682
                        std::vector<uint8_t> sbytes(group.get_order_bytes());
896✔
683
                        sbytes[sbytes.size() - 1] = static_cast<uint8_t>((i + 1) / 2);
896✔
684
                        auto si = Botan::EC_Scalar::deserialize(group, sbytes).value();
1,792✔
685
                        if(i % 2 == 0) {
896✔
686
                           return si;
448✔
687
                        } else {
688
                           return si.negate();
448✔
689
                        }
690
                     } else {
1,792✔
691
                        return Botan::EC_Scalar::random(group, rng);
868✔
692
                     }
693
                  }();
1,792✔
694
                  auto x = Botan::EC_AffinePoint::g_mul(s, rng, ws);
1,792✔
695
                  return x;
1,792✔
696
               }();
3,584✔
697

698
               const auto s1 = Botan::EC_Scalar::random(group, rng);
1,792✔
699
               const auto s2 = Botan::EC_Scalar::random(group, rng);
1,792✔
700

701
               const Botan::EC_Group::Mul2Table mul2_table(h);
1,792✔
702

703
               const auto ref = Botan::EC_AffinePoint::g_mul(s1, rng, ws).add(h.mul(s2, rng, ws));
1,792✔
704

705
               if(auto mul2pt = mul2_table.mul2_vartime(s1, s2)) {
1,792✔
706
                  result.test_eq("ref == mul2t", ref.serialize_uncompressed(), mul2pt->serialize_uncompressed());
7,168✔
707
               } else {
708
                  result.confirm("ref is identity", ref.is_identity());
×
709
               }
×
710
            }
1,792✔
711

712
            result.end_timer();
28✔
713

714
            results.push_back(result);
28✔
715
         }
56✔
716

717
         return results;
1✔
718
      }
1✔
719
};
720

721
BOTAN_REGISTER_TEST("pcurves", "ec_point_arith", EC_Point_Arithmetic_Tests);
722

723
   #if defined(BOTAN_HAS_ECDSA)
724

725
class ECC_Invalid_Key_Tests final : public Text_Based_Test {
×
726
   public:
727
      ECC_Invalid_Key_Tests() : Text_Based_Test("pubkey/ecc_invalid.vec", "SubjectPublicKey") {}
2✔
728

729
      bool clear_between_callbacks() const override { return false; }
5✔
730

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

734
         const std::string encoded = vars.get_req_str("SubjectPublicKey");
5✔
735
         Botan::DataSource_Memory key_data(Botan::hex_decode(encoded));
10✔
736

737
         try {
5✔
738
            auto key = Botan::X509::load_key(key_data);
5✔
739
            result.test_eq("public key fails check", key->check_key(this->rng(), false), false);
×
740
         } catch(Botan::Decoding_Error&) {
5✔
741
            result.test_success("Decoding invalid ECC key results in decoding error exception");
5✔
742
         }
5✔
743

744
         return result;
5✔
745
      }
5✔
746
};
747

748
BOTAN_REGISTER_TEST("pubkey", "ecc_invalid", ECC_Invalid_Key_Tests);
749

750
   #endif
751

752
#endif
753

754
}  // namespace
755

756
}  // 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

© 2025 Coveralls, Inc