Botan 3.13.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 // The requested OID may be an alias for a curve whose canonical OID differs
68 // TODO(Botan4) remove this once we require exactly one canonical OID per curve
69 if(data->oid() != oid) {
70 for(const auto& i : m_registered_curves) {
71 if(i->oid() == data->oid()) {
72 return i;
73 }
74 }
75 }
76
77 m_registered_curves.push_back(data);
78 return data;
79 }
80
81 // Nope, unknown curve
82 return std::shared_ptr<EC_Group_Data>();
83 }
84
85 std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
86 const BigInt& a,
87 const BigInt& b,
88 const BigInt& g_x,
89 const BigInt& g_y,
90 const BigInt& order,
91 const BigInt& cofactor,
92 const OID& oid,
93 EC_Group_Source source) {
94 BOTAN_ASSERT_NOMSG(oid.has_value());
95
96 const lock_guard_type<mutex_type> lock(m_mutex);
97
98 for(auto i : m_registered_curves) {
99 if(i->oid() == oid) {
100 /*
101 * If both OID and params are the same then we are done, just return
102 * the already registered curve obj.
103 *
104 * First verify that the params match, to catch an application
105 * that is attempting to register a EC_Group under the same OID as
106 * another group currently in use
107 */
108 if(!i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
109 throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
110 " but a distinct curve is already registered using that OID");
111 }
112
113 return i;
114 }
115
116 /*
117 * If the same curve was previously created without an OID but is now
118 * being registered again using an OID, save that OID.
119 *
120 * TODO(Botan4) remove this block; this situation won't be possible since
121 * we will require all groups to have an OID
122 */
123 if(i->oid().empty() && i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
124 i->set_oid(oid);
125 return i;
126 }
127 }
128
129 /*
130 * Not found in current list, so we need to create a new entry
131 */
132 auto new_group = [&] {
133 if(auto g = EC_Group::EC_group_info(oid); g != nullptr) {
134 /*
135 * This turned out to be the OID of one of the builtin groups. Verify
136 * that all of the provided parameters match that builtin group.
137 */
138 BOTAN_ARG_CHECK(g->params_match(p, a, b, g_x, g_y, order, cofactor),
139 "Attempting to register an EC group under OID of hardcoded group");
140
141 return g;
142 } else {
143 /*
144 * This path is taken for an application registering a new EC_Group with an OID specified
145 */
146 return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, source);
147 }
148 }();
149
150 m_registered_curves.push_back(new_group);
151 return new_group;
152 }
153
154 std::shared_ptr<EC_Group_Data> lookup_from_params(const BigInt& p,
155 const BigInt& a,
156 const BigInt& b,
157 std::span<const uint8_t> base_pt,
158 const BigInt& order,
159 const BigInt& cofactor) {
160 const lock_guard_type<mutex_type> lock(m_mutex);
161
162 for(auto i : m_registered_curves) {
163 if(i->params_match(p, a, b, base_pt, order, cofactor)) {
164 return i;
165 }
166 }
167
168 // Try to use the order as a hint to look up the group id
169 const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
170 if(oid_from_order.has_value()) {
171 auto new_group = EC_Group::EC_group_info(oid_from_order);
172
173 // Have to check all params in the (unlikely/malicious) event of an order collision
174 if(new_group && new_group->params_match(p, a, b, base_pt, order, cofactor)) {
175 m_registered_curves.push_back(new_group);
176 return new_group;
177 }
178 }
179
180 return {};
181 }
182
183 // TODO(Botan4) this entire function can be removed since OIDs will be required
184 std::shared_ptr<EC_Group_Data> lookup_or_create_without_oid(const BigInt& p,
185 const BigInt& a,
186 const BigInt& b,
187 const BigInt& g_x,
188 const BigInt& g_y,
189 const BigInt& order,
190 const BigInt& cofactor,
191 EC_Group_Source source) {
192 const lock_guard_type<mutex_type> lock(m_mutex);
193
194 for(auto i : m_registered_curves) {
195 if(i->params_match(p, a, b, g_x, g_y, order, cofactor)) {
196 return i;
197 }
198 }
199
200 // Try to use the order as a hint to look up the group id
201 const OID oid_from_order = EC_Group::EC_group_identity_from_order(order);
202 if(oid_from_order.has_value()) {
203 auto new_group = EC_Group::EC_group_info(oid_from_order);
204
205 // Have to check all params in the (unlikely/malicious) event of an order collision
206 if(new_group && new_group->params_match(p, a, b, g_x, g_y, order, cofactor)) {
207 m_registered_curves.push_back(new_group);
208 return new_group;
209 }
210 }
211
212 /*
213 * At this point we have failed to identify the group; it is not any of
214 * the builtin values, nor is it a group that the user had previously
215 * registered explicitly. We create the group data without an OID.
216 *
217 * TODO(Botan4) remove this; throw an exception instead
218 */
219 auto new_group = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
220 m_registered_curves.push_back(new_group);
221 return new_group;
222 }
223
224 private:
225 mutex_type m_mutex;
226 // TODO(Botan4): Once OID is required we could make this into a map
227 std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
228};
229
230//static
231EC_Group_Data_Map& EC_Group::ec_group_data() {
232 /*
233 * This exists purely to ensure the allocator is constructed before g_ec_data,
234 * which ensures that its destructor runs after ~g_ec_data is complete.
235 */
236
237 static const Allocator_Initializer g_init_allocator;
238 static EC_Group_Data_Map g_ec_data;
239 return g_ec_data;
240}
241
242//static
244 return ec_group_data().clear();
245}
246
247//static
248std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str,
249 const char* a_str,
250 const char* b_str,
251 const char* g_x_str,
252 const char* g_y_str,
253 const char* order_str,
254 const OID& oid) {
255 BOTAN_ARG_CHECK(oid.has_value(), "EC_Group::load_EC_group_info OID must be set");
256
257 const BigInt p(p_str);
258 const BigInt a(a_str);
259 const BigInt b(b_str);
260 const BigInt g_x(g_x_str);
261 const BigInt g_y(g_y_str);
262 const BigInt order(order_str);
263 const BigInt cofactor(1); // implicit
264
265 return EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
266}
267
268//static
269std::pair<std::shared_ptr<EC_Group_Data>, bool> EC_Group::DER_decode_EC_group(std::span<const uint8_t> der,
270 EC_Group_Source source) {
271 BER_Decoder dec(der, BER_Decoder::Limits::DER());
272
273 auto next_obj_type = dec.peek_next_object().type_tag();
274
275 if(next_obj_type == ASN1_Type::ObjectId) {
276 OID oid;
277 dec.decode(oid).verify_end();
278
279 auto data = ec_group_data().lookup(oid);
280 if(!data) {
281 throw Decoding_Error(fmt("Unknown namedCurve OID '{}'", oid.to_string()));
282 }
283
284 return std::make_pair(data, false);
285 } else if(next_obj_type == ASN1_Type::Sequence) {
286 BigInt p;
287 BigInt a;
288 BigInt b;
289 BigInt order;
290 BigInt cofactor;
291 std::vector<uint8_t> base_pt;
292 std::vector<uint8_t> seed;
293
294 dec.start_sequence()
295 .decode_and_check<size_t>(1, "Unknown ECC param version code")
296 .start_sequence()
297 .decode_and_check(OID({1, 2, 840, 10045, 1, 1}), "Only prime ECC fields supported")
298 .decode(p)
299 .end_cons()
300 .start_sequence()
301 .decode_octet_string_bigint(a)
302 .decode_octet_string_bigint(b)
304 .end_cons()
305 .decode(base_pt, ASN1_Type::OctetString)
306 .decode(order)
307 .decode(cofactor)
308 .end_cons()
309 .verify_end();
310
311 // TODO(Botan4) Require cofactor == 1
312 if(cofactor <= 0 || cofactor >= 16) {
313 throw Decoding_Error("Invalid ECC cofactor parameter");
314 }
315
316 if(p.bits() < 112 || p.bits() > 521 || p.signum() < 0) {
317 throw Decoding_Error("ECC p parameter is invalid size");
318 }
319
320 // A can be zero
321 if(a.signum() < 0 || a >= p) {
322 throw Decoding_Error("Invalid ECC a parameter");
323 }
324
325 // B must be > 0
326 if(b.signum() <= 0 || b >= p) {
327 throw Decoding_Error("Invalid ECC b parameter");
328 }
329
330 if(order.signum() <= 0 || order >= 2 * p) {
331 throw Decoding_Error("Invalid ECC group order");
332 }
333
334 if(auto data = ec_group_data().lookup_from_params(p, a, b, base_pt, order, cofactor)) {
335 return std::make_pair(data, true);
336 }
337
338 /*
339 TODO(Botan4) the remaining code is used only to handle the case of decoding an EC_Group
340 which is neither a builtin group nor a group that was registered by the application.
341 It can all be removed and replaced with a throw
342 */
343
345 if(!is_bailie_psw_probable_prime(p, mod_p)) {
346 throw Decoding_Error("ECC p parameter is not a prime");
347 }
348
351 throw Decoding_Error("Invalid ECC order parameter");
352 }
353
354 const size_t p_bytes = p.bytes();
355 if(base_pt.size() != 1 + p_bytes && base_pt.size() != 1 + 2 * p_bytes) {
356 throw Decoding_Error("Invalid ECC base point encoding");
357 }
358
359 auto [g_x, g_y] = [&]() {
360 const uint8_t hdr = base_pt[0];
361
362 if(hdr == 0x04 && base_pt.size() == 1 + 2 * p_bytes) {
363 const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
364 const BigInt y = BigInt::from_bytes(std::span{base_pt}.subspan(1 + p_bytes, p_bytes));
365
366 if(x < p && y < p) {
367 return std::make_pair(x, y);
368 }
369 } else if((hdr == 0x02 || hdr == 0x03) && base_pt.size() == 1 + p_bytes) {
370 // TODO(Botan4) remove this branch; we won't support compressed points
371 const BigInt x = BigInt::from_bytes(std::span{base_pt}.subspan(1, p_bytes));
372 BigInt y = sqrt_modulo_prime(((x * x + a) * x + b) % p, p);
373
374 if(x < p && y >= 0) {
375 const bool y_mod_2 = (hdr & 0x01) == 1;
376 if(y.get_bit(0) != y_mod_2) {
377 y = p - y;
378 }
379
380 return std::make_pair(x, y);
381 }
382 }
383
384 throw Decoding_Error("Invalid ECC base point encoding");
385 }();
386
387 // TODO(Botan4) we can remove this check since we'll only accept pre-registered groups
388 auto y2 = mod_p.square(g_y);
389 auto x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
390 if(y2 != x3_ax_b) {
391 throw Decoding_Error("Invalid ECC base point");
392 }
393
394 /*
395 * Create the group data without registering it in the global map.
396 *
397 * Applications that need persistent custom groups should register them
398 * via the relevant EC_Group constructor
399 */
400 auto data = EC_Group_Data::create(p, a, b, g_x, g_y, order, cofactor, OID(), source);
401 return std::make_pair(data, true);
402 } else if(next_obj_type == ASN1_Type::Null) {
403 throw Decoding_Error("Decoding ImplicitCA ECC parameters is not supported");
404 } else {
405 throw Decoding_Error(
406 fmt("Unexpected tag {} while decoding ECC domain params", asn1_tag_to_string(next_obj_type)));
407 }
408}
409
410EC_Group::EC_Group() = default;
411
412EC_Group::~EC_Group() = default;
413
414EC_Group::EC_Group(const EC_Group&) = default;
415
416EC_Group& EC_Group::operator=(const EC_Group&) = default;
417
418// Internal constructor
419EC_Group::EC_Group(std::shared_ptr<EC_Group_Data>&& data) : m_data(std::move(data)) {}
420
421//static
422bool EC_Group::supports_named_group(std::string_view name) {
423 if(name.empty()) {
424 return false;
425 }
426
427 // Is it one of the groups compiled into the library?
428 if(EC_Group::known_named_groups().contains(std::string(name))) {
429 return true;
430 }
431
432 // Is it a custom group registered by the application?
433 if(auto oid = OID::from_name(name)) {
434 try {
435 if(ec_group_data().lookup(oid.value()) != nullptr) {
436 return true;
437 }
438 } catch(Not_Implemented&) {
439 // This would be thrown for example if the group is a known curve
440 // but the relevant module that enables it is not compiled in
441 }
442 }
443
444 // Not known
445 return false;
446}
447
448//static
450#if defined(BOTAN_HAS_LEGACY_EC_POINT) || defined(BOTAN_HAS_PCURVES_GENERIC)
451 return true;
452#else
453 return false;
454#endif
455}
456
457//static
459#if defined(BOTAN_HAS_LEGACY_EC_POINT)
460 return true;
461#else
462 return false;
463#endif
464}
465
466//static
468 auto data = ec_group_data().lookup(oid);
469
470 if(!data) {
471 throw Invalid_Argument(fmt("No EC_Group associated with OID '{}'", oid.to_string()));
472 }
473
474 return EC_Group(std::move(data));
475}
476
477//static
478EC_Group EC_Group::from_name(std::string_view name) {
479 std::shared_ptr<EC_Group_Data> data;
480
481 if(auto oid = OID::from_name(name)) {
482 data = ec_group_data().lookup(oid.value());
483 }
484
485 if(!data) {
486 throw Invalid_Argument(fmt("Unknown EC_Group '{}'", name));
487 }
488
489 return EC_Group(std::move(data));
490}
491
492EC_Group::EC_Group(std::string_view str) {
493 if(str.empty()) {
494 return; // no initialization / uninitialized
495 }
496
497 try {
498 const OID oid = OID::from_string(str);
499 if(oid.has_value()) {
500 m_data = ec_group_data().lookup(oid);
501 }
502 } catch(...) {}
503
504 if(m_data == nullptr) {
505 if(str.size() > 30 && str.starts_with("-----BEGIN EC PARAMETERS-----")) {
506 // OK try it as PEM ...
507 const auto der = PEM_Code::decode_check_label(str, "EC PARAMETERS");
508
509 auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
510 this->m_data = data.first;
511 this->m_explicit_encoding = data.second;
512 }
513 }
514
515 if(m_data == nullptr) {
516 throw Invalid_Argument(fmt("Unknown ECC group '{}'", str));
517 }
518}
519
520//static
521EC_Group EC_Group::from_PEM(std::string_view pem) {
522 const auto der = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
523 return EC_Group(der);
524}
525
527 const BigInt& a,
528 const BigInt& b,
529 const BigInt& base_x,
530 const BigInt& base_y,
531 const BigInt& order,
532 const BigInt& cofactor,
533 const OID& oid) {
534 BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
535 BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
536 BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
537 BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
538
540 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
541
543 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
544
545 // Check that 4*a^3 + 27*b^2 != 0
546 const auto discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
547 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
548 BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
549
550 // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
551 auto y2 = mod_p.square(base_y);
552 auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
553 BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
554
555 if(oid.has_value()) {
556 m_data = ec_group_data().lookup_or_create(
557 p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
558 } else {
559 m_data = ec_group_data().lookup_or_create_without_oid(
560 p, a, b, base_x, base_y, order, cofactor, EC_Group_Source::ExternalSource);
561 }
562}
563
565 const BigInt& p,
566 const BigInt& a,
567 const BigInt& b,
568 const BigInt& base_x,
569 const BigInt& base_y,
570 const BigInt& order) {
571 BOTAN_ARG_CHECK(oid.has_value(), "An OID is required for creating an EC_Group");
572
573 // TODO(Botan4) remove this and require 192 bits minimum
574#if defined(BOTAN_DISABLE_DEPRECATED_FEATURES)
575 constexpr size_t p_bits_lower_bound = 192;
576#else
577 constexpr size_t p_bits_lower_bound = 128;
578#endif
579
580 BOTAN_ARG_CHECK(p.bits() >= p_bits_lower_bound, "EC_Group p too small");
581 BOTAN_ARG_CHECK(p.bits() <= 521, "EC_Group p too large");
582
583 if(p.bits() == 521) {
584 const auto p521 = BigInt::power_of_2(521) - 1;
585 BOTAN_ARG_CHECK(p == p521, "EC_Group with p of 521 bits must be 2**521-1");
586 } else if(p.bits() == 239) {
587 const auto x962_p239 = []() {
588 BigInt p239;
589 for(size_t i = 0; i != 239; ++i) {
590 if(i < 47 || ((i >= 94) && (i != 143))) {
591 p239.set_bit(i);
592 }
593 }
594 return p239;
595 }();
596
597 BOTAN_ARG_CHECK(p == x962_p239, "EC_Group with p of 239 bits must be the X9.62 prime");
598 } else {
599 BOTAN_ARG_CHECK(p.bits() % 32 == 0, "EC_Group p must be a multiple of 32 bits");
600 }
601
602 BOTAN_ARG_CHECK(p % 4 == 3, "EC_Group p must be congruent to 3 modulo 4");
603
604 BOTAN_ARG_CHECK(a >= 0 && a < p, "EC_Group a is invalid");
605 BOTAN_ARG_CHECK(b > 0 && b < p, "EC_Group b is invalid");
606 BOTAN_ARG_CHECK(base_x >= 0 && base_x < p, "EC_Group base_x is invalid");
607 BOTAN_ARG_CHECK(base_y >= 0 && base_y < p, "EC_Group base_y is invalid");
608 BOTAN_ARG_CHECK(p.bits() == order.bits(), "EC_Group p and order must have the same number of bits");
609
611 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(p, mod_p), "EC_Group p is not prime");
612
614 BOTAN_ARG_CHECK(is_bailie_psw_probable_prime(order, mod_order), "EC_Group order is not prime");
615
616 // This catches someone "ignoring" a cofactor and just trying to
617 // provide the subgroup order
618 BOTAN_ARG_CHECK((p - order).abs().bits() <= (p.bits() / 2) + 1, "Hasse bound invalid");
619
620 // Check that 4*a^3 + 27*b^2 != 0
621 const auto discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
622 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
623 BOTAN_ARG_CHECK(discriminant != 0, "EC_Group discriminant is invalid");
624
625 // Check that the generator (base_x,base_y) is on the curve; y^2 = x^3 + a*x + b
626 auto y2 = mod_p.square(base_y);
627 auto x3_ax_b = mod_p.reduce(mod_p.cube(base_x) + mod_p.multiply(a, base_x) + b);
628 BOTAN_ARG_CHECK(y2 == x3_ax_b, "EC_Group generator is not on the curve");
629
630 const BigInt cofactor(1);
631
632 m_data =
633 ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource);
634}
635
636EC_Group::EC_Group(std::span<const uint8_t> der) {
637 auto data = DER_decode_EC_group(der, EC_Group_Source::ExternalSource);
638 m_data = data.first;
639 m_explicit_encoding = data.second;
640}
641
642// static
643bool EC_Group::unregister(const OID& oid) {
644 return ec_group_data().unregister(oid);
645}
646
647const EC_Group_Data& EC_Group::data() const {
648 if(m_data == nullptr) {
649 throw Invalid_State("EC_Group uninitialized");
650 }
651 return *m_data;
652}
653
654size_t EC_Group::get_p_bits() const {
655 return data().p_bits();
656}
657
658size_t EC_Group::get_p_bytes() const {
659 return data().p_bytes();
660}
661
663 return data().order_bits();
664}
665
667 return data().order_bytes();
668}
669
670const BigInt& EC_Group::get_p() const {
671 return data().p();
672}
673
674const BigInt& EC_Group::get_a() const {
675 return data().a();
676}
677
678const BigInt& EC_Group::get_b() const {
679 return data().b();
680}
681
682#if defined(BOTAN_HAS_LEGACY_EC_POINT)
683const EC_Point& EC_Group::get_base_point() const {
684 return data().base_point();
685}
686
687const EC_Point& EC_Group::generator() const {
688 return data().base_point();
689}
690
691bool EC_Group::verify_public_element(const EC_Point& point) const {
692 //check that public point is not at infinity
693 if(point.is_zero()) {
694 return false;
695 }
696
697 //check that public point is on the curve
698 if(point.on_the_curve() == false) {
699 return false;
700 }
701
702 //check that public point has order q
703 if((point * get_order()).is_zero() == false) {
704 return false;
705 }
706
707 if(has_cofactor()) {
708 if((point * get_cofactor()).is_zero()) {
709 return false;
710 }
711 }
712
713 return true;
714}
715
716#endif
717
719 return data().order();
720}
721
722const BigInt& EC_Group::get_g_x() const {
723 return data().g_x();
724}
725
726const BigInt& EC_Group::get_g_y() const {
727 return data().g_y();
728}
729
731 return data().cofactor();
732}
733
735 return data().has_cofactor();
736}
737
739 return data().oid();
740}
741
743 return data().source();
744}
745
747 return data().engine();
748}
749
750bool EC_Group::hash_to_curve_supported(std::string_view hash_fn) const {
751 return data().hash_to_curve_supported(hash_fn);
752}
753
754std::vector<uint8_t> EC_Group::DER_encode() const {
755 const auto& der_named_curve = data().der_named_curve();
756 // TODO(Botan4) this can be removed because an OID will always be defined
757 if(der_named_curve.empty()) {
758 throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
759 }
760
761 return der_named_curve;
762}
763
764std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const {
765 if(form == EC_Group_Encoding::Explicit) {
766 std::vector<uint8_t> output;
767 DER_Encoder der(output);
768 const size_t ecpVers1 = 1;
769 const OID curve_type("1.2.840.10045.1.1"); // prime field
770
771 const size_t p_bytes = get_p_bytes();
772
773 const auto generator = EC_AffinePoint::generator(*this).serialize_uncompressed();
774
775 der.start_sequence()
776 .encode(ecpVers1)
778 .encode(curve_type)
779 .encode(get_p())
780 .end_cons()
782 .encode(get_a().serialize(p_bytes), ASN1_Type::OctetString)
783 .encode(get_b().serialize(p_bytes), ASN1_Type::OctetString)
784 .end_cons()
785 .encode(generator, ASN1_Type::OctetString)
786 .encode(get_order())
788 .end_cons();
789 return output;
790 } else if(form == EC_Group_Encoding::NamedCurve) {
791 return this->DER_encode();
792 } else if(form == EC_Group_Encoding::ImplicitCA) {
793 return {0x00, 0x05};
794 } else {
795 throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
796 }
797}
798
800 const std::vector<uint8_t> der = DER_encode(form);
801 return PEM_Code::encode(der, "EC PARAMETERS");
802}
803
804bool EC_Group::operator==(const EC_Group& other) const {
805 if(m_data == other.m_data) {
806 return true; // same shared rep
807 }
808
809 return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() &&
810 get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() &&
811 get_cofactor() == other.get_cofactor());
812}
813
814bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const {
815 const bool is_builtin = source() == EC_Group_Source::Builtin;
816
817 if(is_builtin && !strong) {
818 return true;
819 }
820
821 // TODO(Botan4) this can probably all be removed once the deprecated EC_Group
822 // constructor is removed, since at that point it no longer becomes possible
823 // to create an EC_Group which fails to satisfy these conditions
824
825 const BigInt& p = get_p();
826 const BigInt& a = get_a();
827 const BigInt& b = get_b();
828 const BigInt& order = get_order();
829
830 if(p <= 3 || order <= 0) {
831 return false;
832 }
833 if(a < 0 || a >= p) {
834 return false;
835 }
836 if(b <= 0 || b >= p) {
837 return false;
838 }
839
840 const size_t test_prob = 128;
841 const bool is_randomly_generated = is_builtin;
842
843 //check if field modulus is prime
844 if(!is_prime(p, rng, test_prob, is_randomly_generated)) {
845 return false;
846 }
847
848 //check if order is prime
849 if(!is_prime(order, rng, test_prob, is_randomly_generated)) {
850 return false;
851 }
852
853 //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
855
856 const BigInt discriminant = mod_p.reduce(mod_p.multiply(BigInt::from_s32(4), mod_p.cube(a)) +
857 mod_p.multiply(BigInt::from_s32(27), mod_p.square(b)));
858
859 if(discriminant == 0) {
860 return false;
861 }
862
863 //check for valid cofactor
864 if(get_cofactor() < 1) {
865 return false;
866 }
867
868 // Check that the generator (g_x, g_y) is on the curve: y^2 == x^3 + a*x + b
869 const BigInt& g_x = get_g_x();
870 const BigInt& g_y = get_g_y();
871 const BigInt y2 = mod_p.square(g_y);
872 const BigInt x3_ax_b = mod_p.reduce(mod_p.cube(g_x) + mod_p.multiply(a, g_x) + b);
873 if(y2 != x3_ax_b) {
874 return false;
875 }
876
877 // Check that the generator has the claimed order: [order]G == identity,
878 auto g_pt = EC_AffinePoint::from_bigint_xy(*this, get_g_x(), get_g_y());
879 if(!g_pt) {
880 return false;
881 }
882 const auto neg_one = EC_Scalar::one(*this).negate();
883 const auto n_minus_one_g = EC_AffinePoint::g_mul(neg_one, rng);
884 if(n_minus_one_g != g_pt->negate()) {
885 return false;
886 }
887
888#if defined(BOTAN_HAS_LEGACY_EC_POINT)
889 // Reject if [cofactor]G is the identity. pcurves does not support cofactor != 1
890 // so this can only matter when the legacy backend is in use.
891 if(has_cofactor()) {
892 const EC_Point& base_point = get_base_point();
893 if((base_point * get_cofactor()).is_zero()) {
894 return false;
895 }
896 }
897#endif
898
899 // check the Hasse bound (roughly)
900 if((p - get_cofactor() * order).abs().bits() > (p.bits() / 2) + 1) {
901 return false;
902 }
903
904 return true;
905}
906
907EC_Group::Mul2Table::Mul2Table(EC_Group::Mul2Table&& other) noexcept = default;
908
910
911EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {}
912
914
915std::optional<EC_AffinePoint> EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const {
916 auto pt = m_tbl->mul2_vartime(x._inner(), y._inner());
917 if(pt) {
918 return EC_AffinePoint::_from_inner(std::move(pt));
919 } else {
920 return {};
921 }
922}
923
925 const EC_Scalar& x,
926 const EC_Scalar& y) const {
927 return m_tbl->mul2_vartime_x_mod_order_eq(v._inner(), x._inner(), y._inner());
928}
929
931 const EC_Scalar& c,
932 const EC_Scalar& x,
933 const EC_Scalar& y) const {
934 return this->mul2_vartime_x_mod_order_eq(v, c * x, c * y);
935}
936
937} // 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:42
static Barrett_Reduction for_public_modulus(const BigInt &m)
Definition barrett.cpp:33
void set_bit(size_t n)
Definition bigint.h:516
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:906
static BigInt from_s32(int32_t n)
Definition bigint.cpp:42
BigInt & square(secure_vector< word > &ws)
Definition big_ops2.cpp:191
DER_Encoder & start_sequence()
Definition der_enc.h:86
DER_Encoder & end_cons()
Definition der_enc.cpp:208
DER_Encoder & encode(bool b)
Definition der_enc.cpp:313
static std::optional< EC_AffinePoint > from_bigint_xy(const EC_Group &group, const BigInt &x, const BigInt &y)
Definition ec_apoint.cpp:93
static EC_AffinePoint g_mul(const EC_Scalar &scalar, RandomNumberGenerator &rng)
Multiply by the group generator returning a complete point.
T serialize_uncompressed() const
Definition ec_apoint.h:232
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
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:915
BOTAN_FUTURE_EXPLICIT Mul2Table(const EC_AffinePoint &h)
Definition ec_group.cpp:911
bool mul2_vartime_x_mod_order_eq(const EC_Scalar &v, const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:924
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:478
static EC_Group from_PEM(std::string_view pem)
Definition ec_group.cpp:521
const BigInt & get_b() const
Definition ec_group.cpp:678
const BigInt & get_a() const
Definition ec_group.cpp:674
const BigInt & get_g_y() const
Definition ec_group.cpp:726
const BigInt & get_cofactor() const
Definition ec_group.cpp:730
BigInt mod_order(const BigInt &x) const
Definition ec_group.h:757
bool operator==(const EC_Group &other) const
Definition ec_group.cpp:804
static bool supports_application_specific_group_with_cofactor()
Definition ec_group.cpp:458
EC_Group_Engine engine() const
Definition ec_group.cpp:746
EC_Group_Source source() const
Definition ec_group.cpp:742
const BigInt & get_p() const
Definition ec_group.cpp:670
bool verify_group(RandomNumberGenerator &rng, bool strong=false) const
Definition ec_group.cpp:814
const BigInt & get_order() const
Definition ec_group.cpp:718
size_t get_p_bits() const
Definition ec_group.cpp:654
static EC_Group from_OID(const OID &oid)
Definition ec_group.cpp:467
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:754
const BigInt & get_g_x() const
Definition ec_group.cpp:722
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:526
const OID & get_curve_oid() const
Definition ec_group.cpp:738
static bool supports_application_specific_group()
Definition ec_group.cpp:449
static const std::set< std::string > & known_named_groups()
Definition ec_named.cpp:477
bool has_cofactor() const
Definition ec_group.cpp:734
static size_t clear_registered_curve_data()
Definition ec_group.cpp:243
static bool unregister(const OID &oid)
Definition ec_group.cpp:643
static bool supports_named_group(std::string_view name)
Definition ec_group.cpp:422
EC_Group & operator=(const EC_Group &)
size_t get_p_bytes() const
Definition ec_group.cpp:658
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:799
bool hash_to_curve_supported(std::string_view hash_fn) const
Definition ec_group.cpp:750
size_t get_order_bits() const
Definition ec_group.cpp:662
size_t get_order_bytes() const
Definition ec_group.cpp:666
static EC_Scalar one(const EC_Group &group)
Definition ec_scalar.cpp:68
const EC_Scalar_Data & _inner() const
Definition ec_scalar.h:277
EC_Scalar negate() const
static std::optional< OID > from_name(std::string_view name)
Definition asn1_oid.cpp:66
bool has_value() const
Definition asn1_obj.h:474
std::string to_string() const
Definition asn1_oid.cpp:123
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:80
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:40
std::string asn1_tag_to_string(ASN1_Type type)
Definition asn1_obj.cpp:129
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:145
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:58
BigInt sqrt_modulo_prime(const BigInt &a, const BigInt &p)
Definition numthry.cpp:27
EC_Group_Source
Definition ec_group.h:38