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

randombit / botan / 25457312714

06 May 2026 07:43PM UTC coverage: 89.331% (-2.3%) from 91.667%
25457312714

push

github

randombit
In TLS 1.3 verification of client certs, check the correct extension for OCSP

This was checking if the client asked us (the server) for OCSP, instead of
checking if we asked the client for OCSP when we sent the CertificateRequest.

107574 of 120422 relevant lines covered (89.33%)

11482758.98 hits per line

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

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

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

44
         const lock_guard_type<mutex_type> lock(m_mutex);
13✔
45
         for(size_t i = 0; i < m_registered_curves.size(); i++) {
110✔
46
            if(m_registered_curves[i]->oid() == oid) {
104✔
47
               m_registered_curves.erase(m_registered_curves.begin() + i);
7✔
48
               return true;
7✔
49
            }
50
         }
51
         return false;
52
      }
13✔
53

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

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

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

66
         if(data) {
627✔
67
            m_registered_curves.push_back(data);
614✔
68
            return data;
614✔
69
         }
70

71
         // Nope, unknown curve
72
         return std::shared_ptr<EC_Group_Data>();
13✔
73
      }
23,496✔
74

75
      std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
44✔
76
                                                      const BigInt& a,
77
                                                      const BigInt& b,
78
                                                      const BigInt& g_x,
79
                                                      const BigInt& g_y,
80
                                                      const BigInt& order,
81
                                                      const BigInt& cofactor,
82
                                                      const OID& oid,
83
                                                      EC_Group_Source source) {
84
         BOTAN_ASSERT_NOMSG(oid.has_value());
44✔
85

86
         const lock_guard_type<mutex_type> lock(m_mutex);
44✔
87

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

103
               return i;
33✔
104
            }
105

106
            /*
107
            * If the same curve was previously created without an OID but is now
108
            * being registered again using an OID, save that OID.
109
            *
110
            * TODO(Botan4) remove this block; this situation won't be possible since
111
            * we will require all groups to have an OID
112
            */
113
            if(i->oid().empty() && i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
791✔
114
               i->set_oid(oid);
×
115
               return i;
×
116
            }
117
         }
824✔
118

119
         /*
120
         * Not found in current list, so we need to create a new entry
121
         */
122
         auto new_group = [&] {
×
123
            if(auto g = EC_Group::EC_group_info(oid); g != nullptr) {
11✔
124
               /*
125
               * This turned out to be the OID of one of the builtin groups. Verify
126
               * that all of the provided parameters match that builtin group.
127
               */
128
               BOTAN_ARG_CHECK(g->params_match(p, a, b, g_x, g_y, order, cofactor),
×
129
                               "Attempting to register an EC group under OID of hardcoded group");
130

131
               return g;
×
132
            } else {
133
               /*
134
               * This path is taken for an application registering a new EC_Group with an OID specified
135
               */
136
               return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
11✔
137
            }
11✔
138
         }();
11✔
139

140
         m_registered_curves.push_back(new_group);
11✔
141
         return new_group;
11✔
142
      }
55✔
143

144
      std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
68✔
145
                                                        const BigInt& a,
146
                                                        const BigInt& b,
147
                                                        std::span<const uint8_t> base_pt,
148
                                                        const BigInt& order,
149
                                                        const BigInt& cofactor) {
150
         const lock_guard_type<mutex_type> lock(m_mutex);
68✔
151

152
         for(auto i : m_registered_curves) {
833✔
153
            if(i->params_match(p, a, b, base_pt, order, cofactor)) {
817✔
154
               return i;
52✔
155
            }
156
         }
817✔
157

158
         // Try to use the order as a hint to look up the group id
159
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
16✔
160
         if(oid_from_order.has_value()) {
16✔
161
            auto new_group = EC_Group::EC_group_info(oid_from_order);
12✔
162

163
            // Have to check all params in the (unlikely/malicious) event of an order collision
164
            if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) {
12✔
165
               m_registered_curves.push_back(new_group);
4✔
166
               return new_group;
4✔
167
            }
168
         }
