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

randombit / botan / 24014286417

06 Apr 2026 12:48AM UTC coverage: 89.453% (-0.001%) from 89.454%
24014286417

Pull #5521

github

web-flow
Merge 2fb3a6cd7 into 417709dd7
Pull Request #5521: Rollup of small fixes

105877 of 118360 relevant lines covered (89.45%)

11453159.57 hits per line

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

87.95
/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,771✔
32
         const lock_guard_type<mutex_type> lock(m_mutex);
1,771✔
33
         const size_t count = m_registered_curves.size();
1,771✔
34
         m_registered_curves.clear();
1,771✔
35
         return count;
1,771✔
36
      }
1,771✔
37

38
      bool unregister(const OID& oid) {
13✔
39
         // TODO(Botan4)
40
         if(oid.empty()) {
13✔
41
            throw Invalid_Argument("OID must not be empty");
1✔
42
         }
43

44
         const lock_guard_type<mutex_type> lock(m_mutex);
12✔
45
         for(size_t i = 0; i < m_registered_curves.size(); i++) {
110✔
46
            if(m_registered_curves[i]->oid() == oid) {
104✔
47
               m_registered_curves.erase(m_registered_curves.begin() + i);
6✔
48
               return true;
6✔
49
            }
50
         }
51
         return false;
52
      }
12✔
53

54
      std::shared_ptr<EC_Group_Data> lookup(const OID& oid) {
22,559✔
55
         const lock_guard_type<mutex_type> lock(m_mutex);
22,559✔
56

57
         for(auto i : m_registered_curves) {
234,558✔
58
            if(i->oid() == oid) {
233,925✔
59
               return i;
21,926✔
60
            }
61
         }
233,925✔
62

63
         // Not found, check hardcoded data
64
         std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
633✔
65

66
         if(data) {
633✔
67
            m_registered_curves.push_back(data);
615✔
68
            return data;
615✔
69
         }
70

71
         // Nope, unknown curve
72
         return std::shared_ptr<EC_Group_Data>();
18✔
73
      }
23,192✔
74

75
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
44✔
76
                                                      const BigInt& a,
77
                                                      const BigInt& b,
78
                                                      const BigInt& g_x,
79
                                                      const BigInt& g_y,
80
                                                      const BigInt& order,
81
                                                      const BigInt& cofactor,
82
                                                      const OID& oid,
83
                                                      EC_Group_Source source) {
84
         BOTAN_ASSERT_NOMSG(oid.has_value());
44✔
85

86
         const lock_guard_type<mutex_type> lock(m_mutex);
44✔
87

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

103
               return i;
33✔
104
            }
105

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

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

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

140
         m_registered_curves.push_back(new_group);
11✔
141
         return new_group;
11✔
142
      }
55✔
143

144
      std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
69✔
145
                                                        const BigInt& a,
146
                                                        const BigInt& b,
147
                                                        std::span<const uint8_t> base_pt,
148
                                                        const BigInt& order,
149
                                                        const BigInt& cofactor) {
150
         const lock_guard_type<mutex_type> lock(m_mutex);
69✔
151

152
         for(auto i : m_registered_curves) {
834✔
153
            if(i->params_match(p, a, b, base_pt, order, cofactor)) {
817✔
154
               return i;
52✔
155
            }
156
         }
817✔
157

158
         // Try to use the order as a hint to look up the group id
159
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
17✔
160
         if(oid_from_order.has_value()) {
17✔
161
            auto new_group = EC_Group::EC_group_info(oid_from_order);
13✔
162

163
            // Have to check all params in the (unlikely/malicious) event of an order collision
164
            if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) {
13✔
165
               m_registered_curves.push_back(new_group);
4✔
166
               return new_group;
4✔
167
            }
168
         }
13✔
169

170
         return {};
12✔
171
      }
85✔
172

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

184
         for(auto i : m_registered_curves) {
30✔
185
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
28✔
186
               return i;
×
187
            }
188
         }
28✔
189

190
         // Try to use the order as a hint to look up the group id
