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

randombit / botan / 21399773049

27 Jan 2026 01:51PM UTC coverage: 90.072% (-0.001%) from 90.073%
21399773049

Pull #5268

github

web-flow
Merge dd3337d77 into 0d718b146
Pull Request #5268: Avoid validation of known EC groups when decoding an explicit curve block

102164 of 113425 relevant lines covered (90.07%)

11494463.5 hits per line

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

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

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

41
         for(auto i : m_registered_curves) {
129,012✔
42
            if(i->oid() == oid) {
127,830✔
43
               return i;
23,851✔
44
            }
45
         }
127,830✔
46

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

50
         if(data) {
1,182✔
51
            m_registered_curves.push_back(data);
1,122✔
52
            return data;
1,122✔
53
         }
54

55
         // Nope, unknown curve
56
         return std::shared_ptr<EC_Group_Data>();
60✔
57
      }
26,215✔
58

59
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
43✔
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());
43✔
69

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

72
         for(auto i : m_registered_curves) {
874✔
73
            if(i->oid() == oid) {
864✔
74
               /*
75
               * If both OID and params are the same then we are done, just return
76
               * the already registered curve obj.
77
               *
78
               * First verify that the params match, to catch an application
79
               * that is attempting to register a EC_Group under the same OID as
80
               * another group currently in use
81
               */
82
               if(!i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
33✔
83
                  throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
×
84
                                         " but a distinct curve is already registered using that OID");
×
85
               }
86

87
               return i;
33✔
88
            }
89

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

103
         /*
104
         * Not found in current list, so we need to create a new entry
105
         */
106
         auto new_group = [&] {
×
107
            if(auto g = EC_Group::EC_group_info(oid); g != nullptr) {
10✔
108
               /*
109
               * This turned out to be the OID of one of the builtin groups. Verify
110
               * that all of the provided parameters match that builtin group.
111
               */
112
               BOTAN_ARG_CHECK(g->params_match(p, a, b, g_x, g_y, order, cofactor),
×
113
                               "Attempting to register an EC group under OID of hardcoded group");
114

115
               return g;
×
116
            } else {
117
               /*
118
               * This path is taken for an application registering a new EC_Group with an OID specified
119
               */
120
               return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
10✔
121
            }
10✔
122
         }();
10✔
123

124
         m_registered_curves.push_back(new_group);
10✔
125
         return new_group;
10✔
126
      }
53✔
127

128
      std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
68✔
129
                                                        const BigInt& a,
130
                                                        const BigInt& b,
131
                                                        std::span<const uint8_t> base_pt,
132
                                                        const BigInt& order,
133
                                                        const BigInt& cofactor) {
134
         const lock_guard_type<mutex_type> lock(m_mutex);
68✔
135

136
         for(auto i : m_registered_curves) {
831✔
137
            if(i->params_match(p, a, b, base_pt, order, cofactor)) {
816✔
138
               return i;
53✔
139
            }
140
         }
816✔
141

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

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

154
         return {};
10✔
155
      }
82✔
156

157
      // TODO(Botan4) this entire function can be removed since OIDs will be required
158
      std::shared_ptr<EC_Group_Data> lookup_or_create_without_oid(const BigInt& p,
6✔
159
                                                                  const BigInt& a,
160
                                                                  const BigInt& b,
161
                                                                  const BigInt& g_x,
162
                                                                  const BigInt& g_y,
163
                                                                  const BigInt& order,
164
                                                                  const BigInt& cofactor,
165
                                                                  EC_Group_Source source) {
166
         const lock_guard_type<mutex_type> lock(m_mutex);
6✔
167

168
         for(auto i : m_registered_curves) {
100✔
169
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
94✔
170
               return i;
×
171
            }
172
         }
94✔
173

174
         // Try to use the order as a hint to look up the group id
175
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
6✔
176
         if(oid_from_order.has_value()) {
6✔
177
            auto new_group = EC_Group::EC_group_info(oid_from_order);
3✔
178

179
            // Have to check all params in the (unlikely/malicious) event of an order collision
180
            if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
3✔
181
               m_registered_curves.push_back(new_group);
1✔
182
               return new_group;
1✔
183
            }