12✔
169

170
         return {};
11✔
171
      }
83✔
172

173
      // TODO(Botan4) this entire function can be removed since OIDs will be required
174
      std::shared_ptr<EC_Group_Data> lookup_or_create_without_oid(const BigInt& p,
2✔
175
                                                                  const BigInt& a,
176
                                                                  const BigInt& b,
177
                                                                  const BigInt& g_x,
178
                                                                  const BigInt& g_y,
179
                                                                  const BigInt& order,
180
                                                                  const BigInt& cofactor,
181
                                                                  EC_Group_Source source) {
182
         const lock_guard_type<mutex_type> lock(m_mutex);
2✔
183

184
         for(auto i : m_registered_curves) {
30✔
185
            if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
28✔
186
               return i;
×
187
            }
188
         }
28✔
189

190
         // Try to use the order as a hint to look up the group id
191
         const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
2✔
192
         if(oid_from_order.has_value()) {
2✔
193
            auto new_group = EC_Group::EC_group_info(oid_from_order);
1✔
194

195
            // Have to check all params in the (unlikely/malicious) event of an order collision
196
            if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
1✔
197
               m_registered_curves.push_back(new_group);
1✔
198
               return new_group;
1✔
199
            }
200
         }
1✔
201

202
         /*
203
         * At this point we have failed to identify the group; it is not any of
204
         * the builtin values, nor is it a group that the user had previously
205
         * registered explicitly. We create the group data without an OID.
206
         *
207
         * TODO(Botan4) remove this; throw an exception instead
208
         */
209
         auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
1✔
210
         m_registered_curves.push_back(new_group);
1✔
211
         return new_group;
1✔
212
      }
5✔
213

214
   private:
215
      mutex_type m_mutex;
216
      // TODO(Botan4): Once OID is required we could make this into a map
217
      std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
218
};
219

220
//static
221
EC_Group_Data_Map& EC_Group::ec_group_data() {
24,769✔
222
   /*
223
   * This exists purely to ensure the allocator is constructed before g_ec_data,
224
   * which ensures that its destructor runs after ~g_ec_data is complete.
225
   */
226

227
   static const Allocator_Initializer g_init_allocator;
24,769✔
228
   static EC_Group_Data_Map g_ec_data;
24,769✔
229
   return g_ec_data;
24,769✔
230
}
231

232
//static
233
size_t EC_Group::clear_registered_curve_data() {
1,772✔
234
   return ec_group_data().clear();
1,772✔
235
}
236

237
//static
238
std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
627✔
239
                                                            const char* a_str,
240
                                                            const char* b_str,
241
                                                            const char* g_x_str,
242
                                                            const char* g_y_str,
243
                                                            const char* order_str,
244
                                                            const OID& oid) {
245
   BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
627✔
246

247
   const BigInt p(p_str);
627✔
248
   const BigInt a(a_str);
627✔
249
   const BigInt b(b_str);
627✔
250
   const BigInt g_x(g_x_str);
627✔
251
   const BigInt g_y(g_y_str);
627✔
252
   const BigInt order(order_str);
627✔
253
   const BigInt cofactor(1);  // implicit
627✔
254

255
   return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
1,254✔
256
}
627✔
257

