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

randombit / botan / 13124760045

03 Feb 2025 11:15PM UTC coverage: 91.232% (+0.001%) from 91.231%
13124760045

push

github

web-flow
Merge pull request #4630 from randombit/jack/just-for-you-rene

Add EC_Group::engine

94186 of 103238 relevant lines covered (91.23%)

11349067.77 hits per line

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

87.84
/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/reducer.h>
19
#include <botan/rng.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,770✔
32
         lock_guard_type<mutex_type> lock(m_mutex);
1,770✔
33
         size_t count = m_registered_curves.size();
1,770✔
34
         m_registered_curves.clear();
1,770✔
35
         return count;
1,770✔
36
      }
1,770✔
37

38
      std::shared_ptr<EC_Group_Data> lookup(const OID& oid) {
21,780✔
39
         lock_guard_type<mutex_type> lock(m_mutex);
21,780✔
40

41
         for(auto i : m_registered_curves) {
160,884✔
42
            if(i->oid() == oid) {
159,693✔
43
               return i;
20,589✔
44
            }
45
         }
159,693✔
46

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

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

58
            m_registered_curves.push_back(data);
1,138✔
59
            return data;
1,138✔
60
         }
61

62
         // Nope, unknown curve
63
         return std::shared_ptr<EC_Group_Data>();
53✔
64
      }
22,971✔
65

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

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

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

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

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

105
            /*
106
            * Check for someone trying to reuse an already in-use OID
107
            */
108
            if(same_oid && !same_params) {
763✔
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
            /*
114
            * If the same curve was previously created without an OID but is now
115
            * being registered again using an OID, save that OID.
116
            */
117
            if(same_params && i->oid().empty() && !oid.empty()) {
763✔
118
               i->set_oid(oid);
×
119
               return i;
×
120
            }
121
         }
1,531✔
122

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

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

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

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

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

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

160
         m_registered_curves.push_back(new_group);
17✔
161
         return new_group;
17✔
162
      }
108✔
163

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

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

176
   static Allocator_Initializer g_init_allocator;
23,641✔
177
   static EC_Group_Data_Map g_ec_data;
23,641✔
178
   return g_ec_data;
23,641✔
179
}
180

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

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

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

205
//static
206
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
6,390✔
207
                                                                              EC_Group_Source source) {
208
   BER_Decoder ber(bits);
6,390✔
209

210
   auto next_obj_type = ber.peek_next_object().type_tag();
6,390✔
211

212
   if(next_obj_type == ASN1_Type::ObjectId) {
6,382✔
213
      OID oid;
6,055✔
214
      ber.decode(oid);
6,055✔
215

216
      auto data = ec_group_data().lookup(oid);
6,013✔
217
      if(!data) {
6,013✔
218
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
106✔
219
      }
220

221
      return std::make_pair(data, false);
5,960✔
222
   } else if(next_obj_type == ASN1_Type::Sequence) {
6,435✔
223
      BigInt p, a, b, order, cofactor;
324✔
224
      std::vector<uint8_t> base_pt;
324✔
225
      std::vector<uint8_t> seed;
324✔
226

227
      ber.start_sequence()
324✔
228
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
625✔
229
         .start_sequence()
302✔
230
         .decode_and_check(OID("1.2.840.10045.1.1"), "Only prime ECC fields supported")
604✔
231
         .decode(p)
301✔
232
         .end_cons()
301✔
233
         .start_sequence()
575✔
234
         .decode_octet_string_bigint(a)
301✔
235
         .decode_octet_string_bigint(b)
300✔
236
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString)
298✔
237
         .end_cons()
298✔
238
         .decode(base_pt, ASN1_Type::OctetString)
295✔
239
         .decode(order)
288✔
240
         .decode(cofactor)
287✔
241
         .end_cons()
287✔
242
         .verify_end();
286✔
243

244
      if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) {
286✔
245
         throw Decoding_Error("ECC p parameter is invalid size");
134✔
246
      }
247

248
      auto mod_p = Modular_Reducer::for_public_modulus(p);
152✔
249
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
152✔
250
         throw Decoding_Error("ECC p parameter is not a prime");
32✔
251
      }
252

253
      if(a.is_negative() || a >= p) {
240✔
254
         throw Decoding_Error("Invalid ECC a parameter");
9✔
255
      }
256

257
      if(b <= 0 || b >= p) {
222✔
258
         throw Decoding_Error("Invalid ECC b parameter");
55✔
259
      }
260

261
      if(order.is_negative() || order.is_zero() || order >= 2 * p) {
221✔
262
         throw Decoding_Error("Invalid ECC group order");
2✔
263
      }
264

265
      auto mod_order = Modular_Reducer::for_public_modulus(order);