191
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
2✔
192
         if(oid_from_order.has_value()) {
2✔
193
            auto new_group = EC_Group::EC_group_info(oid_from_order);
1✔
194

195
            // Have to check all params in the (unlikely/malicious) event of an order collision
196
            if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
1✔
197
               m_registered_curves.push_back(new_group);
1✔
198
               return new_group;
1✔
199
            }
200
         }
1✔
201

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

214
   private:
215
      mutex_type m_mutex;
216
      // TODO(Botan4): Once OID is required we could make this into a map
217
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
218
};
219

220
//static
221
EC_Group_Data_Map& EC_Group::ec_group_data() {
24,458✔
222
   /*
223
   * This exists purely to ensure the allocator is constructed before g_ec_data,
224
   * which ensures that its destructor runs after ~g_ec_data is complete.
225
   */
226

227
   static const Allocator_Initializer g_init_allocator;
24,458✔
228
   static EC_Group_Data_Map g_ec_data;
24,458✔
229
   return g_ec_data;
24,458✔
230
}
231

232
//static
233
size_t EC_Group::clear_registered_curve_data() {
1,771✔
234
   return ec_group_data().clear();
1,771✔
235
}
236

237
//static
238
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
629✔
239
                                                            const char* a_str,
240
                                                            const char* b_str,
241
                                                            const char* g_x_str,
242
                                                            const char* g_y_str,
243
                                                            const char* order_str,
244
                                                            const OID& oid) {
245
   BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
629✔
246

247
   const BigInt p(p_str);
629✔
248
   const BigInt a(a_str);
629✔
249
   const BigInt b(b_str);
629✔
250
   const BigInt g_x(g_x_str);
629✔
251
   const BigInt g_y(g_y_str);
629✔
252
   const BigInt order(order_str);
629✔
253
   const BigInt cofactor(1);  // implicit
629✔
254

255
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
1,258✔
256
}
629✔
257

258
//static
259
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
6,803✔
260
                                                                              EC_Group_Source source) {
261
   BER_Decoder ber(bits);
6,803✔
262

263
   auto next_obj_type = ber.peek_next_object().type_tag();
6,803✔
264

265
   if(next_obj_type == ASN1_Type::ObjectId) {
6,803✔
266
      OID oid;
6,490✔
267
      ber.decode(oid);
6,490✔
268

269
      auto data = ec_group_data().lookup(oid);
6,487✔
270
      if(!data) {
6,487✔
271
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
32✔
272
      }
273

274
      return std::make_pair(data, false);
6,471✔
275
   } else if(next_obj_type == ASN1_Type::Sequence) {
6,819✔
276
      BigInt p;
312✔
277
      BigInt a;
312✔
278
      BigInt b;
312✔
279
      BigInt order;
312✔
280
      BigInt cofactor;
312✔
281
      std::vector<uint8_t> base_pt;
312✔
282
      std::vector<uint8_t> seed;
312✔
283

284
      ber.start_sequence()
312✔
285
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
624✔
286
         .start_sequence()
312✔
287
         .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
624✔
288
         .decode(p)
312✔
289
         .end_cons()
312✔
290
         .start_sequence()
312✔
291
         .decode_octet_string_bigint(a)
312✔
292
         .decode_octet_string_bigint(b)
312✔
293
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString)
312✔
294
         .end_cons()
312✔
295
         .decode(base_pt, ASN1_Type::OctetString)
312✔
296
         .decode(order)
312✔
297
         .decode(cofactor)
312✔
298
         .end_cons()
312✔
299
         .verify_end();
312✔
300

301
      // TODO(Botan4) Require cofactor == 1
302
      if(cofactor <= 0 || cofactor >= 16) {
620✔
303
         throw Decoding_Error("Invalid ECC cofactor parameter");
8✔
304
      }
305

306
      if(p.bits() < 112 || p.bits() > 521 || p.signum() < 0) {
304✔
307
         throw Decoding_Error("ECC p parameter is invalid size");
134✔
308
      }
309

310
      // A can be zero
311
      if(a.signum() < 0 || a >= p) {
340✔
312
         throw Decoding_Error("Invalid ECC a parameter");
20✔
313
      }
314

315
      // B must be > 0
316
      if(b.signum() <= 0 || b >= p) {
299✔
317
         throw Decoding_Error("Invalid ECC b parameter");
77✔
318
      }
319

320
      if(order.signum() <= 0 || order >= 2 * p) {
212✔
321
         throw Decoding_Error("Invalid ECC group order");
4✔
322
      }
323

324
      if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
318✔
325
         return std::make_pair(data, true);
56✔
326
      }
