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

randombit / botan / 21863016989

10 Feb 2026 11:27AM UTC coverage: 90.077% (+0.009%) from 90.068%
21863016989

Pull #5306

github

web-flow
Merge c89f34c03 into 1d119e57a
Pull Request #5306: Allow unregistering EC Groups

102266 of 113532 relevant lines covered (90.08%)

11412379.13 hits per line

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

88.14
/src/lib/pubkey/ec_group/ec_group.cpp
1
/*
2
* ECC Domain Parameters
3
*
4
* (C) 2007 Falko Strenzke, FlexSecure GmbH
5
* (C) 2008,2018,2024 Jack Lloyd
6
* (C) 2018 Tobias Niemann
7
*
8
* Botan is released under the Simplified BSD License (see license.txt)
9
*/
10

11
#include <botan/ec_group.h>
12

13
#include <botan/ber_dec.h>
14
#include <botan/der_enc.h>
15
#include <botan/mutex.h>
16
#include <botan/numthry.h>
17
#include <botan/pem.h>
18
#include <botan/rng.h>
19
#include <botan/internal/barrett.h>
20
#include <botan/internal/ec_inner_data.h>
21
#include <botan/internal/fmt.h>
22
#include <botan/internal/primality.h>
23
#include <vector>
24

25
namespace Botan {
26

27
class EC_Group_Data_Map final {
28
   public:
29
      EC_Group_Data_Map() = default;
30

31
      size_t clear() {
1,765✔
32
         const lock_guard_type<mutex_type> lock(m_mutex);
1,765✔
33
         const size_t count = m_registered_curves.size();
1,765✔
34
         m_registered_curves.clear();
1,765✔
35
         return count;
1,765✔
36
      }
1,765✔
37

38
      bool unregister(const OID& oid) {
12✔
39
         const lock_guard_type<mutex_type> lock(m_mutex);
12✔
40
         for(size_t i = 0; i < m_registered_curves.size(); i++) {
118✔
41
            if(m_registered_curves[i]->oid() == oid) {
112✔
42
               m_registered_curves.erase(m_registered_curves.begin() + i);
6✔
43
               return true;
6✔
44
            }
45
         }
46
         return false;
47
      }
12✔
48

49
      std::shared_ptr<EC_Group_Data> lookup(const OID& oid) {
25,035✔
50
         const lock_guard_type<mutex_type> lock(m_mutex);
25,035✔
51

52
         for(auto i : m_registered_curves) {
237,010✔
53
            if(i->oid() == oid) {
235,823✔
54
               return i;
23,848✔
55
            }
56
         }
235,823✔
57

58
         // Not found, check hardcoded data
59
         std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
1,187✔
60

61
         if(data) {
1,187✔
62
            m_registered_curves.push_back(data);
1,126✔
63
            return data;
1,126✔
64
         }
65

66
         // Nope, unknown curve
67
         return std::shared_ptr<EC_Group_Data>();
61✔
68
      }
26,222✔
69

70
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
44✔
71
                                                      const BigInt& a,
72
                                                      const BigInt& b,
73
                                                      const BigInt& g_x,
74
                                                      const BigInt& g_y,
75
                                                      const BigInt& order,
76
                                                      const BigInt& cofactor,
77
                                                      const OID& oid,
78
                                                      EC_Group_Source source) {
79
         BOTAN_ASSERT_NOMSG(oid.has_value());
44✔
80

81
         const lock_guard_type<mutex_type> lock(m_mutex);
44✔
82

83
         for(auto i : m_registered_curves) {
909✔
84
            if(i->oid() == oid) {
898✔
85
               /*
86
               * If both OID and params are the same then we are done, just return
87
               * the already registered curve obj.
88
               *
89
               * First verify that the params match, to catch an application
90
               * that is attempting to register a EC_Group under the same OID as
91
               * another group currently in use
92
               */
93
               if(!i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
33✔
94
                  throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
×
95
                                         " but a distinct curve is already registered using that OID");
×
96
               }
97

98
               return i;
33✔
99
            }
100

101
            /*
102
            * If the same curve was previously created without an OID but is now
103
            * being registered again using an OID, save that OID.
104
            *
105
            * TODO(Botan4) remove this block; this situation won't be possible since
106
            * we will require all groups to have an OID
107
            */
108
            if(i->oid().empty() && i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
865✔
109
               i->set_oid(oid);
×
110
               return i;
×
111
            }
112
         }
898✔
113

114
         /*
115
         * Not found in current list, so we need to create a new entry
116
         */
117
         auto new_group = [&] {
×
118
            if(auto g = EC_Group::EC_group_info(oid); g != nullptr) {
11✔
119
               /*
120
               * This turned out to be the OID of one of the builtin groups. Verify
121
               * that all of the provided parameters match that builtin group.
122
               */
123
               BOTAN_ARG_CHECK(g->params_match(p, a, b, g_x, g_y, order, cofactor),
×
124
                               "Attempting to register an EC group under OID of hardcoded group");
125

126
               return g;
×
127
            } else {
128
               /*
129
               * This path is taken for an application registering a new EC_Group with an OID specified
130
               */
131
               return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
11✔
132
            }
11✔
133
         }();
11✔
134

135
         m_registered_curves.push_back(new_group);
11✔
136
         return new_group;
11✔
137
      }
