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

randombit / botan / 12847739325

18 Jan 2025 07:45PM UTC coverage: 91.207% (-0.006%) from 91.213%
12847739325

push

github

web-flow
Merge pull request #4570 from randombit/jack/ec-bn-cleanups

Gate creation of Barrett params for reduction mod order by legacy_ec_point

93417 of 102423 relevant lines covered (91.21%)

11509321.71 hits per line

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

90.27
/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/pem.h>
17
#include <botan/reducer.h>
18
#include <botan/rng.h>
19
#include <botan/internal/ec_inner_data.h>
20
#include <botan/internal/fmt.h>
21
#include <botan/internal/primality.h>
22
#include <vector>
23

24
namespace Botan {
25

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

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

37
      std::shared_ptr<EC_Group_Data> lookup(const OID& oid) {
20,614✔
38
         lock_guard_type<mutex_type> lock(m_mutex);
20,614✔
39

40
         for(auto i : m_registered_curves) {
159,203✔
41
            if(i->oid() == oid) {
158,013✔
42
               return i;
19,424✔
43
            }
44
         }
158,013✔
45

46
         // Not found, check hardcoded data
47
         std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
1,190✔
48

49
         if(data) {
1,190✔
50
            for(auto curve : m_registered_curves) {
2,269✔
51
               if(curve->oid().empty() == true && curve->params_match(*data)) {
1,132✔
52
                  curve->set_oid(oid);
×
53
                  return curve;
×
54
               }
55
            }
1,132✔
56

57
            m_registered_curves.push_back(data);
1,137✔
58
            return data;
1,137✔
59
         }
60

61
         // Nope, unknown curve
62
         return std::shared_ptr<EC_Group_Data>();
53✔
63
      }
21,804✔
64

65
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
96✔
66
                                                      const BigInt& a,
67
                                                      const BigInt& b,
68
                                                      const BigInt& g_x,
69
                                                      const BigInt& g_y,
70
                                                      const BigInt& order,
71
                                                      const BigInt& cofactor,
72
                                                      const OID& oid,
73
                                                      EC_Group_Source source) {
74
         lock_guard_type<mutex_type> lock(m_mutex);
96✔
75

76
         for(auto i : m_registered_curves) {
1,594✔
77
            /*
78
            * The params may be the same but you are trying to register under a
79
            * different OID than the one we are using, so using a different
80
            * group, since EC_Group's model assumes a single OID per group.
81
            */
82
            if(!oid.empty() && !i->oid().empty() && i->oid() != oid) {
1,576✔
83
               continue;
692✔
84
            }
85

86
            const bool same_oid = !oid.empty() && i->oid() == oid;
884✔
87
            const bool same_params = i->params_match(p, a, b, g_x, g_y, order, cofactor);
884✔
88

89
            /*
90
            * If the params and OID are the same then we are done, just return
91
            * the already registered curve obj.
92
            */
93
            if(same_params && same_oid) {
884✔
94
               return i;
33✔
95
            }
96

97
            /*
98
            * If same params and the new OID is empty, then that's ok too
99
            */
100
            if(same_params && oid.empty()) {
851✔
101
               return i;
45✔
102
            }
103

104
            /*
105
            * Check for someone trying to reuse an already in-use OID
106
            */
107
            if(same_oid && !same_params) {
806✔
108
               throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
×
109
                                      " but a distinct curve is already registered using that OID");
×
110
            }
111

112
            /*
113
            * If the same curve was previously created without an OID but is now
114
            * being registered again using an OID, save that OID.
115
            */
116
            if(same_params && i->oid().empty() && !oid.empty()) {
806✔
117
               i->set_oid(oid);
×
118
               return i;
×
119
            }
120
         }
1,576✔
121

122
         /*
123
         Not found in current list, so we need to create a new entry
124

125
         If an OID is set, try to look up relative our static tables to detect a duplicate
126
         registration under an OID
127
         */
128

129
         auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
18✔
130

131
         if(oid.has_value()) {
18✔
132
            std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
7✔
133
            if(data != nullptr && !new_group->params_match(*data)) {
7✔
134
               throw Invalid_Argument("Attempting to register an EC group under OID of hardcoded group");
×
135
            }
136
         } else {
7✔
137
            // Here try to use the order as a hint to look up the group id, to identify common groups
138
            const OID oid_from_store = EC_Group::EC_group_identity_from_order(order);
11✔
139
            if(oid_from_store.has_value()) {
11✔
140
               std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid_from_store);
8✔
141

142
               /*
143
               If EC_group_identity_from_order returned an OID then looking up that OID
144
               must always return a result.
145
               */
146
               BOTAN_ASSERT_NOMSG(data != nullptr);
8✔
147

148
               /*
149
               It is possible (if unlikely) that someone is registering another group
150
               that happens to have an order equal to that of a well known group -
151
               so verify all values before assigning the OID.
152
               */
153
               if(new_group->params_match(*data)) {
8✔
154
                  new_group->set_oid(oid_from_store);
6✔
155
               }
156
            }