56✔
327

328
      /*
329
      TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
330
      which is neither a builtin group nor a group that was registered by the application.
331
      It can all be removed and replaced with a throw
332
      */
333

334
      auto mod_p = Barrett_Reduction::for_public_modulus(p);
12✔
335
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
12✔
336
         throw Decoding_Error("ECC p parameter is not a prime");
3✔
337
      }
338

339
      auto mod_order = Barrett_Reduction::for_public_modulus(order);
9✔
340
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
9✔
341
         throw Decoding_Error("Invalid ECC order parameter");
2✔
342
      }
343

344
      const size_t p_bytes = p.bytes();
7✔
345
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
7✔
346
         throw Decoding_Error("Invalid ECC base point encoding");
×
347
      }
348

349
      auto [g_x, g_y] = [&]() {
10✔
350
         const uint8_t hdr = base_pt[0];
7✔
351

352
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
7✔
353
            const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
7✔
354
            const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes));
7✔
355

356
            if(x < p && y < p) {
7✔
357
               return std::make_pair(x, y);
7✔
358
            }
359
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
7✔
360
            // TODO(Botan4) remove this branch; we won't support compressed points
361
            const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
×
362
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
363

364
            if(x < p && y >= 0) {
×
365
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
366
               if(y.get_bit(0) != y_mod_2) {
×
367
                  y = p - y;
×
368
               }
369

370
               return std::make_pair(x, y);
×
371
            }
372
         }
×
373

374
         throw Decoding_Error("Invalid ECC base point encoding");
×
375
      }();
7✔
376

377
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
378
      auto y2 = mod_p.square(g_y);
7✔
379
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
7✔
380
      if(y2 != x3_ax_b) {
7✔
381
         throw Decoding_Error("Invalid ECC base point");
1✔
382
      }
383

384
      /*
385
      * Create the group data without registering it in the global map.
386
      *
387
      * Applications that need persistent custom groups should register them
388
      * via the relevant EC_Group constructor
389
      */
390
      auto data = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
6✔
391
      return std::make_pair(data, true);
6✔
392
   } else if(next_obj_type == ASN1_Type::Null) {
1,661✔
393
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
×
394
   } else {
395
      throw Decoding_Error(
1✔
396
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
2✔
397
   }
398
}
6,803✔
399

400
EC_Group::EC_Group() = default;
×
401

402
EC_Group::~EC_Group() = default;
113,256✔
403

404
EC_Group::EC_Group(const EC_Group&) = default;
55,351✔
405

406
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
407

408
// Internal constructor
409
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
16,014✔
410

411
//static
412
bool EC_Group::supports_named_group(std::string_view name) {
15,408✔
413
   return EC_Group::known_named_groups().contains(std::string(name));
15,408✔
414
}
415

416
//static
417
bool EC_Group::supports_application_specific_group() {
26✔
418
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
419
   return true;
26✔
420
#else
421
   return false;
422
#endif
423
}
424

425
//static
426
bool EC_Group::supports_application_specific_group_with_cofactor() {
5✔
427
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
428
   return true;
5✔
429
#else
430
   return false;
431
#endif
432
}
433

434
//static
435
EC_Group EC_Group::from_OID(const OID& oid) {
75✔
436
   auto data = ec_group_data().lookup(oid);
75✔
437

438
   if(!data) {
75✔
439
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
4✔
440
   }
441

442
   return EC_Group(std::move(data));
73✔
443
}
73✔
444

