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

randombit / botan / 22219971043

20 Feb 2026 10:10AM UTC coverage: 90.331% (+0.001%) from 90.33%
22219971043

Pull #5356

github

web-flow
Merge fc5a2e175 into 2cf293ee6
Pull Request #5356: Refactor: Generalize the SEC.1 decoder

103030 of 114058 relevant lines covered (90.33%)

11449806.04 hits per line

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

87.41
/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/ec_sec1.h>
22
#include <botan/internal/fmt.h>
23
#include <botan/internal/primality.h>
24
#include <botan/internal/stl_util.h>
25
#include <vector>
26

27
namespace Botan {
28

29
class EC_Group_Data_Map final {
30
   public:
31
      EC_Group_Data_Map() = default;
32

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

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

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

56
      std::shared_ptr<EC_Group_Data> lookup(const OID& oid) {
25,116✔
57
         const lock_guard_type<mutex_type> lock(m_mutex);
25,116✔
58

59
         for(auto i : m_registered_curves) {
238,565✔
60
            if(i->oid() == oid) {
237,365✔
61
               return i;
23,916✔
62
            }
63
         }
237,365✔
64

65
         // Not found, check hardcoded data
66
         std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
1,200✔
67

68
         if(data) {
1,200✔
69
            m_registered_curves.push_back(data);
1,138✔
70
            return data;
1,138✔
71
         }
72

73
         // Nope, unknown curve
74
         return std::shared_ptr<EC_Group_Data>();
62✔
75
      }
26,316✔
76

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

88
         const lock_guard_type<mutex_type> lock(m_mutex);
44✔
89

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

105
               return i;
33✔
106
            }
107

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

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

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

142
         m_registered_curves.push_back(new_group);
11✔
143
         return new_group;
11✔
144
      }
55✔
145

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

154
         for(auto i : m_registered_curves) {
867✔
155
            if(i->params_match(p, a, b, base_pt, order, cofactor)) {
851✔
156
               return i;
53✔
157
            }
158
         }
851✔
159

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

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

172
         return {};
11✔
173
      }
84✔
174

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

186
         for(auto i : m_registered_curves) {
98✔
187
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
91✔
188
               return i;
×
189
            }
190
         }
91✔
191

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

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

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

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

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

229
   static const Allocator_Initializer g_init_allocator;
27,020✔
230
   static EC_Group_Data_Map g_ec_data;
27,020✔
231
   return g_ec_data;
27,020✔
232
}
233

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

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

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

257
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
2,308✔
258
}
1,154✔
259

260
//static
261
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
9,535✔
262
                                                                              EC_Group_Source source) {
263
   BER_Decoder ber(bits);
9,535✔
264

265
   auto next_obj_type = ber.peek_next_object().type_tag();
9,535✔
266

267
   if(next_obj_type == ASN1_Type::ObjectId) {
9,527✔
268
      OID oid;
9,173✔
269
      ber.decode(oid);
9,173✔
270

271
      auto data = ec_group_data().lookup(oid);
9,131✔
272
      if(!data) {
9,131✔
273
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
120✔
274
      }
275

276
      return std::make_pair(data, false);
9,071✔
277
   } else if(next_obj_type == ASN1_Type::Sequence) {
9,587✔
278
      BigInt p;
350✔
279
      BigInt a;
350✔
280
      BigInt b;
350✔
281
      BigInt order;
350✔
282
      BigInt cofactor;
350✔
283
      std::vector<uint8_t> base_pt;
350✔
284
      std::vector<uint8_t> seed;
350✔
285

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

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

308
      if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) {
304✔
309
         throw Decoding_Error("ECC p parameter is invalid size");
134✔
310
      }
311

312
      if(a.is_negative() || a >= p) {
340✔
313
         throw Decoding_Error("Invalid ECC a parameter");
20✔
314
      }
315

316
      if(b <= 0 || b >= p) {
299✔
317
         throw Decoding_Error("Invalid ECC b parameter");
77✔
318
      }
319

320
      if(order.is_negative() || order.is_zero() || order >= 2 * p) {
286✔
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)) {
356✔
325
         return std::make_pair(data, true);
57✔
326
      }
57✔
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);
11✔
335
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
11✔
336
         throw Decoding_Error("ECC p parameter is not a prime");
3✔
337
      }
338

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

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