184
         }
3✔
185

186
         /*
187
         * At this point we have failed to identify the group; it is not any of
188
         * the builtin values, nor is it a group that the user had previously
189
         * registered explicitly. We create the group data without an OID.
190
         *
191
         * TODO(Botan4) remove this; throw an exception instead
192
         */
193
         auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
5✔
194
         m_registered_curves.push_back(new_group);
5✔
195
         return new_group;
5✔
196
      }
17✔
197

198
   private:
199
      mutex_type m_mutex;
200
      // TODO(Botan4): Once OID is required we could make this into a map
201
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
202
};
203

204
//static
205
EC_Group_Data_Map& EC_Group::ec_group_data() {
26,914✔
206
   /*
207
   * This exists purely to ensure the allocator is constructed before g_ec_data,
208
   * which ensures that its destructor runs after ~g_ec_data is complete.
209
   */
210

211
   static const Allocator_Initializer g_init_allocator;
26,914✔
212
   static EC_Group_Data_Map g_ec_data;
26,914✔
213
   return g_ec_data;
26,914✔
214
}
215

216
//static
217
size_t EC_Group::clear_registered_curve_data() {
1,764✔
218
   return ec_group_data().clear();
1,764✔
219
}
220

221
//static
222
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
1,136✔
223
                                                            const char* a_str,
224
                                                            const char* b_str,
225
                                                            const char* g_x_str,
226
                                                            const char* g_y_str,
227
                                                            const char* order_str,
228
                                                            const OID& oid) {
229
   BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
1,136✔
230

231
   const BigInt p(p_str);
1,136✔
232
   const BigInt a(a_str);
1,136✔
233
   const BigInt b(b_str);
1,136✔
234
   const BigInt g_x(g_x_str);
1,136✔
235
   const BigInt g_y(g_y_str);
1,136✔
236
   const BigInt order(order_str);
1,136✔
237
   const BigInt cofactor(1);  // implicit
1,136✔
238

239
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
2,272✔
240
}
1,136✔
241

242
//static
243
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
9,534✔
244
                                                                              EC_Group_Source source) {
245
   BER_Decoder ber(bits);
9,534✔
246

247
   auto next_obj_type = ber.peek_next_object().type_tag();
9,534✔
248

249
   if(next_obj_type == ASN1_Type::ObjectId) {
9,526✔
250
      OID oid;
9,173✔
251
      ber.decode(oid);
9,173✔
252

253
      auto data = ec_group_data().lookup(oid);
9,131✔
254
      if(!data) {
9,131✔
255
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
120✔
256
      }
257

258
      return std::make_pair(data, false);
9,071✔
259
   } else if(next_obj_type == ASN1_Type::Sequence) {
9,586✔
260
      BigInt p;
349✔
261
      BigInt a;
349✔
262
      BigInt b;
349✔
263
      BigInt order;
349✔
264
      BigInt cofactor;
349✔
265
      std::vector<uint8_t> base_pt;
349✔
266
      std::vector<uint8_t> seed;
349✔
267

268
      ber.start_sequence()
349✔
269
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
675✔
270
         .start_sequence()
327✔
271
         .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
654✔
272
         .decode(p)
326✔
273
         .end_cons()
326✔
274
         .start_sequence()
326✔
275
         .decode_octet_string_bigint(a)
326✔
276
         .decode_octet_string_bigint(b)
325✔
277
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString)
323✔
278
         .end_cons()
323✔
279
         .decode(base_pt, ASN1_Type::OctetString)
320✔
280
         .decode(order)
313✔
281
         .decode(cofactor)
312✔
282
         .end_cons()
312✔
283
         .verify_end();
311✔
284

285
      // TODO(Botan4) Require cofactor == 1
286
      if(cofactor <= 0 || cofactor >= 16) {
618✔
287
         throw Decoding_Error("Invalid ECC cofactor parameter");
8✔
288
      }
289

290
      if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) {
303✔
291
         throw Decoding_Error("ECC p parameter is invalid size");
134✔
292
      }
