• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

randombit / botan / 20622746011

31 Dec 2025 04:13PM UTC coverage: 90.428% (+0.003%) from 90.425%
20622746011

push

github

web-flow
Merge pull request #5201 from polarnis/argon2-simd64

Use `SIMD_2x64` in Argon2 on all available architectures

101655 of 112416 relevant lines covered (90.43%)

25974095.18 hits per line

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

93.59
/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/rng.h>
19
   #include <botan/x509_key.h>
20
   #include <botan/internal/barrett.h>
21
   #include <botan/internal/ec_inner_data.h>
22
#endif
23

24
namespace Botan_Tests {
25

26
namespace {
27

28
#if defined(BOTAN_HAS_ECC_GROUP)
29

30
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
31

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

39
   auto flip_prob = [](size_t i) -> double {
308,080✔
40
      if(i % 64 == 0) {
306,960✔
41
         return .5;
42
      }
43
      if(i % 32 == 0) {
301,960✔
44
         return .4;
45
      }
46
      if(i % 8 == 0) {
297,120✔
47
         return .05;
28,720✔
48
      }
49
      return .01;
50
   };
51

52
   bool active = (rng.next_byte() > 128) ? true : false;
1,120✔
53
   for(size_t i = 0; i != bits; ++i) {
308,080✔
54
      x <<= 1;
306,960✔
55
      x += static_cast<int>(active);
306,960✔
56

57
      const double prob = flip_prob(i);
306,960✔
58
      const double sample = double(rng.next_byte() % 100) / 100.0;  // biased
306,960✔
59

60
      if(sample < prob) {
306,960✔
61
         active = !active;
9,989✔
62
      }
63
   }
64

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

71
   if(max > 0) {
1,119✔
72
      while(x >= max) {
1,279✔
73
         const size_t b = x.bits() - 1;
160✔
74
         BOTAN_ASSERT(x.get_bit(b) == true, "Set");
320✔
75
         x.clear_bit(b);
160✔
76
      }
77
   }
78

79
   return x;
1,119✔
80
}
1,120✔
81

82
Botan::EC_Point create_random_point(Botan::RandomNumberGenerator& rng, const Botan::EC_Group& group) {
224✔
83
   const Botan::BigInt& p = group.get_p();
224✔
84
   auto mod_p = Botan::Barrett_Reduction::for_public_modulus(p);
224✔
85

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

93
      if(sqrt_y > 1) {
472✔
94
         BOTAN_ASSERT_EQUAL(mod_p.square(sqrt_y), y, "Square root is correct");
448✔
95
         return group.point(x, sqrt_y);
224✔
96
      }
97
   }
472✔
98
}
224✔
99

100
class ECC_Randomized_Tests final : public Test {
2✔
101
   public:
102
      std::vector<Test::Result> run() override;
103
};
104

