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

randombit / botan / 25650639339

10 May 2026 07:03PM UTC coverage: 89.326% (-0.002%) from 89.328%
25650639339

push

github

web-flow
Merge pull request #5592 from randombit/jack/bn-hardening

Various BigInt/mp related hardenings and bug fixes

107853 of 120741 relevant lines covered (89.33%)

11294230.95 hits per line

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

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

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

44
         const lock_guard_type<mutex_type> lock(m_mutex);
13✔
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);
7✔
48
               return true;
7✔
49
            }
50
         }
51
         return false;
52
      }
13✔
53

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

57
         for(auto i : m_registered_curves) {
235,077✔
58
            if(i->oid() == oid) {
234,411✔
59
               return i;
22,240✔
60
            }
61
         }
234,411✔
62

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

66
         if(data) {
666✔
67
            // The requested OID may be an alias for a curve whose canonical OID differs
68
            // TODO(Botan4) remove this once we require exactly one canonical OID per curve
69
            if(data->oid() != oid) {
653✔
70
               for(const auto& i : m_registered_curves) {
70✔
71
                  if(i->oid() == data->oid()) {
68✔
72
                     return i;
40✔
73
                  }
74
               }
75
            }
76

77
            m_registered_curves.push_back(data);
613✔
78
            return data;
613✔
79
         }
80

81
         // Nope, unknown curve
82
         return std::shared_ptr<EC_Group_Data>();
13✔
83
      }
23,572✔
84

85
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
44✔
86
                                                      const BigInt& a,
87
                                                      const BigInt& b,
88
                                                      const BigInt& g_x,
89
                                                      const BigInt& g_y,
90
                                                      const BigInt& order,
91
                                                      const BigInt& cofactor,
92
                                                      const OID& oid,
93
                                                      EC_Group_Source source) {
94
         BOTAN_ASSERT_NOMSG(oid.has_value());
44✔
95

96
         const lock_guard_type<mutex_type> lock(m_mutex);
44✔
97

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

113
               return i;
33✔
114
            }
115

116
            /*
117
            * If the same curve was previously created without an OID but is now
118
            * being registered again using an OID, save that OID.
119
            *
120
            * TODO(Botan4) remove this block; this situation won't be possible since
121
            * we will require all groups to have an OID
122
            */
123
            if(i->oid().empty() && i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
791✔
124
               i->set_oid(oid);
×
125
               return i;
×
126
            }
127
         }
824✔
128

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

141
               return g;
×
142
            } else {
143
               /*
144
               * This path is taken for an application registering a new EC_Group with an OID specified
145
               */
146
               return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
11✔
147
            }
11✔
148
         }();
11✔
149

150
         m_registered_curves.push_back(new_group);
11✔
151
         return new_group;
11✔
152
      }
55✔
153

154
      std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
68✔
155
                                                        const BigInt& a,
156
                                                        const BigInt& b,
157
                                                        std::span<const uint8_t> base_pt,
158
                                                        const BigInt& order,
159
                                                        const BigInt& cofactor) {
160
         const lock_guard_type<mutex_type> lock(m_mutex);
68✔
161

162
         for(auto i : m_registered_curves) {
830✔
163
            if(i->params_match(p, a, b, base_pt, order, cofactor)) {
814✔
164
               return i;
52✔
165
            }
166
         }
814✔
167

168
         // Try to use the order as a hint to look up the group id
169
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
16✔
170
         if(oid_from_order.has_value()) {
16✔
171
            auto new_group = EC_Group::EC_group_info(oid_from_order);
12✔
172

173
            // Have to check all params in the (unlikely/malicious) event of an order collision
174
            if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) {
12✔
175
               m_registered_curves.push_back(new_group);
4✔
176
               return new_group;
4✔
177
            }
178
         }
12✔
179

180
         return {};
11✔
181
      }
83✔
182

183
      // TODO(Botan4) this entire function can be removed since OIDs will be required
184
      std::shared_ptr<EC_Group_Data> lookup_or_create_without_oid(const BigInt& p,
2✔
185
                                                                  const BigInt& a,
186
                                                                  const BigInt& b,
187
                                                                  const BigInt& g_x,
188
                                                                  const BigInt& g_y,
189
                                                                  const BigInt& order,
190
                                                                  const BigInt& cofactor,
191
                                                                  EC_Group_Source source) {
192
         const lock_guard_type<mutex_type> lock(m_mutex);
2✔
193

194
         for(auto i : m_registered_curves) {
30✔
195
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
28✔
196
               return i;
×
197
            }
198
         }