258
//static
259
std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::DER_decode_EC_group(std::span<const uint8_t> der,
6,963✔
260
                                                                              EC_Group_Source source) {
261
   BER_Decoder dec(der, BER_Decoder::Limits::DER());
6,963✔
262

263
   auto next_obj_type = dec.peek_next_object().type_tag();
6,963✔
264

265
   if(next_obj_type == ASN1_Type::ObjectId) {
6,963✔
266
      OID oid;
6,821✔
267
      dec.decode(oid);
6,821✔
268

269
      auto data = ec_group_data().lookup(oid);
6,821✔
270
      if(!data) {
6,821✔
271
         throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
18✔
272
      }
273

274
      return std::make_pair(data, false);
6,812✔
275
   } else if(next_obj_type == ASN1_Type::Sequence) {
6,972✔
276
      BigInt p;
141✔
277
      BigInt a;
141✔
278
      BigInt b;
141✔
279
      BigInt order;
141✔
280
      BigInt cofactor;
141✔
281
      std::vector<uint8_t> base_pt;
141✔
282
      std::vector<uint8_t> seed;
141✔
283

284
      dec.start_sequence()
141✔
285
         .decode_and_check<size_t>(1, "Unknown ECC param version code")
282✔
286
         .start_sequence()
141✔
287
         .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
282✔
288
         .decode(p)
112✔
289
         .end_cons()
112✔
290
         .start_sequence()
112✔
291
         .decode_octet_string_bigint(a)
112✔
292
         .decode_octet_string_bigint(b)
112✔
293
         .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString, ASN1_Class::Universal)
94✔
294
         .end_cons()
94✔
295
         .decode(base_pt, ASN1_Type::OctetString)
94✔
296
         .decode(order)
92✔
297
         .decode(cofactor)
91✔
298
         .end_cons()
91✔
299
         .verify_end();
91✔
300

301
      // TODO(Botan4) Require cofactor == 1
302
      if(cofactor <= 0 || cofactor >= 16) {
180✔
303
         throw Decoding_Error("Invalid ECC cofactor parameter");
6✔
304
      }
305

306
      if(p.bits() < 112 || p.bits() > 521 || p.signum() < 0) {
85✔
307
         throw Decoding_Error("ECC p parameter is invalid size");
×
308
      }
309

310
      // A can be zero
311
      if(a.signum() < 0 || a >= p) {
170✔
312
         throw Decoding_Error("Invalid ECC a parameter");
4✔
313
      }
314

315
      // B must be > 0
316
      if(b.signum() <= 0 || b >= p) {
161✔
317
         throw Decoding_Error("Invalid ECC b parameter");
10✔
318
      }
319

320
      if(order.signum() <= 0 || order >= 2 * p) {
208✔
321
         throw Decoding_Error("Invalid ECC group order");
3✔
322
      }
323

324
      if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
146✔
325
         return std::make_pair(data, true);
56✔
326
      }
56✔
327

328
      /*
329
      TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
330
      which is neither a builtin group nor a group that was registered by the application.
331
      It can all be removed and replaced with a throw
332
      */
333

334
      auto mod_p = Barrett_Reduction::for_public_modulus(p);
11✔
335
      if(!is_bailie_psw_probable_prime(p, mod_p)) {
11✔
336
         throw Decoding_Error("ECC p parameter is not a prime");
2✔
337
      }
338

339
      auto mod_order = Barrett_Reduction::for_public_modulus(order);
9✔
340
      if(!is_bailie_psw_probable_prime(order, mod_order)) {
9✔
341
         throw Decoding_Error("Invalid ECC order parameter");
2✔
342
      }
343

344
      const size_t p_bytes = p.bytes();
7✔
345
      if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
7✔
346
         throw Decoding_Error("Invalid ECC base point encoding");
×
347
      }
348

349
      auto [g_x, g_y] = [&]() {
10✔
350
         const uint8_t hdr = base_pt[0];
7✔
351

352
         if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
7✔
353
            const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
7✔
354
            const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes));
7✔
355

356
            if(x < p && y < p) {
7✔
357
               return std::make_pair(x, y);
7✔
358
            }
359
         } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
7✔
360
            // TODO(Botan4) remove this branch; we won't support compressed points
361
            const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
×
362
            BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
×
363

364
            if(x < p && y >= 0) {
×
365
               const bool y_mod_2 = (hdr & 0x01) == 1;
×
366
               if(y.get_bit(0) != y_mod_2) {
×
367
                  y = p - y;
×
368
               }
369

370
               return std::make_pair(x, y);
×
371
            }