55✔
138

139
      std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
69✔
140
                                                        const BigInt& a,
141
                                                        const BigInt& b,
142
                                                        std::span<const uint8_t> base_pt,
143
                                                        const BigInt& order,
144
                                                        const BigInt& cofactor) {
145
         const lock_guard_type<mutex_type> lock(m_mutex);
69✔
146

147
         for(auto i : m_registered_curves) {
866✔
148
            if(i->params_match(p, a, b, base_pt, order, cofactor)) {
850✔
149
               return i;
53✔
150
            }
151
         }
850✔
152

153
         // Try to use the order as a hint to look up the group id
154
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
16✔
155
         if(oid_from_order.has_value()) {
16✔
156
            auto new_group = EC_Group::EC_group_info(oid_from_order);
12✔
157

158
            // Have to check all params in the (unlikely/malicious) event of an order collision
159
            if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) {
12✔
160
               m_registered_curves.push_back(new_group);
4✔
161
               return new_group;
4✔
162
            }
163
         }
12✔
164

165
         return {};
11✔
166
      }
84✔
167

168
      // TODO(Botan4) this entire function can be removed since OIDs will be required
169
      std::shared_ptr<EC_Group_Data> lookup_or_create_without_oid(const BigInt& p,
7✔
170
                                                                  const BigInt& a,
171
                                                                  const BigInt& b,
172
                                                                  const BigInt& g_x,
173
                                                                  const BigInt& g_y,
174
                                                                  const BigInt& order,
175
                                                                  const BigInt& cofactor,
176
                                                                  EC_Group_Source source) {
177
         const lock_guard_type<mutex_type> lock(m_mutex);
7✔
178

179
         for(auto i : m_registered_curves) {
98✔
180
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
91✔
181
               return i;
×
182
            }
183
         }
91✔
184

185
         // Try to use the order as a hint to look up the group id
186
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
7✔
187
         if(oid_from_order.has_value()) {
7✔
188
            auto new_group = EC_Group::EC_group_info(oid_from_order);
4✔
189

190
            // Have to check all params in the (unlikely/malicious) event of an order collision
191
            if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
4✔
192
               m_registered_curves.push_back(new_group);
1✔
193
               return new_group;
1✔
194
            }
195
         }
4✔
196

197
         /*
198
         * At this point we have failed to identify the group; it is not any of
199
         * the builtin values, nor is it a group that the user had previously
200
         * registered explicitly. We create the group data without an OID.
201
         *
202
         * TODO(Botan4) remove this; throw an exception instead
203
         */
204
         auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
6✔
205
         m_registered_curves.push_back(new_group);
6✔
206
         return new_group;
6✔
207
      }
20✔
208

209
   private:
210
      mutex_type m_mutex;