28✔
199

200
         // Try to use the order as a hint to look up the group id
201
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
2✔
202
         if(oid_from_order.has_value()) {
2✔
203
            auto new_group = EC_Group::EC_group_info(oid_from_order);
1✔
204

205
            // Have to check all params in the (unlikely/malicious) event of an order collision
206
            if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
1✔
207
               m_registered_curves.push_back(new_group);
1✔
208
               return new_group;
1✔
209
            }
210
         }
1✔
211

212
         /*
213
         * At this point we have failed to identify the group; it is not any of
214
         * the builtin values, nor is it a group that the user had previously
215
         * registered explicitly. We create the group data without an OID.
216
         *
217
         * TODO(Botan4) remove this; throw an exception instead
218
         */
219
         auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
1✔
220
         m_registered_curves.push_back(new_group);
1✔
221
         return new_group;
1✔
222
      }
5✔
223

224
   private:
225
      mutex_type m_mutex;
226
      // TODO(Botan4): Once OID is required we could make this into a map
227
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
228
};
229

230
//static
231
EC_Group_Data_Map& EC_Group::ec_group_data() {
24,810✔
232
   /*
233
   * This exists purely to ensure the allocator is constructed before g_ec_data,
234
   * which ensures that its destructor runs after ~g_ec_data is complete.
235
   */
236

237
   static const Allocator_Initializer g_init_allocator;
24,810✔
238
   static EC_Group_Data_Map g_ec_data;
24,810✔
239
   return g_ec_data;
24,810✔
240
}
241

242
//static
243
size_t EC_Group::clear_registered_curve_data() {
1,776✔
244
   return ec_group_data().clear();
1,776✔
245
}
246

247
//static
248
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
666✔
249
                                                            const char* a_str,
250
                                                            const char* b_str,
251
                                                            const char* g_x_str,
252
                                                            const char* g_y_str,
253
                                                            const char* order_str,
254
                                                            const OID& oid) {
255
   BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
666✔
256

257
   const BigInt p(p_str);
666✔
258
   const BigInt a(a_str);
666✔
259
   const BigInt b(b_str);
666✔
260
   const BigInt g_x(g_x_str);
666✔
261
   const BigInt g_y(g_y_str);
666✔
262
   const BigInt order(order_str);
666✔
263
   const BigInt cofactor(1);  // implicit
666✔
264

265
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
1,332✔
266
}
666✔
267

268
//static
269
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::DER_decode_EC_group(std::span<const uint8_t> der,
6,947✔
270
                                                                              EC_Group_Source source) {
271
   BER_Decoder dec(der, BER_Decoder::Limits::DER());
6,947✔
272

273
   auto next_obj_type = dec.peek_next_object().type_tag();
6,947✔
274

275
   if(next_obj_type == ASN1_Type::ObjectId) {
6,947✔
276
      OID oid;
6,805✔
277
      dec.decode(oid);
6,805✔
278

279
      auto data = ec_group_data().lookup(oid);
6,805✔
280
      if(!data) {
6,805✔
281
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
18✔
282
      }
283

284
      return std::make_pair(data, false);
6,796✔
285
   } else if(next_obj_type == ASN1_Type::Sequence) {
6,956✔
286
      BigInt p;
141✔
287
      BigInt a;
141✔
288
      BigInt b;
141✔
289
      BigInt order;
141✔
290
      BigInt cofactor;
141✔
291
      std::vector<uint8_t> base_pt;
141✔
292
      std::vector<uint8_t> seed;
141✔
293

294
      dec.start_sequence()
141✔
295
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
282✔
296
         .start_sequence()
141✔
297
         .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
282✔
298
         .decode(p)
112✔
299
         .end_cons()
112✔
300
         .start_sequence()
112✔
301
         .decode_octet_string_bigint(a)
112✔
302
         .decode_octet_string_bigint(b)
112✔
303
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString, ASN1_Class::Universal)
94✔
304
         .end_cons()
94✔
305
         .decode(base_pt, ASN1_Type::OctetString)
94✔
306
         .decode(order)
92✔
307
         .decode(cofactor)
91✔
308
         .end_cons()
91✔
309
         .verify_end();
91✔
310

311
      // TODO(Botan4) Require cofactor == 1
312
      if(cofactor <= 0 || cofactor >= 16) {
180✔
313
         throw Decoding_Error("Invalid ECC cofactor parameter");
6✔
314
      }
315

316
      if(p.bits() < 112 || p.bits() > 521 || p.signum() < 0) {
85✔
317
         throw Decoding_Error("ECC p parameter is invalid size");
×
318
      }
319

320
      // A can be zero
321
      if(a.signum() < 0 || a >= p) {
170✔
322
         throw Decoding_Error("Invalid ECC a parameter");
4✔
323
      }
324

325
      // B must be > 0
326
      if(b.signum() <= 0 || b >= p) {
161✔
327
         throw Decoding_Error("Invalid ECC b parameter");
10✔
328
      }
329

330
      if(order.signum() <= 0 || order >= 2 * p) {
208✔
331
         throw Decoding_Error("Invalid ECC group order");
3✔
332
      }
333

334
      if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
146✔
335
         return std::make_pair(data, true);
56✔
336
      }