8✔
157
         }
11✔
158

159
         m_registered_curves.push_back(new_group);
18✔
160
         return new_group;
18✔
161
      }
114✔
162

163
   private:
164
      mutex_type m_mutex;
165
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
166
};
167

168
//static
169
EC_Group_Data_Map& EC_Group::ec_group_data() {
22,480✔
170
   /*
171
   * This exists purely to ensure the allocator is constructed before g_ec_data,
172
   * which ensures that its destructor runs after ~g_ec_data is complete.
173
   */
174

175
   static Allocator_Initializer g_init_allocator;
22,480✔
176
   static EC_Group_Data_Map g_ec_data;
22,480✔
177
   return g_ec_data;
22,480✔
178
}
179

180
//static
181
size_t EC_Group::clear_registered_curve_data() {
1,770✔
182
   return ec_group_data().clear();
1,770✔
183
}
184

185
//static
186
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
1,145✔
187
                                                            const char* a_str,
188
                                                            const char* b_str,
189
                                                            const char* g_x_str,
190
                                                            const char* g_y_str,
191
                                                            const char* order_str,
192
                                                            const OID& oid) {
193
   const BigInt p(p_str);
1,145✔
194
   const BigInt a(a_str);
1,145✔
195
   const BigInt b(b_str);
1,145✔
196
   const BigInt g_x(g_x_str);
1,145✔
197
   const BigInt g_y(g_y_str);
1,145✔
198
   const BigInt order(order_str);
1,145✔
199
   const BigInt cofactor(1);  // implicit
1,145✔
200

201
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
1,145✔
202
}
8,015✔
203

204
//static
205
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
5,256✔
206
                                                                              EC_Group_Source source) {
207
   BER_Decoder ber(bits);
5,256✔
208
   BER_Object obj = ber.get_next_object();
5,256✔
209

210
   if(obj.type() == ASN1_Type::ObjectId) {
5,248✔
211
      OID oid;
4,916✔
212
      BER_Decoder(bits).decode(oid);
5,011✔
213

214
      auto data = ec_group_data().lookup(oid);
4,874✔
215
      if(!data) {
4,874✔
216
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
106✔
217
      }
218

219
      return std::make_pair(data, false);
4,821✔
220
   }
4,969✔
221

222
   if(obj.type() == ASN1_Type::Sequence) {
332✔
223
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
224

225
      BigInt p, a, b, order, cofactor;
329✔
226
      std::vector<uint8_t> base_pt;
329✔
227
      std::vector<uint8_t> seed;
329✔
228

229
      BER_Decoder(bits)
657✔
230
         .start_sequence()
328✔
231
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
635✔
232
         .start_sequence()
307✔
233
         .decode_and_check(OID("1.2.840.10045.1.1"), "Only prime ECC fields supported")
614✔
234
         .decode(p)
306✔
235
         .end_cons()
306✔
236
         .start_sequence()
306✔
237
         .decode_octet_string_bigint(a)
306✔
238
         .decode_octet_string_bigint(b)
305✔
239
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString)
303✔
240
         .end_cons()
303✔
241
         .decode(base_pt, ASN1_Type::OctetString)
300✔
242
         .decode(order)
293✔
243
         .decode(cofactor)
292✔
244
         .end_cons()
292✔
245
         .verify_end();
291✔
246

247
      if(p.bits() < 112 || p.bits() > 521) {
291✔
248
         throw Decoding_Error("ECC p parameter is invalid size");
132✔
249
      }
250

251
      if(p.is_negative() || !is_bailie_psw_probable_prime(p)) {
159✔
252
         throw Decoding_Error("ECC p parameter is not a prime");
34✔
253
      }
254

255
      if(a.is_negative() || a >= p) {
250✔
256
         throw Decoding_Error("Invalid ECC a parameter");
9✔
257
      }
258

259
      if(b <= 0 || b >= p) {
232✔
260
         throw Decoding_Error("Invalid ECC b parameter");
55✔
261
      }
262

263
      if(order <= 0 || order >= 2 * p || !is_bailie_psw_probable_prime(order)) {
453✔
264
         throw Decoding_Error("Invalid ECC order parameter");
2✔
265
      }
266

267
      if(cofactor <= 0 || cofactor >= 16) {
116✔
268
         throw Decoding_Error("Invalid ECC cofactor parameter");
3✔
269
      }
270

271
      const auto [g_x, g_y] = Botan::OS2ECP(base_pt.data(), base_pt.size(), p, a, b);
56✔
272

273
      auto data = ec_group_data().lookup_or_create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
55✔
274
      return std::make_pair(data, true);
55✔
275
#else
276
      BOTAN_UNUSED(source);
277
      throw Not_Implemented("Support for decoding explicit curve params is not supported in this build configuration");
278
#endif
279
   }
