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

randombit / botan / 13328620852

14 Feb 2025 11:51AM UTC coverage: 91.634% (-0.007%) from 91.641%
13328620852

Pull #4687

github

web-flow
Merge 4536a8a2b into 3d3fb3931
Pull Request #4687: Refactor EC_Group_Data_Map::lookup_or_create

94937 of 103604 relevant lines covered (91.63%)

11129303.55 hits per line

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

88.17
/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,793✔
39
         lock_guard_type<mutex_type> lock(m_mutex);
21,793✔
40

41
         for(auto i : m_registered_curves) {
160,925✔
42
            if(i->oid() == oid) {
159,734✔
43
               return i;
20,602✔
44
            }
45
         }
159,734✔
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
            m_registered_curves.push_back(data);
1,138✔
52
            return data;
1,138✔
53
         }
54

55
         // Nope, unknown curve
56
         return std::shared_ptr<EC_Group_Data>();
53✔
57
      }
22,984✔
58

59
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
40✔
60
                                                      const BigInt& a,
61
                                                      const BigInt& b,
62
                                                      const BigInt& g_x,
63
                                                      const BigInt& g_y,
64
                                                      const BigInt& order,
65
                                                      const BigInt& cofactor,
66
                                                      const OID& oid,
67
                                                      EC_Group_Source source) {
68
         BOTAN_ASSERT_NOMSG(oid.has_value());
40✔
69

70
         lock_guard_type<mutex_type> lock(m_mutex);
40✔
71

72
         for(auto i : m_registered_curves) {
774✔
73
            if(i->oid() == oid) {
767✔
74
               /*
75
               * If both OID and params are the same then we are done, just return
76
               * the already registered curve obj.
77
               *
78
               * If the params don't match then the user attempted to create some
79
               * other set of parameters under an already known OID
80
               */
81
               if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
33✔
82
                  return i;
33✔
83
               } else {
84
                  throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
×
85
                                         " but a distinct curve is already registered using that OID");
×
86
               }
87
            }
88

89
            /*
90
            * If the same curve was previously created without an OID but is now
91
            * being registered again using an OID, save that OID.
92
            *
93
            * TODO(Botan4) remove this block; this situation won't be possible since
94
            * we will require all groups to have an OID
95
            */
96
            if(i->oid().empty() && i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
734✔
97
               i->set_oid(oid);
×
98
               return i;
×
99
            }
100
         }
767✔
101

102
         /*
103
         Not found in current list, so we need to create a new entry
104
         */
105
         auto new_group = EC_Group::EC_group_info(oid);
7✔
106
         if(new_group) {
7✔
107
            /*
108
            * This turned out to be the OID of one of the builtin groups. Verify
109
            * that all of the provided parameters match that builtin group.
110
            */
111
            if(!new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
×
112
               throw Invalid_Argument("Attempting to register an EC group under OID of hardcoded group");
×
113
            }
114
         } else {
115
            /*
116
            * This path is taken for an application registering a new EC_Group with an OID specified
117
            */
118
            new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
14✔
119
         }
120

121
         m_registered_curves.push_back(new_group);
7✔
122
         return new_group;
7✔
123
      }
47✔
124

125
      std::shared_ptr<EC_Group_Data> lookup_or_create_without_oid(const BigInt& p,
51✔
126
                                                                  const BigInt& a,
127
                                                                  const BigInt& b,
128
                                                                  const BigInt& g_x,
129
                                                                  const BigInt& g_y,
130
                                                                  const BigInt& order,
131
                                                                  const BigInt& cofactor,
132
                                                                  EC_Group_Source source) {
133
         lock_guard_type<mutex_type> lock(m_mutex);
51✔
134

135
         for(auto i : m_registered_curves) {
771✔
136
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
761✔
137
               return i;
41✔
138
            }
139
         }
761✔
140

141
         // Try to use the order as a hint to look up the group id
142
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
10✔
143
         if(oid_from_order.has_value()) {
10✔
144
            auto new_group = EC_Group::EC_group_info(oid_from_order);
7✔
145

146
            // Have to check all params in the (unlikely/malicious) event of an order collision
147
            if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
7✔
148
               m_registered_curves.push_back(new_group);
5✔
149
               return new_group;
5✔
150
            }
151
         }
7✔
152

153
         /*
154
         * At this point we have failed to identify the group; it is not any of
155
         * the builtin values, nor is it a group that the user had previously
156
         * registered explicitly. We create the group data without an OID.
157
         *
158
         * TODO(Botan4) remove this; throw an exception instead
159
         */