211
      // TODO(Botan4): Once OID is required we could make this into a map
212
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
213
};
214

215
//static
216
EC_Group_Data_Map& EC_Group::ec_group_data() {
26,932✔
217
   /*
218
   * This exists purely to ensure the allocator is constructed before g_ec_data,
219
   * which ensures that its destructor runs after ~g_ec_data is complete.
220
   */
221

222
   static const Allocator_Initializer g_init_allocator;
26,932✔
223
   static EC_Group_Data_Map g_ec_data;
26,932✔
224
   return g_ec_data;
26,932✔
225
}
226

227
//static
228
size_t EC_Group::clear_registered_curve_data() {
1,765✔
229
   return ec_group_data().clear();
1,765✔
230
}
231

232
//static
233
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
1,142✔
234
                                                            const char* a_str,
235
                                                            const char* b_str,
236
                                                            const char* g_x_str,
237
                                                            const char* g_y_str,
238
                                                            const char* order_str,
239
                                                            const OID& oid) {
240
   BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
1,142✔
241

242
   const BigInt p(p_str);
1,142✔
243
   const BigInt a(a_str);
1,142✔
244
   const BigInt b(b_str);
1,142✔
245
   const BigInt g_x(g_x_str);
1,142✔
246
   const BigInt g_y(g_y_str);
1,142✔
247
   const BigInt order(order_str);
1,142✔
248
   const BigInt cofactor(1);  // implicit
1,142✔
249

250
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
2,284✔
251
}
1,142✔
252

253
//static
254
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
9,535✔
255
                                                                              EC_Group_Source source) {
256
   BER_Decoder ber(bits);
9,535✔
257

258
   auto next_obj_type = ber.peek_next_object().type_tag();
9,535✔
259

260
   if(next_obj_type == ASN1_Type::ObjectId) {
9,527✔
261
      OID oid;
9,173✔
262
      ber.decode(oid);
9,173✔
263

264
      auto data = ec_group_data().lookup(oid);
9,131✔
265
      if(!data) {
9,131✔
266
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
120✔
267
      }
268

269
      return std::make_pair(data, false);
9,071✔
270
   } else if(next_obj_type == ASN1_Type::Sequence) {
9,587✔
271
      BigInt p;
350✔
272
      BigInt a;
350✔
273
      BigInt b;
350✔
274
      BigInt order;
350✔
275
      BigInt cofactor;
350✔
276
      std::vector<uint8_t> base_pt;
350✔
277
      std::vector<uint8_t> seed;
350✔
278

279
      ber.start_sequence()
350✔
280
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
677✔
281
         .start_sequence()
328✔
282
         .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
656✔
283
         .decode(p)
327✔
284
         .end_cons()
327✔
285
         .start_sequence()
327✔
286
         .decode_octet_string_bigint(a)
327✔
287
         .decode_octet_string_bigint(b)
326✔
288
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString)
324✔
289
         .end_cons()
324✔
290
         .decode(base_pt, ASN1_Type::OctetString)
321✔
291
         .decode(order)
314✔
292
         .decode(cofactor)
313✔
293
         .end_cons()
313✔
294
         .verify_end();
312✔
295

296
      // TODO(Botan4) Require cofactor == 1
297
      if(cofactor <= 0 || cofactor >= 16) {
620✔
298
         throw Decoding_Error("Invalid ECC cofactor parameter");
8✔
299
      }
300

301
      if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) {
304✔
302
         throw Decoding_Error("ECC p parameter is invalid size");
134✔
303
      }
304

305
      if(a.is_negative() || a >= p) {
340✔
306
         throw Decoding_Error("Invalid ECC a parameter");
20✔
307
      }
308

309
      if(b <= 0 || b >= p) {
299✔
310
         throw Decoding_Error("Invalid ECC b parameter");
77✔
311
      }
312

313
      if(order.is_negative() || order.is_zero() || order >= 2 * p) {
286✔
314
         throw Decoding_Error("Invalid ECC group order");
4✔
315
      }
316

317
      if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
356✔
318
         return std::make_pair(data, true);
