Botan 3.11.0
Crypto and TLS for C&
ec_group.cpp
Go to the documentation of this file.
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
25namespace Botan {
26
27class EC_Group_Data_Map final {
28 public:
29 EC_Group_Data_Map() = default;
30
31 size_t clear() {
32 const lock_guard_type<mutex_type> lock(m_mutex);
33 const size_t count = m_registered_curves.size();
34 m_registered_curves.clear();
35 return count;
36 }
37
38 bool unregister(const OID& oid) {
39 // TODO(Botan4)
40 if(oid.empty()) {
41 throw Invalid_Argument("OID must not be empty");
42 }
43
44 const lock_guard_type<mutex_type> lock(m_mutex);
45 for(size_t i = 0; i < m_registered_curves.size(); i++) {
46 if(m_registered_curves[i]->oid() == oid) {
47 m_registered_curves.erase(m_registered_curves.begin() + i);
48 return true;
49 }
50 }
51 return false;
52 }
53
54 std::shared_ptr<EC_Group_Data> lookup(const OID& oid) {
55 const lock_guard_type<mutex_type> lock(m_mutex);
56
57 for(auto i : m_registered_curves) {
58 if(i->oid() == oid) {
59 return i;
60 }
61 }
62
63 // Not found, check hardcoded data
64 std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
65
66 if(data) {
67 m_registered_curves.push_back(data);
68 return data;
69 }
70
71 // Nope, unknown curve
72 return std::shared_ptr<EC_Group_Data>();
73 }
74
75 std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
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());
85
86 const lock_guard_type<mutex_type> lock(m_mutex);
87
88 for(auto i : m_registered_curves) {
89 if(i->oid() == oid) {
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)) {
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;
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)) {
114 i->set_oid(oid);
115 return i;
116 }
117 }
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) {
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);
137 }
138 }();
139
140 m_registered_curves.push_back(new_group);
141 return new_group;
142 }
143
144 std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
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);
151
152 for(auto i : m_registered_curves) {
153 if(i->params_match(p, a, b, base_pt, order, cofactor)) {
154 return i;
155 }
156 }
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);
160 if(oid_from_order.has_value()) {
161 auto new_group = EC_Group::EC_group_info(oid_from_order);
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)) {
165 m_registered_curves.push_back(new_group);
166 return new_group;
167 }
168 }
169
170 return {};
171 }
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,
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);
183
184 for(auto i : m_registered_curves) {
185 if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
186 return i;
187 }
188 }
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);
192 if(oid_from_order.has_value()) {
193 auto new_group = EC_Group::EC_group_info(oid_from_order);
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)) {
197 m_registered_curves.push_back(new_group);
198 return new_group;
199 }
200 }
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);
210 m_registered_curves.push_back(new_group);
211 return new_group;
212 }
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
221EC_Group_Data_Map& EC_Group::ec_group_data() {
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;
228 static EC_Group_Data_Map g_ec_data;
229 return g_ec_data;
230}
231
232//static
234 return ec_group_data().clear();
235}
236
237//static
238std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
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");
246
247 const BigInt p(p_str);
248 const BigInt a(a_str);
249 const BigInt b(b_str);
250 const BigInt g_x(g_x_str);
251 const BigInt g_y(g_y_str);
252 const BigInt order(order_str);
253 const BigInt cofactor(1); // implicit
254
255 return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
256}
257
258//static
259std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::BER_decode_EC_group(std::span<const uint8_t> bits,
260 EC_Group_Source source) {
261 BER_Decoder ber(bits);
262
263 auto next_obj_type = ber.peek_next_object().type_tag();
264
265 if(next_obj_type == ASN1_Type::ObjectId) {
266 OID oid;
267 ber.decode(oid);
268
269 auto data = ec_group_data().lookup(oid);
270 if(!data) {
271 throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
272 }
273
274 return std::make_pair(data, false);
275 } else if(next_obj_type == ASN1_Type::Sequence) {
276 BigInt p;
277 BigInt a;
278 BigInt b;
279 BigInt order;
280 BigInt cofactor;
281 std::vector<uint8_t> base_pt;
282 std::vector<uint8_t> seed;
283
284 ber.start_sequence()
285 .decode_and_check<size_t>(1, "Unknown ECC param version code")
286 .start_sequence()
287 .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
288 .decode(p)
289 .end_cons()
290 .start_sequence()
291 .decode_octet_string_bigint(a)
292 .decode_octet_string_bigint(b)
293 .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString)
294 .end_cons()
295 .decode(base_pt, ASN1_Type::OctetString)
296 .decode(order)
297 .decode(cofactor)
298 .end_cons()
299 .verify_end();
300
301 // TODO(Botan4) Require cofactor == 1
302 if(cofactor <= 0 || cofactor >= 16) {
303 throw Decoding_Error("Invalid ECC cofactor parameter");
304 }
305
306 if(p.bits() < 112 || p.bits() > 521 || p.is_negative()) {
307 throw Decoding_Error("ECC p parameter is invalid size");
308 }
309
310 if(a.is_negative() || a >= p) {
311 throw Decoding_Error("Invalid ECC a parameter");
312 }
313
314 if(b <= 0 || b >= p) {
315 throw Decoding_Error("Invalid ECC b parameter");
316 }
317
318 if(order.is_negative() || order.is_zero() || order >= 2 * p) {
319 throw Decoding_Error("Invalid ECC group order");
320 }
321
322 if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
323 return std::make_pair(data, true);
324 }
325
326 /*
327 TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
328 which is neither a builtin group nor a group that was registered by the application.
329 It can all be removed and replaced with a throw
330 */
331
333 if(!is_bailie_psw_probable_prime(p, mod_p)) {
334 throw Decoding_Error("ECC p parameter is not a prime");
335 }
336
339 throw Decoding_Error("Invalid ECC order parameter");
340 }
341
342 const size_t p_bytes = p.bytes();
343 if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
344 throw Decoding_Error("Invalid ECC base point encoding");
345 }
346
347 auto [g_x, g_y] = [&]() {
348 const uint8_t hdr = base_pt[0];
349
350 if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
351 const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
352 const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes));
353
354 if(x < p && y < p) {
355 return std::make_pair(x, y);
356 }
357 } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
358 // TODO(Botan4) remove this branch; we won't support compressed points
359 const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
360 BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
361
362 if(x < p && y >= 0) {
363 const bool y_mod_2 = (hdr & 0x01) == 1;
364 if(y.get_bit(0) != y_mod_2) {
365 y = p - y;
366 }
367
368 return std::make_pair(x, y);
369 }
370 }
371
372 throw Decoding_Error("Invalid ECC base point encoding");
373 }();
374
375 // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
376 auto y2 = mod_p.square(g_y);
377 auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
378 if(y2 != x3_ax_b) {
379 throw Decoding_Error("Invalid ECC base point");
380 }
381
382 auto data = ec_group_data().lookup_or_create_without_oid(p, a, b, g_x, g_y, order, cofactor, source);
383 return std::make_pair(data, true);
384 } else if(next_obj_type == ASN1_Type::Null) {
385 throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
386 } else {
387 throw Decoding_Error(
388 fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
389 }
390}
391
392EC_Group::EC_Group() = default;
393
394EC_Group::~EC_Group() = default;
395
396EC_Group::EC_Group(const EC_Group&) = default;
397
398EC_Group& EC_Group::operator=(const EC_Group&) = default;
399
400// Internal constructor
401EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
402
403//static
404bool EC_Group::supports_named_group(std::string_view name) {
405 return EC_Group::known_named_groups().contains(std::string(name));
406}
407
408//static
410#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
411 return true;
412#else
413 return false;
414#endif
415}
416
417//static
419#if defined(BOTAN_HAS_LEGACY_EC_POINT)
420 return true;
421#else
422 return false;
423#endif
424}
425
426//static
428 auto data = ec_group_data().lookup(oid);
429
430 if(!data) {
431 throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
432 }
433
434 return EC_Group(std::move(data));
435}
436
437//static
438EC_Group EC_Group::from_name(std::string_view name) {
439 std::shared_ptr<EC_Group_Data> data;
440
441 if(auto oid = OID::from_name(name)) {
442 data = ec_group_data().lookup(oid.value());
443 }
444
445 if(!data) {
446 throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
447 }
448
449 return EC_Group(std::move(data));
450}
451
452EC_Group::EC_Group(std::string_view str) {
453 if(str.empty()) {
454 return; // no initialization / uninitialized
455 }
456
457 try {
458 const OID oid = OID::from_string(str);
459 if(oid.has_value()) {
460 m_data = ec_group_data().lookup(oid);
461 }
462 } catch(...) {}
463
464 if(m_data == nullptr) {
465 if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
466 // OK try it as PEM ...
467 const auto ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
468
469 auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
470 this->m_data = data.first;
471 this->m_explicit_encoding = data.second;
472 }
473 }
474
475 if(m_data == nullptr) {
476 throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
477 }
478}
479
480//static
481EC_Group EC_Group::from_PEM(std::string_view pem) {
482 const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
483 return EC_Group(ber);
484}
485
487 const BigInt& a,
488 const BigInt& b,
489 const BigInt& base_x,
490 const BigInt& base_y,
491 const BigInt& order,
492 const BigInt& cofactor,
493 const OID& oid) {
494 if(oid.has_value()) {
495 m_data = ec_group_data().lookup_or_create(
496 p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
497 } else {
498 m_data = ec_group_data().lookup_or_create_without_oid(
499 p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
500 }
501}
502
504 const BigInt& p,
505 const BigInt& a,
506 const BigInt& b,
507 const BigInt& base_x,
508 const BigInt& base_y,
509 const BigInt& order) {
510 BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
511
512 // TODO(Botan4) remove this and require 192 bits minimum
513#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
514 constexpr size_t p_bits_lower_bound = 192;
515#else
516 constexpr size_t p_bits_lower_bound = 128;
517#endif
518
519 BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
520 BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
521
522 if(p.bits() == 521) {
523 const auto p521 = BigInt::power_of_2(521) - 1;
524 BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
525 } else if(p.bits() == 239) {
526 const auto x962_p239 = []() {
527 BigInt p239;
528 for(size_t i = 0; i != 239; ++i) {
529 if(i < 47 || ((i >= 94) && (i != 143))) {
530 p239.set_bit(i);
531 }
532 }
533 return p239;
534 }();
535
536 BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
537 } else {
538 BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
539 }
540
541 BOTAN_ARG_CHECK(p % 4 == 3, "EC_Group p must be congruent to 3 modulo 4");
542
543 BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
544 BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
545 BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
546 BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
547 BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
548
550 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
551
553 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
554
555 // This catches someone "ignoring" a cofactor and just trying to
556 // provide the subgroup order
557 BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid");
558
559 // Check that 4*a^3 + 27*b^2 != 0
560 const auto discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
561 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
562 BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
563
564 // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
565 auto y2 = mod_p.square(base_y);
566 auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
567 BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
568
569 const BigInt cofactor(1);
570
571 m_data =
572 ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
573}
574
575EC_Group::EC_Group(std::span<const uint8_t> ber) {
576 auto data = BER_decode_EC_group(ber, EC_Group_Source::ExternalSource);
577 m_data = data.first;
578 m_explicit_encoding = data.second;
579}
580
581// static
582bool EC_Group::unregister(const OID& oid) {
583 return ec_group_data().unregister(oid);
584}
585
586const EC_Group_Data& EC_Group::data() const {
587 if(m_data == nullptr) {
588 throw Invalid_State("EC_Group uninitialized");
589 }
590 return *m_data;
591}
592
593size_t EC_Group::get_p_bits() const {
594 return data().p_bits();
595}
596
597size_t EC_Group::get_p_bytes() const {
598 return data().p_bytes();
599}
600
602 return data().order_bits();
603}
604
606 return data().order_bytes();
607}
608
609const BigInt& EC_Group::get_p() const {
610 return data().p();
611}
612
613const BigInt& EC_Group::get_a() const {
614 return data().a();
615}
616
617const BigInt& EC_Group::get_b() const {
618 return data().b();
619}
620
621#if defined(BOTAN_HAS_LEGACY_EC_POINT)
622const EC_Point& EC_Group::get_base_point() const {
623 return data().base_point();
624}
625
626const EC_Point& EC_Group::generator() const {
627 return data().base_point();
628}
629
630bool EC_Group::verify_public_element(const EC_Point& point) const {
631 //check that public point is not at infinity
632 if(point.is_zero()) {
633 return false;
634 }
635
636 //check that public point is on the curve
637 if(point.on_the_curve() == false) {
638 return false;
639 }
640
641 //check that public point has order q
642 if((point * get_order()).is_zero() == false) {
643 return false;
644 }
645
646 if(has_cofactor()) {
647 if((point * get_cofactor()).is_zero()) {
648 return false;
649 }
650 }
651
652 return true;
653}
654
655#endif
656
658 return data().order();
659}
660
661const BigInt& EC_Group::get_g_x() const {
662 return data().g_x();
663}
664
665const BigInt& EC_Group::get_g_y() const {
666 return data().g_y();
667}
668
670 return data().cofactor();
671}
672
674 return data().has_cofactor();
675}
676
678 return data().oid();
679}
680
682 return data().source();
683}
684
686 return data().engine();
687}
688
689std::vector<uint8_t> EC_Group::DER_encode() const {
690 const auto& der_named_curve = data().der_named_curve();
691 // TODO(Botan4) this can be removed because an OID will always be defined
692 if(der_named_curve.empty()) {
693 throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
694 }
695
696 return der_named_curve;
697}
698
699std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
700 if(form == EC_Group_Encoding::Explicit) {
701 std::vector<uint8_t> output;
702 DER_Encoder der(output);
703 const size_t ecpVers1 = 1;
704 const OID curve_type("1.2.840.10045.1.1"); // prime field
705
706 const size_t p_bytes = get_p_bytes();
707
708 const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
709
710 der.start_sequence()
711 .encode(ecpVers1)
713 .encode(curve_type)
714 .encode(get_p())
715 .end_cons()
717 .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
718 .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
719 .end_cons()
720 .encode(generator, ASN1_Type::OctetString)
721 .encode(get_order())
723 .end_cons();
724 return output;
725 } else if(form == EC_Group_Encoding::NamedCurve) {
726 return this->DER_encode();
727 } else if(form == EC_Group_Encoding::ImplicitCA) {
728 return {0x00, 0x05};
729 } else {
730 throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
731 }
732}
733
735 const std::vector<uint8_t> der = DER_encode(form);
736 return PEM_Code::encode(der, "EC PARAMETERS");
737}
738
739bool EC_Group::operator==(const EC_Group& other) const {
740 if(m_data == other.m_data) {
741 return true; // same shared rep
742 }
743
744 return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
745 get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
746 get_cofactor() == other.get_cofactor());
747}
748
749bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
750 const bool is_builtin = source() == EC_Group_Source::Builtin;
751
752 if(is_builtin && !strong) {
753 return true;
754 }
755
756 // TODO(Botan4) this can probably all be removed once the deprecated EC_Group
757 // constructor is removed, since at that point it no longer becomes possible
758 // to create an EC_Group which fails to satisfy these conditions
759
760 const BigInt& p = get_p();
761 const BigInt& a = get_a();
762 const BigInt& b = get_b();
763 const BigInt& order = get_order();
764
765 if(p <= 3 || order <= 0) {
766 return false;
767 }
768 if(a < 0 || a >= p) {
769 return false;
770 }
771 if(b <= 0 || b >= p) {
772 return false;
773 }
774
775 const size_t test_prob = 128;
776 const bool is_randomly_generated = is_builtin;
777
778 //check if field modulus is prime
779 if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
780 return false;
781 }
782
783 //check if order is prime
784 if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
785 return false;
786 }
787
788 //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
790
791 const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
792 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
793
794 if(discriminant == 0) {
795 return false;
796 }
797
798 //check for valid cofactor
799 if(get_cofactor() < 1) {
800 return false;
801 }
802
803#if defined(BOTAN_HAS_LEGACY_EC_POINT)
804 const EC_Point& base_point = get_base_point();
805 //check if the base point is on the curve
806 if(!base_point.on_the_curve()) {
807 return false;
808 }
809 if((base_point * get_cofactor()).is_zero()) {
810 return false;
811 }
812 //check if order of the base point is correct
813 if(!(base_point * order).is_zero()) {
814 return false;
815 }
816#endif
817
818 // check the Hasse bound (roughly)
819 if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
820 return false;
821 }
822
823 return true;
824}
825
826EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
827
829
830EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
831
833
834std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
835 auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
836 if(pt) {
837 return EC_AffinePoint::_from_inner(std::move(pt));
838 } else {
839 return {};
840 }
841}
842
844 const EC_Scalar& x,
845 const EC_Scalar& y) const {
846 return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
847}
848
850 const EC_Scalar& c,
851 const EC_Scalar& x,
852 const EC_Scalar& y) const {
853 return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
854}
855
856} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
static Barrett_Reduction for_public_modulus(const BigInt &m)
Definition barrett.cpp:34
void set_bit(size_t n)
Definition bigint.h:479
static BigInt from_bytes(std::span< const uint8_t > bytes)
Definition bigint.cpp:83
size_t bits() const
Definition bigint.cpp:307
static BigInt power_of_2(size_t n)
Definition bigint.h:836
static BigInt from_s32(int32_t n)
Definition bigint.cpp:42
DER_Encoder & start_sequence()
Definition der_enc.h:67
DER_Encoder & end_cons()
Definition der_enc.cpp:173
DER_Encoder & encode(bool b)
Definition der_enc.cpp:252
T serialize_uncompressed() const
Definition ec_apoint.h:203
static EC_AffinePoint _from_inner(std::unique_ptr< EC_AffinePoint_Data > inner)
static EC_AffinePoint generator(const EC_Group &group)
Return the standard group generator.
Definition ec_apoint.cpp:84
Table for computing g*x + h*y.
Definition ec_group.h:326
Mul2Table & operator=(const Mul2Table &other)=delete
std::optional< EC_AffinePoint > mul2_vartime(const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:834
BOTAN_FUTURE_EXPLICIT Mul2Table(const EC_AffinePoint &h)
Definition ec_group.cpp:830
bool mul2_vartime_x_mod_order_eq(const EC_Scalar &v, const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:843
static std::shared_ptr< EC_Group_Data > create(const BigInt &p, const BigInt &a, const BigInt &b, const BigInt &g_x, const BigInt &g_y, const BigInt &order, const BigInt &cofactor, const OID &oid, EC_Group_Source source)
static EC_Group from_name(std::string_view name)
Definition ec_group.cpp:438
static EC_Group from_PEM(std::string_view pem)
Definition ec_group.cpp:481
const BigInt & get_b() const
Definition ec_group.cpp:617
const BigInt & get_a() const
Definition ec_group.cpp:613
const BigInt & get_g_y() const
Definition ec_group.cpp:665
const BigInt & get_cofactor() const
Definition ec_group.cpp:669
BigInt mod_order(const BigInt &x) const
Definition ec_group.h:656
bool operator==(const EC_Group &other) const
Definition ec_group.cpp:739
static bool supports_application_specific_group_with_cofactor()
Definition ec_group.cpp:418
EC_Group_Engine engine() const
Definition ec_group.cpp:685
EC_Group_Source source() const
Definition ec_group.cpp:681
const BigInt & get_p() const
Definition ec_group.cpp:609
bool verify_group(RandomNumberGenerator &rng, bool strong=false) const
Definition ec_group.cpp:749
const BigInt & get_order() const
Definition ec_group.cpp:657
size_t get_p_bits() const
Definition ec_group.cpp:593
static EC_Group from_OID(const OID &oid)
Definition ec_group.cpp:427
static std::shared_ptr< EC_Group_Data > EC_group_info(const OID &oid)
Definition ec_named.cpp:15
std::vector< uint8_t > DER_encode() const
Definition ec_group.cpp:689
const BigInt & get_g_x() const
Definition ec_group.cpp:661
EC_Group(const BigInt &p, const BigInt &a, const BigInt &b, const BigInt &base_x, const BigInt &base_y, const BigInt &order, const BigInt &cofactor, const OID &oid=OID())
Definition ec_group.cpp:486
const OID & get_curve_oid() const
Definition ec_group.cpp:677
static bool supports_application_specific_group()
Definition ec_group.cpp:409
static const std::set< std::string > & known_named_groups()
Definition ec_named.cpp:476
bool has_cofactor() const
Definition ec_group.cpp:673
static size_t clear_registered_curve_data()
Definition ec_group.cpp:233
static bool unregister(const OID &oid)
Definition ec_group.cpp:582
static bool supports_named_group(std::string_view name)
Definition ec_group.cpp:404
EC_Group & operator=(const EC_Group &)
size_t get_p_bytes() const
Definition ec_group.cpp:597
static OID EC_group_identity_from_order(const BigInt &order)
Definition ec_named.cpp:356
std::string PEM_encode(EC_Group_Encoding form=EC_Group_Encoding::Explicit) const
Definition ec_group.cpp:734
size_t get_order_bits() const
Definition ec_group.cpp:601
size_t get_order_bytes() const
Definition ec_group.cpp:605
bool on_the_curve() const
Definition ec_point.cpp:675
const EC_Scalar_Data & _inner() const
Definition ec_scalar.h:244
static std::optional< OID > from_name(std::string_view name)
Definition asn1_oid.cpp:72
bool has_value() const
Definition asn1_obj.h:271
std::string to_string() const
Definition asn1_oid.cpp:125
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:86
std::string encode(const uint8_t der[], size_t length, std::string_view label, size_t width)
Definition pem.cpp:39
secure_vector< uint8_t > decode_check_label(DataSource &source, std::string_view label_want)
Definition pem.cpp:49
secure_vector< uint8_t > decode(DataSource &source, std::string &label)
Definition pem.cpp:62
noop_mutex mutex_type
Definition mutex.h:37
std::string asn1_tag_to_string(ASN1_Type type)
Definition asn1_obj.cpp:98
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
BigInt abs(const BigInt &n)
Definition numthry.h:22
secure_vector< T > lock(const std::vector< T > &in)
Definition secmem.h:80
bool is_bailie_psw_probable_prime(const BigInt &n, const Barrett_Reduction &mod_n)
Definition primality.cpp:98
bool is_prime(const BigInt &n, RandomNumberGenerator &rng, size_t prob, bool is_random)
Definition numthry.cpp:381
EC_Group_Engine
Definition ec_group.h:48
lock_guard< T > lock_guard_type
Definition mutex.h:55
BigInt sqrt_modulo_prime(const BigInt &a, const BigInt &p)
Definition numthry.cpp:27
EC_Group_Source
Definition ec_group.h:38