349
      auto [g_x, g_y] = [&] {
9✔
350
         auto base_pt_components =
6✔
351
            sec1_decode(base_pt,
352
                        p_bytes,
353
                        overloaded{
354
                           [](const SEC1_Identity) -> std::pair<BigInt, BigInt> {
×
355
                              throw Decoding_Error("Identity is not a valid base point");
×
356
                           },  //
357
                           [&](const SEC1_Compressed compressed) -> std::optional<std::pair<BigInt, BigInt>> {
×
358
                              BigInt x = BigInt::from_bytes(compressed.x);
×
359
                              BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
360

361
                              if(x < p && y >= 0) {
×
362
                                 if(y.is_even() != compressed.y_is_even.as_bool()) {
×
363
                                    y = p - y;
×
364
                                 }
365

366
                                 return std::pair{std::move(x), std::move(y)};
×
367
                              }
368

369
                              return {};
×
370
                           },
×
371
                           [&](const SEC1_Full full) -> std::optional<std::pair<BigInt, BigInt>> {
6✔
372
                              auto xy = std::pair{BigInt::from_bytes(full.x), BigInt::from_bytes(full.y)};
6✔
373

374
                              if(xy.first < p && xy.second < p) {
6✔
375
                                 return xy;
6✔
376
                              }
377

378
                              return {};
×
379
                           },
6✔
380
                        });
6✔
381

382
         if(!base_pt_components.has_value()) {
6✔
383
            throw Decoding_Error("Invalid ECC base point encoding");
×
384
         }
385

386
         return std::move(base_pt_components).value();
6✔
387
      }();
12✔
388

389
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
390
      auto y2 = mod_p.square(g_y);
6✔
391
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
6✔
392
      if(y2 != x3_ax_b) {
6✔
393
         throw Decoding_Error("Invalid ECC base point");
1✔
394
      }
395

396
      auto data = ec_group_data().lookup_or_create_without_oid(p, a, b, g_x, g_y, order, cofactor, source);
5✔
397
      return std::make_pair(data, true);
5✔
398
   } else if(next_obj_type == ASN1_Type::Null) {
1,888✔
399
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
1✔
400
   } else {
401
      throw Decoding_Error(
3✔
402
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
6✔
403
   }
404
}
9,535✔
405

406
EC_Group::EC_Group() = default;
×
407

408
EC_Group::~EC_Group() = default;
122,261✔
409

410
EC_Group::EC_Group(const EC_Group&) = default;
59,476✔
411

412
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
413

414
// Internal constructor
415
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,934✔
416

417
//static
418
bool EC_Group::supports_named_group(std::string_view name) {
15,381✔
419
   return EC_Group::known_named_groups().contains(std::string(name));
15,381✔
420
}
421

422
//static
423
bool EC_Group::supports_application_specific_group() {
26✔
424
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
425
   return true;
26✔
426
#else
427
   return false;
428
#endif
429
}
430

431
//static
432
bool EC_Group::supports_application_specific_group_with_cofactor() {
5✔
433
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
434
   return true;
5✔
435
#else
436
   return false;
437
#endif
438
}
439

440
//static
441
EC_Group EC_Group::from_OID(const OID& oid) {
77✔
442
   auto data = ec_group_data().lookup(oid);
77✔
443

444
   if(!data) {
77✔
445
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
4✔
446
   }
447

448
   return EC_Group(std::move(data));
75✔
449
}
75✔
450

451
//static
452
EC_Group EC_Group::from_name(std::string_view name) {
15,860✔
453
   std::shared_ptr<EC_Group_Data> data;
15,860✔
454

455
   if(auto oid = OID::from_name(name)) {
15,860✔
456
      data = ec_group_data().lookup(oid.value());
31,718✔
457
   }
×
458

459
   if(!data) {
15,859✔
460
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
461
   }
462

463
   return EC_Group(std::move(data));
15,859✔
464
}
15,859✔
465

466
EC_Group::EC_Group(std::string_view str) {
49✔
467
   if(str.empty()) {
49✔
468
      return;  // no initialization / uninitialized
469
   }
470

471
   try {
49✔
472
      const OID oid = OID::from_string(str);
49✔
473
      if(oid.has_value()) {
49✔
474
         m_data = ec_group_data().lookup(oid);
49✔
475
      }
476
   } catch(...) {}
49✔
477

478
   if(m_data == nullptr) {
49✔
479
      if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
×
480
         // OK try it as PEM ...
481
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
×
482

483
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
×
484
         this->m_data = data.first;
×
485
         this->m_explicit_encoding = data.second;
×
486
      }
×
487
   }
488