56✔
337

338
      /*
339
      TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
340
      which is neither a builtin group nor a group that was registered by the application.
341
      It can all be removed and replaced with a throw
342
      */
343

344
      auto mod_p = Barrett_Reduction::for_public_modulus(p);
11✔
345
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
11✔
346
         throw Decoding_Error("ECC p parameter is not a prime");
2✔
347
      }
348

349
      auto mod_order = Barrett_Reduction::for_public_modulus(order);
9✔
350
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
9✔
351
         throw Decoding_Error("Invalid ECC order parameter");
2✔
352
      }
353

354
      const size_t p_bytes = p.bytes();
7✔
355
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
7✔
356
         throw Decoding_Error("Invalid ECC base point encoding");
×
357
      }
358

359
      auto [g_x, g_y] = [&]() {
10✔
360
         const uint8_t hdr = base_pt[0];
7✔
361

362
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
7✔
363
            const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
7✔
364
            const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes));
7✔
365

366
            if(x < p && y < p) {
7✔
367
               return std::make_pair(x, y);
7✔
368
            }
369
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
7✔
370
            // TODO(Botan4) remove this branch; we won't support compressed points
371
            const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
×
372
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
373

374
            if(x < p && y >= 0) {
×
375
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
376
               if(y.get_bit(0) != y_mod_2) {
×
377
                  y = p - y;
×
378
               }
379

380
               return std::make_pair(x, y);
×
381
            }
382
         }
×
383

384
         throw Decoding_Error("Invalid ECC base point encoding");
×
385
      }();
7✔
386

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

394
      /*
395
      * Create the group data without registering it in the global map.
396
      *
397
      * Applications that need persistent custom groups should register them
398
      * via the relevant EC_Group constructor
399
      */
400
      auto data = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
6✔
401
      return std::make_pair(data, true);
6✔
402
   } else if(next_obj_type == ASN1_Type::Null) {
634✔
403
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
×
404
   } else {
405
      throw Decoding_Error(
1✔
406
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
2✔
407
   }
408
}
6,947✔
409

410
EC_Group::EC_Group() = default;
×
411

412
EC_Group::~EC_Group() = default;
114,823✔
413

414
EC_Group::EC_Group(const EC_Group&) = default;
56,128✔
415

416
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
417

418
// Internal constructor
419
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
16,040✔
420

421
//static
422
bool EC_Group::supports_named_group(std::string_view name) {
15,405✔
423
   if(name.empty()) {
15,405✔
424
      return false;
425
   }
426

427
   // Is it one of the groups compiled into the library?
428
   if(EC_Group::known_named_groups().contains(std::string(name))) {
15,405✔
429
      return true;
430
   }
431

432
   // Is it a custom group registered by the application?
433
   if(auto oid = OID::from_name(name)) {
5✔
434
      try {
3✔
435
         if(ec_group_data().lookup(oid.value()) != nullptr) {
4✔
436
            return true;
1✔
437
         }
438
      } catch(Not_Implemented&) {
×
439
         // This would be thrown for example if the group is a known curve
440
         // but the relevant module that enables it is not compiled in
441
      }
×
442
   }
1✔
443

444
   // Not known
445
   return false;
4✔
446
}
447

448
//static
449
bool EC_Group::supports_application_specific_group() {
26✔
450
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
451
   return true;
26✔
452
#else
453
   return false;
454
#endif
455
}
456

457
//static
458
bool EC_Group::supports_application_specific_group_with_cofactor() {
5✔
459
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
460
   return true;
5✔
461
#else
462
   return false;
463
#endif
464
}
465

