Botan 3.12.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::DER_decode_EC_group(std::span<const uint8_t> der,
260 EC_Group_Source source) {
261 BER_Decoder dec(der, BER_Decoder::Limits::DER());
262
263 auto next_obj_type = dec.peek_next_object().type_tag();
264
265 if(next_obj_type == ASN1_Type::ObjectId) {
266 OID oid;
267 dec.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 dec.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)
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.signum() < 0) {
307 throw Decoding_Error("ECC p parameter is invalid size");
308 }
309
310 // A can be zero
311 if(a.signum() < 0 || a >= p) {
312 throw Decoding_Error("Invalid ECC a parameter");
313 }
314
315 // B must be > 0
316 if(b.signum() <= 0 || b >= p) {
317 throw Decoding_Error("Invalid ECC b parameter");
318 }
319
320 if(order.signum() <= 0 || order >= 2 * p) {
321 throw Decoding_Error("Invalid ECC group order");
322 }
323
324 if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
325 return std::make_pair(data, true);
326 }
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
335 if(!is_bailie_psw_probable_prime(p, mod_p)) {
336 throw Decoding_Error("ECC p parameter is not a prime");
337 }
338
341 throw Decoding_Error("Invalid ECC order parameter");
342 }
343
344 const size_t p_bytes = p.bytes();
345 if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
346 throw Decoding_Error("Invalid ECC base point encoding");
347 }
348
349 auto [g_x, g_y] = [&]() {
350 const uint8_t hdr = base_pt[0];
351
352 if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
353 const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
354 const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes));
355
356 if(x < p && y < p) {
357 return std::make_pair(x, y);
358 }
359 } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
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 }();
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);
379 auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
380 if(y2 != x3_ax_b) {
381 throw Decoding_Error("Invalid ECC base point");
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);
391 return std::make_pair(data, true);
392 } else if(next_obj_type == ASN1_Type::Null) {
393 throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
394 } else {
395 throw Decoding_Error(
396 fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
397 }
398}
399
400EC_Group::EC_Group() = default;
401
402EC_Group::~EC_Group() = default;
403
404EC_Group::EC_Group(const EC_Group&) = default;
405
406EC_Group& EC_Group::operator=(const EC_Group&) = default;
407
408// Internal constructor
409EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
410
411//static
412bool EC_Group::supports_named_group(std::string_view name) {
413 if(name.empty()) {
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))) {
419 return true;
420 }
421
422 // Is it a custom group registered by the application?
423 if(auto oid = OID::from_name(name)) {
424 try {
425 if(ec_group_data().lookup(oid.value()) != nullptr) {
426 return true;
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 }
433
434 // Not known
435 return false;
436}
437
438//static
440#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
441 return true;
442#else
443 return false;
444#endif
445}
446
447//static
449#if defined(BOTAN_HAS_LEGACY_EC_POINT)
450 return true;
451#else
452 return false;
453#endif
454}
455
456//static
458 auto data = ec_group_data().lookup(oid);
459
460 if(!data) {
461 throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
462 }
463
464 return EC_Group(std::move(data));
465}
466
467//static
468EC_Group EC_Group::from_name(std::string_view name) {
469 std::shared_ptr<EC_Group_Data> data;
470
471 if(auto oid = OID::from_name(name)) {
472 data = ec_group_data().lookup(oid.value());
473 }
474
475 if(!data) {
476 throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
477 }
478
479 return EC_Group(std::move(data));
480}
481
482EC_Group::EC_Group(std::string_view str) {
483 if(str.empty()) {
484 return; // no initialization / uninitialized
485 }
486
487 try {
488 const OID oid = OID::from_string(str);
489 if(oid.has_value()) {
490 m_data = ec_group_data().lookup(oid);
491 }
492 } catch(...) {}
493
494 if(m_data == nullptr) {
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) {
506 throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
507 }
508}
509
510//static
511EC_Group EC_Group::from_PEM(std::string_view pem) {
512 const auto der = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
513 return EC_Group(der);
514}
515
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) {
524 if(oid.has_value()) {
525 m_data = ec_group_data().lookup_or_create(
526 p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
527 } else {
528 m_data = ec_group_data().lookup_or_create_without_oid(
529 p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
530 }
531}
532
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) {
540 BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
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;
547#endif
548
549 BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
550 BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
551
552 if(p.bits() == 521) {
553 const auto p521 = BigInt::power_of_2(521) - 1;
554 BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
555 } else if(p.bits() == 239) {
556 const auto x962_p239 = []() {
557 BigInt p239;
558 for(size_t i = 0; i != 239; ++i) {
559 if(i < 47 || ((i >= 94) && (i != 143))) {
560 p239.set_bit(i);
561 }
562 }
563 return p239;
564 }();
565
566 BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
567 } else {
568 BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
569 }
570
571 BOTAN_ARG_CHECK(p % 4 == 3, "EC_Group p must be congruent to 3 modulo 4");
572
573 BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
574 BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
575 BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
576 BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
577 BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
578
580 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
581
583 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
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");
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)) +
591 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
592 BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
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);
596 auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
597 BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
598
599 const BigInt cofactor(1);
600
601 m_data =
602 ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
603}
604
605EC_Group::EC_Group(std::span<const uint8_t> der) {
606 auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
607 m_data = data.first;
608 m_explicit_encoding = data.second;
609}
610
611// static
612bool EC_Group::unregister(const OID& oid) {
613 return ec_group_data().unregister(oid);
614}
615
616const EC_Group_Data& EC_Group::data() const {
617 if(m_data == nullptr) {
618 throw Invalid_State("EC_Group uninitialized");
619 }
620 return *m_data;
621}
622
623size_t EC_Group::get_p_bits() const {
624 return data().p_bits();
625}
626
627size_t EC_Group::get_p_bytes() const {
628 return data().p_bytes();
629}
630
632 return data().order_bits();
633}
634
636 return data().order_bytes();
637}
638
639const BigInt& EC_Group::get_p() const {
640 return data().p();
641}
642
643const BigInt& EC_Group::get_a() const {
644 return data().a();
645}
646
647const BigInt& EC_Group::get_b() const {
648 return data().b();
649}
650
651#if defined(BOTAN_HAS_LEGACY_EC_POINT)
652const EC_Point& EC_Group::get_base_point() const {
653 return data().base_point();
654}
655
656const EC_Point& EC_Group::generator() const {
657 return data().base_point();
658}
659
660bool 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
688 return data().order();
689}
690
691const BigInt& EC_Group::get_g_x() const {
692 return data().g_x();
693}
694
695const BigInt& EC_Group::get_g_y() const {
696 return data().g_y();
697}
698
700 return data().cofactor();
701}
702
704 return data().has_cofactor();
705}
706
708 return data().oid();
709}
710
712 return data().source();
713}
714
716 return data().engine();
717}
718
719std::vector<uint8_t> EC_Group::DER_encode() const {
720 const auto& der_named_curve = data().der_named_curve();
721 // TODO(Botan4) this can be removed because an OID will always be defined
722 if(der_named_curve.empty()) {
723 throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
724 }
725
726 return der_named_curve;
727}
728
729std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
730 if(form == EC_Group_Encoding::Explicit) {
731 std::vector<uint8_t> output;
732 DER_Encoder der(output);
733 const size_t ecpVers1 = 1;
734 const OID curve_type("1.2.840.10045.1.1"); // prime field
735
736 const size_t p_bytes = get_p_bytes();
737
738 const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
739
740 der.start_sequence()
741 .encode(ecpVers1)
743 .encode(curve_type)
744 .encode(get_p())
745 .end_cons()
747 .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
748 .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
749 .end_cons()
750 .encode(generator, ASN1_Type::OctetString)
751 .encode(get_order())
753 .end_cons();
754 return output;
755 } else if(form == EC_Group_Encoding::NamedCurve) {
756 return this->DER_encode();
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
765 const std::vector<uint8_t> der = DER_encode(form);
766 return PEM_Code::encode(der, "EC PARAMETERS");
767}
768
769bool EC_Group::operator==(const EC_Group& other) const {
770 if(m_data == other.m_data) {
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() &&
775 get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
776 get_cofactor() == other.get_cofactor());
777}
778
779bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
780 const bool is_builtin = source() == EC_Group_Source::Builtin;
781
782 if(is_builtin && !strong) {
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();
791 const BigInt& a = get_a();
792 const BigInt& b = get_b();
793 const BigInt& order = get_order();
794
795 if(p <= 3 || order <= 0) {
796 return false;
797 }
798 if(a < 0 || a >= p) {
799 return false;
800 }
801 if(b <= 0 || b >= p) {
802 return false;
803 }
804
805 const size_t test_prob = 128;
806 const bool is_randomly_generated = is_builtin;
807
808 //check if field modulus is prime
809 if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
810 return false;
811 }
812
813 //check if order is prime
814 if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
815 return false;
816 }
817
818 //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
820
821 const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
822 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
823
824 if(discriminant == 0) {
825 return false;
826 }
827
828 //check for valid cofactor
829 if(get_cofactor() < 1) {
830 return false;
831 }
832
833#if defined(BOTAN_HAS_LEGACY_EC_POINT)
834 const EC_Point& base_point = get_base_point();
835 //check if the base point is on the curve
836 if(!base_point.on_the_curve()) {
837 return false;
838 }
839 if((base_point * get_cofactor()).is_zero()) {
840 return false;
841 }
842 //check if order of the base point is correct
843 if(!(base_point * order).is_zero()) {
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) {
850 return false;
851 }
852
853 return true;
854}
855
856EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
857
859
860EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
861
863
864std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
865 auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
866 if(pt) {
867 return EC_AffinePoint::_from_inner(std::move(pt));
868 } else {
869 return {};
870 }
871}
872
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());
877}
878
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);
884}
885
886} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
static Limits DER()
Definition ber_dec.h:35
static Barrett_Reduction for_public_modulus(const BigInt &m)
Definition barrett.cpp:33
void set_bit(size_t n)
Definition bigint.h:490
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:853
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:245
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:330
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:864
BOTAN_FUTURE_EXPLICIT Mul2Table(const EC_AffinePoint &h)
Definition ec_group.cpp:860
bool mul2_vartime_x_mod_order_eq(const EC_Scalar &v, const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:873
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:468
static EC_Group from_PEM(std::string_view pem)
Definition ec_group.cpp:511
const BigInt & get_b() const
Definition ec_group.cpp:647
const BigInt & get_a() const
Definition ec_group.cpp:643
const BigInt & get_g_y() const
Definition ec_group.cpp:695
const BigInt & get_cofactor() const
Definition ec_group.cpp:699
BigInt mod_order(const BigInt &x) const
Definition ec_group.h:660
bool operator==(const EC_Group &other) const
Definition ec_group.cpp:769
static bool supports_application_specific_group_with_cofactor()
Definition ec_group.cpp:448
EC_Group_Engine engine() const
Definition ec_group.cpp:715
EC_Group_Source source() const
Definition ec_group.cpp:711
const BigInt & get_p() const
Definition ec_group.cpp:639
bool verify_group(RandomNumberGenerator &rng, bool strong=false) const
Definition ec_group.cpp:779
const BigInt & get_order() const
Definition ec_group.cpp:687
size_t get_p_bits() const
Definition ec_group.cpp:623
static EC_Group from_OID(const OID &oid)
Definition ec_group.cpp:457
static std::shared_ptr< EC_Group_Data > EC_group_info(const OID &oid)
Definition ec_named.cpp:16
std::vector< uint8_t > DER_encode() const
Definition ec_group.cpp:719
const BigInt & get_g_x() const
Definition ec_group.cpp:691
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:516
const OID & get_curve_oid() const
Definition ec_group.cpp:707
static bool supports_application_specific_group()
Definition ec_group.cpp:439
static const std::set< std::string > & known_named_groups()
Definition ec_named.cpp:477
bool has_cofactor() const
Definition ec_group.cpp:703
static size_t clear_registered_curve_data()
Definition ec_group.cpp:233
static bool unregister(const OID &oid)
Definition ec_group.cpp:612
static bool supports_named_group(std::string_view name)
Definition ec_group.cpp:412
EC_Group & operator=(const EC_Group &)
size_t get_p_bytes() const
Definition ec_group.cpp:627
static OID EC_group_identity_from_order(const BigInt &order)
Definition ec_named.cpp:357
std::string PEM_encode(EC_Group_Encoding form=EC_Group_Encoding::Explicit) const
Definition ec_group.cpp:764
size_t get_order_bits() const
Definition ec_group.cpp:631
size_t get_order_bytes() const
Definition ec_group.cpp:635
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