445
//static
446
EC_Group EC_Group::from_name(std::string_view name) {
15,942✔
447
   std::shared_ptr<EC_Group_Data> data;
15,942✔
448

449
   if(auto oid = OID::from_name(name)) {
15,942✔
450
      data = ec_group_data().lookup(oid.value());
31,882✔
451
   }
×
452

453
   if(!data) {
15,941✔
454
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
455
   }
456

457
   return EC_Group(std::move(data));
15,941✔
458
}
15,941✔
459

460
EC_Group::EC_Group(std::string_view str) {
56✔
461
   if(str.empty()) {
56✔
462
      return;  // no initialization / uninitialized
463
   }
464

465
   try {
56✔
466
      const OID oid = OID::from_string(str);
56✔
467
      if(oid.has_value()) {
56✔
468
         m_data = ec_group_data().lookup(oid);
56✔
469
      }
470
   } catch(...) {}
56✔
471

472
   if(m_data == nullptr) {
56✔
473
      if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
×
474
         // OK try it as PEM ...
475
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
×
476

477
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
×
478
         this->m_data = data.first;
×
479
         this->m_explicit_encoding = data.second;
×
480
      }
×
481
   }
482

483
   if(m_data == nullptr) {
56✔
484
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
485
   }
486
}
×
487

488
//static
489
EC_Group EC_Group::from_PEM(std::string_view pem) {
4✔
490
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
4✔
491
   return EC_Group(ber);
4✔
492
}
4✔
493

494
EC_Group::EC_Group(const BigInt& p,
4✔
495
                   const BigInt& a,
496
                   const BigInt& b,
497
                   const BigInt& base_x,
498
                   const BigInt& base_y,
499
                   const BigInt& order,
500
                   const BigInt& cofactor,
501
                   const OID& oid) {
4✔
502
   if(oid.has_value()) {
4✔
503
      m_data = ec_group_data().lookup_or_create(
4✔
504
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
505
   } else {
506
      m_data = ec_group_data().lookup_or_create_without_oid(
4✔
507
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
2✔
508
   }
509
}
4✔
510

511
EC_Group::EC_Group(const OID& oid,
51✔
512
                   const BigInt& p,
513
                   const BigInt& a,
514
                   const BigInt& b,
515
                   const BigInt& base_x,
516
                   const BigInt& base_y,
517
                   const BigInt& order) {
51✔
518
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
51✔
519

520
   // TODO(Botan4) remove this and require 192 bits minimum
521
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
522
   constexpr size_t p_bits_lower_bound = 192;
523
#else
524
   constexpr size_t p_bits_lower_bound = 128;
51✔
525
#endif
526

527
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
51✔
528
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
51✔
529

530
   if(p.bits() == 521) {
51✔
531
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
532
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
533
   } else if(p.bits() == 239) {
51✔
534
      const auto x962_p239 = []() {
18✔
535
         BigInt p239;
3✔
536
         for(size_t i = 0; i != 239; ++i) {
720✔
537
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
538
               p239.set_bit(i);
717✔
539
            }
540
         }
541
         return p239;
3✔
542
      }();
3✔
543

544
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
545
   } else {
3✔
546
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
47✔
547
   }
548

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

551
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
92✔
552
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
92✔
553
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
92✔
554
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
92✔
555
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
46✔
556

557
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
43✔
558
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
43✔
559

560
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
43✔
561
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
43✔
562

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

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

572
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
573
   auto y2 = mod_p.square(base_y);
43✔
574
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
43✔
575
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
43✔
576

577
   const BigInt cofactor(1);
42✔
578

579
   m_data =
42✔
580
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
42✔
581
}
139✔
582

583
EC_Group::EC_Group(std::span<const uint8_t> ber) {
6,803✔
584
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
6,803✔
585
   m_data = data.first;
6,533✔
586
   m_explicit_encoding = data.second;
6,533✔
587
}
6,803✔
588

589
// static
590
bool EC_Group::unregister(const OID& oid) {
13✔
591
   return ec_group_data().unregister(oid);
13✔
592
}
593

594
const EC_Group_Data& EC_Group::data() const {
137,406✔
595
   if(m_data == nullptr) {
137,406✔
596
      throw Invalid_State("EC_Group uninitialized");
×
597
   }
598
   return *m_data;
137,406✔
599
}
600

601
size_t EC_Group::get_p_bits() const {
2,351✔
602
   return data().p_bits();
2,351✔
603
}
604

605
size_t EC_Group::get_p_bytes() const {
14,390✔
606
   return data().p_bytes();
14,390✔
607
}
608

609
size_t EC_Group::get_order_bits() const {
2,798✔
610
   return data().order_bits();
2,798✔
611
}
612

613
size_t EC_Group::get_order_bytes() const {
35,753✔
614
   return data().order_bytes();
35,753✔
615
}
616

617
const BigInt& EC_Group::get_p() const {
27,852✔
618
   return data().p();
27,852✔
619
}
620

621
const BigInt& EC_Group::get_a() const {
557✔
622
   return data().a();
557✔
623
}
624

625
const BigInt& EC_Group::get_b() const {
445✔
626
   return data().b();
445✔
627
}
628

629
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
630
const EC_Point& EC_Group::get_base_point() const {
678✔
631
   return data().base_point();
678✔
632
}
633

634
const EC_Point& EC_Group::generator() const {
×
635
   return data().base_point();
×
636
}
637

638
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
639
   //check that public point is not at infinity
640
   if(point.is_zero()) {
×
641
      return false;
642
   }
643

644
   //check that public point is on the curve
645
   if(point.on_the_curve() == false) {
×
646
      return false;
647
   }
648

649
   //check that public point has order q
650
   if((point * get_order()).is_zero() == false) {
×
651
      return false;
652
   }
653

654
   if(has_cofactor()) {
×
655
      if((point * get_cofactor()).is_zero()) {
×
656
         return false;
657
      }
658
   }
659

660
   return true;
661
}
662