105
std::vector<Test::Result> ECC_Randomized_Tests::run() {
2✔
106
   std::vector<Test::Result> results;
2✔
107
   for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
58✔
108
      Test::Result result("ECC randomized " + group_name);
56✔
109

110
      result.start_timer();
56✔
111

112
      auto group = Botan::EC_Group::from_name(group_name);
56✔
113

114
      const Botan::EC_Point pt = create_random_point(this->rng(), group);
56✔
115

116
      try {
56✔
117
         std::vector<Botan::BigInt> blind_ws;
56✔
118
         const size_t trials = (Test::run_long_tests() ? 10 : 3);
56✔
119
         for(size_t i = 0; i < trials; ++i) {
616✔
120
            const Botan::BigInt a = test_integer(rng(), group.get_order_bits(), group.get_order());
560✔
121
            const Botan::BigInt b = test_integer(rng(), group.get_order_bits(), group.get_order());
560✔
122
            const Botan::BigInt c = group.mod_order(a + b);
560✔
123

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

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

132
            Botan::EC_Point A1 = P + Q;
560✔
133
            Botan::EC_Point A2 = Q + P;
560✔
134

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

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

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

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

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

165
   return results;
2✔
166
}
×
167

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

170
   #endif
171

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

177
         for(const std::string& group_name : Botan::EC_Group::known_named_groups()) {
58✔
178
            Test::Result result("EC_Group " + group_name);
56✔
179

180
            result.start_timer();
56✔
181

182
            const auto group = Botan::EC_Group::from_name(group_name);
56✔
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

241
            // get a copy
242
            Botan::EC_Point q = p;
56✔
243

244
            p.randomize_repr(this->rng());
56✔
245
            q.randomize_repr(this->rng());
56✔
246

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

250
            q.force_affine();
56✔
251

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

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

261
            result.end_timer();
56✔
262

263
            results.push_back(result);
56✔
264
         }
168✔
265

266
         return results;
2✔
267
      }
×
268

269
   private:
270
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
271

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

277
         for(auto scheme : {Botan::EC_Point_Format::Uncompressed,
168✔
278
                            Botan::EC_Point_Format::Compressed,
279
                            Botan::EC_Point_Format::Hybrid}) {
224✔
280
            try {
168✔
281
               result.test_eq("encoded/decode rt works", group.OS2ECP(pt.encode(scheme)), pt);
504✔
282
            } catch(Botan::Exception& e) {
×
283
               result.test_failure("Failed to round trip encode a random point", e.what());
×
284
            }
×
285

286
            try {
168✔
287
               result.test_eq("encoded/decode rt works", group.OS2ECP(zero.encode(scheme)), zero);
504✔
288
            } catch(Botan::Exception& e) {
×
289
               result.test_failure("Failed to round trip encode a random point", e.what());
×
290
            }
×
291
         }
292
      }
56✔
293

294
      static void test_basic_math(Test::Result& result, const Botan::EC_Group& group) {
56✔
295
         const Botan::EC_Point& G = group.get_base_point();
56✔
296

297
         Botan::EC_Point p1 = G * 2;
112✔
298
         p1 += G;
56✔
299

300
         result.test_eq("point addition", p1, G * 3);
168✔
301

302
         p1 -= G * 2;
112✔
303

304
         result.test_eq("point subtraction", p1, G);
56✔
305

306
         // The scalar multiplication algorithm relies on this being true:
307
         try {
56✔
308
            const Botan::EC_Point zero_coords = group.point(0, 0);
112✔
309
            result.confirm("point (0,0) is not on the curve", !zero_coords.on_the_curve());
×
310
         } catch(Botan::Exception&) {
56✔
311
            result.test_success("point (0,0) is rejected");
56✔
312
         }
56✔
313
      }
56✔
314

315
      void test_point_swap(Test::Result& result, const Botan::EC_Group& group) {
56✔
316
         const Botan::EC_Point a(create_random_point(this->rng(), group));
56✔
317
         Botan::EC_Point b(create_random_point(this->rng(), group));
56✔
318
         b *= Botan::BigInt(this->rng(), 20);
56✔
319

320
         Botan::EC_Point c(a);
56✔
321
         Botan::EC_Point d(b);
56✔
322

323
         d.swap(c);
56✔
324
         result.test_eq("swap correct", a, d);
56✔
325
         result.test_eq("swap correct", b, c);
56✔
326
      }
56✔
327