2,303✔
280

281
   if(obj.type() == ASN1_Type::Null) {
3✔
282
      throw Decoding_Error("Cannot handle ImplicitCA ECC parameters");
1✔
283
   } else {
284
      throw Decoding_Error(fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(obj.type())));
4✔
285
   }
286
}
5,256✔
287

288
EC_Group::EC_Group() = default;
15✔
289

290
EC_Group::~EC_Group() = default;
127,419✔
291

292
EC_Group::EC_Group(const EC_Group&) = default;
46,093✔
293

294
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
295

296
// Internal constructor
297
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,684✔
298

299
//static
300
EC_Group EC_Group::from_OID(const OID& oid) {
71✔
301
   auto data = ec_group_data().lookup(oid);
71✔
302

303
   if(!data) {
71✔
304
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
×
305
   }
306

307
   return EC_Group(std::move(data));
71✔
308
}
71✔
309

310
//static
311
EC_Group EC_Group::from_name(std::string_view name) {
15,613✔
312
   std::shared_ptr<EC_Group_Data> data;
15,613✔
313

314
   if(auto oid = OID::from_name(name)) {
15,613✔
315
      data = ec_group_data().lookup(oid.value());
31,226✔
316
   }
×
317

318
   if(!data) {
15,613✔
319
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
320
   }
321

322
   return EC_Group(std::move(data));
15,613✔
323
}
15,613✔
324

325
EC_Group::EC_Group(std::string_view str) {
61✔
326
   if(str.empty()) {
61✔
327
      return;  // no initialization / uninitialized
328
   }
329

330
   try {
61✔
331
      const OID oid = OID::from_string(str);
61✔
332
      if(oid.has_value()) {
56✔
333
         m_data = ec_group_data().lookup(oid);
56✔
334
      }
335
   } catch(...) {}
61✔
336

337
   if(m_data == nullptr) {
61✔
338
      if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----") {
5✔
339
         // OK try it as PEM ...
340
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
5✔
341

342
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
5✔
343
         this->m_data = data.first;
5✔
344
         this->m_explicit_encoding = data.second;
5✔
345
      }
10✔
346
   }
347

348
   if(m_data == nullptr) {
61✔
349
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
350
   }
351
}
×
352

353
//static
354
EC_Group EC_Group::from_PEM(std::string_view pem) {
1✔
355
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
1✔
356
   return EC_Group(ber);
1✔
357
}
1✔
358

359
EC_Group::EC_Group(const BigInt& p,
3✔
360
                   const BigInt& a,
361
                   const BigInt& b,
362
                   const BigInt& base_x,
363
                   const BigInt& base_y,
364
                   const BigInt& order,
365
                   const BigInt& cofactor,
366
                   const OID& oid) {
3✔
367
   m_data =
3✔
368
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
3✔
369
}
3✔
370

371
EC_Group::EC_Group(const OID& oid,
47✔
372
                   const BigInt& p,
373
                   const BigInt& a,
374
                   const BigInt& b,
375
                   const BigInt& base_x,
376
                   const BigInt& base_y,
377
                   const BigInt& order) {
47✔
378
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
47✔
379

380
   // TODO(Botan4) remove this and require 192 bits minimum
381
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
382
   constexpr size_t p_bits_lower_bound = 192;
383
#else
384
   constexpr size_t p_bits_lower_bound = 128;
47✔
385
#endif
386

387
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
47✔
388
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
47✔
389

390
   if(p.bits() == 521) {
47✔
391
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
392
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
393
   } else if(p.bits() == 239) {
47✔
394
      const auto x962_p239 = []() {
18✔
395
         BigInt p239;
3✔
396
         for(size_t i = 0; i != 239; ++i) {
720✔
397
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
398
               p239.set_bit(i);
717✔
399
            }
400
         }
401
         return p239;
3✔
402
      }();
3✔
403

404
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
405
   } else {
3✔
406
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
43✔
407
   }
