/src/botan/src/lib/pubkey/ec_group/ec_group.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ECC Domain Parameters |
3 | | * |
4 | | * (C) 2007 Falko Strenzke, FlexSecure GmbH |
5 | | * (C) 2008,2018 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/pem.h> |
17 | | #include <botan/reducer.h> |
18 | | #include <botan/rng.h> |
19 | | #include <botan/internal/fmt.h> |
20 | | #include <botan/internal/point_mul.h> |
21 | | #include <botan/internal/primality.h> |
22 | | #include <vector> |
23 | | |
24 | | #if defined(BOTAN_HAS_EC_HASH_TO_CURVE) |
25 | | #include <botan/internal/ec_h2c.h> |
26 | | #endif |
27 | | |
28 | | namespace Botan { |
29 | | |
30 | | class EC_Group_Data final { |
31 | | public: |
32 | | EC_Group_Data(const BigInt& p, |
33 | | const BigInt& a, |
34 | | const BigInt& b, |
35 | | const BigInt& g_x, |
36 | | const BigInt& g_y, |
37 | | const BigInt& order, |
38 | | const BigInt& cofactor, |
39 | | const OID& oid, |
40 | | EC_Group_Source source) : |
41 | | m_curve(p, a, b), |
42 | | m_base_point(m_curve, g_x, g_y), |
43 | | m_g_x(g_x), |
44 | | m_g_y(g_y), |
45 | | m_order(order), |
46 | | m_cofactor(cofactor), |
47 | | m_mod_order(order), |
48 | | m_base_mult(m_base_point, m_mod_order), |
49 | | m_oid(oid), |
50 | | m_p_bits(p.bits()), |
51 | | m_order_bits(order.bits()), |
52 | | m_a_is_minus_3(a == p - 3), |
53 | | m_a_is_zero(a.is_zero()), |
54 | 1 | m_source(source) {} |
55 | | |
56 | | bool params_match(const BigInt& p, |
57 | | const BigInt& a, |
58 | | const BigInt& b, |
59 | | const BigInt& g_x, |
60 | | const BigInt& g_y, |
61 | | const BigInt& order, |
62 | 0 | const BigInt& cofactor) const { |
63 | 0 | return (this->p() == p && this->a() == a && this->b() == b && this->order() == order && |
64 | 0 | this->cofactor() == cofactor && this->g_x() == g_x && this->g_y() == g_y); |
65 | 0 | } |
66 | | |
67 | 0 | bool params_match(const EC_Group_Data& other) const { |
68 | 0 | return params_match( |
69 | 0 | other.p(), other.a(), other.b(), other.g_x(), other.g_y(), other.order(), other.cofactor()); |
70 | 0 | } |
71 | | |
72 | 0 | void set_oid(const OID& oid) { |
73 | 0 | BOTAN_STATE_CHECK(m_oid.empty()); |
74 | 0 | m_oid = oid; |
75 | 0 | } |
76 | | |
77 | 0 | const OID& oid() const { return m_oid; } |
78 | | |
79 | 1.37k | const BigInt& p() const { return m_curve.get_p(); } |
80 | | |
81 | 1.37k | const BigInt& a() const { return m_curve.get_a(); } |
82 | | |
83 | 1.37k | const BigInt& b() const { return m_curve.get_b(); } |
84 | | |
85 | 4.97k | const BigInt& order() const { return m_order; } |
86 | | |
87 | 0 | const BigInt& cofactor() const { return m_cofactor; } |
88 | | |
89 | 0 | const BigInt& g_x() const { return m_g_x; } |
90 | | |
91 | 0 | const BigInt& g_y() const { return m_g_y; } |
92 | | |
93 | 0 | size_t p_bits() const { return m_p_bits; } |
94 | | |
95 | 0 | size_t p_bytes() const { return (m_p_bits + 7) / 8; } |
96 | | |
97 | 0 | size_t order_bits() const { return m_order_bits; } |
98 | | |
99 | 0 | size_t order_bytes() const { return (m_order_bits + 7) / 8; } |
100 | | |
101 | 1.70k | const CurveGFp& curve() const { return m_curve; } |
102 | | |
103 | 1 | const EC_Point& base_point() const { return m_base_point; } |
104 | | |
105 | 0 | bool a_is_minus_3() const { return m_a_is_minus_3; } |
106 | | |
107 | 0 | bool a_is_zero() const { return m_a_is_zero; } |
108 | | |
109 | 0 | BigInt mod_order(const BigInt& x) const { return m_mod_order.reduce(x); } |
110 | | |
111 | 0 | BigInt square_mod_order(const BigInt& x) const { return m_mod_order.square(x); } |
112 | | |
113 | 0 | BigInt multiply_mod_order(const BigInt& x, const BigInt& y) const { return m_mod_order.multiply(x, y); } |
114 | | |
115 | 0 | BigInt multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const { |
116 | 0 | return m_mod_order.multiply(m_mod_order.multiply(x, y), z); |
117 | 0 | } |
118 | | |
119 | 0 | BigInt inverse_mod_order(const BigInt& x) const { return inverse_mod(x, m_order); } |
120 | | |
121 | 4.12k | EC_Point blinded_base_point_multiply(const BigInt& k, RandomNumberGenerator& rng, std::vector<BigInt>& ws) const { |
122 | 4.12k | return m_base_mult.mul(k, rng, m_order, ws); |
123 | 4.12k | } |
124 | | |
125 | 0 | EC_Group_Source source() const { return m_source; } |
126 | | |
127 | | private: |
128 | | CurveGFp m_curve; |
129 | | EC_Point m_base_point; |
130 | | |
131 | | BigInt m_g_x; |
132 | | BigInt m_g_y; |
133 | | BigInt m_order; |
134 | | BigInt m_cofactor; |
135 | | Modular_Reducer m_mod_order; |
136 | | EC_Point_Base_Point_Precompute m_base_mult; |
137 | | OID m_oid; |
138 | | size_t m_p_bits; |
139 | | size_t m_order_bits; |
140 | | bool m_a_is_minus_3; |
141 | | bool m_a_is_zero; |
142 | | EC_Group_Source m_source; |
143 | | }; |
144 | | |
145 | | class EC_Group_Data_Map final { |
146 | | public: |
147 | 1 | EC_Group_Data_Map() = default; |
148 | | |
149 | 0 | size_t clear() { |
150 | 0 | lock_guard_type<mutex_type> lock(m_mutex); |
151 | 0 | size_t count = m_registered_curves.size(); |
152 | 0 | m_registered_curves.clear(); |
153 | 0 | return count; |
154 | 0 | } |
155 | | |
156 | 1 | std::shared_ptr<EC_Group_Data> lookup(const OID& oid) { |
157 | 1 | lock_guard_type<mutex_type> lock(m_mutex); |
158 | | |
159 | 1 | for(auto i : m_registered_curves) { |
160 | 0 | if(i->oid() == oid) { |
161 | 0 | return i; |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | // Not found, check hardcoded data |
166 | 1 | std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid); |
167 | | |
168 | 1 | if(data) { |
169 | 1 | for(auto curve : m_registered_curves) { |
170 | 0 | if(curve->oid().empty() == true && curve->params_match(*data)) { |
171 | 0 | curve->set_oid(oid); |
172 | 0 | return curve; |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | 1 | m_registered_curves.push_back(data); |
177 | 1 | return data; |
178 | 1 | } |
179 | | |
180 | | // Nope, unknown curve |
181 | 0 | return std::shared_ptr<EC_Group_Data>(); |
182 | 1 | } |
183 | | |
184 | | std::shared_ptr<EC_Group_Data> lookup_or_create(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 | | const OID& oid, |
192 | 0 | EC_Group_Source source) { |
193 | 0 | lock_guard_type<mutex_type> lock(m_mutex); |
194 | |
|
195 | 0 | for(auto i : m_registered_curves) { |
196 | | /* |
197 | | * The params may be the same but you are trying to register under a |
198 | | * different OID than the one we are using, so using a different |
199 | | * group, since EC_Group's model assumes a single OID per group. |
200 | | */ |
201 | 0 | if(!oid.empty() && !i->oid().empty() && i->oid() != oid) { |
202 | 0 | continue; |
203 | 0 | } |
204 | | |
205 | 0 | const bool same_oid = !oid.empty() && i->oid() == oid; |
206 | 0 | const bool same_params = i->params_match(p, a, b, g_x, g_y, order, cofactor); |
207 | | |
208 | | /* |
209 | | * If the params and OID are the same then we are done, just return |
210 | | * the already registered curve obj. |
211 | | */ |
212 | 0 | if(same_params && same_oid) { |
213 | 0 | return i; |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * If same params and the new OID is empty, then that's ok too |
218 | | */ |
219 | 0 | if(same_params && oid.empty()) { |
220 | 0 | return i; |
221 | 0 | } |
222 | | |
223 | | /* |
224 | | * Check for someone trying to reuse an already in-use OID |
225 | | */ |
226 | 0 | if(same_oid && !same_params) { |
227 | 0 | throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() + |
228 | 0 | " but a distinct curve is already registered using that OID"); |
229 | 0 | } |
230 | | |
231 | | /* |
232 | | * If the same curve was previously created without an OID but is now |
233 | | * being registered again using an OID, save that OID. |
234 | | */ |
235 | 0 | if(same_params && i->oid().empty() && !oid.empty()) { |
236 | 0 | i->set_oid(oid); |
237 | 0 | return i; |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | Not found in current list, so we need to create a new entry |
243 | | |
244 | | If an OID is set, try to look up relative our static tables to detect a duplicate |
245 | | registration under an OID |
246 | | */ |
247 | | |
248 | 0 | std::shared_ptr<EC_Group_Data> new_group = |
249 | 0 | std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, source); |
250 | |
|
251 | 0 | if(oid.has_value()) { |
252 | 0 | std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid); |
253 | 0 | if(data != nullptr && !new_group->params_match(*data)) { |
254 | 0 | throw Invalid_Argument("Attempting to register an EC group under OID of hardcoded group"); |
255 | 0 | } |
256 | 0 | } else { |
257 | | // Here try to use the order as a hint to look up the group id, to identify common groups |
258 | 0 | const OID oid_from_store = EC_Group::EC_group_identity_from_order(order); |
259 | 0 | if(oid_from_store.has_value()) { |
260 | 0 | std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid_from_store); |
261 | | |
262 | | /* |
263 | | If EC_group_identity_from_order returned an OID then looking up that OID |
264 | | must always return a result. |
265 | | */ |
266 | 0 | BOTAN_ASSERT_NOMSG(data != nullptr); |
267 | | |
268 | | /* |
269 | | It is possible (if unlikely) that someone is registering another group |
270 | | that happens to have an order equal to that of a well known group - |
271 | | so verify all values before assigning the OID. |
272 | | */ |
273 | 0 | if(new_group->params_match(*data)) { |
274 | 0 | new_group->set_oid(oid_from_store); |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 0 | m_registered_curves.push_back(new_group); |
280 | 0 | return new_group; |
281 | 0 | } |
282 | | |
283 | | private: |
284 | | mutex_type m_mutex; |
285 | | std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves; |
286 | | }; |
287 | | |
288 | | //static |
289 | 1 | EC_Group_Data_Map& EC_Group::ec_group_data() { |
290 | | /* |
291 | | * This exists purely to ensure the allocator is constructed before g_ec_data, |
292 | | * which ensures that its destructor runs after ~g_ec_data is complete. |
293 | | */ |
294 | | |
295 | 1 | static Allocator_Initializer g_init_allocator; |
296 | 1 | static EC_Group_Data_Map g_ec_data; |
297 | 1 | return g_ec_data; |
298 | 1 | } |
299 | | |
300 | | //static |
301 | 0 | size_t EC_Group::clear_registered_curve_data() { return ec_group_data().clear(); } |
302 | | |
303 | | //static |
304 | | std::shared_ptr<EC_Group_Data> EC_Group::load_EC_group_info(const char* p_str, |
305 | | const char* a_str, |
306 | | const char* b_str, |
307 | | const char* g_x_str, |
308 | | const char* g_y_str, |
309 | | const char* order_str, |
310 | 1 | const OID& oid) { |
311 | 1 | const BigInt p(p_str); |
312 | 1 | const BigInt a(a_str); |
313 | 1 | const BigInt b(b_str); |
314 | 1 | const BigInt g_x(g_x_str); |
315 | 1 | const BigInt g_y(g_y_str); |
316 | 1 | const BigInt order(order_str); |
317 | 1 | const BigInt cofactor(1); // implicit |
318 | | |
319 | 1 | return std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin); |
320 | 1 | } |
321 | | |
322 | | //static |
323 | 0 | std::shared_ptr<EC_Group_Data> EC_Group::BER_decode_EC_group(const uint8_t bits[], size_t len, EC_Group_Source source) { |
324 | 0 | BER_Decoder ber(bits, len); |
325 | 0 | BER_Object obj = ber.get_next_object(); |
326 | |
|
327 | 0 | if(obj.type() == ASN1_Type::Null) [[unlikely]] { |
328 | 0 | throw Decoding_Error("Cannot handle ImplicitCA ECC parameters"); |
329 | 0 | } else if(obj.type() == ASN1_Type::ObjectId) { |
330 | 0 | OID dom_par_oid; |
331 | 0 | BER_Decoder(bits, len).decode(dom_par_oid); |
332 | 0 | return ec_group_data().lookup(dom_par_oid); |
333 | 0 | } else if(obj.type() == ASN1_Type::Sequence) { |
334 | 0 | BigInt p, a, b, order, cofactor; |
335 | 0 | std::vector<uint8_t> base_pt; |
336 | 0 | std::vector<uint8_t> seed; |
337 | |
|
338 | 0 | BER_Decoder(bits, len) |
339 | 0 | .start_sequence() |
340 | 0 | .decode_and_check<size_t>(1, "Unknown ECC param version code") |
341 | 0 | .start_sequence() |
342 | 0 | .decode_and_check(OID("1.2.840.10045.1.1"), "Only prime ECC fields supported") |
343 | 0 | .decode(p) |
344 | 0 | .end_cons() |
345 | 0 | .start_sequence() |
346 | 0 | .decode_octet_string_bigint(a) |
347 | 0 | .decode_octet_string_bigint(b) |
348 | 0 | .decode_optional_string(seed, ASN1_Type::BitString, ASN1_Type::BitString) |
349 | 0 | .end_cons() |
350 | 0 | .decode(base_pt, ASN1_Type::OctetString) |
351 | 0 | .decode(order) |
352 | 0 | .decode(cofactor) |
353 | 0 | .end_cons() |
354 | 0 | .verify_end(); |
355 | |
|
356 | 0 | if(p.bits() < 64 || p.is_negative() || !is_bailie_psw_probable_prime(p)) { |
357 | 0 | throw Decoding_Error("Invalid ECC p parameter"); |
358 | 0 | } |
359 | | |
360 | 0 | if(a.is_negative() || a >= p) { |
361 | 0 | throw Decoding_Error("Invalid ECC a parameter"); |
362 | 0 | } |
363 | | |
364 | 0 | if(b <= 0 || b >= p) { |
365 | 0 | throw Decoding_Error("Invalid ECC b parameter"); |
366 | 0 | } |
367 | | |
368 | 0 | if(order <= 0 || !is_bailie_psw_probable_prime(order)) { |
369 | 0 | throw Decoding_Error("Invalid ECC order parameter"); |
370 | 0 | } |
371 | | |
372 | 0 | if(cofactor <= 0 || cofactor >= 16) { |
373 | 0 | throw Decoding_Error("Invalid ECC cofactor parameter"); |
374 | 0 | } |
375 | | |
376 | 0 | std::pair<BigInt, BigInt> base_xy = Botan::OS2ECP(base_pt.data(), base_pt.size(), p, a, b); |
377 | |
|
378 | 0 | return ec_group_data().lookup_or_create(p, a, b, base_xy.first, base_xy.second, order, cofactor, OID(), source); |
379 | 0 | } else { |
380 | 0 | throw Decoding_Error("Unexpected tag while decoding ECC domain params"); |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | 0 | EC_Group::EC_Group() = default; |
385 | | |
386 | 1 | EC_Group::~EC_Group() = default; |
387 | | |
388 | 0 | EC_Group::EC_Group(const OID& domain_oid) { |
389 | 0 | this->m_data = ec_group_data().lookup(domain_oid); |
390 | 0 | if(!this->m_data) { |
391 | 0 | throw Invalid_Argument("Unknown EC_Group " + domain_oid.to_string()); |
392 | 0 | } |
393 | 0 | } |
394 | | |
395 | 1 | EC_Group::EC_Group(std::string_view str) { |
396 | 1 | if(str.empty()) { |
397 | 0 | return; // no initialization / uninitialized |
398 | 0 | } |
399 | | |
400 | 1 | try { |
401 | 1 | const OID oid = OID::from_string(str); |
402 | 1 | if(oid.has_value()) { |
403 | 1 | m_data = ec_group_data().lookup(oid); |
404 | 1 | } |
405 | 1 | } catch(...) {} |
406 | | |
407 | 1 | if(m_data == nullptr) { |
408 | 0 | if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----") { |
409 | | // OK try it as PEM ... |
410 | 0 | secure_vector<uint8_t> ber = PEM_Code::decode_check_label(str, "EC PARAMETERS"); |
411 | 0 | this->m_data = BER_decode_EC_group(ber.data(), ber.size(), EC_Group_Source::ExternalSource); |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | 1 | if(m_data == nullptr) { |
416 | 0 | throw Invalid_Argument(fmt("Unknown ECC group '{}'", str)); |
417 | 0 | } |
418 | 1 | } |
419 | | |
420 | | //static |
421 | 0 | EC_Group EC_Group::EC_Group_from_PEM(std::string_view pem) { |
422 | 0 | const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS"); |
423 | 0 | return EC_Group(ber.data(), ber.size()); |
424 | 0 | } |
425 | | |
426 | | EC_Group::EC_Group(const BigInt& p, |
427 | | const BigInt& a, |
428 | | const BigInt& b, |
429 | | const BigInt& base_x, |
430 | | const BigInt& base_y, |
431 | | const BigInt& order, |
432 | | const BigInt& cofactor, |
433 | 0 | const OID& oid) { |
434 | 0 | m_data = |
435 | 0 | ec_group_data().lookup_or_create(p, a, b, base_x, base_y, order, cofactor, oid, EC_Group_Source::ExternalSource); |
436 | 0 | } |
437 | | |
438 | 0 | EC_Group::EC_Group(const uint8_t ber[], size_t ber_len) { |
439 | 0 | m_data = BER_decode_EC_group(ber, ber_len, EC_Group_Source::ExternalSource); |
440 | 0 | } |
441 | | |
442 | 14.9k | const EC_Group_Data& EC_Group::data() const { |
443 | 14.9k | if(m_data == nullptr) { |
444 | 0 | throw Invalid_State("EC_Group uninitialized"); |
445 | 0 | } |
446 | 14.9k | return *m_data; |
447 | 14.9k | } |
448 | | |
449 | 0 | bool EC_Group::a_is_minus_3() const { return data().a_is_minus_3(); } |
450 | | |
451 | 0 | bool EC_Group::a_is_zero() const { return data().a_is_zero(); } |
452 | | |
453 | 0 | size_t EC_Group::get_p_bits() const { return data().p_bits(); } |
454 | | |
455 | 0 | size_t EC_Group::get_p_bytes() const { return data().p_bytes(); } |
456 | | |
457 | 0 | size_t EC_Group::get_order_bits() const { return data().order_bits(); } |
458 | | |
459 | 0 | size_t EC_Group::get_order_bytes() const { return data().order_bytes(); } |
460 | | |
461 | 1.37k | const BigInt& EC_Group::get_p() const { return data().p(); } |
462 | | |
463 | 1.37k | const BigInt& EC_Group::get_a() const { return data().a(); } |
464 | | |
465 | 1.37k | const BigInt& EC_Group::get_b() const { return data().b(); } |
466 | | |
467 | 1 | const EC_Point& EC_Group::get_base_point() const { return data().base_point(); } |
468 | | |
469 | 4.97k | const BigInt& EC_Group::get_order() const { return data().order(); } |
470 | | |
471 | 0 | const BigInt& EC_Group::get_g_x() const { return data().g_x(); } |
472 | | |
473 | 0 | const BigInt& EC_Group::get_g_y() const { return data().g_y(); } |
474 | | |
475 | 0 | const BigInt& EC_Group::get_cofactor() const { return data().cofactor(); } |
476 | | |
477 | 0 | BigInt EC_Group::mod_order(const BigInt& k) const { return data().mod_order(k); } |
478 | | |
479 | 0 | BigInt EC_Group::square_mod_order(const BigInt& x) const { return data().square_mod_order(x); } |
480 | | |
481 | 0 | BigInt EC_Group::multiply_mod_order(const BigInt& x, const BigInt& y) const { return data().multiply_mod_order(x, y); } |
482 | | |
483 | 0 | BigInt EC_Group::multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const { |
484 | 0 | return data().multiply_mod_order(x, y, z); |
485 | 0 | } |
486 | | |
487 | 0 | BigInt EC_Group::inverse_mod_order(const BigInt& x) const { return data().inverse_mod_order(x); } |
488 | | |
489 | 0 | const OID& EC_Group::get_curve_oid() const { return data().oid(); } |
490 | | |
491 | 0 | EC_Group_Source EC_Group::source() const { return data().source(); } |
492 | | |
493 | 0 | size_t EC_Group::point_size(EC_Point_Format format) const { |
494 | | // Hybrid and standard format are (x,y), compressed is y, +1 format byte |
495 | 0 | if(format == EC_Point_Format::Compressed) { |
496 | 0 | return (1 + get_p_bytes()); |
497 | 0 | } else { |
498 | 0 | return (1 + 2 * get_p_bytes()); |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | 0 | EC_Point EC_Group::OS2ECP(const uint8_t bits[], size_t len) const { return Botan::OS2ECP(bits, len, data().curve()); } |
503 | | |
504 | 1.70k | EC_Point EC_Group::point(const BigInt& x, const BigInt& y) const { |
505 | | // TODO: randomize the representation? |
506 | 1.70k | return EC_Point(data().curve(), x, y); |
507 | 1.70k | } |
508 | | |
509 | 0 | EC_Point EC_Group::point_multiply(const BigInt& x, const EC_Point& pt, const BigInt& y) const { |
510 | 0 | EC_Point_Multi_Point_Precompute xy_mul(get_base_point(), pt); |
511 | 0 | return xy_mul.multi_exp(x, y); |
512 | 0 | } |
513 | | |
514 | | EC_Point EC_Group::blinded_base_point_multiply(const BigInt& k, |
515 | | RandomNumberGenerator& rng, |
516 | 4.12k | std::vector<BigInt>& ws) const { |
517 | 4.12k | return data().blinded_base_point_multiply(k, rng, ws); |
518 | 4.12k | } |
519 | | |
520 | | BigInt EC_Group::blinded_base_point_multiply_x(const BigInt& k, |
521 | | RandomNumberGenerator& rng, |
522 | 0 | std::vector<BigInt>& ws) const { |
523 | 0 | const EC_Point pt = data().blinded_base_point_multiply(k, rng, ws); |
524 | |
|
525 | 0 | if(pt.is_zero()) { |
526 | 0 | return BigInt::zero(); |
527 | 0 | } |
528 | 0 | return pt.get_affine_x(); |
529 | 0 | } |
530 | | |
531 | 0 | BigInt EC_Group::random_scalar(RandomNumberGenerator& rng) const { |
532 | 0 | return BigInt::random_integer(rng, BigInt::one(), get_order()); |
533 | 0 | } |
534 | | |
535 | | EC_Point EC_Group::blinded_var_point_multiply(const EC_Point& point, |
536 | | const BigInt& k, |
537 | | RandomNumberGenerator& rng, |
538 | 4.97k | std::vector<BigInt>& ws) const { |
539 | 4.97k | EC_Point_Var_Point_Precompute mul(point, rng, ws); |
540 | 4.97k | return mul.mul(k, rng, get_order(), ws); |
541 | 4.97k | } |
542 | | |
543 | 0 | EC_Point EC_Group::zero_point() const { return EC_Point(data().curve()); } |
544 | | |
545 | | EC_Point EC_Group::hash_to_curve(std::string_view hash_fn, |
546 | | const uint8_t input[], |
547 | | size_t input_len, |
548 | | std::string_view domain, |
549 | 0 | bool random_oracle) const { |
550 | 0 | return this->hash_to_curve( |
551 | 0 | hash_fn, input, input_len, reinterpret_cast<const uint8_t*>(domain.data()), domain.size(), random_oracle); |
552 | 0 | } |
553 | | |
554 | | EC_Point EC_Group::hash_to_curve(std::string_view hash_fn, |
555 | | const uint8_t input[], |
556 | | size_t input_len, |
557 | | const uint8_t domain_sep[], |
558 | | size_t domain_sep_len, |
559 | 0 | bool random_oracle) const { |
560 | 0 | #if defined(BOTAN_HAS_EC_HASH_TO_CURVE) |
561 | | |
562 | | // Only have SSWU currently |
563 | 0 | if(get_a().is_zero() || get_b().is_zero() || get_p() % 4 == 1) { |
564 | 0 | throw Not_Implemented("EC_Group::hash_to_curve not available for this curve type"); |
565 | 0 | } |
566 | | |
567 | 0 | return hash_to_curve_sswu(*this, hash_fn, input, input_len, domain_sep, domain_sep_len, random_oracle); |
568 | |
|
569 | | #else |
570 | | BOTAN_UNUSED(hash_fn, random_oracle, input, input_len, domain_sep, domain_sep_len); |
571 | | throw Not_Implemented("EC_Group::hash_to_curve functionality not available in this configuration"); |
572 | | #endif |
573 | 0 | } |
574 | | |
575 | 0 | std::vector<uint8_t> EC_Group::DER_encode(EC_Group_Encoding form) const { |
576 | 0 | std::vector<uint8_t> output; |
577 | |
|
578 | 0 | DER_Encoder der(output); |
579 | |
|
580 | 0 | if(form == EC_Group_Encoding::Explicit) { |
581 | 0 | const size_t ecpVers1 = 1; |
582 | 0 | const OID curve_type("1.2.840.10045.1.1"); // prime field |
583 | |
|
584 | 0 | const size_t p_bytes = get_p_bytes(); |
585 | |
|
586 | 0 | der.start_sequence() |
587 | 0 | .encode(ecpVers1) |
588 | 0 | .start_sequence() |
589 | 0 | .encode(curve_type) |
590 | 0 | .encode(get_p()) |
591 | 0 | .end_cons() |
592 | 0 | .start_sequence() |
593 | 0 | .encode(BigInt::encode_1363(get_a(), p_bytes), ASN1_Type::OctetString) |
594 | 0 | .encode(BigInt::encode_1363(get_b(), p_bytes), ASN1_Type::OctetString) |
595 | 0 | .end_cons() |
596 | 0 | .encode(get_base_point().encode(EC_Point_Format::Uncompressed), ASN1_Type::OctetString) |
597 | 0 | .encode(get_order()) |
598 | 0 | .encode(get_cofactor()) |
599 | 0 | .end_cons(); |
600 | 0 | } else if(form == EC_Group_Encoding::NamedCurve) { |
601 | 0 | const OID oid = get_curve_oid(); |
602 | 0 | if(oid.empty()) { |
603 | 0 | throw Encoding_Error("Cannot encode EC_Group as OID because OID not set"); |
604 | 0 | } |
605 | 0 | der.encode(oid); |
606 | 0 | } else if(form == EC_Group_Encoding::ImplicitCA) { |
607 | 0 | der.encode_null(); |
608 | 0 | } else { |
609 | 0 | throw Internal_Error("EC_Group::DER_encode: Unknown encoding"); |
610 | 0 | } |
611 | | |
612 | 0 | return output; |
613 | 0 | } |
614 | | |
615 | 0 | std::string EC_Group::PEM_encode() const { |
616 | 0 | const std::vector<uint8_t> der = DER_encode(EC_Group_Encoding::Explicit); |
617 | 0 | return PEM_Code::encode(der, "EC PARAMETERS"); |
618 | 0 | } |
619 | | |
620 | 0 | bool EC_Group::operator==(const EC_Group& other) const { |
621 | 0 | if(m_data == other.m_data) { |
622 | 0 | return true; // same shared rep |
623 | 0 | } |
624 | | |
625 | 0 | return (get_p() == other.get_p() && get_a() == other.get_a() && get_b() == other.get_b() && |
626 | 0 | get_g_x() == other.get_g_x() && get_g_y() == other.get_g_y() && get_order() == other.get_order() && |
627 | 0 | get_cofactor() == other.get_cofactor()); |
628 | 0 | } |
629 | | |
630 | 0 | bool EC_Group::verify_public_element(const EC_Point& point) const { |
631 | | //check that public point is not at infinity |
632 | 0 | if(point.is_zero()) { |
633 | 0 | return false; |
634 | 0 | } |
635 | | |
636 | | //check that public point is on the curve |
637 | 0 | if(point.on_the_curve() == false) { |
638 | 0 | return false; |
639 | 0 | } |
640 | | |
641 | | //check that public point has order q |
642 | 0 | if((point * get_order()).is_zero() == false) { |
643 | 0 | return false; |
644 | 0 | } |
645 | | |
646 | 0 | if(get_cofactor() > 1) { |
647 | 0 | if((point * get_cofactor()).is_zero()) { |
648 | 0 | return false; |
649 | 0 | } |
650 | 0 | } |
651 | | |
652 | 0 | return true; |
653 | 0 | } |
654 | | |
655 | 0 | bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const { |
656 | 0 | const bool is_builtin = source() == EC_Group_Source::Builtin; |
657 | |
|
658 | 0 | if(is_builtin && !strong) { |
659 | 0 | return true; |
660 | 0 | } |
661 | | |
662 | 0 | const BigInt& p = get_p(); |
663 | 0 | const BigInt& a = get_a(); |
664 | 0 | const BigInt& b = get_b(); |
665 | 0 | const BigInt& order = get_order(); |
666 | 0 | const EC_Point& base_point = get_base_point(); |
667 | |
|
668 | 0 | if(p <= 3 || order <= 0) { |
669 | 0 | return false; |
670 | 0 | } |
671 | 0 | if(a < 0 || a >= p) { |
672 | 0 | return false; |
673 | 0 | } |
674 | 0 | if(b <= 0 || b >= p) { |
675 | 0 | return false; |
676 | 0 | } |
677 | | |
678 | 0 | const size_t test_prob = 128; |
679 | 0 | const bool is_randomly_generated = is_builtin; |
680 | | |
681 | | //check if field modulus is prime |
682 | 0 | if(!is_prime(p, rng, test_prob, is_randomly_generated)) { |
683 | 0 | return false; |
684 | 0 | } |
685 | | |
686 | | //check if order is prime |
687 | 0 | if(!is_prime(order, rng, test_prob, is_randomly_generated)) { |
688 | 0 | return false; |
689 | 0 | } |
690 | | |
691 | | //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero |
692 | 0 | const Modular_Reducer mod_p(p); |
693 | |
|
694 | 0 | const BigInt discriminant = mod_p.reduce(mod_p.multiply(4, mod_p.cube(a)) + mod_p.multiply(27, mod_p.square(b))); |
695 | |
|
696 | 0 | if(discriminant == 0) { |
697 | 0 | return false; |
698 | 0 | } |
699 | | |
700 | | //check for valid cofactor |
701 | 0 | if(get_cofactor() < 1) { |
702 | 0 | return false; |
703 | 0 | } |
704 | | |
705 | | //check if the base point is on the curve |
706 | 0 | if(!base_point.on_the_curve()) { |
707 | 0 | return false; |
708 | 0 | } |
709 | 0 | if((base_point * get_cofactor()).is_zero()) { |
710 | 0 | return false; |
711 | 0 | } |
712 | | //check if order of the base point is correct |
713 | 0 | if(!(base_point * order).is_zero()) { |
714 | 0 | return false; |
715 | 0 | } |
716 | | |
717 | 0 | return true; |
718 | 0 | } |
719 | | |
720 | | } // namespace Botan |