372
         }
×
373

374
         throw Decoding_Error("Invalid ECC base point encoding");
×
375
      }();
7✔
376

377
      // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
378
      auto y2 = mod_p.square(g_y);
7✔
379
      auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
7✔
380
      if(y2 != x3_ax_b) {
7✔
381
         throw Decoding_Error("Invalid ECC base point");
1✔
382
      }
383

384
      /*
385
      * Create the group data without registering it in the global map.
386
      *
387
      * Applications that need persistent custom groups should register them
388
      * via the relevant EC_Group constructor
389
      */
390
      auto data = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
6✔
391
      return std::make_pair(data, true);
6✔
392
   } else if(next_obj_type == ASN1_Type::Null) {
634✔
393
      throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
×
394
   } else {
395
      throw Decoding_Error(
1✔
396
         fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
2✔
397
   }
398
}
6,963✔
399

400
EC_Group::EC_Group() = default;
×
401

402
EC_Group::~EC_Group() = default;
114,852✔
403

404
EC_Group::EC_Group(const EC_Group&) = default;
56,172✔
405

406
EC_Group& EC_Group::operator=(const EC_Group&) = default;
×
407

408
// Internal constructor
409
EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
15,987✔
410

411
//static
412
bool EC_Group::supports_named_group(std::string_view name) {
15,405✔
413
   if(name.empty()) {
15,405✔
414
      return false;
415
   }
416

417
   // Is it one of the groups compiled into the library?
418
   if(EC_Group::known_named_groups().contains(std::string(name))) {
15,405✔
419
      return true;
420
   }
421

422
   // Is it a custom group registered by the application?
423
   if(auto oid = OID::from_name(name)) {
5✔
424
      try {
3✔
425
         if(ec_group_data().lookup(oid.value()) != nullptr) {
4✔
426
            return true;
1✔
427
         }
428
      } catch(Not_Implemented&) {
×
429
         // This would be thrown for example if the group is a known curve
430
         // but the relevant module that enables it is not compiled in
431
      }
×
432
   }
1✔
433

434
   // Not known
435
   return false;
4✔
436
}
437

438
//static
439
bool EC_Group::supports_application_specific_group() {
26✔
440
#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
441
   return true;
26✔
442
#else
443
   return false;
444
#endif
445
}
446

447
//static
448
bool EC_Group::supports_application_specific_group_with_cofactor() {
5✔
449
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
450
   return true;
5✔
451
#else
452
   return false;
453
#endif
454
}
455

456
//static
457
EC_Group EC_Group::from_OID(const OID& oid) {
75✔
458
   auto data = ec_group_data().lookup(oid);
75✔
459

460
   if(!data) {
75✔
461
      throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
4✔
462
   }
463

464
   return EC_Group(std::move(data));
73✔
465
}
73✔
466

467
//static
468
EC_Group EC_Group::from_name(std::string_view name) {
15,915✔
469
   std::shared_ptr<EC_Group_Data> data;
15,915✔
470

471
   if(auto oid = OID::from_name(name)) {
15,915✔
472
      data = ec_group_data().lookup(oid.value());
31,828✔
473
   }
×
474

475
   if(!data) {
15,914✔
476
      throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
×
477
   }
478

479
   return EC_Group(std::move(data));
15,914✔
480
}
15,914✔
481

482
EC_Group::EC_Group(std::string_view str) {
56✔
483
   if(str.empty()) {
56✔
484
      return;  // no initialization / uninitialized
485
   }
486

487
   try {
56✔
488
      const OID oid = OID::from_string(str);
56✔
489
      if(oid.has_value()) {
56✔
490
         m_data = ec_group_data().lookup(oid);
56✔
491
      }
492
   } catch(...) {}
56✔
493

494
   if(m_data == nullptr) {
56✔
495
      if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
×
496
         // OK try it as PEM ...
497
         const auto der = PEM_Code::decode_check_label(str, "EC PARAMETERS");
×
498

499
         auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
×
500
         this->m_data = data.first;
×
501
         this->m_explicit_encoding = data.second;
×
502
      }
×
503
   }