160
         auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
5✔
161
         m_registered_curves.push_back(new_group);
5✔
162
         return new_group;
5✔
163
      }
66✔
164

165
   private:
166
      mutex_type m_mutex;
167
      // TODO(Botan4): Once OID is required we could make this into a map
168
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
169
};
170

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

178
   static Allocator_Initializer g_init_allocator;
23,654✔
179
   static EC_Group_Data_Map g_ec_data;
23,654✔
180
   return g_ec_data;
23,654✔
181
}
182

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

188
//static
189
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
1,145✔
190
                                                            const char* a_str,
191
                                                            const char* b_str,
192
                                                            const char* g_x_str,
193
                                                            const char* g_y_str,
194
                                                            const char* order_str,
195
                                                            const OID& oid) {
196
   BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
1,145✔
197

198
   const BigInt p(p_str);
1,145✔
199
   const BigInt a(a_str);
1,145✔
200
   const BigInt b(b_str);
1,145✔
201
   const BigInt g_x(g_x_str);
1,145✔
202
   const BigInt g_y(g_y_str);
1,145✔
203
   const BigInt order(order_str);
1,145✔
204
   const BigInt cofactor(1);  // implicit
1,145✔
205

206
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
2,290✔
207
}
1,145✔
208

209
//static
210
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
6,394✔
211
                                                                              EC_Group_Source source) {
212
   BER_Decoder ber(bits);
6,394✔
213

214
   auto next_obj_type = ber.peek_next_object().type_tag();
6,394✔
215

216
   if(next_obj_type == ASN1_Type::ObjectId) {
6,386✔
217
      OID oid;
6,059✔
218
      ber.decode(oid);
6,059✔
219

220
      auto data = ec_group_data().lookup(oid);
6,017✔
221
      if(!data) {
6,017✔
222
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
106✔
223
      }
224

225
      return std::make_pair(data, false);
5,964✔
226
   } else if(next_obj_type == ASN1_Type::Sequence) {
6,439✔
227
      BigInt p, a, b, order, cofactor;
324✔
228
      std::vector<uint8_t> base_pt;
324✔
229
      std::vector<uint8_t> seed;
324✔
230

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

248
      if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) {
286✔
249
         throw Decoding_Error("ECC p parameter is invalid size");
134✔
250
      }
251

252
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
253
      auto mod_p = Modular_Reducer::for_public_modulus(p);
152✔
254
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
152✔
255
         throw Decoding_Error("ECC p parameter is not a prime");
32✔
256
      }
257

258
      if(a.is_negative() || a >= p) {
240✔
259
         throw Decoding_Error("Invalid ECC a parameter");
9✔
260
      }
261

262
      if(b <= 0 || b >= p) {
222✔
263
         throw Decoding_Error("Invalid ECC b parameter");
55✔
264
      }
265

266
      if(order.is_negative() || order.is_zero() || order >= 2 * p) {
323✔
267
         throw Decoding_Error("Invalid ECC group order");
2✔
268
      }
269

270
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
271
      auto mod_order = Modular_Reducer::for_public_modulus(order);
54✔
272
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
54✔
273
         throw Decoding_Error("Invalid ECC order parameter");
×
274
      }
275

276
      // TODO(Botan4) Require cofactor == 1
277
      if(cofactor <= 0 || cofactor >= 16) {
106✔
278
         throw Decoding_Error("Invalid ECC cofactor parameter");
3✔
279
      }
280

281
      const size_t p_bytes = p.bytes();
51✔
282
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
51✔
283
         throw Decoding_Error("Invalid ECC base point encoding");
×
284
      }
285

286
      auto [g_x, g_y] = [&]() {
54✔
287
         const uint8_t hdr = base_pt[0];
51✔
288

289
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
51✔
290
            BigInt x = BigInt::decode(&base_pt[1], p_bytes);
50✔
291
            BigInt y = BigInt::decode(&base_pt[p_bytes + 1], p_bytes);
50✔
292

293
            if(x < p && y < p) {
50✔
294
               return std::make_pair(x, y);
50✔
295
            }
296
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
51✔
297
            // TODO(Botan4) remove this branch; we won't support compressed points
298
            BigInt x = BigInt::decode(&base_pt[1], p_bytes);
×
299
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
300

301
            if(x < p && y >= 0) {
×
302
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
303
               if(y.get_bit(0) != y_mod_2) {
×
304
                  y = p - y;
×
305
               }
306

307
               return std::make_pair(x, y);
×
308
            }
309
         }