663
#endif
664

665
const BigInt& EC_Group::get_order() const {
7,930✔
666
   return data().order();
7,930✔
667
}
668

669
const BigInt& EC_Group::get_g_x() const {
1,670✔
670
   return data().g_x();
1,670✔
671
}
672

673
const BigInt& EC_Group::get_g_y() const {
1,670✔
674
   return data().g_y();
1,670✔
675
}
676

677
const BigInt& EC_Group::get_cofactor() const {
165✔
678
   return data().cofactor();
165✔
679
}
680

681
bool EC_Group::has_cofactor() const {
11,911✔
682
   return data().has_cofactor();
11,911✔
683
}
684

685
const OID& EC_Group::get_curve_oid() const {
26,319✔
686
   return data().oid();
26,319✔
687
}
688

689
EC_Group_Source EC_Group::source() const {
190✔
690
   return data().source();
190✔
691
}
692

693
EC_Group_Engine EC_Group::engine() const {
2✔
694
   return data().engine();
2✔
695
}
696

697
std::vector<uint8_t> EC_Group::DER_encode() const {
2,725✔
698
   const auto& der_named_curve = data().der_named_curve();
2,725✔
699
   // TODO(Botan4) this can be removed because an OID will always be defined
700
   if(der_named_curve.empty()) {
2,725✔
701
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
702
   }
703

704
   return der_named_curve;
2,725✔
705
}
706