328
      static void test_zeropoint(Test::Result& result, const Botan::EC_Group& group) {
56✔
329
         Botan::EC_Point zero = group.zero_point();
56✔
330

331
         result.test_throws("Zero point throws", "Cannot convert zero point to affine", [&]() { zero.get_affine_x(); });
168✔
332
         result.test_throws("Zero point throws", "Cannot convert zero point to affine", [&]() { zero.get_affine_y(); });
168✔
333

334
         const Botan::EC_Point p1 = group.get_base_point() * 2;
56✔
335

336
         result.confirm("point is on the curve", p1.on_the_curve());
112✔
337
         result.confirm("point is not zero", !p1.is_zero());
112✔
338

339
         Botan::EC_Point p2 = p1;
56✔
340
         p2 -= p1;
56✔
341

342
         result.confirm("p - q with q = p results in zero", p2.is_zero());
112✔
343

344
         const Botan::EC_Point minus_p1 = -p1;
56✔
345
         result.confirm("point is on the curve", minus_p1.on_the_curve());
112✔
346
         const Botan::EC_Point shouldBeZero = p1 + minus_p1;
56✔
347
         result.confirm("point is on the curve", shouldBeZero.on_the_curve());
112✔
348
         result.confirm("point is zero", shouldBeZero.is_zero());
112✔
349

350
         result.test_eq("minus point x", minus_p1.get_affine_x(), p1.get_affine_x());
112✔
351
         result.test_eq("minus point y", minus_p1.get_affine_y(), group.get_p() - p1.get_affine_y());
112✔
352

353
         result.confirm("zero point is zero", zero.is_zero());
112✔
354
         result.confirm("zero point is on the curve", zero.on_the_curve());
112✔
355
         result.test_eq("addition of zero does nothing", p1, p1 + zero);
112✔
356
         result.test_eq("addition of zero does nothing", p1, zero + p1);
112✔
357
         result.test_eq("addition of zero does nothing", p1, p1 - zero);
112✔
358
         result.confirm("zero times anything is the zero point", (zero * 39193).is_zero());
168✔
359

360
         for(auto scheme : {Botan::EC_Point_Format::Uncompressed,
392✔
361
                            Botan::EC_Point_Format::Compressed,
362
                            Botan::EC_Point_Format::Hybrid}) {
224✔
363
            const std::vector<uint8_t> v = zero.encode(scheme);
168✔
364
            result.test_eq("encoded/decode rt works", group.OS2ECP(v), zero);
336✔
365
         }
168✔
366
      }
56✔
367
   #endif
368
};
369

370
BOTAN_REGISTER_TEST("pubkey", "ec_group", EC_Group_Tests);
371

372
Test::Result test_decoding_with_seed() {
2✔
373
   Test::Result result("Decode EC_Group with seed");
2✔
374

375
   try {
2✔
376
      if(Botan::EC_Group::supports_named_group("secp384r1")) {
2✔
377
         const auto secp384r1 = Botan::EC_Group::from_name("secp384r1");
2✔
378
         const auto secp384r1_with_seed =
2✔
379
            Botan::EC_Group::from_PEM(Test::read_data_file("x509/ecc/secp384r1_seed.pem"));
4✔
380
         result.confirm("decoding worked", secp384r1_with_seed.initialized());
4✔
381
         result.test_eq("P-384 prime", secp384r1_with_seed.get_p(), secp384r1.get_p());
2✔
382
      }
2✔
383
   } catch(Botan::Exception& e) {
×
384
      result.test_failure(e.what());
×
385
   }
×
386

387
   return result;
2✔
388
}
×
389

390
Test::Result test_mixed_points() {
2✔
391
   Test::Result result("Mixed Point Arithmetic");
2✔
392

393
   if(Botan::EC_Group::supports_named_group("secp256r1") && Botan::EC_Group::supports_named_group("secp384r1")) {
2✔
394
      const auto secp256r1 = Botan::EC_Group::from_name("secp256r1");
2✔
395
      const auto secp384r1 = Botan::EC_Group::from_name("secp384r1");
2✔
396

397
   #if defined(BOTAN_HAS_LEGACY_EC_POINT)
398
      const Botan::EC_Point& G256 = secp256r1.get_base_point();
2✔
399
      const Botan::EC_Point& G384 = secp384r1.get_base_point();
2✔
400

401
      result.test_throws("Mixing points from different groups", [&] { const Botan::EC_Point p = G256 + G384; });
6✔
402
   #endif
403

404
      const auto p1 = Botan::EC_AffinePoint::generator(secp256r1);
2✔
405
      const auto p2 = Botan::EC_AffinePoint::generator(secp384r1);
2✔
406
      result.test_throws("Mixing points from different groups", [&] { auto p3 = p1.add(p2); });
6✔
407
   }
2✔
408

409
   return result;
2✔
410
}
×
411