293

294
      if(a.is_negative() || a >= p) {
338✔
295
         throw Decoding_Error("Invalid ECC a parameter");
20✔
296
      }
297

298
      if(b <= 0 || b >= p) {
297✔
299
         throw Decoding_Error("Invalid ECC b parameter");
77✔
300
      }
301

302
      if(order.is_negative() || order.is_zero() || order >= 2 * p) {
282✔
303
         throw Decoding_Error("Invalid ECC group order");
4✔
304
      }
305

306
      if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
355✔
307
         return std::make_pair(data, true);
57✔
308
      }
57✔
309

310
      /*
311
      TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
312
      which is neither a builtin group nor a group that was registered by the application.
313
      It can all be removed and replaced with a throw
314
      */
315

316
      auto mod_p = Barrett_Reduction::for_public_modulus(p);
10✔
317
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
10✔
318
         throw Decoding_Error("ECC p parameter is not a prime");
3✔
319
      }
320

321
      auto mod_order = Barrett_Reduction::for_public_modulus(order);
7✔
322
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
7✔
323
         throw Decoding_Error("Invalid ECC order parameter");
2✔
324
      }
325

326
      const size_t p_bytes = p.bytes();
5✔
327
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
5✔
328
         throw Decoding_Error("Invalid ECC base point encoding");
×
329
      }
330

331
      auto [g_x, g_y] = [&]() {
8✔
332
         const uint8_t hdr = base_pt[0];
5✔
333

334
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
5✔
335
            const BigInt x = BigInt::decode(&base_pt[1], p_bytes);
5✔
336
            const BigInt y = BigInt::decode(&base_pt[p_bytes + 1], p_bytes);
5✔
337

338
            if(x < p && y < p) {
5✔
339
               return std::make_pair(x, y);
5✔
340
            }
341
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
5✔
342
            // TODO(Botan4) remove this branch; we won't support compressed points
343
            const BigInt x = BigInt::decode(&base_pt[1], p_bytes);
×
344
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
345

346
            if(x < p && y >= 0) {
×
347
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
348
               if(y.get_bit(0) != y_mod_2) {
×
349
                  y = p - y;
×
350
               }
351

352
               return std::make_pair(x, y);
×
353
            }
354
         }
×
355

356
         throw Decoding_Error("Invalid ECC base point encoding");
×
357
      }();
5✔
358

359
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
360
      auto y2 = mod_p.square(g_y);
5✔
361
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
5✔
362
      if(y2 != x3_ax_b) {
5✔
363
         throw Decoding_Error("Invalid ECC base point");
1✔
364
      }
365

366
      auto data = ec_group_data().lookup_or_create_without_oid(p, a, b, g_x, g_y, order, cofactor, source);
4✔
367
      return std::make_pair(data, true);
4✔
368
   } else if(next_obj_type == ASN1_Type::Null) {
1,882✔
369
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
1✔
370
   } else {
371
      throw Decoding_Error(
3✔
372
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
6✔
373
   }
374
}
9,534✔
375

376
EC_Group::EC_Group() = default;
×
377

378
EC_Group::~EC_Group() = default;
147,566✔
379

380
EC_Group::EC_Group(const EC_Group&) = default;
59,016✔
381

382
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
383

384
// Internal constructor
385
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,853✔
386

387
//static
388
bool EC_Group::supports_named_group(std::string_view name) {
15,320✔
389
   return EC_Group::known_named_groups().contains(std::string(name));
15,320✔
390
}
391

392
//static
393
bool EC_Group::supports_application_specific_group() {
25✔
394
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
395
   return true;
25✔
396
#else
397
   return false;
398
#endif
399
}
400

401
//static
402
bool EC_Group::supports_application_specific_group_with_cofactor() {
5✔
403
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
404
   return true;
5✔
405
#else
406
   return false;
407
#endif
408
}
409