57✔
319
      }
57✔
320

321
      /*
322
      TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
323
      which is neither a builtin group nor a group that was registered by the application.
324
      It can all be removed and replaced with a throw
325
      */
326

327
      auto mod_p = Barrett_Reduction::for_public_modulus(p);
11✔
328
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
11✔
329
         throw Decoding_Error("ECC p parameter is not a prime");
3✔
330
      }
331

332
      auto mod_order = Barrett_Reduction::for_public_modulus(order);
8✔
333
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
8✔
334
         throw Decoding_Error("Invalid ECC order parameter");
2✔
335
      }
336

337
      const size_t p_bytes = p.bytes();
6✔
338
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
6✔
339
         throw Decoding_Error("Invalid ECC base point encoding");
×
340
      }
341

342
      auto [g_x, g_y] = [&]() {
9✔
343
         const uint8_t hdr = base_pt[0];
6✔
344

345
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
6✔
346
            const BigInt x = BigInt::decode(&base_pt[1], p_bytes);
6✔
347
            const BigInt y = BigInt::decode(&base_pt[p_bytes + 1], p_bytes);
6✔
348

349
            if(x < p && y < p) {
6✔
350
               return std::make_pair(x, y);
6✔
351
            }
352
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
6✔
353
            // TODO(Botan4) remove this branch; we won't support compressed points
354
            const BigInt x = BigInt::decode(&base_pt[1], p_bytes);
×
355
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
356

357
            if(x < p && y >= 0) {
×
358
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
359
               if(y.get_bit(0) != y_mod_2) {
×
360
                  y = p - y;
×
361
               }
362

363
               return std::make_pair(x, y);
×
364
            }
365
         }
×
366

367
         throw Decoding_Error("Invalid ECC base point encoding");
×
368
      }();
6✔
369

370
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
371
      auto y2 = mod_p.square(g_y);
6✔
372
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
6✔
373
      if(y2 != x3_ax_b) {
6✔
374
         throw Decoding_Error("Invalid ECC base point");
1✔
375
      }
376

377
      auto data = ec_group_data().lookup_or_create_without_oid(p, a, b, g_x, g_y, order, cofactor, source);
5✔
378
      return std::make_pair(data, true);
5✔
379
   } else if(next_obj_type == ASN1_Type::Null) {
1,888✔
380
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
1✔
381
   } else {
382
      throw Decoding_Error(
3✔
383
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
6✔
384
   }
385
}
9,535✔
386

387
EC_Group::EC_Group() = default;
×
388

389
EC_Group::~EC_Group() = default;
121,598✔
390

391
EC_Group::EC_Group(const EC_Group&) = default;
59,086✔
392

393
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
394

395
// Internal constructor
396
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,854✔
397

398
//static
399
bool EC_Group::supports_named_group(std::string_view name) {
15,320✔
400
   return EC_Group::known_named_groups().contains(std::string(name));
15,320✔
401
}
402

403
//static
404
bool EC_Group::supports_application_specific_group() {
26✔
405
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
406
   return true;
26✔
407
#else
408
   return false;
409
#endif
410
}
411

412
//static
413
bool EC_Group::supports_application_specific_group_with_cofactor() {
5✔
414
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
415
   return true;
5✔
416
#else
417
   return false;
418
#endif
419
}
420

421
//static
422
EC_Group EC_Group::from_OID(const OID& oid) {
75✔
423
   auto data = ec_group_data().lookup(oid);
75✔
424

425
   if(!data) {
75✔
426
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
2✔
427
   }
428

429
   return EC_Group(std::move(data));
74✔
430
}
74✔
431

432
//static
433
EC_Group EC_Group::from_name(std::string_view name) {
15,781✔
434
   std::shared_ptr<EC_Group_Data> data;
15,781✔
435

436
   if(auto oid = OID::from_name(name)) {
15,781✔
437
      data = ec_group_data().lookup(oid.value());
31,560✔
438
   }
×
439

440
   if(!data) {
15,780✔
441
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
442
   }
443

444
   return EC_Group(std::move(data));
15,780✔
445
}
15,780✔
446