×
310

311
         throw Decoding_Error("Invalid ECC base point encoding");
1✔
312
      }();
51✔
313

314
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
315
      auto y2 = mod_p.square(g_y);
50✔
316
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
50✔
317
      if(y2 != x3_ax_b) {
50✔
318
         throw Decoding_Error("Invalid ECC base point");
×
319
      }
320

321
      auto data = ec_group_data().lookup_or_create_without_oid(p, a, b, g_x, g_y, order, cofactor, source);
50✔
322
      return std::make_pair(data, true);
50✔
323
   } else if(next_obj_type == ASN1_Type::Null) {
2,003✔
324
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
1✔
325
   } else {
326
      throw Decoding_Error(
2✔
327
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
4✔
328
   }
329
}
6,394✔
330

331
EC_Group::EC_Group() = default;
×
332

333
EC_Group::~EC_Group() = default;
133,976✔
334

335
EC_Group::EC_Group(const EC_Group&) = default;
48,649✔
336

337
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
338

339
// Internal constructor
340
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,720✔
341

342
//static
343
bool EC_Group::supports_application_specific_group() {
28✔
344
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
345
   return true;
28✔
346
#else
347
   return false;
348
#endif
349
}
350

351
//static
352
EC_Group EC_Group::from_OID(const OID& oid) {
71✔
353
   auto data = ec_group_data().lookup(oid);
71✔
354

355
   if(!data) {
71✔
356
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
×
357
   }
358

359
   return EC_Group(std::move(data));
71✔
360
}
71✔
361

362
//static
363
EC_Group EC_Group::from_name(std::string_view name) {
15,649✔
364
   std::shared_ptr<EC_Group_Data> data;
15,649✔
365

366
   if(auto oid = OID::from_name(name)) {
15,649✔
367
      data = ec_group_data().lookup(oid.value());
31,298✔
368
   }
×
369

370
   if(!data) {
15,649✔
371
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
372
   }
373

374
   return EC_Group(std::move(data));
15,649✔
375
}
15,649✔
376

377
EC_Group::EC_Group(std::string_view str) {
61✔
378
   if(str.empty()) {
61✔
379
      return;  // no initialization / uninitialized
380
   }
381

382
   try {
61✔
383
      const OID oid = OID::from_string(str);
61✔
384
      if(oid.has_value()) {
56✔
385
         m_data = ec_group_data().lookup(oid);
56✔
386
      }
387
   } catch(...) {}
61✔
388

389
   if(m_data == nullptr) {
61✔
390
      if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----") {
5✔
391
         // OK try it as PEM ...
392
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
5✔
393

394
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
5✔
395
         this->m_data = data.first;
5✔
396
         this->m_explicit_encoding = data.second;
5✔
397
      }
10✔
398
   }
399

400
   if(m_data == nullptr) {
61✔
401
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
402
   }
403
}
×
404

405
//static
406
EC_Group EC_Group::from_PEM(std::string_view pem) {
1✔
407
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
1✔
408
   return EC_Group(ber);
1✔
409
}
1✔
410

411
EC_Group::EC_Group(const BigInt& p,
3✔
412
                   const BigInt& a,
413
                   const BigInt& b,
414
                   const BigInt& base_x,
415
                   const BigInt& base_y,
416
                   const BigInt& order,
417
                   const BigInt& cofactor,
418
                   const OID& oid) {
3✔
419
   if(oid.has_value()) {
3✔
420
      m_data = ec_group_data().lookup_or_create(
4✔
421
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
422
   } else {
423
      m_data = ec_group_data().lookup_or_create_without_oid(
2✔
424
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
1✔
425
   }
426
}
3✔
427

428
EC_Group::EC_Group(const OID& oid,
47✔
429
                   const BigInt& p,
430
                   const BigInt& a,
431
                   const BigInt& b,
432
                   const BigInt& base_x,
433
                   const BigInt& base_y,
434
                   const BigInt& order) {
47✔
435
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
47✔
436

437
   // TODO(Botan4) remove this and require 192 bits minimum
438
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
439
   constexpr size_t p_bits_lower_bound = 192;
440
#else
441
   constexpr size_t p_bits_lower_bound = 128;
47✔
442
#endif
443

444
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
47✔
445
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
47✔
446

447
   if(p.bits() == 521) {
47✔
448
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
449
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
450
   } else if(p.bits() == 239) {
47✔
451
      const auto x962_p239 = []() {
18✔
452
         BigInt p239;
3✔
453
         for(size_t i = 0; i != 239; ++i) {
720✔
454
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
455
               p239.set_bit(i);
717✔
456
            }
457
         }
458
         return p239;
3✔
459
      }();
3✔
460

461
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
462
   } else {
3✔
463
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
43✔
464
   }
465

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

468
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
84✔
469
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
84✔
470
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
84✔
471
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
84✔
472
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
42✔
473

474
   auto mod_p = Modular_Reducer::for_public_modulus(p);
39✔
475
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
39✔
476

477
   auto mod_order = Modular_Reducer::for_public_modulus(order);
39✔
478
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
39✔
479

480
   // This catches someone "ignoring" a cofactor and just trying to
481
   // provide the subgroup order
482
   BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid");
39✔
483

484
   // Check that 4*a^3 + 27*b^2 != 0
485
   const auto discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b)));