410
//static
411
EC_Group EC_Group::from_OID(const OID& oid) {
73✔
412
   auto data = ec_group_data().lookup(oid);
73✔
413

414
   if(!data) {
73✔
415
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
×
416
   }
417

418
   return EC_Group(std::move(data));
73✔
419
}
73✔
420

421
//static
422
EC_Group EC_Group::from_name(std::string_view name) {
15,781✔
423
   std::shared_ptr<EC_Group_Data> data;
15,781✔
424

425
   if(auto oid = OID::from_name(name)) {
15,781✔
426
      data = ec_group_data().lookup(oid.value());
31,560✔
427
   }
×
428

429
   if(!data) {
15,780✔
430
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
431
   }
432

433
   return EC_Group(std::move(data));
15,780✔
434
}
15,780✔
435

436
EC_Group::EC_Group(std::string_view str) {
49✔
437
   if(str.empty()) {
49✔
438
      return;  // no initialization / uninitialized
439
   }
440

441
   try {
49✔
442
      const OID oid = OID::from_string(str);
49✔
443
      if(oid.has_value()) {
49✔
444
         m_data = ec_group_data().lookup(oid);
49✔
445
      }
446
   } catch(...) {}
49✔
447

448
   if(m_data == nullptr) {
49✔
449
      if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
×
450
         // OK try it as PEM ...
451
         const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
×
452

453
         auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
×
454
         this->m_data = data.first;
×
455
         this->m_explicit_encoding = data.second;
×
456
      }
×
457
   }
458

459
   if(m_data == nullptr) {
49✔
460
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
461
   }
462
}
×
463

464
//static
465
EC_Group EC_Group::from_PEM(std::string_view pem) {
4✔
466
   const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
4✔
467
   return EC_Group(ber);
4✔
468
}
4✔
469

470
EC_Group::EC_Group(const BigInt& p,
4✔
471
                   const BigInt& a,
472
                   const BigInt& b,
473
                   const BigInt& base_x,
474
                   const BigInt& base_y,
475
                   const BigInt& order,
476
                   const BigInt& cofactor,
477
                   const OID& oid) {
4✔
478
   if(oid.has_value()) {
4✔
479
      m_data = ec_group_data().lookup_or_create(
4✔
480
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
481
   } else {
482
      m_data = ec_group_data().lookup_or_create_without_oid(
4✔
483
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
2✔
484
   }
485
}
4✔
486

487
EC_Group::EC_Group(const OID& oid,
50✔
488
                   const BigInt& p,
489
                   const BigInt& a,
490
                   const BigInt& b,
491
                   const BigInt& base_x,
492
                   const BigInt& base_y,
493
                   const BigInt& order) {
50✔
494
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
50✔
495

496
   // TODO(Botan4) remove this and require 192 bits minimum
497
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
498
   constexpr size_t p_bits_lower_bound = 192;
499
#else
500
   constexpr size_t p_bits_lower_bound = 128;
50✔
501
#endif
502

503
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
50✔
504
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
50✔
505

506
   if(p.bits() == 521) {
50✔
507
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
508
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
509
   } else if(p.bits() == 239) {
50✔
510
      const auto x962_p239 = []() {
18✔
511
         BigInt p239;
3✔
512
         for(size_t i = 0; i != 239; ++i) {
720✔
513
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
514
               p239.set_bit(i);
717✔
515
            }
516
         }
517
         return p239;
3✔
518
      }();
3✔
519

520
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
521
   } else {
3✔
522
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
46✔
523
   }
524

525
   BOTAN_ARG_CHECK(p % 4 == 3, "EC_Group p must be congruent to 3 modulo 4");
47✔
526

527
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
90✔
528
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
90✔
529
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
90✔
530
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
90✔
531
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
45✔
532

533
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
42✔
534
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
42✔
535

536
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
42✔
537
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
42✔
538

539
   // This catches someone "ignoring" a cofactor and just trying to
540
   // provide the subgroup order
541
   BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid");
42✔
542

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

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

553
   const BigInt cofactor(1);
41✔
554

555
   m_data =
41✔
556
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
41✔
557
}
136✔
558

559
EC_Group::EC_Group(std::span<const uint8_t> ber) {
9,534✔
560
   auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
9,534✔
561
   m_data = data.first;
9,132✔
562
   m_explicit_encoding = data.second;
9,132✔
563
}
9,534✔
564

565
const EC_Group_Data& EC_Group::data() const {
137,104✔
566
   if(m_data == nullptr) {
137,104✔
567
      throw Invalid_State("EC_Group uninitialized");
×
568
   }
569
   return *m_data;
137,104✔
570
}
571

572
size_t EC_Group::get_p_bits() const {
1,630✔
573
   return data().p_bits();
1,630✔
574
}
575

576
size_t EC_Group::get_p_bytes() const {
13,664✔
577
   return data().p_bytes();
13,664✔
578
}
579

580
size_t EC_Group::get_order_bits() const {
2,751✔
581
   return data().order_bits();
2,751✔
582
}
583

584
size_t EC_Group::get_order_bytes() const {
37,927✔
585
   return data().order_bytes();
37,927✔
586
}
587

588
const BigInt& EC_Group::get_p() const {
26,568✔
589
   return data().p();
26,568✔
590
}
591

592
const BigInt& EC_Group::get_a() const {
479✔
593
   return data().a();
479✔
594
}
595

596
const BigInt& EC_Group::get_b() const {
367✔
597
   return data().b();
367✔
598
}
599

600
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
601
const EC_Point& EC_Group::get_base_point() const {
677✔
602
   return data().base_point();
677✔
603
}
604

605
const EC_Point& EC_Group::generator() const {
×
606
   return data().base_point();
×
607
}
608

609
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
610
   //check that public point is not at infinity
611
   if(point.is_zero()) {
×
612
      return false;
613
   }
614

615
   //check that public point is on the curve
616
   if(point.on_the_curve() == false) {
×
617
      return false;
618
   }
619

620
   //check that public point has order q
621
   if((point * get_order()).is_zero() == false) {
×
622
      return false;
623
   }
624

625
   if(has_cofactor()) {
×
626
      if((point * get_cofactor()).is_zero()) {
×
627
         return false;
628
      }
629
   }
630

631
   return true;
632
}
633