447
EC_Group::EC_Group(std::string_view str) {
49✔
448
   if(str.empty()) {
49✔
449
      return;  // no initialization / uninitialized
450
   }
451

452
   try {
49✔
453
      const OID oid = OID::from_string(str);
49✔
454
      if(oid.has_value()) {
49✔
455
         m_data = ec_group_data().lookup(oid);
49✔
456
      }
457
   } catch(...) {}
49✔
458

459
   if(m_data == nullptr) {
49✔
460
      if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
×
461
         // OK try it as PEM ...
462
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
×
463

464
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
×
465
         this->m_data = data.first;
×
466
         this->m_explicit_encoding = data.second;
×
467
      }
×
468
   }
469

470
   if(m_data == nullptr) {
49✔
471
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
472
   }
473
}
×
474

475
//static
476
EC_Group EC_Group::from_PEM(std::string_view pem) {
4✔
477
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
4✔
478
   return EC_Group(ber);
4✔
479
}
4✔
480

481
EC_Group::EC_Group(const BigInt& p,
4✔
482
                   const BigInt& a,
483
                   const BigInt& b,
484
                   const BigInt& base_x,
485
                   const BigInt& base_y,
486
                   const BigInt& order,
487
                   const BigInt& cofactor,
488
                   const OID& oid) {
4✔
489
   if(oid.has_value()) {
4✔
490
      m_data = ec_group_data().lookup_or_create(
4✔
491
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
492
   } else {
493
      m_data = ec_group_data().lookup_or_create_without_oid(
4✔
494
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
2✔
495
   }
496
}
4✔
497

498
EC_Group::EC_Group(const OID& oid,
51✔
499
                   const BigInt& p,
500
                   const BigInt& a,
501
                   const BigInt& b,
502
                   const BigInt& base_x,
503
                   const BigInt& base_y,
504
                   const BigInt& order) {
51✔
505
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
51✔
506

507
   // TODO(Botan4) remove this and require 192 bits minimum
508
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
509
   constexpr size_t p_bits_lower_bound = 192;
510
#else
511
   constexpr size_t p_bits_lower_bound = 128;
51✔
512
#endif
513

514
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
51✔
515
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
51✔
516

517
   if(p.bits() == 521) {
51✔
518
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
519
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
520
   } else if(p.bits() == 239) {
51✔
521
      const auto x962_p239 = []() {
18✔
522
         BigInt p239;
3✔
523
         for(size_t i = 0; i != 239; ++i) {
720✔
524
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
525
               p239.set_bit(i);
717✔
526
            }
527
         }
528
         return p239;
3✔
529
      }();
3✔
530

531
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
532
   } else {
3✔
533
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
47✔
534
   }
535

536
   BOTAN_ARG_CHECK(p % 4 == 3, "EC_Group p must be congruent to 3 modulo 4");
48✔
537

538
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
92✔
539
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
92✔
540
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
92✔
541
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
92✔
542
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
46✔
543

544
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
43✔
545
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
43✔
546

547
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
43✔
548
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
43✔
549

550
   // This catches someone "ignoring" a cofactor and just trying to
551
   // provide the subgroup order
552
   BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid");
43✔
553

554
   // Check that 4*a^3 + 27*b^2 != 0
555
   const auto discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
86✔
556
                                          mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
130✔
557
   BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
43✔
558

559
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
560
   auto y2 = mod_p.square(base_y);
43✔
561
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
43✔
562
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
43✔
563

564
   const BigInt cofactor(1);
42✔
565

566
   m_data =
42✔
567
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
42✔
568
}
139✔
569

570
EC_Group::EC_Group(std::span<const uint8_t> ber) {
9,535✔
571
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
9,535✔
572
   m_data = data.first;
9,133✔
573
   m_explicit_encoding = data.second;
9,133✔
574
}
9,535✔
575

576
// static
577
bool EC_Group::unregister(const OID& oid) {
12✔
578
   return ec_group_data().unregister(oid);
12✔
579
}
580

581
const EC_Group_Data& EC_Group::data() const {
137,189✔
582
   if(m_data == nullptr) {
137,189✔
583
      throw Invalid_State("EC_Group uninitialized");
×
584
   }
585
   return *m_data;
137,189✔
586
}
587

588
size_t EC_Group::get_p_bits() const {
1,630✔
589
   return data().p_bits();
1,630✔
590
}
591

592
size_t EC_Group::get_p_bytes() const {
13,665✔
593
   return data().p_bytes();
13,665✔
594
}
595

596
size_t EC_Group::get_order_bits() const {
2,751✔
597
   return data().order_bits();
2,751✔
598
}
599

600
size_t EC_Group::get_order_bytes() const {
37,927✔
601
   return data().order_bytes();
37,927✔
602
}
603

604
const BigInt& EC_Group::get_p() const {
26,574✔
605
   return data().p();
26,574✔
606
}
607

608
const BigInt& EC_Group::get_a() const {
505✔
609
   return data().a();
505✔
610
}
611

612
const BigInt& EC_Group::get_b() const {
393✔
613
   return data().b();
393✔
614
}
615

616
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
617
const EC_Point& EC_Group::get_base_point() const {
677✔
618
   return data().base_point();
677✔
619
}
620

621
const EC_Point& EC_Group::generator() const {
×
622
   return data().base_point();
×
623
}
624

625
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
626
   //check that public point is not at infinity
627
   if(point.is_zero()) {
×
628
      return false;
629
   }
630

631
   //check that public point is on the curve
632
   if(point.on_the_curve() == false) {
×
633
      return false;
634
   }
635

636
   //check that public point has order q
637
   if((point * get_order()).is_zero() == false) {
×
638
      return false;
639
   }
640

641
   if(has_cofactor()) {
×
642
      if((point * get_cofactor()).is_zero()) {
×
643
         return false;
644
      }
645
   }
646

647
   return true;
648
}
649