504

505
   if(m_data == nullptr) {
56✔
506
      throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
×
507
   }
508
}
×
509

510
//static
511
EC_Group EC_Group::from_PEM(std::string_view pem) {
4✔
512
   const auto der = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
4✔
513
   return EC_Group(der);
4✔
514
}
4✔
515

516
EC_Group::EC_Group(const BigInt& p,
4✔
517
                   const BigInt& a,
518
                   const BigInt& b,
519
                   const BigInt& base_x,
520
                   const BigInt& base_y,
521
                   const BigInt& order,
522
                   const BigInt& cofactor,
523
                   const OID& oid) {
4✔
524
   if(oid.has_value()) {
4✔
525
      m_data = ec_group_data().lookup_or_create(
4✔
526
         p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
2✔
527
   } else {
528
      m_data = ec_group_data().lookup_or_create_without_oid(
4✔
529
         p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
2✔
530
   }
531
}
4✔
532

533
EC_Group::EC_Group(const OID& oid,
51✔
534
                   const BigInt& p,
535
                   const BigInt& a,
536
                   const BigInt& b,
537
                   const BigInt& base_x,
538
                   const BigInt& base_y,
539
                   const BigInt& order) {
51✔
540
   BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
51✔
541

542
   // TODO(Botan4) remove this and require 192 bits minimum
543
#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
544
   constexpr size_t p_bits_lower_bound = 192;
545
#else
546
   constexpr size_t p_bits_lower_bound = 128;
51✔
547
#endif
548

549
   BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
51✔
550
   BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
51✔
551

552
   if(p.bits() == 521) {
51✔
553
      const auto p521 = BigInt::power_of_2(521) - 1;
2✔
554
      BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
1✔
555
   } else if(p.bits() == 239) {
51✔
556
      const auto x962_p239 = []() {
18✔
557
         BigInt p239;
3✔
558
         for(size_t i = 0; i != 239; ++i) {
720✔
559
            if(i < 47 || ((i >= 94) && (i != 143))) {
717✔
560
               p239.set_bit(i);
717✔
561
            }
562
         }
563
         return p239;
3✔
564
      }();
3✔
565

566
      BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
3✔
567
   } else {
3✔
568
      BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
47✔
569
   }
570

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

573
   BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
92✔
574
   BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
92✔
575
   BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
92✔
576
   BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
92✔
577
   BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
46✔
578

579
   auto mod_p = Barrett_Reduction::for_public_modulus(p);
43✔
580
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
43✔
581

582
   auto mod_order = Barrett_Reduction::for_public_modulus(order);
43✔
583
   BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
43✔
584

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

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

594
   // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
595
   auto y2 = mod_p.square(base_y);
43✔
596
   auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
43✔
597
   BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
43✔
598

599
   const BigInt cofactor(1);
42✔
600

601
   m_data =
42✔
602
      ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
42✔
603
}
139✔
604

605
EC_Group::EC_Group(std::span<const uint8_t> der) {
6,963✔
606
   auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
6,963✔
607
   m_data = data.first;
6,874✔
608
   m_explicit_encoding = data.second;
6,874✔
609
}
6,963✔
610

611
// static
612
bool EC_Group::unregister(const OID& oid) {
14✔
613
   return ec_group_data().unregister(oid);
14✔
614
}
615

616
const EC_Group_Data& EC_Group::data() const {
139,187✔
617
   if(m_data == nullptr) {
139,187✔
618
      throw Invalid_State("EC_Group uninitialized");
×
619
   }
620
   return *m_data;
139,187✔
621
}
622

623
size_t EC_Group::get_p_bits() const {
2,765✔
624
   return data().p_bits();
2,765✔
625
}
626

627
size_t EC_Group::get_p_bytes() const {
14,406✔
628
   return data().p_bytes();
14,406✔
629
}
630

631
size_t EC_Group::get_order_bits() const {
2,836✔
632
   return data().order_bits();
2,836✔
633
}
634

635
size_t EC_Group::get_order_bytes() const {
36,333✔
636
   return data().order_bytes();
36,333✔
637
}
638

639
const BigInt& EC_Group::get_p() const {
27,897✔
640
   return data().p();
27,897✔
641
}
642

643
const BigInt& EC_Group::get_a() const {
591✔
644
   return data().a();
591✔
645
}
646

647
const BigInt& EC_Group::get_b() const {
479✔
648
   return data().b();
479✔
649
}
650

651
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
652
const EC_Point& EC_Group::get_base_point() const {
678✔
653
   return data().base_point();
678✔
654
}
655

656
const EC_Point& EC_Group::generator() const {
×
657
   return data().base_point();
×
658
}
659

660
bool EC_Group::verify_public_element(const EC_Point& point) const {
×
661
   //check that public point is not at infinity
662
   if(point.is_zero()) {
×
663
      return false;
664
   }
665

666
   //check that public point is on the curve
667
   if(point.on_the_curve() == false) {
×
668
      return false;
669
   }
670

671
   //check that public point has order q
672
   if((point * get_order()).is_zero() == false) {
×
673
      return false;
674
   }
675

676
   if(has_cofactor()) {
×
677
      if((point * get_cofactor()).is_zero()) {
×
678
         return false;
679
      }
680
   }
681

682
   return true;
683
}
684

685
#endif
686

687
const BigInt& EC_Group::get_order() const {
7,926✔
688
   return data().order();
7,926✔
689
}
690

691
const BigInt& EC_Group::get_g_x() const {
1,700✔
692
   return data().g_x();
1,700✔
693
}
694

695
const BigInt& EC_Group::get_g_y() const {
1,700✔
696
   return data().g_y();
1,700✔
697
}
698

699
const BigInt& EC_Group::get_cofactor() const {
165✔
700
   return data().cofactor();
165✔
701
}
702

703
bool EC_Group::has_cofactor() const {
11,961✔
704
   return data().has_cofactor();
11,961✔
705
}
706

707
const OID& EC_Group::get_curve_oid() const {
26,739✔
708
   return data().oid();
26,739✔
709
}
710

711
EC_Group_Source EC_Group::source() const {
190✔
712
   return data().source();
190✔
713
}
714

715
EC_Group_Engine EC_Group::engine() const {
2✔
716
   return data().engine();
2✔
717
}
718

719
std::vector<uint8_t> EC_Group::DER_encode() const {
2,819✔
720
   const auto& der_named_curve = data().der_named_curve();
2,819✔
721
   // TODO(Botan4) this can be removed because an OID will always be defined
722
   if(der_named_curve.empty()) {
2,819✔
723
      throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
×
724
   }
725

726
   return der_named_curve;
2,819✔
727
}
728

729
std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
2,814✔
730
   if(form == EC_Group_Encoding::Explicit) {
2,814✔
731
      std::vector<uint8_t> output;
30✔
732
      DER_Encoder der(output);
30✔
733
      const size_t ecpVers1 = 1;
30✔
734
      const OID curve_type("1.2.840.10045.1.1");  // prime field
30✔
735

736
      const size_t p_bytes = get_p_bytes();
30✔
737

738
      const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
30✔
739

740
      der.start_sequence()
30✔
741
         .encode(ecpVers1)
30✔
742
         .start_sequence()
30✔
743
         .encode(curve_type)
30✔
744
         .encode(get_p())
30✔
745
         .end_cons()
30✔
746
         .start_sequence()
30✔
747
         .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
30✔
748
         .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
60✔
749
         .end_cons()
30✔
750
         .encode(generator, ASN1_Type::OctetString)
30✔
751
         .encode(get_order())
30✔
752
         .encode(get_cofactor())
30✔
753
         .end_cons();
30✔
754
      return output;
30✔
755
   } else if(form == EC_Group_Encoding::NamedCurve) {
2,814✔
756
      return this->DER_encode();
2,784✔
757
   } else if(form == EC_Group_Encoding::ImplicitCA) {
×
758
      return {0x00, 0x05};
×
759
   } else {
760
      throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
×
761
   }
762
}
763

764
std::string EC_Group::PEM_encode(EC_Group_Encoding form) const {
3✔
765
   const std::vector<uint8_t> der = DER_encode(form);
3✔
766
   return PEM_Code::encode(der, "EC PARAMETERS");
3✔
767
}
3✔
768

769
bool EC_Group::operator==(const EC_Group& other) const {
61✔
770
   if(m_data == other.m_data) {
61✔
771
      return true;  // same shared rep
772
   }
773

774
   return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
9✔
775
           get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
9✔
776
           get_cofactor() == other.get_cofactor());
4✔
777
}
778