634
#endif
635

636
const BigInt& EC_Group::get_order() const {
7,890✔
637
   return data().order();
7,890✔
638
}
639

640
const BigInt& EC_Group::get_g_x() const {
1,016✔
641
   return data().g_x();
1,016✔
642
}
643

644
const BigInt& EC_Group::get_g_y() const {
1,016✔
645
   return data().g_y();
1,016✔
646
}
647

648
const BigInt& EC_Group::get_cofactor() const {
158✔
649
   return data().cofactor();
158✔
650
}
651

652
bool EC_Group::has_cofactor() const {
11,865✔
653
   return data().has_cofactor();
11,865✔
654
}
655

656
const OID& EC_Group::get_curve_oid() const {
28,230✔
657
   return data().oid();
28,230✔
658
}
659

660
EC_Group_Source EC_Group::source() const {
139✔
661
   return data().source();
139✔
662
}
663

664
EC_Group_Engine EC_Group::engine() const {
2✔
665
   return data().engine();
2✔
666
}
667

668
std::vector<uint8_t> EC_Group::DER_encode() const {
2,725✔
669
   const auto& der_named_curve = data().der_named_curve();
2,725✔
670
   // TODO(Botan4) this can be removed because an OID will always be defined
671
   if(der_named_curve.empty()) {
2,725✔
672
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
673
   }
674

675
   return der_named_curve;
2,725✔
676
}
677