489
   if(m_data == nullptr) {
49✔
490
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
491
   }
492
}
×
493

494
//static
495
EC_Group EC_Group::from_PEM(std::string_view pem) {
4✔
496
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
4✔
497
   return EC_Group(ber);
4✔
498
}
4✔
499

500
EC_Group::EC_Group(const BigInt& p,
4✔
501
                   const BigInt& a,
502
                   const BigInt& b,
503
                   const BigInt& base_x,
504
                   const BigInt& base_y,
505
                   const BigInt& order,
506
                   const BigInt& cofactor,
507
                   const OID& oid) {
4✔
508
   if(oid.has_value()) {
4✔
509
      m_data = ec_group_data().lookup_or_create(
4✔
510
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
511
   } else {
512
      m_data = ec_group_data().lookup_or_create_without_oid(
4✔
513
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
2✔
514
   }
515
}
4✔
516

517
EC_Group::EC_Group(const OID& oid,
51✔
518
                   const BigInt& p,
519
                   const BigInt& a,
520
                   const BigInt& b,
521
                   const BigInt& base_x,
522
                   const BigInt& base_y,
523
                   const BigInt& order) {
51✔
524
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
51✔
525

526
   // TODO(Botan4) remove this and require 192 bits minimum
527
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
528
   constexpr size_t p_bits_lower_bound = 192;
529
#else
530
   constexpr size_t p_bits_lower_bound = 128;
51✔
531
#endif
532

533
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
51✔
534
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
51✔
535

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

550
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
551
   } else {
3✔
552
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
47✔
553
   }
554

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

557
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
92✔
558
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
92✔
559
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
92✔
560
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
92✔
561
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
46✔
562

563
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
43✔
564
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
43✔
565

566
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
43✔
567
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
43✔
568

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

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

578
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
579
   auto y2 = mod_p.square(base_y);
43✔
580
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
43✔
581
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
43✔
582

583
   const BigInt cofactor(1);
42✔
584

585
   m_data =
42✔
586
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
42✔
587
}
139✔
588

589
EC_Group::EC_Group(std::span<const uint8_t> ber) {
9,535✔
590
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
9,535✔
591
   m_data = data.first;
9,133✔
592
   m_explicit_encoding = data.second;
9,133✔
593
}
9,535✔
594

595
// static
596
bool EC_Group::unregister(const OID& oid) {
13✔
597
   return ec_group_data().unregister(oid);
13✔
598
}
599

600
const EC_Group_Data& EC_Group::data() const {
138,121✔
601
   if(m_data == nullptr) {
138,121✔
602
      throw Invalid_State("EC_Group uninitialized");
×
603
   }
604
   return *m_data;
138,121✔
605
}
606

607
size_t EC_Group::get_p_bits() const {
1,630✔
608
   return data().p_bits();
1,630✔
609
}
610

611
size_t EC_Group::get_p_bytes() const {
13,726✔
612
   return data().p_bytes();
13,726✔
613
}
614

615
size_t EC_Group::get_order_bits() const {
2,789✔
616
   return data().order_bits();
2,789✔
617
}
618

619
size_t EC_Group::get_order_bytes() const {
38,275✔
620
   return data().order_bytes();
38,275✔
621
}
622

623
const BigInt& EC_Group::get_p() const {
26,583✔
624
   return data().p();
26,583✔
625
}
626

627
const BigInt& EC_Group::get_a() const {
570✔
628
   return data().a();
570✔
629
}
630

631
const BigInt& EC_Group::get_b() const {
458✔
632
   return data().b();
458✔
633
}
634

635
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
636
const EC_Point& EC_Group::get_base_point() const {
678✔
637
   return data().base_point();
678✔
638
}
639

640
const EC_Point& EC_Group::generator() const {
×
641
   return data().base_point();
×
642
}
643

644
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
645
   //check that public point is not at infinity
646
   if(point.is_zero()) {
×
647
      return false;
648
   }
649

650
   //check that public point is on the curve
651
   if(point.on_the_curve() == false) {
×
652
      return false;
653
   }
654

655
   //check that public point has order q
656
   if((point * get_order()).is_zero() == false) {
×
657
      return false;
658
   }
659

660
   if(has_cofactor()) {
×
661
      if((point * get_cofactor()).is_zero()) {
×
662
         return false;
663
      }
664
   }
665

666
   return true;
667
}
668

669
#endif
670

671
const BigInt& EC_Group::get_order() const {
7,924✔
672
   return data().order();
7,924✔
673
}
674