408

409
   BOTAN_ARG_CHECK(p % 4 == 3, "EC_Group p must be congruent to 3 modulo 4");
44✔
410

411
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
84✔
412
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
84✔
413
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
84✔
414
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
84✔
415
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
42✔
416

417
   Modular_Reducer mod_p(p);
39✔
418
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
39✔
419
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order), "EC_Group order is not prime");
39✔
420

421
   // This catches someone "ignoring" a cofactor and just trying to
422
   // provide the subgroup order
423
   BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid");
117✔
424

425
   // Check that 4*a^3 + 27*b^2 != 0
426
   const auto discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b)));
273✔
427
   BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
39✔
428

429
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
430
   auto y2 = mod_p.square(base_y);
39✔
431
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
157✔
432
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
39✔
433

434
   BigInt cofactor(1);
38✔
435

436
   m_data =
38✔
437
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
38✔
438
}
164✔
439

440
EC_Group::EC_Group(std::span<const uint8_t> ber) {
5,251✔
441
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
5,251✔
442
   m_data = data.first;
4,871✔
443
   m_explicit_encoding = data.second;
4,871✔
444
}
5,251✔
445

446
const EC_Group_Data& EC_Group::data() const {
117,582✔
447
   if(m_data == nullptr) {
117,582✔
448
      throw Invalid_State("EC_Group uninitialized");
×
449
   }
450
   return *m_data;
117,582✔
451
}
452

453
size_t EC_Group::get_p_bits() const {
1,281✔
454
   return data().p_bits();
1,281✔
455
}
456

457
size_t EC_Group::get_p_bytes() const {
13,612✔
458
   return data().p_bytes();
13,612✔
459
}
460

461
size_t EC_Group::get_order_bits() const {
1,144✔
462
   return data().order_bits();
1,144✔
463
}
464

465
size_t EC_Group::get_order_bytes() const {
26,704✔
466
   return data().order_bytes();
26,704✔
467
}
468

469
const BigInt& EC_Group::get_p() const {
26,571✔
470
   return data().p();
26,571✔
471
}
472

473
const BigInt& EC_Group::get_a() const {
456✔
474
   return data().a();
456✔
475
}
476

477
const BigInt& EC_Group::get_b() const {
344✔
478
   return data().b();
344✔
479
}
480

481
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
482
const EC_Point& EC_Group::get_base_point() const {
677✔
483
   return data().base_point();
677✔
484
}
485

486
const EC_Point& EC_Group::generator() const {
×
487
   return data().base_point();
×
488
}
489

490
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
491
   //check that public point is not at infinity
492
   if(point.is_zero()) {
×
493
      return false;
494
   }
495

496
   //check that public point is on the curve
497
   if(point.on_the_curve() == false) {
×
498
      return false;
499
   }
500

501
   //check that public point has order q
502
   if((point * get_order()).is_zero() == false) {
×
503
      return false;
504
   }
505

506
   if(has_cofactor()) {
×
507
      if((point * get_cofactor()).is_zero()) {
×
508
         return false;
509
      }
510
   }
511

512
   return true;
513
}
514

515
#endif
516

517
const BigInt& EC_Group::get_order() const {
7,904✔
518
   return data().order();
7,904✔
519
}
520

521
const BigInt& EC_Group::get_g_x() const {
979✔
522
   return data().g_x();
979✔
523
}
524

525
const BigInt& EC_Group::get_g_y() const {
979✔
526
   return data().g_y();
979✔
527
}
528

529
const BigInt& EC_Group::get_cofactor() const {
208✔
530
   return data().cofactor();
208✔
531
}
532

533
bool EC_Group::has_cofactor() const {
11,809✔
534
   return data().has_cofactor();
11,809✔
535
}
536

537
const OID& EC_Group::get_curve_oid() const {
23,459✔
538
   return data().oid();
23,459✔
539
}
540

541
EC_Group_Source EC_Group::source() const {
134✔
542
   return data().source();
134✔
543
}
544

545
std::vector<uint8_t> EC_Group::DER_encode() const {
1,321✔
546
   const auto& der_named_curve = data().der_named_curve();
1,321✔
547
   // TODO(Botan4) this can be removed because an OID will always be defined
548
   if(der_named_curve.empty()) {
1,321✔
549
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
550
   }
551

552
   return der_named_curve;
1,321✔
553
}
554