678
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
2,720✔
679
   if(form == EC_Group_Encoding::Explicit) {
2,720✔
680
      std::vector<uint8_t> output;
30✔
681
      DER_Encoder der(output);
30✔
682
      const size_t ecpVers1 = 1;
30✔
683
      const OID curve_type("1.2.840.10045.1.1");  // prime field
30✔
684

685
      const size_t p_bytes = get_p_bytes();
30✔
686

687
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
30✔
688

689
      der.start_sequence()
30✔
690
         .encode(ecpVers1)
30✔
691
         .start_sequence()
30✔
692
         .encode(curve_type)
30✔
693
         .encode(get_p())
30✔
694
         .end_cons()
30✔
695
         .start_sequence()
30✔
696
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
30✔
697
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
60✔
698
         .end_cons()
30✔
699
         .encode(generator, ASN1_Type::OctetString)
30✔
700
         .encode(get_order())
30✔
701
         .encode(get_cofactor())
30✔
702
         .end_cons();
30✔
703
      return output;
30✔
704
   } else if(form == EC_Group_Encoding::NamedCurve) {
2,720✔
705
      return this->DER_encode();
2,690✔
706
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
707
      return {0x00, 0x05};
×
708
   } else {
709
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
710
   }
711
}
712

713
std::string EC_Group::PEM_encode(EC_Group_Encoding form) const {
3✔
714
   const std::vector<uint8_t> der = DER_encode(form);
3✔
715
   return PEM_Code::encode(der, "EC PARAMETERS");
3✔
716
}
3✔
717

718
bool EC_Group::operator==(const EC_Group& other) const {
58✔
719
   if(m_data == other.m_data) {
58✔
720
      return true;  // same shared rep
721
   }
722

723
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
5✔
724
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
5✔
725
           get_cofactor() == other.get_cofactor());
2✔
726
}
727

728
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
139✔
729
   const bool is_builtin = source() == EC_Group_Source::Builtin;
139✔
730

731
   if(is_builtin && !strong) {
139✔
732
      return true;
733
   }
734

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

739
   const BigInt& p = get_p();
28✔
740
   const BigInt& a = get_a();
28✔
741
   const BigInt& b = get_b();
28✔
742
   const BigInt& order = get_order();
28✔
743

744
   if(p <= 3 || order <= 0) {
56✔
745
      return false;
×
746
   }
747
   if(a < 0 || a >= p) {
56✔
748
      return false;
×
749
   }
750
   if(b <= 0 || b >= p) {
56✔
751
      return false;
×
752
   }
753

754
   const size_t test_prob = 128;
28✔
755
   const bool is_randomly_generated = is_builtin;
28✔
756

757
   //check if field modulus is prime
758
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
28✔
759
      return false;
760
   }
761

762
   //check if order is prime
763
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
28✔
764
      return false;
765
   }
766

767
   //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
768
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
28✔
769

770
   const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
56✔
771
                                            mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
84✔
772

773
   if(discriminant == 0) {
28✔
774
      return false;
775
   }
776

777
   //check for valid cofactor
778
   if(get_cofactor() < 1) {
28✔
779
      return false;
780
   }
781

782
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
783
   const EC_Point& base_point = get_base_point();
28✔
784
   //check if the base point is on the curve
785
   if(!base_point.on_the_curve()) {
28✔
786
      return false;
787
   }
788
   if((base_point * get_cofactor()).is_zero()) {
56✔
789
      return false;
790
   }
791
   //check if order of the base point is correct
792
   if(!(base_point * order).is_zero()) {
28✔
793
      return false;
794
   }
795
#endif
796

797
   // check the Hasse bound (roughly)
798
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
28✔
799
      return false;
800
   }
801

802
   return true;
803
}
56✔
804

805
EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
×
806

807
EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default;
×
808

809
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
14,979✔
810

811
EC_Group::Mul2Table::~Mul2Table() = default;
14,979✔
812

813
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,101✔
814
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,101✔
815
   if(pt) {
2,101✔
816
      return EC_AffinePoint::_from_inner(std::move(pt));
4,202✔
817
   } else {
818
      return {};
×
819
   }
820
}
2,101✔
821

822
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
24,754✔
823
                                                      const EC_Scalar& x,
824
                                                      const EC_Scalar& y) const {
825
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
24,754✔
826
}
827

828
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
24,566✔
829
                                                      const EC_Scalar& c,
830
                                                      const EC_Scalar& x,
831
                                                      const EC_Scalar& y) const {
832
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
24,566✔
833
}
834

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