40✔
486
   BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
39✔
487

488
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
489
   auto y2 = mod_p.square(base_y);
39✔
490
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
39✔
491
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
39✔
492

493
   BigInt cofactor(1);
38✔
494

495
   m_data =
38✔
496
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
38✔
497
}
127✔
498

499
EC_Group::EC_Group(std::span<const uint8_t> ber) {
6,389✔
500
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
6,389✔
501
   m_data = data.first;
6,009✔
502
   m_explicit_encoding = data.second;
6,009✔
503
}
6,389✔
504

505
const EC_Group_Data& EC_Group::data() const {
122,863✔
506
   if(m_data == nullptr) {
122,863✔
507
      throw Invalid_State("EC_Group uninitialized");
×
508
   }
509
   return *m_data;
122,863✔
510
}
511

512
size_t EC_Group::get_p_bits() const {
1,288✔
513
   return data().p_bits();
1,288✔
514
}
515

516
size_t EC_Group::get_p_bytes() const {
13,661✔
517
   return data().p_bytes();
13,661✔
518
}
519

520
size_t EC_Group::get_order_bits() const {
1,147✔
521
   return data().order_bits();
1,147✔
522
}
523

524
size_t EC_Group::get_order_bytes() const {
30,476✔
525
   return data().order_bytes();
30,476✔
526
}
527

528
const BigInt& EC_Group::get_p() const {
26,583✔
529
   return data().p();
26,583✔
530
}
531

532
const BigInt& EC_Group::get_a() const {
480✔
533
   return data().a();
480✔
534
}
535

536
const BigInt& EC_Group::get_b() const {
368✔
537
   return data().b();
368✔
538
}
539

540
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
541
const EC_Point& EC_Group::get_base_point() const {
677✔
542
   return data().base_point();
677✔
543
}
544

545
const EC_Point& EC_Group::generator() const {
×
546
   return data().base_point();
×
547
}
548

549
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
550
   //check that public point is not at infinity
551
   if(point.is_zero()) {
×
552
      return false;
553
   }
554

555
   //check that public point is on the curve
556
   if(point.on_the_curve() == false) {
×
557
      return false;
558
   }
559

560
   //check that public point has order q
561
   if((point * get_order()).is_zero() == false) {
×
562
      return false;
563
   }
564

565
   if(has_cofactor()) {
×
566
      if((point * get_cofactor()).is_zero()) {
×
567
         return false;
568
      }
569
   }
570

571
   return true;
572
}
573

574
#endif
575

576
const BigInt& EC_Group::get_order() const {
7,903✔
577
   return data().order();
7,903✔
578
}
579

580
const BigInt& EC_Group::get_g_x() const {
979✔
581
   return data().g_x();
979✔
582
}
583

584
const BigInt& EC_Group::get_g_y() const {
979✔
585
   return data().g_y();
979✔
586
}
587

588
const BigInt& EC_Group::get_cofactor() const {
208✔
589
   return data().cofactor();
208✔
590
}
591

592
bool EC_Group::has_cofactor() const {
11,810✔
593
   return data().has_cofactor();
11,810✔
594
}
595

596
const OID& EC_Group::get_curve_oid() const {
24,843✔
597
   return data().oid();
24,843✔
598
}
599

600
EC_Group_Source EC_Group::source() const {
134✔
601
   return data().source();
134✔
602
}
603

604
EC_Group_Engine EC_Group::engine() const {
2✔
605
   return data().engine();
2✔
606
}
607

608
std::vector<uint8_t> EC_Group::DER_encode() const {
1,325✔
609
   const auto& der_named_curve = data().der_named_curve();
1,325✔
610
   // TODO(Botan4) this can be removed because an OID will always be defined
611
   if(der_named_curve.empty()) {
1,325✔
612
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
613
   }
614

615
   return der_named_curve;
1,325✔
616
}
617