650
#endif
651

652
const BigInt& EC_Group::get_order() const {
7,895✔
653
   return data().order();
7,895✔
654
}
655

656
const BigInt& EC_Group::get_g_x() const {
1,021✔
657
   return data().g_x();
1,021✔
658
}
659

660
const BigInt& EC_Group::get_g_y() const {
1,021✔
661
   return data().g_y();
1,021✔
662
}
663

664
const BigInt& EC_Group::get_cofactor() const {
162✔
665
   return data().cofactor();
162✔
666
}
667

668
bool EC_Group::has_cofactor() const {
11,865✔
669
   return data().has_cofactor();
11,865✔
670
}
671

672
const OID& EC_Group::get_curve_oid() const {
28,237✔
673
   return data().oid();
28,237✔
674
}
675

676
EC_Group_Source EC_Group::source() const {
139✔
677
   return data().source();
139✔
678
}
679

680
EC_Group_Engine EC_Group::engine() const {
2✔
681
   return data().engine();
2✔
682
}
683

684
std::vector<uint8_t> EC_Group::DER_encode() const {
2,725✔
685
   const auto& der_named_curve = data().der_named_curve();
2,725✔
686
   // TODO(Botan4) this can be removed because an OID will always be defined
687
   if(der_named_curve.empty()) {
2,725✔
688
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
689
   }
690

691
   return der_named_curve;
2,725✔
692
}
693

694
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
2,720✔
695
   if(form == EC_Group_Encoding::Explicit) {
2,720✔
696
      std::vector<uint8_t> output;
30✔
697
      DER_Encoder der(output);
30✔
698
      const size_t ecpVers1 = 1;
30✔
699
      const OID curve_type("1.2.840.10045.1.1");  // prime field
30✔
700

701
      const size_t p_bytes = get_p_bytes();
30✔
702

703
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
30✔
704

705
      der.start_sequence()
30✔
706
         .encode(ecpVers1)
30✔
707
         .start_sequence()
30✔
708
         .encode(curve_type)
30✔
709
         .encode(get_p())
30✔
710
         .end_cons()
30✔
711
         .start_sequence()
30✔
712
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
30✔
713
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
60✔
714
         .end_cons()
30✔
715
         .encode(generator, ASN1_Type::OctetString)
30✔
716
         .encode(get_order())
30✔
717
         .encode(get_cofactor())
30✔
718
         .end_cons();
30✔
719
      return output;
30✔
720
   } else if(form == EC_Group_Encoding::NamedCurve) {
2,720✔
721
      return this->DER_encode();
2,690✔
722
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
723
      return {0x00, 0x05};
×
724
   } else {
725
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
726
   }