466
//static
467
EC_Group EC_Group::from_OID(const OID& oid) {
132✔
468
   auto data = ec_group_data().lookup(oid);
132✔
469

470
   if(!data) {
132✔
471
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
4✔
472
   }
473

474
   return EC_Group(std::move(data));
130✔
475
}
130✔
476

477
//static
478
EC_Group EC_Group::from_name(std::string_view name) {
15,911✔
479
   std::shared_ptr<EC_Group_Data> data;
15,911✔
480

481
   if(auto oid = OID::from_name(name)) {
15,911✔
482
      data = ec_group_data().lookup(oid.value());
31,820✔
483
   }
×
484

485
   if(!data) {
15,910✔
486
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
487
   }
488

489
   return EC_Group(std::move(data));
15,910✔
490
}
15,910✔
491

492
EC_Group::EC_Group(std::string_view str) {
56✔
493
   if(str.empty()) {
56✔
494
      return;  // no initialization / uninitialized
495
   }
496

497
   try {
56✔
498
      const OID oid = OID::from_string(str);
56✔
499
      if(oid.has_value()) {
56✔
500
         m_data = ec_group_data().lookup(oid);
56✔
501
      }
502
   } catch(...) {}
56✔
503

504
   if(m_data == nullptr) {
56✔
505
      if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
×
506
         // OK try it as PEM ...
507
         const auto der = PEM_Code::decode_check_label(str, "EC PARAMETERS");
×
508

509
         auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
×
510
         this->m_data = data.first;
×
511
         this->m_explicit_encoding = data.second;
×
512
      }
×
513
   }
514

515
   if(m_data == nullptr) {
56✔
516
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
517
   }
518
}
×
519

520
//static
521
EC_Group EC_Group::from_PEM(std::string_view pem) {
4✔
522
   const auto der = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
4✔
523
   return EC_Group(der);
4✔
524
}
4✔
525

526
EC_Group::EC_Group(const BigInt& p,
6✔
527
                   const BigInt& a,
528
                   const BigInt& b,
529
                   const BigInt& base_x,
530
                   const BigInt& base_y,
531
                   const BigInt& order,
532
                   const BigInt& cofactor,
533
                   const OID& oid) {
6✔
534
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
12✔
535
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
12✔
536
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
12✔
537
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
12✔
538

539
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
6✔
540
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
6✔
541

542
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
6✔
543
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
6✔
544

545
   // Check that 4*a^3 + 27*b^2 != 0
546
   const auto discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
12✔
547
                                          mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
20✔
548
   BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
6✔
549

550
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
551
   auto y2 = mod_p.square(base_y);
6✔
552
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
6✔
553
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
6✔
554

555
   if(oid.has_value()) {
4✔
556
      m_data = ec_group_data().lookup_or_create(
4✔
557
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
558
   } else {
559
      m_data = ec_group_data().lookup_or_create_without_oid(
4✔
560
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
2✔
561
   }
562
}
22✔
563

564
EC_Group::EC_Group(const OID& oid,
51✔
565
                   const BigInt& p,
566
                   const BigInt& a,
567
                   const BigInt& b,
568
                   const BigInt& base_x,
569
                   const BigInt& base_y,
570
                   const BigInt& order) {
51✔
571
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
51✔
572

573
   // TODO(Botan4) remove this and require 192 bits minimum
574
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
575
   constexpr size_t p_bits_lower_bound = 192;
576
#else
577
   constexpr size_t p_bits_lower_bound = 128;
51✔
578
#endif
579

580
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
51✔
581
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
51✔
582

583
   if(p.bits() == 521) {
51✔
584
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
585
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
586
   } else if(p.bits() == 239) {
51✔
587
      const auto x962_p239 = []() {
18✔
588
         BigInt p239;
3✔
589
         for(size_t i = 0; i != 239; ++i) {
720✔
590
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
591
               p239.set_bit(i);
717✔
592
            }
593
         }
594
         return p239;
3✔
595
      }();
3✔
596

597
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
598
   } else {
3✔
599
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
47✔
600
   }
601

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

604
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
92✔
605
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
92✔
606
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
92✔
607
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
92✔
608
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
46✔
609

610
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
43✔
611
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
43✔
612

613
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
43✔
614
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
43✔
615

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

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

625
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
626
   auto y2 = mod_p.square(base_y);
43✔
627
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
43✔
628
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
43✔
629

630
   const BigInt cofactor(1);
42✔
631

632
   m_data =
42✔
633
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
42✔
634
}
139✔
635

636
EC_Group::EC_Group(std::span<const uint8_t> der) {
6,947✔
637
   auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
6,947✔
638
   m_data = data.first;
6,858✔
639
   m_explicit_encoding = data.second;
6,858✔
640
}
6,947✔
641

642
// static
643
bool EC_Group::unregister(const OID& oid) {
14✔
644
   return ec_group_data().unregister(oid);
14✔
645
}
646

647
const EC_Group_Data& EC_Group::data() const {
139,239✔
648
   if(m_data == nullptr) {
139,239✔
649
      throw Invalid_State("EC_Group uninitialized");
×
650
   }
651
   return *m_data;
139,239✔
652
}
653

654
size_t EC_Group::get_p_bits() const {
2,751✔
655
   return data().p_bits();
2,751✔
656
}
657

658
size_t EC_Group::get_p_bytes() const {
14,440✔
659
   return data().p_bytes();
14,440✔
660
}
661

662
size_t EC_Group::get_order_bits() const {
2,832✔
663
   return data().order_bits();
2,832✔
664
}
665

666
size_t EC_Group::get_order_bytes() const {
36,308✔
667
   return data().order_bytes();
36,308✔
668
}
669

670
const BigInt& EC_Group::get_p() const {
27,966✔
671
   return data().p();
27,966✔
672
}
673

674
const BigInt& EC_Group::get_a() const {
551✔
675
   return data().a();
551✔
676
}
677

678
const BigInt& EC_Group::get_b() const {
439✔
679
   return data().b();
439✔
680
}
681

682
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
683
const EC_Point& EC_Group::get_base_point() const {
649✔
684
   return data().base_point();
649✔
685
}
686

687
const EC_Point& EC_Group::generator() const {
×
688
   return data().base_point();
×
689
}
690

691
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
692
   //check that public point is not at infinity
693
   if(point.is_zero()) {
×
694
      return false;
695
   }
696

697
   //check that public point is on the curve
698
   if(point.on_the_curve() == false) {
×
699
      return false;
700
   }
701

702
   //check that public point has order q
703
   if((point * get_order()).is_zero() == false) {
×
704
      return false;
705
   }
706

707
   if(has_cofactor()) {
×
708
      if((point * get_cofactor()).is_zero()) {
×
709
         return false;
710
      }
711
   }
712

713
   return true;
714
}
715