54✔
266
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
54✔
267
         throw Decoding_Error("Invalid ECC order parameter");
×
268
      }
269

270
      if(cofactor <= 0 || cofactor >= 16) {
106✔
271
         throw Decoding_Error("Invalid ECC cofactor parameter");
3✔
272
      }
273

274
      const size_t p_bytes = p.bytes();
51✔
275
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
51✔
276
         throw Decoding_Error("Invalid ECC base point encoding");
×
277
      }
278

279
      auto [g_x, g_y] = [&]() {
50✔
280
         const uint8_t hdr = base_pt[0];
51✔
281

282
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
51✔
283
            BigInt x = BigInt::decode(&base_pt[1], p_bytes);
50✔
284
            BigInt y = BigInt::decode(&base_pt[p_bytes + 1], p_bytes);
50✔
285

286
            if(x < p && y < p) {
50✔
287
               return std::make_pair(x, y);
50✔
288
            }
289
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
101✔
290
            BigInt x = BigInt::decode(&base_pt[1], p_bytes);
×
291
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
292

293
            if(x < p && y >= 0) {
×
294
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
295
               if(y.get_bit(0) != y_mod_2) {
×
296
                  y = p - y;
×
297
               }
298

299
               return std::make_pair(x, y);
×
300
            }
301
         }
×
302

303
         throw Decoding_Error("Invalid ECC base point encoding");
1✔
304
      }();
51✔
305

306
      auto y2 = mod_p.square(g_y);
50✔
307
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
200✔
308
      if(y2 != x3_ax_b) {
50✔
309
         throw Decoding_Error("Invalid ECC base point");
×
310
      }
311

312
      auto data = ec_group_data().lookup_or_create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
50✔
313
      return std::make_pair(data, true);
50✔
314
   } else if(next_obj_type == ASN1_Type::Null) {
2,477✔
315
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
1✔
316
   } else {
317
      throw Decoding_Error(
2✔
318
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
4✔
319
   }
320
}
6,390✔
321

322
EC_Group::EC_Group() = default;
×
323

324
EC_Group::~EC_Group() = default;
133,860✔
325

326
EC_Group::EC_Group(const EC_Group&) = default;
48,612✔
327

328
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
329

330
// Internal constructor
331
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,711✔
332

333
//static
334
bool EC_Group::supports_application_specific_group() {
28✔
335
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
336
   return true;
28✔
337
#else
338
   return false;
339
#endif
340
}
341

342
//static
343
EC_Group EC_Group::from_OID(const OID& oid) {
71✔
344
   auto data = ec_group_data().lookup(oid);
71✔
345

346
   if(!data) {
71✔
347
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
×
348
   }
349

350
   return EC_Group(std::move(data));
71✔
351
}
71✔
352

353
//static
354
EC_Group EC_Group::from_name(std::string_view name) {
15,640✔
355
   std::shared_ptr<EC_Group_Data> data;
15,640✔
356

357
   if(auto oid = OID::from_name(name)) {
15,640✔
358
      data = ec_group_data().lookup(oid.value());
31,280✔
359
   }
×
360

361
   if(!data) {
15,640✔
362
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
363
   }
364

365
   return EC_Group(std::move(data));
15,640✔
366
}
15,640✔
367

368
EC_Group::EC_Group(std::string_view str) {
61✔
369
   if(str.empty()) {
61✔
370
      return;  // no initialization / uninitialized
371
   }
372

373
   try {
61✔
374
      const OID oid = OID::from_string(str);
61✔
375
      if(oid.has_value()) {
56✔
376
         m_data = ec_group_data().lookup(oid);
56✔
377
      }
378
   } catch(...) {}
61✔
379

380
   if(m_data == nullptr) {
61✔
381
      if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----") {
5✔
382
         // OK try it as PEM ...
383
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
5✔
384

385
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
5✔
386
         this->m_data = data.first;
5✔
387
         this->m_explicit_encoding = data.second;
5✔
388
      }
10✔
389
   }
390

391
   if(m_data == nullptr) {
61✔
392
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
393
   }
394
}
×
395

396
//static
397
EC_Group EC_Group::from_PEM(std::string_view pem) {
1✔
398
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
1✔
399
   return EC_Group(ber);
1✔
400
}
1✔
401

402
EC_Group::EC_Group(const BigInt& p,
3✔
403
                   const BigInt& a,
404
                   const BigInt& b,
405
                   const BigInt& base_x,
406
                   const BigInt& base_y,
407
                   const BigInt& order,
408
                   const BigInt& cofactor,
409
                   const OID& oid) {
3✔
410
   m_data =
3✔
411
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
3✔
412
}
3✔
413