675
const BigInt& EC_Group::get_g_x() const {
1,071✔
676
   return data().g_x();
1,071✔
677
}
678

679
const BigInt& EC_Group::get_g_y() const {
1,071✔
680
   return data().g_y();
1,071✔
681
}
682

683
const BigInt& EC_Group::get_cofactor() const {
165✔
684
   return data().cofactor();
165✔
685
}
686

687
bool EC_Group::has_cofactor() const {
11,903✔
688
   return data().has_cofactor();
11,903✔
689
}
690

691
const OID& EC_Group::get_curve_oid() const {
28,361✔
692
   return data().oid();
28,361✔
693
}
694

695
EC_Group_Source EC_Group::source() const {
190✔
696
   return data().source();
190✔
697
}
698

699
EC_Group_Engine EC_Group::engine() const {
2✔
700
   return data().engine();
2✔
701
}
702

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

710
   return der_named_curve;
2,725✔
711
}
712

713
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
2,720✔
714
   if(form == EC_Group_Encoding::Explicit) {
2,720✔
715
      std::vector<uint8_t> output;
30✔
716
      DER_Encoder der(output);
30✔
717
      const size_t ecpVers1 = 1;
30✔
718
      const OID curve_type("1.2.840.10045.1.1");  // prime field
30✔
719

720
      const size_t p_bytes = get_p_bytes();
30✔
721

722
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
30✔
723

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

748
std::string EC_Group::PEM_encode(EC_Group_Encoding form) const {
3✔
749
   const std::vector<uint8_t> der = DER_encode(form);
3✔
750
   return PEM_Code::encode(der, "EC PARAMETERS");
3✔
751
}
3✔
752

753
bool EC_Group::operator==(const EC_Group& other) const {
58✔
754
   if(m_data == other.m_data) {
58✔
755
      return true;  // same shared rep
756
   }
757

758
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
9✔
759
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
9✔
760
           get_cofactor() == other.get_cofactor());
4✔
761
}
762

763
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
190✔
764
   const bool is_builtin = source() == EC_Group_Source::Builtin;
190✔
765

766
   if(is_builtin && !strong) {
190✔
767
      return true;
768
   }
769

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

774
   const BigInt& p = get_p();
29✔
775
   const BigInt& a = get_a();
29✔
776
   const BigInt& b = get_b();
29✔
777
   const BigInt& order = get_order();
29✔
778

779
   if(p <= 3 || order <= 0) {
58✔
780
      return false;
×
781
   }
782
   if(a < 0 || a >= p) {
58✔
783
      return false;
×
784
   }
785
   if(b <= 0 || b >= p) {
58✔
786
      return false;
×
787
   }
788

789
   const size_t test_prob = 128;
29✔
790
   const bool is_randomly_generated = is_builtin;
29✔
791

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

797
   //check if order is prime
798
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
29✔
799
      return false;
800
   }
801

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

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

808
   if(discriminant == 0) {
29✔
809
      return false;
810
   }
811

812
   //check for valid cofactor
813
   if(get_cofactor() < 1) {
29✔
814
      return false;
815
   }
816

817
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
818
   const EC_Point& base_point = get_base_point();
29✔
819
   //check if the base point is on the curve
820
   if(!base_point.on_the_curve()) {
29✔
821
      return false;
822
   }
823
   if((base_point * get_cofactor()).is_zero()) {
58✔
824
      return false;
825
   }
826
   //check if order of the base point is correct
827
   if(!(base_point * order).is_zero()) {
29✔
828
      return false;
829
   }
830
#endif
831

832
   // check the Hasse bound (roughly)
833
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
29✔
834
      return false;
835
   }
836

837
   return true;
838
}
58✔
839

840
EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
×
841

842
EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default;
×
843

844
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
15,098✔
845

846
EC_Group::Mul2Table::~Mul2Table() = default;
15,098✔
847

848
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,167✔
849
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,167✔
850
   if(pt) {
2,167✔
851
      return EC_AffinePoint::_from_inner(std::move(pt));
4,334✔
852
   } else {
853
      return {};
×
854
   }
855
}
2,167✔
856

857
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
24,855✔
858
                                                      const EC_Scalar& x,
859
                                                      const EC_Scalar& y) const {
860
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
24,855✔
861
}
862

863
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
24,627✔
864
                                                      const EC_Scalar& c,
865
                                                      const EC_Scalar& x,
866
                                                      const EC_Scalar& y) const {
867
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
24,627✔
868
}
869

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