716
#endif
717

718
const BigInt& EC_Group::get_order() const {
7,926✔
719
   return data().order();
7,926✔
720
}
721

722
const BigInt& EC_Group::get_g_x() const {
1,758✔
723
   return data().g_x();
1,758✔
724
}
725

726
const BigInt& EC_Group::get_g_y() const {
1,758✔
727
   return data().g_y();
1,758✔
728
}
729

730
const BigInt& EC_Group::get_cofactor() const {
136✔
731
   return data().cofactor();
136✔
732
}
733

734
bool EC_Group::has_cofactor() const {
11,986✔
735
   return data().has_cofactor();
11,986✔
736
}
737

738
const OID& EC_Group::get_curve_oid() const {
26,728✔
739
   return data().oid();
26,728✔
740
}
741

742
EC_Group_Source EC_Group::source() const {
190✔
743
   return data().source();
190✔
744
}
745

746
EC_Group_Engine EC_Group::engine() const {
2✔
747
   return data().engine();
2✔
748
}
749

750
std::vector<uint8_t> EC_Group::DER_encode() const {
2,819✔
751
   const auto& der_named_curve = data().der_named_curve();
2,819✔
752
   // TODO(Botan4) this can be removed because an OID will always be defined
753
   if(der_named_curve.empty()) {
2,819✔
754
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
755
   }
756

757
   return der_named_curve;
2,819✔
758
}
759

760
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
2,814✔
761
   if(form == EC_Group_Encoding::Explicit) {
2,814✔
762
      std::vector<uint8_t> output;
30✔
763
      DER_Encoder der(output);
30✔
764
      const size_t ecpVers1 = 1;
30✔
765
      const OID curve_type("1.2.840.10045.1.1");  // prime field
30✔
766

767
      const size_t p_bytes = get_p_bytes();
30✔
768

769
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
30✔
770

771
      der.start_sequence()
30✔
772
         .encode(ecpVers1)
30✔
773
         .start_sequence()
30✔
774
         .encode(curve_type)
30✔
775
         .encode(get_p())
30✔
776
         .end_cons()
30✔
777
         .start_sequence()
30✔
778
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
30✔
779
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
60✔
780
         .end_cons()
30✔
781
         .encode(generator, ASN1_Type::OctetString)
30✔
782
         .encode(get_order())
30✔
783
         .encode(get_cofactor())
30✔
784
         .end_cons();
30✔
785
      return output;
30✔
786
   } else if(form == EC_Group_Encoding::NamedCurve) {
2,814✔
787
      return this->DER_encode();
2,784✔
788
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
789
      return {0x00, 0x05};
×
790
   } else {
791
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
792
   }