707
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
2,720✔
708
   if(form == EC_Group_Encoding::Explicit) {
2,720✔
709
      std::vector<uint8_t> output;
30✔
710
      DER_Encoder der(output);
30✔
711
      const size_t ecpVers1 = 1;
30✔
712
      const OID curve_type("1.2.840.10045.1.1");  // prime field
30✔
713

714
      const size_t p_bytes = get_p_bytes();
30✔
715

716
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
30✔
717

718
      der.start_sequence()
30✔
719
         .encode(ecpVers1)
30✔
720
         .start_sequence()
30✔
721
         .encode(curve_type)
30✔
722
         .encode(get_p())
30✔
723
         .end_cons()
30✔
724
         .start_sequence()
30✔
725
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
30✔
726
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
60✔
727
         .end_cons()
30✔
728
         .encode(generator, ASN1_Type::OctetString)
30✔
729
         .encode(get_order())
30✔
730
         .encode(get_cofactor())
30✔
731
         .end_cons();
30✔
732
      return output;
30✔
733
   } else if(form == EC_Group_Encoding::NamedCurve) {
2,720✔
734
      return this->DER_encode();
2,690✔
735
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
736
      return {0x00, 0x05};
×
737
   } else {
738
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
739
   }
740
}
741

742
std::string EC_Group::PEM_encode(EC_Group_Encoding form) const {
3✔
743
   const std::vector<uint8_t> der = DER_encode(form);
3✔
744
   return PEM_Code::encode(der, "EC PARAMETERS");
3✔
745
}
3✔
746

747
bool EC_Group::operator==(const EC_Group& other) const {
58✔
748
   if(m_data == other.m_data) {
58✔
749
      return true;  // same shared rep
750
   }
751

752
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
9✔
753
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
9✔
754
           get_cofactor() == other.get_cofactor());
4✔
755
}
756

757
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
190✔
758
   const bool is_builtin = source() == EC_Group_Source::Builtin;
190✔
759

760
   if(is_builtin && !strong) {
190✔
761
      return true;
762
   }
763

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

768
   const BigInt& p = get_p();
29✔
769
   const BigInt& a = get_a();
29✔
770
   const BigInt& b = get_b();
29✔
771
   const BigInt& order = get_order();
29✔
772

773
   if(p <= 3 || order <= 0) {
58✔
774
      return false;
×
775
   }
776
   if(a < 0 || a >= p) {
58✔
777
      return false;
×
778
   }
779
   if(b <= 0 || b >= p) {
58✔
780
      return false;
×
781
   }
782

783
   const size_t test_prob = 128;
29✔
784
   const bool is_randomly_generated = is_builtin;
29✔
785

786
   //check if field modulus is prime
787
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
29✔
788
      return false;
789
   }
790

791
   //check if order is prime
792
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
29✔
793
      return false;
794
   }
795

796
   //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
797
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
29✔
798

799
   const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
58✔
800
                                            mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
87✔
801

802
   if(discriminant == 0) {
29✔
803
      return false;
804
   }
805

806
   //check for valid cofactor
807
   if(get_cofactor() < 1) {
29✔
808
      return false;
809
   }
810

811
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
812
   const EC_Point& base_point = get_base_point();
29✔
813
   //check if the base point is on the curve
814
   if(!base_point.on_the_curve()) {
29✔
815
      return false;
816
   }
817
   if((base_point * get_cofactor()).is_zero()) {
58✔
818
      return false;
819
   }
820
   //check if order of the base point is correct
821
   if(!(base_point * order).is_zero()) {
29✔
822
      return false;
823
   }
824
#endif
825

826
   // check the Hasse bound (roughly)
827
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
29✔
828
      return false;
829
   }
830

831
   return true;
832
}
58✔
833

834
EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
×
835

836
EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default;
×
837

838
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
13,315✔
839

840
EC_Group::Mul2Table::~Mul2Table() = default;
13,315✔
841

842
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,434✔
843
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,434✔
844
   if(pt) {
2,434✔
845
      return EC_AffinePoint::_from_inner(std::move(pt));
4,868✔
846
   } else {
847
      return {};
×
848
   }
849
}
2,434✔
850

851
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
32,930✔
852
                                                      const EC_Scalar& x,
853
                                                      const EC_Scalar& y) const {
854
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
32,930✔
855
}
856

857
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
32,533✔
858
                                                      const EC_Scalar& c,
859
                                                      const EC_Scalar& x,
860
                                                      const EC_Scalar& y) const {
861
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
32,533✔
862
}
863

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