779
bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
190✔
780
   const bool is_builtin = source() == EC_Group_Source::Builtin;
190✔
781

782
   if(is_builtin && !strong) {
190✔
783
      return true;
784
   }
785

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

790
   const BigInt& p = get_p();
29✔
791
   const BigInt& a = get_a();
29✔
792
   const BigInt& b = get_b();
29✔
793
   const BigInt& order = get_order();
29✔
794

795
   if(p <= 3 || order <= 0) {
58✔
796
      return false;
×
797
   }
798
   if(a < 0 || a >= p) {
58✔
799
      return false;
×
800
   }
801
   if(b <= 0 || b >= p) {
58✔
802
      return false;
×
803
   }
804

805
   const size_t test_prob = 128;
29✔
806
   const bool is_randomly_generated = is_builtin;
29✔
807

808
   //check if field modulus is prime
809
   if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
29✔
810
      return false;
811
   }
812

813
   //check if order is prime
814
   if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
29✔
815
      return false;
816
   }
817

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

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

824
   if(discriminant == 0) {
29✔
825
      return false;
826
   }
827

828
   //check for valid cofactor
829
   if(get_cofactor() < 1) {
29✔
830
      return false;
831
   }
832

833
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
834
   const EC_Point& base_point = get_base_point();
29✔
835
   //check if the base point is on the curve