793
}
794

795
std::string EC_Group::PEM_encode(EC_Group_Encoding form) const {
3✔
796
   const std::vector<uint8_t> der = DER_encode(form);
3✔
797
   return PEM_Code::encode(der, "EC PARAMETERS");
3✔
798
}
3✔
799

800
bool EC_Group::operator==(const EC_Group& other) const {
61✔
801
   if(m_data == other.m_data) {
61✔
802
      return true;  // same shared rep
803
   }
804

805
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
9✔
806
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
9✔
807
           get_cofactor() == other.get_cofactor());
4✔
808
}
809

810
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
190✔
811
   const bool is_builtin = source() == EC_Group_Source::Builtin;
190✔
812

813
   if(is_builtin && !strong) {
190✔
814
      return true;
815
   }
816

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

821
   const BigInt& p = get_p();
29✔
822
   const BigInt& a = get_a();
29✔
823
   const BigInt& b = get_b();
29✔
824
   const BigInt& order = get_order();
29✔
825

826
   if(p <= 3 || order <= 0) {
58✔
827
      return false;
×
828
   }
829
   if(a < 0 || a >= p) {
58✔
830
      return false;
×
831
   }
832
   if(b <= 0 || b >= p) {
58✔
833
      return false;
×
834
   }
835

836
   const size_t test_prob = 128;
29✔
837
   const bool is_randomly_generated = is_builtin;
29✔
838

839
   //check if field modulus is prime
840
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
29✔
841
      return false;
842
   }
843

844
   //check if order is prime
845
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
29✔
846
      return false;
847
   }
848

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

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

855
   if(discriminant == 0) {
29✔
856
      return false;
857
   }
858

859
   //check for valid cofactor
860
   if(get_cofactor() < 1) {
29✔
861
      return false;
862
   }
863

864
   // Check that the generator (g_x, g_y) is on the curve: y^2 == x^3 + a*x + b
865
   const BigInt& g_x = get_g_x();
29✔
866
   const BigInt& g_y = get_g_y();
29✔
867
   const BigInt y2 = mod_p.square(g_y);
29✔
868
   const BigInt x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
29✔
869
   if(y2 != x3_ax_b) {
29✔
870
      return false;
871
   }
872

873
   // Check that the generator has the claimed order: [order]G == identity,
874
   auto g_pt = EC_AffinePoint::from_bigint_xy(*this, get_g_x(), get_g_y());
29✔
875
   if(!g_pt) {
29✔
876
      return false;
877
   }
878
   const auto neg_one = EC_Scalar::one(*this).negate();
29✔
879
   const auto n_minus_one_g = EC_AffinePoint::g_mul(neg_one, rng);
29✔
880
   if(n_minus_one_g != g_pt->negate()) {
58✔
881
      return false;
882
   }
883

884
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
885
   // Reject if [cofactor]G is the identity. pcurves does not support cofactor != 1
886
   // so this can only matter when the legacy backend is in use.
887
   if(has_cofactor()) {
29✔
888
      const EC_Point& base_point = get_base_point();
×
889
      if((base_point * get_cofactor()).is_zero()) {
×
890
         return false;
891
      }
892
   }
893
#endif
894

895
   // check the Hasse bound (roughly)
896
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
29✔
897
      return false;
898
   }
899

900
   return true;
901
}
87✔
902

903
EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
×
904

905
EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default;
×
906

907
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
13,584✔
908

909
EC_Group::Mul2Table::~Mul2Table() = default;
13,584✔
910

911
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,428✔
912
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,428✔
913
   if(pt) {
2,428✔
914
      return EC_AffinePoint::_from_inner(std::move(pt));
4,856✔
915
   } else {
916
      return {};
×
917
   }
918
}
2,428✔
919

920
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
33,219✔
921
                                                      const EC_Scalar& x,
922
                                                      const EC_Scalar& y) const {
923
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
33,219✔
924
}
925

926
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
32,821✔
927
                                                      const EC_Scalar& c,
928
                                                      const EC_Scalar& x,
929
                                                      const EC_Scalar& y) const {
930
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
32,821✔
931
}
932

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