414
EC_Group::EC_Group(const OID& oid,
47✔
415
                   const BigInt& p,
416
                   const BigInt& a,
417
                   const BigInt& b,
418
                   const BigInt& base_x,
419
                   const BigInt& base_y,
420
                   const BigInt& order) {
47✔
421
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
47✔
422

423
   // TODO(Botan4) remove this and require 192 bits minimum
424
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
425
   constexpr size_t p_bits_lower_bound = 192;
426
#else
427
   constexpr size_t p_bits_lower_bound = 128;
47✔
428
#endif
429

430
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
47✔
431
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
47✔
432

433
   if(p.bits() == 521) {
47✔
434
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
435
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
436
   } else if(p.bits() == 239) {
47✔
437
      const auto x962_p239 = []() {
18✔
438
         BigInt p239;
3✔
439
         for(size_t i = 0; i != 239; ++i) {
720✔
440
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
441
               p239.set_bit(i);
717✔
442
            }
443
         }
444
         return p239;
3✔
445
      }();
3✔
446

447
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
448
   } else {
3✔
449
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
43✔
450
   }
451

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

454
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
84✔
455
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
84✔
456
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
84✔
457
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
84✔
458
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
42✔
459

460
   auto mod_p = Modular_Reducer::for_public_modulus(p);
39✔
461
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
39✔
462

463
   auto mod_order = Modular_Reducer::for_public_modulus(order);
39✔
464
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
39✔
465

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

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

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

479
   BigInt cofactor(1);
38✔
480

481
   m_data =
38✔
482
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
38✔
483
}
165✔
484

485
EC_Group::EC_Group(std::span<const uint8_t> ber) {
6,385✔
486
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
6,385✔
487
   m_data = data.first;
6,005✔
488
   m_explicit_encoding = data.second;
6,005✔
489
}
6,385✔
490

491
const EC_Group_Data& EC_Group::data() const {
122,709✔
492
   if(m_data == nullptr) {
122,709✔
493
      throw Invalid_State("EC_Group uninitialized");
×
494
   }
495
   return *m_data;
122,709✔
496
}
497

498
size_t EC_Group::get_p_bits() const {
1,284✔
499
   return data().p_bits();
1,284✔
500
}
501

502
size_t EC_Group::get_p_bytes() const {
13,634✔
503
   return data().p_bytes();
13,634✔
504
}
505

506
size_t EC_Group::get_order_bits() const {
1,147✔
507
   return data().order_bits();
1,147✔
508
}
509

510
size_t EC_Group::get_order_bytes() const {
30,466✔
511
   return data().order_bytes();
30,466✔
512
}
513

514
const BigInt& EC_Group::get_p() const {
26,562✔
515
   return data().p();
26,562✔
516
}
517

518
const BigInt& EC_Group::get_a() const {
449✔
519
   return data().a();
449✔
520
}
521

522
const BigInt& EC_Group::get_b() const {
337✔
523
   return data().b();
337✔
524
}
525

526
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
527
const EC_Point& EC_Group::get_base_point() const {
677✔
528
   return data().base_point();
677✔
529
}
530

531
const EC_Point& EC_Group::generator() const {
×
532
   return data().base_point();
×
533
}
534

535
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
536
   //check that public point is not at infinity
537
   if(point.is_zero()) {
×
538
      return false;
539
   }
540

541
   //check that public point is on the curve
542
   if(point.on_the_curve() == false) {
×
543
      return false;
544
   }
545

546
   //check that public point has order q
547
   if((point * get_order()).is_zero() == false) {
×
548
      return false;
549
   }
550

551
   if(has_cofactor()) {
×
552
      if((point * get_cofactor()).is_zero()) {
×
553
         return false;
554
      }
555
   }
556

557
   return true;
558
}
559

560
#endif
561

562
const BigInt& EC_Group::get_order() const {
7,903✔
563
   return data().order();
7,903✔
564
}
565

566
const BigInt& EC_Group::get_g_x() const {
979✔
567
   return data().g_x();
979✔
568
}
569

570
const BigInt& EC_Group::get_g_y() const {
979✔
571
   return data().g_y();
979✔
572
}
573

574
const BigInt& EC_Group::get_cofactor() const {
208✔
575
   return data().cofactor();
208✔
576
}
577

578
bool EC_Group::has_cofactor() const {
11,808✔
579
   return data().has_cofactor();
11,808✔
580
}
581

582
const OID& EC_Group::get_curve_oid() const {
24,819✔
583
   return data().oid();
24,819✔
584
}
585

586
EC_Group_Source EC_Group::source() const {
134✔
587
   return data().source();
134✔
588
}
589

590
EC_Group_Engine EC_Group::engine() const {
2✔
591
   return data().engine();
2✔
592
}
593