412
class ECC_Unit_Tests final : public Test {
2✔
413
   public:
414
      std::vector<Test::Result> run() override {
2✔
415
         std::vector<Test::Result> results;
2✔
416

417
         results.push_back(test_decoding_with_seed());
4✔
418
         results.push_back(test_mixed_points());
4✔
419

420
         return results;
2✔
421
      }
×
422
};
423

424
BOTAN_REGISTER_TEST("pubkey", "ecc_unit", ECC_Unit_Tests);
425

426
class EC_Group_Registration_Tests final : public Test {
2✔
427
   public:
428
      std::vector<Test::Result> run() override {
2✔
429
         std::vector<Test::Result> results;
2✔
430

431
         if(Botan::EC_Group::supports_application_specific_group()) {
2✔
432
            results.push_back(test_ecc_registration());
4✔
433
            results.push_back(test_ec_group_from_params());
4✔
434
            results.push_back(test_ec_group_bad_registration());
4✔
435
            results.push_back(test_ec_group_duplicate_orders());
4✔
436
            results.push_back(test_ec_group_registration_with_custom_oid());
4✔
437
         }
438

439
         return results;
2✔
440
      }
×
441

442
   private:
443
      Test::Result test_ecc_registration() {
2✔
444
         Test::Result result("ECC registration");
2✔
445

446
         // numsp256d1
447
         const Botan::BigInt p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43");
2✔
448
         const Botan::BigInt a("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40");
2✔
449
         const Botan::BigInt b("0x25581");
2✔
450
         const Botan::BigInt order("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE43C8275EA265C6020AB20294751A825");
2✔
451

452
         const Botan::BigInt g_x("0x01");
2✔
453
         const Botan::BigInt g_y("0x696F1853C1E466D7FC82C96CCEEEDD6BD02C2F9375894EC10BF46306C2B56C77");
2✔
454

455
         const Botan::OID oid("1.3.6.1.4.1.25258.4.1");
2✔
456

457
         // Creating this object implicitly registers the curve for future use ...
458
         const Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
2✔
459

460
         auto group = Botan::EC_Group::from_OID(oid);
2✔
461

462
         result.test_eq("Group registration worked", group.get_p(), p);
2✔
463

464
         // TODO(Botan4) this could change to == Generic
465
         result.confirm("Group is not pcurve", group.engine() != Botan::EC_Group_Engine::Optimized);
4✔
466

467
         return result;
2✔
468
      }
2✔
469

470
      Test::Result test_ec_group_from_params() {
2✔
471
         Test::Result result("EC_Group from params");
2✔
472

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

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

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

484
         const Botan::OID oid("1.2.840.10045.3.1.7");
2✔
485

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

491
         return result;
2✔
492
      }
2✔
493

494
      Test::Result test_ec_group_bad_registration() {
2✔
495
         Test::Result result("EC_Group registering non-match");
2✔
496

497
         Botan::EC_Group::clear_registered_curve_data();
2✔
498

499
         // secp256r1 params except with a bad B param
500
         const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
2✔
501
         const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
2✔
502
         const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604C");
2✔
503

504
         const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
2✔
505
         const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
2✔
506
         const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
2✔
507

508
         const Botan::OID oid("1.2.840.10045.3.1.7");
2✔
509

510
         try {
2✔
511
            const Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
2✔
512
            result.test_failure("Should have failed");
×
513
         } catch(Botan::Invalid_Argument&) {
2✔
514
            result.test_success("Got expected exception");
2✔
515
         }
2✔
516

517
         return result;
4✔
518
      }
2✔
519

520
      Test::Result test_ec_group_duplicate_orders() {
2✔
521
         Test::Result result("EC_Group with duplicate group order");
2✔
522

523
         Botan::EC_Group::clear_registered_curve_data();
2✔
524

525
         // secp256r1
526
         const Botan::BigInt p("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
2✔
527
         const Botan::BigInt a("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
2✔
528
         const Botan::BigInt b("0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
2✔
529

530
         const Botan::BigInt g_x("0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
2✔
531
         const Botan::BigInt g_y("0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");
2✔
532
         const Botan::BigInt order("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
2✔
533

534
         const Botan::OID oid("1.3.6.1.4.1.25258.100.0");  // some other random OID
2✔
535

536
         const Botan::EC_Group reg_group(oid, p, a, b, g_x, g_y, order);
2✔
537
         result.test_success("Registration success");
2✔
538
         result.confirm("Group has correct OID", reg_group.get_curve_oid() == oid);
4✔
539

540
         // We can now get it by OID:
541
         const auto hc_group = Botan::EC_Group::from_OID(oid);
2✔
542
         result.confirm("Group has correct OID", hc_group.get_curve_oid() == oid);
4✔
543

544
         // Existing secp256r1 unmodified:
545
         const Botan::OID secp160r1("1.2.840.10045.3.1.7");
2✔
546
         const auto other_group = Botan::EC_Group::from_OID(secp160r1);
2✔
547
         result.confirm("Group has correct OID", other_group.get_curve_oid() == secp160r1);
4✔
548

549
         return result;
2✔
550
      }
2✔
551

552
      Test::Result test_ec_group_registration_with_custom_oid() {
2✔
553
         Test::Result result("EC_Group registration of standard group with custom OID");
2✔
554

555
         Botan::EC_Group::clear_registered_curve_data();
2✔
556

557
         const Botan::OID secp256r1_oid("1.2.840.10045.3.1.7");
2✔
558
         const auto secp256r1 = Botan::EC_Group::from_OID(secp256r1_oid);
2✔
559
         result.confirm("Group has correct OID", secp256r1.get_curve_oid() == secp256r1_oid);
4✔
560

561
         const Botan::OID custom_oid("1.3.6.1.4.1.25258.100.99");  // some other random OID
2✔
562

563
         Botan::OID::register_oid(custom_oid, "secp256r1");
2✔
564

565
         const Botan::EC_Group reg_group(custom_oid,
2✔
566
                                         secp256r1.get_p(),
567
                                         secp256r1.get_a(),
568
                                         secp256r1.get_b(),
569
                                         secp256r1.get_g_x(),
570
                                         secp256r1.get_g_y(),
571
                                         secp256r1.get_order());
2✔
572

573
         result.test_success("Registration success");
2✔
574
         result.confirm("Group has correct OID", reg_group.get_curve_oid() == custom_oid);
4✔
575

576
         // We can now get it by OID:
577
         result.confirm("Group has correct OID", Botan::EC_Group::from_OID(custom_oid).get_curve_oid() == custom_oid);
4✔
578

579
         // In the current data model of EC_Group there is a 1:1 OID:group, so these
580
         // have distinct underlying data
581
         result.confirm("Groups have different inner data pointers", reg_group._data() != secp256r1._data());
4✔
582

583
   #if defined(BOTAN_HAS_PCURVES_SECP256R1)
584
         // However we should have gotten a pcurves out of the deal *and* it
585
         // should be the exact same shared_ptr as the official curve
586

587
         result.confirm("Group is pcurves based", reg_group.engine() == Botan::EC_Group_Engine::Optimized);
4✔
588

589
         try {
2✔
590
            const auto& pcurve = reg_group._data()->pcurve();
2✔
591
            result.confirm("Group with custom OID got the same pcurve pointer",
2✔
592
                           &pcurve == &secp256r1._data()->pcurve());
2✔
593
         } catch(...) {
×
594
            result.test_failure("Group with custom OID did not get a pcurve pointer");
×
595
         }
×
596
   #endif
597

598
         return result;
4✔
599
      }
2✔
600
};
601

602
BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "ec_group_registration", EC_Group_Registration_Tests);
603

604
class EC_PointEnc_Tests final : public Test {
2✔
605
   public:
606
      std::vector<Test::Result> run() override {
2✔
607
         std::vector<Test::Result> results;
2✔
608

609
         auto& rng = Test::rng();
2✔
610

611
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
58✔
612
            const auto group = Botan::EC_Group::from_name(group_id);
56✔
613

614
            Result result("EC_AffinePoint encoding " + group_id);
56✔
615

616
            result.start_timer();
56✔
617

618
            for(size_t trial = 0; trial != 100; ++trial) {
5,656✔
619
               const auto scalar = Botan::EC_Scalar::random(group, rng);
5,600✔
620
               const auto pt = Botan::EC_AffinePoint::g_mul(scalar, rng);
5,600✔
621

622
               const auto pt_u = pt.serialize_uncompressed();
5,600✔
623
               result.test_eq("Expected uncompressed header", static_cast<size_t>(pt_u[0]), 0x04);
5,600✔
624
               const size_t fe_bytes = (pt_u.size() - 1) / 2;
5,600✔
625
               const auto pt_c = pt.serialize_compressed();
5,600✔
626

627
               result.test_eq("Expected compressed size", pt_c.size(), 1 + fe_bytes);
5,600✔
628
               const uint8_t expected_c_header = (pt_u[pt_u.size() - 1] % 2 == 0) ? 0x02 : 0x03;
5,600✔
629
               result.confirm("Expected compressed header", pt_c[0] == expected_c_header);
11,200✔
630

631
               result.test_eq(
11,200✔
632
                  "Expected compressed x", std::span{pt_c}.subspan(1), std::span{pt_u}.subspan(1, fe_bytes));
633

634
               if(auto d_pt_u = Botan::EC_AffinePoint::deserialize(group, pt_u)) {
5,600✔
635
                  result.test_eq(
11,200✔
636
                     "Deserializing uncompressed returned correct point", d_pt_u->serialize_uncompressed(), pt_u);
11,200✔
637
               } else {
638
                  result.test_failure("Failed to deserialize uncompressed point");
×
639
               }
×
640

641
               if(auto d_pt_c = Botan::EC_AffinePoint::deserialize(group, pt_c)) {
5,600✔
642
                  result.test_eq(
11,200✔
643
                     "Deserializing compressed returned correct point", d_pt_c->serialize_uncompressed(), pt_u);
11,200✔
644
               } else {
645
                  result.test_failure("Failed to deserialize compressed point");
×
646
               }
×
647

648
               const auto neg_pt_c = [&]() {
5,600✔
649
                  auto x = pt_c;
5,600✔
650
                  x[0] ^= 0x01;
5,600✔
651
                  return x;
5,600✔
652
               }();
5,600✔
653

654
               if(auto d_neg_pt_c = Botan::EC_AffinePoint::deserialize(group, neg_pt_c)) {
5,600✔
655
                  result.test_eq("Deserializing compressed with inverted header returned negated point",
11,200✔
656
                                 d_neg_pt_c->serialize_uncompressed(),
11,200✔
657
                                 pt.negate().serialize_uncompressed());
11,200✔
658
               } else {
659
                  result.test_failure("Failed to deserialize compressed point");
×
660
               }
5,600✔
661
            }
5,600✔
662

663
            result.end_timer();
56✔
664

665
            results.push_back(result);
56✔
666
         }
56✔
667

668
         return results;
2✔
669
      }
5,600✔
670
};
671

672
BOTAN_REGISTER_TEST("pubkey", "ec_point_enc", EC_PointEnc_Tests);
673

674
class EC_Point_Arithmetic_Tests final : public Test {
2✔
675
   public:
676
      std::vector<Test::Result> run() override {
2✔
677
         std::vector<Test::Result> results;
2✔
678

679
         auto& rng = Test::rng();
2✔
680

681
         for(const auto& group_id : Botan::EC_Group::known_named_groups()) {
58✔
682
            const auto group = Botan::EC_Group::from_name(group_id);
56✔
683

684
            Result result("EC_AffinePoint arithmetic " + group_id);
56✔
685

686
            result.start_timer();
56✔
687

688
            const auto one = Botan::EC_Scalar::one(group);
56✔
689
            const auto zero = one - one;  // NOLINT(*-redundant-expression)
56✔
690
            const auto g = Botan::EC_AffinePoint::generator(group);
56✔
691
            const auto g_bytes = g.serialize_uncompressed();
56✔
692

693
            const auto id = Botan::EC_AffinePoint::g_mul(zero, rng);
56✔
694
            result.confirm("g*zero is point at identity", id.is_identity());
112✔
695

696
            const auto id2 = id.add(id);
56✔
697
            result.confirm("identity plus itself is identity", id2.is_identity());
112✔
698

699
            const auto g_one = Botan::EC_AffinePoint::g_mul(one, rng);
56✔
700
            result.test_eq("g*one == generator", g_one.serialize_uncompressed(), g_bytes);
112✔
701

702
            const auto g_plus_id = g_one.add(id);
56✔
703
            result.test_eq("g + id == g", g_plus_id.serialize_uncompressed(), g_bytes);
112✔
704

705
            const auto id_plus_g = id.add(g_one);
56✔
706
            result.test_eq("id + g == g", id_plus_g.serialize_uncompressed(), g_bytes);
112✔
707

708
            const auto g_neg_one = Botan::EC_AffinePoint::g_mul(one.negate(), rng);
56✔
709

710
            const auto id_from_g = g_one.add(g_neg_one);
56✔
711
            result.confirm("g - g is identity", id_from_g.is_identity());
112✔
712

713
            const auto g_two = Botan::EC_AffinePoint::g_mul(one + one, rng);
56✔
714
            const auto g_plus_g = g_one.add(g_one);
56✔
715
            result.test_eq("2*g == g+g", g_two.serialize_uncompressed(), g_plus_g.serialize_uncompressed());
168✔
716

717
            result.confirm("Scalar::zero is zero", zero.is_zero());
112✔
718
            result.confirm("(zero+zero) is zero", (zero + zero).is_zero());
112✔
719
            result.confirm("(zero*zero) is zero", (zero * zero).is_zero());
112✔
720
            result.confirm("(zero-zero) is zero", (zero - zero).is_zero());  // NOLINT(*-redundant-expression)
112✔
721

722
            const auto neg_zero = zero.negate();
56✔
723
            result.confirm("zero.negate() is zero", neg_zero.is_zero());
112✔
724

725
            result.confirm("(zero+nz) is zero", (zero + neg_zero).is_zero());
112✔
726
            result.confirm("(nz+nz) is zero", (neg_zero + neg_zero).is_zero());
112✔
727
            result.confirm("(nz+zero) is zero", (neg_zero + zero).is_zero());
112✔
728

729
            result.confirm("Scalar::one is not zero", !one.is_zero());
112✔
730
            result.confirm("(one-one) is zero", (one - one).is_zero());  // NOLINT(*-redundant-expression)
112✔
731
            result.confirm("(one+one.negate()) is zero", (one + one.negate()).is_zero());
168✔
732
            result.confirm("(one.negate()+one) is zero", (one.negate() + one).is_zero());
168✔
733

734
            for(size_t i = 0; i != 16; ++i) {
952✔
735
               const auto pt = Botan::EC_AffinePoint::g_mul(Botan::EC_Scalar::random(group, rng), rng);
896✔
736

737
               const auto a = Botan::EC_Scalar::random(group, rng);
896✔
738
               const auto b = Botan::EC_Scalar::random(group, rng);
896✔
739
               const auto c = a + b;
896✔
740

741
               const auto Pa = pt.mul(a, rng);
896✔
742
               const auto Pb = pt.mul(b, rng);
896✔
743
               const auto Pc = pt.mul(c, rng);
896✔
744

745
               const auto Pc_bytes = Pc.serialize_uncompressed();
896✔
746

747
               const auto Pab = Pa.add(Pb);
896✔
748
               result.test_eq("Pa + Pb == Pc", Pab.serialize_uncompressed(), Pc_bytes);
1,792✔
749

750
               const auto Pba = Pb.add(Pa);
896✔
751
               result.test_eq("Pb + Pa == Pc", Pba.serialize_uncompressed(), Pc_bytes);
1,792✔
752
            }
1,792✔
753

754
            for(size_t i = 0; i != 64; ++i) {
3,640✔
755
               auto h = [&]() {
10,752✔
756
                  const auto s = [&]() {
10,752✔
757
                     if(i == 0) {
3,584✔
758
                        // Test the identity case
759
                        return Botan::EC_Scalar(zero);
56✔
760
                     } else if(i <= 32) {
3,528✔
761
                        // Test cases where the two points have a linear relation
762
                        std::vector<uint8_t> sbytes(group.get_order_bytes());
1,792✔
763
                        sbytes[sbytes.size() - 1] = static_cast<uint8_t>((i + 1) / 2);
1,792✔
764
                        auto si = Botan::EC_Scalar::deserialize(group, sbytes).value();
3,584✔
765
                        if(i % 2 == 0) {
1,792✔
766
                           return si;
896✔
767
                        } else {
768
                           return si.negate();
896✔
769
                        }
770
                     } else {
3,584✔
771
                        return Botan::EC_Scalar::random(group, rng);
1,736✔
772
                     }
773
                  }();
3,584✔
774
                  auto x = Botan::EC_AffinePoint::g_mul(s, rng);
3,584✔
775
                  return x;
3,584✔
776
               }();
7,168✔
777

778
               const auto s1 = Botan::EC_Scalar::random(group, rng);
3,584✔
779
               const auto s2 = Botan::EC_Scalar::random(group, rng);
3,584✔
780

781
               const Botan::EC_Group::Mul2Table mul2_table(h);
3,584✔
782

783
               const auto ref = Botan::EC_AffinePoint::g_mul(s1, rng).add(h.mul(s2, rng));
3,584✔
784

785
               if(auto mul2pt = mul2_table.mul2_vartime(s1, s2)) {
3,584✔
786
                  result.test_eq("ref == mul2t", ref.serialize_uncompressed(), mul2pt->serialize_uncompressed());
14,336✔
787
               } else {
788
                  result.confirm("ref is identity", ref.is_identity());
×
789
               }
×
790
            }
3,584✔
791

792
            result.end_timer();
56✔
793

794
            results.push_back(result);
56✔
795
         }
112✔
796

797
         return results;
2✔
798
      }
×
799
};
800

801
BOTAN_REGISTER_TEST("pubkey", "ec_point_arith", EC_Point_Arithmetic_Tests);
802

803
   #if defined(BOTAN_HAS_ECDSA)
804

805
class ECC_Invalid_Key_Tests final : public Text_Based_Test {
×
806
   public:
807
      ECC_Invalid_Key_Tests() : Text_Based_Test("pubkey/ecc_invalid.vec", "SubjectPublicKey") {}
4✔
808

809
      bool clear_between_callbacks() const override { return false; }
10✔
810

811
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
10✔
812
         Test::Result result("ECC invalid keys");
10✔
813

814
         const std::string encoded = vars.get_req_str("SubjectPublicKey");
10✔
815
         Botan::DataSource_Memory key_data(Botan::hex_decode(encoded));
20✔
816

817
         try {
10✔
818
            auto key = Botan::X509::load_key(key_data);
10✔
819
            result.test_eq("public key fails check", key->check_key(this->rng(), false), false);
×
820
         } catch(Botan::Decoding_Error&) {
10✔
821
            result.test_success("Decoding invalid ECC key results in decoding error exception");
10✔
822
         }
10✔
823

824
         return result;
10✔
825
      }
10✔
826
};
827

828
BOTAN_REGISTER_TEST("pubkey", "ecc_invalid", ECC_Invalid_Key_Tests);
829

830
   #endif
831

832
#endif
833

834
}  // namespace
835

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