727
}
728

729
std::string EC_Group::PEM_encode(EC_Group_Encoding form) const {
3✔
730
   const std::vector<uint8_t> der = DER_encode(form);
3✔
731
   return PEM_Code::encode(der, "EC PARAMETERS");
3✔
732
}
3✔
733

734
bool EC_Group::operator==(const EC_Group& other) const {
58✔
735
   if(m_data == other.m_data) {
58✔
736
      return true;  // same shared rep
737
   }
738

739
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
9✔
740
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
9✔
741
           get_cofactor() == other.get_cofactor());
4✔
742
}
743

744
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
139✔
745
   const bool is_builtin = source() == EC_Group_Source::Builtin;
139✔
746

747
   if(is_builtin && !strong) {
139✔
748
      return true;
749
   }
750

751
   // TODO(Botan4) this can probably all be removed once the deprecated EC_Group
752
   // constructor is removed, since at that point it no longer becomes possible
753
   // to create an EC_Group which fails to satisfy these conditions
754

755
   const BigInt& p = get_p();
28✔
756
   const BigInt& a = get_a();
28✔
757
   const BigInt& b = get_b();
28✔
758
   const BigInt& order = get_order();
28✔
759

760
   if(p <= 3 || order <= 0) {
56✔
761
      return false;
×
762
   }
763
   if(a < 0 || a >= p) {
56✔
764
      return false;
×
765
   }
766
   if(b <= 0 || b >= p) {
56✔
767
      return false;
×
768
   }
769

770
   const size_t test_prob = 128;
28✔
771
   const bool is_randomly_generated = is_builtin;
28✔
772

773
   //check if field modulus is prime
774
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
28✔
775
      return false;
776
   }
777

778
   //check if order is prime
779
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
28✔
780
      return false;
781
   }
782

783
   //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
784
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
28✔
785

786
   const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
56✔
787
                                            mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
84✔
788

789
   if(discriminant == 0) {
28✔
790
      return false;
791
   }
792

793
   //check for valid cofactor
794
   if(get_cofactor() < 1) {
28✔
795
      return false;
796
   }
797

798
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
799
   const EC_Point& base_point = get_base_point();
28✔
800
   //check if the base point is on the curve
801
   if(!base_point.on_the_curve()) {
28✔
802
      return false;
803
   }
804
   if((base_point * get_cofactor()).is_zero()) {
56✔
805
      return false;
806
   }
807
   //check if order of the base point is correct
808
   if(!(base_point * order).is_zero()) {
28✔
809
      return false;
810
   }
811
#endif
812

813
   // check the Hasse bound (roughly)
814
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
28✔
815
      return false;
816
   }
817

818
   return true;
819
}
56✔
820

821
EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
×
822

823
EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default;
×
824

825
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
14,979✔
826

827
EC_Group::Mul2Table::~Mul2Table() = default;
14,979✔
828

829
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,101✔
830
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,101✔
831
   if(pt) {
2,101✔
832
      return EC_AffinePoint::_from_inner(std::move(pt));
4,202✔
833
   } else {
834
      return {};
×
835
   }
836
}
2,101✔
837

838
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
24,715✔
839
                                                      const EC_Scalar& x,
840
                                                      const EC_Scalar& y) const {
841
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
24,715✔
842
}
843

844
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
24,526✔
845
                                                      const EC_Scalar& c,
846
                                                      const EC_Scalar& x,
847
                                                      const EC_Scalar& y) const {
848
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
24,526✔
849
}
850

851
}  // namespace Botan
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