618
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
1,267✔
619
   if(form == EC_Group_Encoding::Explicit) {
1,267✔
620
      std::vector<uint8_t> output;
31✔
621
      DER_Encoder der(output);
31✔
622
      const size_t ecpVers1 = 1;
31✔
623
      const OID curve_type("1.2.840.10045.1.1");  // prime field
31✔
624

625
      const size_t p_bytes = get_p_bytes();
31✔
626

627
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
31✔
628

629
      der.start_sequence()
31✔
630
         .encode(ecpVers1)
31✔
631
         .start_sequence()
31✔
632
         .encode(curve_type)
31✔
633
         .encode(get_p())
31✔
634
         .end_cons()
31✔
635
         .start_sequence()
31✔
636
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
31✔
637
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
62✔
638
         .end_cons()
31✔
639
         .encode(generator, ASN1_Type::OctetString)
31✔
640
         .encode(get_order())
31✔
641
         .encode(get_cofactor())
31✔
642
         .end_cons();
31✔
643
      return output;
31✔
644
   } else if(form == EC_Group_Encoding::NamedCurve) {
1,267✔
645
      return this->DER_encode();
1,236✔
646
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
647
      return {0x00, 0x05};
×
648
   } else {
649
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
650
   }
651
}
652

653
std::string EC_Group::PEM_encode() const {
1✔
654
   const std::vector<uint8_t> der = DER_encode(EC_Group_Encoding::Explicit);
1✔
655
   return PEM_Code::encode(der, "EC PARAMETERS");
1✔
656
}
1✔
657

658
bool EC_Group::operator==(const EC_Group& other) const {
48✔
659
   if(m_data == other.m_data) {
48✔
660
      return true;  // same shared rep
661
   }
662

663
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
×
664
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
×
665
           get_cofactor() == other.get_cofactor());
×
666
}
667

668
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
134✔
669
   const bool is_builtin = source() == EC_Group_Source::Builtin;
134✔
670

671
   if(is_builtin && !strong) {
134✔
672
      return true;
673
   }
674

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

679
   const BigInt& p = get_p();
28✔
680
   const BigInt& a = get_a();
28✔
681
   const BigInt& b = get_b();
28✔
682
   const BigInt& order = get_order();
28✔
683

684
   if(p <= 3 || order <= 0) {
56✔
685
      return false;
×
686
   }
687
   if(a < 0 || a >= p) {
56✔
688
      return false;
×
689
   }
690
   if(b <= 0 || b >= p) {
56✔
691
      return false;
×
692
   }
693

694
   const size_t test_prob = 128;
28✔
695
   const bool is_randomly_generated = is_builtin;
28✔
696

697
   //check if field modulus is prime
698
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
28✔
699
      return false;
700
   }
701

702
   //check if order is prime
703
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
28✔
704
      return false;
705
   }
706

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

710
   const BigInt discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b)));
28✔
711

712
   if(discriminant == 0) {
28✔
713
      return false;
714
   }
715

716
   //check for valid cofactor
717
   if(get_cofactor() < 1) {
28✔
718
      return false;
719
   }
720

721
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
722
   const EC_Point& base_point = get_base_point();
28✔
723
   //check if the base point is on the curve
724
   if(!base_point.on_the_curve()) {
28✔
725
      return false;
726
   }
727
   if((base_point * get_cofactor()).is_zero()) {
56✔
728
      return false;
729
   }
730
   //check if order of the base point is correct
731
   if(!(base_point * order).is_zero()) {
28✔
732
      return false;
733
   }
734
#endif
735

736
   // check the Hasse bound (roughly)
737
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
28✔
738
      return false;
739
   }
740

741
   return true;
742
}
56✔
743

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

746
EC_Group::Mul2Table::~Mul2Table() = default;
13,261✔
747

748
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,126✔
749
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,126✔
750
   if(pt) {
2,126✔
751
      return EC_AffinePoint::_from_inner(std::move(pt));
4,252✔
752
   } else {
753
      return {};
×
754
   }
755
}
2,126✔
756

757
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
22,989✔
758
                                                      const EC_Scalar& x,
759
                                                      const EC_Scalar& y) const {
760
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
22,989✔
761
}
762

763
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
22,819✔
764
                                                      const EC_Scalar& c,
765
                                                      const EC_Scalar& x,
766
                                                      const EC_Scalar& y) const {
767
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
22,819✔
768
}
769

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