836
   if(!base_point.on_the_curve()) {
29✔
837
      return false;
838
   }
839
   if((base_point * get_cofactor()).is_zero()) {
58✔
840
      return false;
841
   }
842
   //check if order of the base point is correct
843
   if(!(base_point * order).is_zero()) {
29✔
844
      return false;
845
   }
846
#endif
847

848
   // check the Hasse bound (roughly)
849
   if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
29✔
850
      return false;
851
   }
852

853
   return true;
854
}
58✔
855

856
EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
×
857

858
EC_Group::Mul2Table& EC_Group::Mul2Table::operator=(EC_Group::Mul2Table&& other) noexcept = default;
×
859

860
EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
13,600✔
861

862
EC_Group::Mul2Table::~Mul2Table() = default;
13,600✔
863

864
std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
2,427✔
865
   auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
2,427✔
866
   if(pt) {
2,427✔
867
      return EC_AffinePoint::_from_inner(std::move(pt));
4,854✔
868
   } else {
869
      return {};
×
870
   }
871
}
2,427✔
872

873
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
33,223✔
874
                                                      const EC_Scalar& x,
875
                                                      const EC_Scalar& y) const {
876
   return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
33,223✔
877
}
878

879
bool EC_Group::Mul2Table::mul2_vartime_x_mod_order_eq(const EC_Scalar& v,
32,827✔
880
                                                      const EC_Scalar& c,
881
                                                      const EC_Scalar& x,
882
                                                      const EC_Scalar& y) const {
883
   return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
32,827✔
884
}
885

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