555
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
1,263✔
556
   if(form == EC_Group_Encoding::Explicit) {
1,263✔
557
      std::vector<uint8_t> output;
31✔
558
      DER_Encoder der(output);
31✔
559
      const size_t ecpVers1 = 1;
31✔
560
      const OID curve_type("1.2.840.10045.1.1");  // prime field
31✔
561

562
      const size_t p_bytes = get_p_bytes();
31✔
563

564
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
31✔
565

566
      der.start_sequence()
31✔
567
         .encode(ecpVers1)
31✔
568
         .start_sequence()
31✔
569
         .encode(curve_type)
31✔
570
         .encode(get_p())
31✔
571
         .end_cons()
31✔
572
         .start_sequence()
31✔
573
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
31✔
574
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
62✔
575
         .end_cons()
31✔
576
         .encode(generator, ASN1_Type::OctetString)
31✔
577
         .encode(get_order())
31✔
578
         .encode(get_cofactor())
31✔
579
         .end_cons();
31✔
580
      return output;
31✔
581
   } else if(form == EC_Group_Encoding::NamedCurve) {
1,263✔
582
      return this->DER_encode();
1,232✔
583
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
584
      return {0x00, 0x05};
×
585
   } else {
586
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
587
   }
588
}
589

590
std::string EC_Group::PEM_encode() const {
1✔
591
   const std::vector<uint8_t> der = DER_encode(EC_Group_Encoding::Explicit);
1✔
592
   return PEM_Code::encode(der, "EC PARAMETERS");
1✔
593
}
1✔
594

595
bool EC_Group::operator==(const EC_Group& other) const {
48✔
596
   if(m_data == other.m_data) {
48✔
597
      return true;  // same shared rep
598
   }
599

600
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
×
601
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
×
602
           get_cofactor() == other.get_cofactor());
×
603
}
604

605
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
134✔
606
   const bool is_builtin = source() == EC_Group_Source::Builtin;
134✔
607

608
   if(is_builtin && !strong) {
134✔
609
      return true;
610
   }
611

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

616
   const BigInt& p = get_p();
28✔
617
   const BigInt& a = get_a();
28✔
618
   const BigInt& b = get_b();
28✔
619
   const BigInt& order = get_order();
28✔
620

621
   if(p <= 3 || order <= 0) {
56✔
622
      return false;
×
623
   }
624
   if(a < 0 || a >= p) {
56✔
625
      return false;
×
626
   }
627
   if(b <= 0 || b >= p) {
56✔
628
      return false;
×
629
   }
630

631
   const size_t test_prob = 128;
28✔
632
   const bool is_randomly_generated = is_builtin;
28✔
633

634
   //check if field modulus is prime
635
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
28✔
636
      return false;
637
   }
638

639
   //check if order is prime
640
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
28✔
641
      return false;
642
   }
643

644
   //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
645
   const Modular_Reducer mod_p(p);
28✔
646

647
   const BigInt discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b)));
196✔
648

649
   if(discriminant == 0) {
28✔
650
      return false;
651
   }
652

653
   //check for valid cofactor
654
   if(get_cofactor() < 1) {
28✔
655
      return false;
656
   }
657

658
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
659
   const EC_Point& base_point = get_base_point();
28✔
660
   //check if the base point is on the curve
661
   if(!base_point.on_the_curve()) {
28✔
662
      return false;
663
   }
664
   if((base_point * get_cofactor()).is_zero()) {
56✔
665
      return false;
666
   }
667
   //check if order of the base point is correct
668
   if(!(base_point * order).is_zero()) {
28✔
669
      return false;
670
   }
671
#endif
672

673
   // check the Hasse bound (roughly)
674
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
112✔
675
      return false;
676
   }
677

678
   return true;
679
}
28✔
680

681
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
13,253✔
682

683
EC_Group::Mul2Table::~Mul2Table() = default;
13,253✔
684

685
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,124✔
686
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,124✔
687
   if(pt) {
2,124✔
688
      return EC_AffinePoint::_from_inner(std::move(pt));
4,248✔
689
   } else {
690
      return {};
×
691
   }
692
}
2,124✔
693

694
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
22,966✔
695
                                                      const EC_Scalar& x,
696
                                                      const EC_Scalar& y) const {
697
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
22,966✔
698
}
699

700
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
22,794✔
701
                                                      const EC_Scalar& c,
702
                                                      const EC_Scalar& x,
703
                                                      const EC_Scalar& y) const {
704
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
22,794✔
705
}
706

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