594
std::vector<uint8_t> EC_Group::DER_encode() const {
1,321✔
595
   const auto& der_named_curve = data().der_named_curve();
1,321✔
596
   // TODO(Botan4) this can be removed because an OID will always be defined
597
   if(der_named_curve.empty()) {
1,321✔
598
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
599
   }
600

601
   return der_named_curve;
1,321✔
602
}
603

604
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
1,263✔
605
   if(form == EC_Group_Encoding::Explicit) {
1,263✔
606
      std::vector<uint8_t> output;
31✔
607
      DER_Encoder der(output);
31✔
608
      const size_t ecpVers1 = 1;
31✔
609
      const OID curve_type("1.2.840.10045.1.1");  // prime field
31✔
610

611
      const size_t p_bytes = get_p_bytes();
31✔
612

613
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
31✔
614

615
      der.start_sequence()
31✔
616
         .encode(ecpVers1)
31✔
617
         .start_sequence()
31✔
618
         .encode(curve_type)
31✔
619
         .encode(get_p())
31✔
620
         .end_cons()
31✔
621
         .start_sequence()
31✔
622
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
31✔
623
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
62✔
624
         .end_cons()
31✔
625
         .encode(generator, ASN1_Type::OctetString)
31✔
626
         .encode(get_order())
31✔
627
         .encode(get_cofactor())
31✔
628
         .end_cons();
31✔
629
      return output;
31✔
630
   } else if(form == EC_Group_Encoding::NamedCurve) {
1,263✔
631
      return this->DER_encode();
1,232✔
632
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
633
      return {0x00, 0x05};
×
634
   } else {
635
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
636
   }
637
}
638

639
std::string EC_Group::PEM_encode() const {
1✔
640
   const std::vector<uint8_t> der = DER_encode(EC_Group_Encoding::Explicit);
1✔
641
   return PEM_Code::encode(der, "EC PARAMETERS");
1✔
642
}
1✔
643

644
bool EC_Group::operator==(const EC_Group& other) const {
48✔
645
   if(m_data == other.m_data) {
48✔
646
      return true;  // same shared rep
647
   }
648

649
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
×
650
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
×
651
           get_cofactor() == other.get_cofactor());
×
652
}
653

654
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
134✔
655
   const bool is_builtin = source() == EC_Group_Source::Builtin;
134✔
656

657
   if(is_builtin && !strong) {
134✔
658
      return true;
659
   }
660

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

665
   const BigInt& p = get_p();
28✔
666
   const BigInt& a = get_a();
28✔
667
   const BigInt& b = get_b();
28✔
668
   const BigInt& order = get_order();
28✔
669

670
   if(p <= 3 || order <= 0) {
56✔
671
      return false;
×
672
   }
673
   if(a < 0 || a >= p) {
56✔
674
      return false;
×
675
   }
676
   if(b <= 0 || b >= p) {
56✔
677
      return false;
×
678
   }
679

680
   const size_t test_prob = 128;
28✔
681
   const bool is_randomly_generated = is_builtin;
28✔
682

683
   //check if field modulus is prime
684
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
28✔
685
      return false;
686
   }
687

688
   //check if order is prime
689
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
28✔
690
      return false;
691
   }
692

693
   //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
694
   auto mod_p = Modular_Reducer::for_public_modulus(p);
28✔
695

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

698
   if(discriminant == 0) {
28✔
699
      return false;
700
   }
701

702
   //check for valid cofactor
703
   if(get_cofactor() < 1) {
28✔
704
      return false;
705
   }
706

707
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
708
   const EC_Point& base_point = get_base_point();
28✔
709
   //check if the base point is on the curve
710
   if(!base_point.on_the_curve()) {
28✔
711
      return false;
712
   }
713
   if((base_point * get_cofactor()).is_zero()) {
56✔
714
      return false;
715
   }
716
   //check if order of the base point is correct
717
   if(!(base_point * order).is_zero()) {
28✔
718
      return false;
719
   }
720
#endif
721

722
   // check the Hasse bound (roughly)
723
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
112✔
724
      return false;
725
   }
726

727
   return true;
728
}
28✔
729

730
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
13,261✔
731

732
EC_Group::Mul2Table::~Mul2Table() = default;
13,261✔
733

734
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,126✔
735
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,126✔
736
   if(pt) {
2,126✔
737
      return EC_AffinePoint::_from_inner(std::move(pt));
4,252✔
738
   } else {
739
      return {};
×
740
   }
741
}
2,126✔
742

743
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
22,993✔
744
                                                      const EC_Scalar& x,
745
                                                      const EC_Scalar& y) const {
746
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
22,993✔
747
}
748

749
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
22,822✔
750
                                                      const EC_Scalar& c,
751
                                                      const EC_Scalar& x,
752
                                                      const EC_Scalar& y) const {
753
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
22,822✔
754
}
755

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