Botan 3.10.0
Crypto and TLS for C&
ec_point.cpp
Go to the documentation of this file.
1/*
2* Point arithmetic on elliptic curves over GF(p)
3*
4* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
5* 2008-2011,2012,2014,2015,2018,2024 Jack Lloyd
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/ec_point.h>
11
12#include <botan/numthry.h>
13#include <botan/rng.h>
14#include <botan/internal/ct_utils.h>
15#include <botan/internal/ec_inner_data.h>
16#include <botan/internal/mod_inv.h>
17#include <botan/internal/monty.h>
18#include <botan/internal/stl_util.h>
19
20namespace Botan {
21
22// The main reason CurveGFp has not been entirely removed already is
23// because a few (deprecated) public APIs of EC_Point rely on them,
24// thus until Botan4 we must continue to access the EC data via a type
25// named CurveGFp
26
27CurveGFp::CurveGFp(const EC_Group_Data* group) : m_group(group) {
28 BOTAN_ASSERT_NONNULL(m_group);
29}
30
31const EC_Group_Data& CurveGFp::group() const {
32 BOTAN_ASSERT_NONNULL(m_group);
33 return *m_group;
34}
35
36const BigInt& CurveGFp::get_a() const {
37 return this->group().a();
38}
39
40const BigInt& CurveGFp::get_b() const {
41 return this->group().b();
42}
43
44const BigInt& CurveGFp::get_p() const {
45 return this->group().p();
46}
47
48size_t CurveGFp::get_p_words() const {
49 return this->group().p_words();
50}
51
52namespace {
53
54void to_rep(const EC_Group_Data& group, BigInt& x, secure_vector<word>& ws) {
55 group.monty().mul_by(x, group.monty().R2(), ws);
56}
57
58void from_rep(const EC_Group_Data& group, BigInt& z, secure_vector<word>& ws) {
59 z = group.monty().redc(z, ws);
60}
61
62BigInt from_rep_to_tmp(const EC_Group_Data& group, const BigInt& x, secure_vector<word>& ws) {
63 return group.monty().redc(x, ws);
64}
65
66inline void fe_mul(const EC_Group_Data& group, BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) {
67 group.monty().mul(z, x, y, ws);
68}
69
70inline void fe_mul(
71 const EC_Group_Data& group, BigInt& z, const word x_w[], size_t x_size, const BigInt& y, secure_vector<word>& ws) {
72 group.monty().mul(z, y, std::span{x_w, x_size}, ws);
73}
74
75template <word M>
76inline void fe_smul(BigInt& z, const BigInt& p, secure_vector<word>& ws) {
77 static_assert(M == 2 || M == 3 || M == 4 || M == 8);
78 z.mod_mul(M, p, ws);
79}
80
81inline BigInt fe_mul(const EC_Group_Data& group, const BigInt& x, const BigInt& y, secure_vector<word>& ws) {
82 return group.monty().mul(x, y, ws);
83}
84
85void fe_sqr(const EC_Group_Data& group, BigInt& z, const BigInt& x, secure_vector<word>& ws) {
86 group.monty().sqr(z, x, ws);
87}
88
89void fe_sqr(const EC_Group_Data& group, BigInt& z, const word x_w[], size_t x_size, secure_vector<word>& ws) {
90 group.monty().sqr(z, std::span{x_w, x_size}, ws);
91}
92
93BigInt fe_sqr(const EC_Group_Data& group, const BigInt& x, secure_vector<word>& ws) {
94 return group.monty().sqr(x, ws);
95}
96
97BigInt invert_element(const EC_Group_Data& group, const BigInt& x, secure_vector<word>& ws) {
98 return group.monty().mul(inverse_mod_public_prime(x, group.p()), group.monty().R3(), ws);
99}
100
101size_t monty_ws_size(const EC_Group_Data& group) {
102 return 2 * group.p_words();
103}
104
105} // namespace
106
107EC_Point::EC_Point(const CurveGFp& curve) : m_curve(curve), m_x(0), m_y(curve.group().monty().R1()), m_z(0) {}
108
110 return EC_Point(m_curve);
111}
112
114 m_curve(curve), m_x(std::move(x)), m_y(std::move(y)), m_z(m_curve.group().monty().R1()) {
115 const auto& group = m_curve.group();
116
117 if(m_x < 0 || m_x >= group.p()) {
118 throw Invalid_Argument("Invalid EC_Point affine x");
119 }
120 if(m_y < 0 || m_y >= group.p()) {
121 throw Invalid_Argument("Invalid EC_Point affine y");
122 }
123
124 secure_vector<word> monty_ws(monty_ws_size(group));
125
126 to_rep(group, m_x, monty_ws);
127 to_rep(group, m_y, monty_ws);
128}
129
131 const auto& group = m_curve.group();
132 secure_vector<word> ws(monty_ws_size(group));
133 randomize_repr(rng, ws);
134}
135
137 if(!rng.is_seeded()) {
138 return;
139 }
140
141 const auto& group = m_curve.group();
142
143 const BigInt mask = BigInt::random_integer(rng, BigInt::from_s32(2), group.p());
144
145 /*
146 * No reason to convert this to Montgomery representation first,
147 * just pretend the random mask was chosen as Redc(mask) and the
148 * random mask we generated above is in the Montgomery
149 * representation.
150 */
151
152 const BigInt mask2 = fe_sqr(group, mask, ws);
153 const BigInt mask3 = fe_mul(group, mask2, mask, ws);
154
155 m_x = fe_mul(group, m_x, mask2, ws);
156 m_y = fe_mul(group, m_y, mask3, ws);
157 m_z = fe_mul(group, m_z, mask, ws);
158}
159
160namespace {
161
162inline void resize_ws(std::vector<BigInt>& ws_bn, size_t cap_size) {
163 BOTAN_ASSERT(ws_bn.size() >= EC_Point::WORKSPACE_SIZE, "Expected size for EC_Point workspace");
164
165 for(auto& ws : ws_bn) {
166 if(ws.size() < cap_size) {
167 ws.get_word_vector().resize(cap_size);
168 }
169 }
170}
171
172} // namespace
173
174void EC_Point::add_affine(const EC_Point& other, std::vector<BigInt>& workspace) {
175 BOTAN_ASSERT_NOMSG(m_curve == other.m_curve);
177
178 const size_t p_words = m_curve.get_p_words();
179 add_affine(other.m_x._data(),
180 std::min(p_words, other.m_x.size()),
181 other.m_y._data(),
182 std::min(p_words, other.m_y.size()),
183 workspace);
184}
185
187 const word x_words[], size_t x_size, const word y_words[], size_t y_size, std::vector<BigInt>& ws_bn) {
188 if((CT::all_zeros(x_words, x_size) & CT::all_zeros(y_words, y_size)).as_bool()) {
189 return;
190 }
191
192 const auto& group = m_curve.group();
193
194 if(is_zero()) {
195 m_x.set_words(x_words, x_size);
196 m_y.set_words(y_words, y_size);
197 m_z = group.monty().R1();
198 return;
199 }
200
201 resize_ws(ws_bn, monty_ws_size(group));
202
203 secure_vector<word>& ws = ws_bn[0].get_word_vector();
204 secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
205
206 BigInt& T0 = ws_bn[2];
207 BigInt& T1 = ws_bn[3];
208 BigInt& T2 = ws_bn[4];
209 BigInt& T3 = ws_bn[5];
210 BigInt& T4 = ws_bn[6];
211
212 /*
213 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
214 simplified with Z2 = 1
215 */
216
217 const BigInt& p = group.p();
218
219 fe_sqr(group, T3, m_z, ws); // z1^2
220 fe_mul(group, T4, x_words, x_size, T3, ws); // x2*z1^2
221
222 fe_mul(group, T2, m_z, T3, ws); // z1^3
223 fe_mul(group, T0, y_words, y_size, T2, ws); // y2*z1^3
224
225 T4.mod_sub(m_x, p, sub_ws); // x2*z1^2 - x1*z2^2
226
227 T0.mod_sub(m_y, p, sub_ws);
228
229 if(T4.is_zero()) {
230 if(T0.is_zero()) {
231 mult2(ws_bn);
232 return;
233 }
234
235 // setting to zero:
236 m_x.clear();
237 m_y = group.monty().R1();
238 m_z.clear();
239 return;
240 }
241
242 fe_sqr(group, T2, T4, ws);
243
244 fe_mul(group, T3, m_x, T2, ws);
245
246 fe_mul(group, T1, T2, T4, ws);
247
248 fe_sqr(group, m_x, T0, ws);
249 m_x.mod_sub(T1, p, sub_ws);
250
251 m_x.mod_sub(T3, p, sub_ws);
252 m_x.mod_sub(T3, p, sub_ws);
253
254 T3.mod_sub(m_x, p, sub_ws);
255
256 fe_mul(group, T2, T0, T3, ws);
257 fe_mul(group, T0, m_y, T1, ws);
258 T2.mod_sub(T0, p, sub_ws);
259 m_y.swap(T2);
260
261 fe_mul(group, T0, m_z, T4, ws);
262 m_z.swap(T0);
263}
264
265void EC_Point::add(const EC_Point& other, std::vector<BigInt>& workspace) {
266 BOTAN_ARG_CHECK(m_curve == other.m_curve, "cannot add points on different curves");
267
268 const size_t p_words = m_curve.get_p_words();
269
270 add(other.m_x._data(),
271 std::min(p_words, other.m_x.size()),
272 other.m_y._data(),
273 std::min(p_words, other.m_y.size()),
274 other.m_z._data(),
275 std::min(p_words, other.m_z.size()),
276 workspace);
277}
278
279void EC_Point::add(const word x_words[],
280 size_t x_size,
281 const word y_words[],
282 size_t y_size,
283 const word z_words[],
284 size_t z_size,
285 std::vector<BigInt>& ws_bn) {
286 if((CT::all_zeros(x_words, x_size) & CT::all_zeros(z_words, z_size)).as_bool()) {
287 return;
288 }
289
290 const auto& group = m_curve.group();
291
292 if(is_zero()) {
293 m_x.set_words(x_words, x_size);
294 m_y.set_words(y_words, y_size);
295 m_z.set_words(z_words, z_size);
296 return;
297 }
298
299 resize_ws(ws_bn, monty_ws_size(group));
300
301 secure_vector<word>& ws = ws_bn[0].get_word_vector();
302 secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
303
304 BigInt& T0 = ws_bn[2];
305 BigInt& T1 = ws_bn[3];
306 BigInt& T2 = ws_bn[4];
307 BigInt& T3 = ws_bn[5];
308 BigInt& T4 = ws_bn[6];
309 BigInt& T5 = ws_bn[7];
310
311 /*
312 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
313 */
314
315 const BigInt& p = group.p();
316
317 fe_sqr(group, T0, z_words, z_size, ws); // z2^2
318 fe_mul(group, T1, m_x, T0, ws); // x1*z2^2
319 fe_mul(group, T3, z_words, z_size, T0, ws); // z2^3
320 fe_mul(group, T2, m_y, T3, ws); // y1*z2^3
321
322 fe_sqr(group, T3, m_z, ws); // z1^2
323 fe_mul(group, T4, x_words, x_size, T3, ws); // x2*z1^2
324
325 fe_mul(group, T5, m_z, T3, ws); // z1^3
326 fe_mul(group, T0, y_words, y_size, T5, ws); // y2*z1^3
327
328 T4.mod_sub(T1, p, sub_ws); // x2*z1^2 - x1*z2^2
329
330 T0.mod_sub(T2, p, sub_ws);
331
332 if(T4.is_zero()) {
333 if(T0.is_zero()) {
334 mult2(ws_bn);
335 return;
336 }
337
338 // setting to zero:
339 m_x.clear();
340 m_y = group.monty().R1();
341 m_z.clear();
342 return;
343 }
344
345 fe_sqr(group, T5, T4, ws);
346
347 fe_mul(group, T3, T1, T5, ws);
348
349 fe_mul(group, T1, T5, T4, ws);
350
351 fe_sqr(group, m_x, T0, ws);
352 m_x.mod_sub(T1, p, sub_ws);
353 m_x.mod_sub(T3, p, sub_ws);
354 m_x.mod_sub(T3, p, sub_ws);
355
356 T3.mod_sub(m_x, p, sub_ws);
357
358 fe_mul(group, m_y, T0, T3, ws);
359 fe_mul(group, T3, T2, T1, ws);
360
361 m_y.mod_sub(T3, p, sub_ws);
362
363 fe_mul(group, T3, z_words, z_size, m_z, ws);
364 fe_mul(group, m_z, T3, T4, ws);
365}
366
367void EC_Point::mult2i(size_t iterations, std::vector<BigInt>& ws_bn) {
368 if(iterations == 0) {
369 return;
370 }
371
372 if(m_y.is_zero()) {
373 *this = EC_Point(m_curve); // setting myself to zero
374 return;
375 }
376
377 /*
378 TODO we can save 2 squarings per iteration by computing
379 a*Z^4 using values cached from previous iteration
380 */
381 for(size_t i = 0; i != iterations; ++i) {
382 mult2(ws_bn);
383 }
384}
385
386// *this *= 2
387void EC_Point::mult2(std::vector<BigInt>& ws_bn) {
388 if(is_zero()) {
389 return;
390 }
391
392 const auto& group = m_curve.group();
393
394 if(m_y.is_zero()) {
395 *this = EC_Point(m_curve); // setting myself to zero
396 return;
397 }
398
399 resize_ws(ws_bn, monty_ws_size(group));
400
401 secure_vector<word>& ws = ws_bn[0].get_word_vector();
402 secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
403
404 BigInt& T0 = ws_bn[2];
405 BigInt& T1 = ws_bn[3];
406 BigInt& T2 = ws_bn[4];
407 BigInt& T3 = ws_bn[5];
408 BigInt& T4 = ws_bn[6];
409
410 /*
411 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc
412 */
413 const BigInt& p = group.p();
414
415 fe_sqr(group, T0, m_y, ws);
416
417 fe_mul(group, T1, m_x, T0, ws);
418 fe_smul<4>(T1, p, sub_ws);
419
420 if(group.a_is_zero()) {
421 // if a == 0 then 3*x^2 + a*z^4 is just 3*x^2
422 fe_sqr(group, T4, m_x, ws); // x^2
423 fe_smul<3>(T4, p, sub_ws); // 3*x^2
424 } else if(group.a_is_minus_3()) {
425 /*
426 if a == -3 then
427 3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2)
428 */
429 fe_sqr(group, T3, m_z, ws); // z^2
430
431 // (x-z^2)
432 T2 = m_x;
433 T2.mod_sub(T3, p, sub_ws);
434
435 // (x+z^2)
436 T3.mod_add(m_x, p, sub_ws);
437
438 fe_mul(group, T4, T2, T3, ws); // (x-z^2)*(x+z^2)
439
440 fe_smul<3>(T4, p, sub_ws); // 3*(x-z^2)*(x+z^2)
441 } else {
442 fe_sqr(group, T3, m_z, ws); // z^2
443 fe_sqr(group, T4, T3, ws); // z^4
444 fe_mul(group, T3, group.monty_a(), T4, ws); // a*z^4
445
446 fe_sqr(group, T4, m_x, ws); // x^2
447 fe_smul<3>(T4, p, sub_ws);
448 T4.mod_add(T3, p, sub_ws); // 3*x^2 + a*z^4
449 }
450
451 fe_sqr(group, T2, T4, ws);
452 T2.mod_sub(T1, p, sub_ws);
453 T2.mod_sub(T1, p, sub_ws);
454
455 fe_sqr(group, T3, T0, ws);
456 fe_smul<8>(T3, p, sub_ws);
457
458 T1.mod_sub(T2, p, sub_ws);
459
460 fe_mul(group, T0, T4, T1, ws);
461 T0.mod_sub(T3, p, sub_ws);
462
463 m_x.swap(T2);
464
465 fe_mul(group, T2, m_y, m_z, ws);
466 fe_smul<2>(T2, p, sub_ws);
467
468 m_y.swap(T0);
469 m_z.swap(T2);
470}
471
472// arithmetic operators
474 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
475 add(rhs, ws);
476 return *this;
477}
478
480 EC_Point minus_rhs = EC_Point(rhs).negate();
481
482 if(is_zero()) {
483 *this = minus_rhs;
484 } else {
485 *this += minus_rhs;
486 }
487
488 return *this;
489}
490
492 *this = scalar * *this;
493 return *this;
494}
495
496EC_Point EC_Point::mul(const BigInt& scalar) const {
497 const size_t scalar_bits = scalar.bits();
498
499 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
500
501 EC_Point R[2] = {this->zero(), *this};
502
503 for(size_t i = scalar_bits; i > 0; i--) {
504 const size_t b = scalar.get_bit(i - 1) ? 1 : 0;
505 R[b ^ 1].add(R[b], ws);
506 R[b].mult2(ws);
507 }
508
509 if(scalar.is_negative()) {
510 R[0].negate();
511 }
512
514
515 return R[0];
516}
517
518//static
519void EC_Point::force_all_affine(std::span<EC_Point> points, secure_vector<word>& ws) {
520 if(points.size() <= 1) {
521 for(auto& point : points) {
522 point.force_affine();
523 }
524 return;
525 }
526
527 for(auto& point : points) {
528 if(point.is_zero()) {
529 throw Invalid_State("Cannot convert zero ECC point to affine");
530 }
531 }
532
533 /*
534 For >= 2 points use Montgomery's trick
535
536 See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography"
537 (Hankerson, Menezes, Vanstone)
538
539 TODO is it really necessary to save all k points in c?
540 */
541
542 const auto& group = points[0].m_curve.group();
543 const BigInt& rep_1 = group.monty().R1();
544
545 if(ws.size() < monty_ws_size(group)) {
546 ws.resize(monty_ws_size(group));
547 }
548
549 std::vector<BigInt> c(points.size());
550 c[0] = points[0].m_z;
551
552 for(size_t i = 1; i != points.size(); ++i) {
553 fe_mul(group, c[i], c[i - 1], points[i].m_z, ws);
554 }
555
556 BigInt s_inv = invert_element(group, c[c.size() - 1], ws);
557
558 BigInt z_inv;
559 BigInt z2_inv;
560 BigInt z3_inv;
561
562 for(size_t i = points.size() - 1; i != 0; i--) {
563 EC_Point& point = points[i];
564
565 fe_mul(group, z_inv, s_inv, c[i - 1], ws);
566
567 s_inv = fe_mul(group, s_inv, point.m_z, ws);
568
569 fe_sqr(group, z2_inv, z_inv, ws);
570 fe_mul(group, z3_inv, z2_inv, z_inv, ws);
571 point.m_x = fe_mul(group, point.m_x, z2_inv, ws);
572 point.m_y = fe_mul(group, point.m_y, z3_inv, ws);
573 point.m_z = rep_1;
574 }
575
576 fe_sqr(group, z2_inv, s_inv, ws);
577 fe_mul(group, z3_inv, z2_inv, s_inv, ws);
578 points[0].m_x = fe_mul(group, points[0].m_x, z2_inv, ws);
579 points[0].m_y = fe_mul(group, points[0].m_y, z3_inv, ws);
580 points[0].m_z = rep_1;
581}
582
584 if(is_zero()) {
585 throw Invalid_State("Cannot convert zero ECC point to affine");
586 }
587
589
590 const auto& group = m_curve.group();
591
592 const BigInt z_inv = invert_element(group, m_z, ws);
593 const BigInt z2_inv = fe_sqr(group, z_inv, ws);
594 const BigInt z3_inv = fe_mul(group, z_inv, z2_inv, ws);
595 m_x = fe_mul(group, m_x, z2_inv, ws);
596 m_y = fe_mul(group, m_y, z3_inv, ws);
597 m_z = group.monty().R1();
598}
599
601 const auto& group = m_curve.group();
602 return m_z == group.monty().R1();
603}
604
606 const auto& group = m_curve.group();
607 const size_t p_bytes = group.p_bytes();
608 secure_vector<uint8_t> b(p_bytes);
609 BigInt::encode_1363(b.data(), b.size(), this->get_affine_x());
610 return b;
611}
612
614 const auto& group = m_curve.group();
615 const size_t p_bytes = group.p_bytes();
616 secure_vector<uint8_t> b(p_bytes);
617 BigInt::encode_1363(b.data(), b.size(), this->get_affine_y());
618 return b;
619}
620
622 const auto& group = m_curve.group();
623 const size_t p_bytes = group.p_bytes();
624 secure_vector<uint8_t> b(2 * p_bytes);
625 BigInt::encode_1363(&b[0], p_bytes, this->get_affine_x()); // NOLINT(*container-data-pointer)
626 BigInt::encode_1363(&b[p_bytes], p_bytes, this->get_affine_y());
627 return b;
628}
629
631 if(is_zero()) {
632 throw Invalid_State("Cannot convert zero point to affine");
633 }
634
635 secure_vector<word> monty_ws;
636
637 const auto& group = m_curve.group();
638
639 if(is_affine()) {
640 return from_rep_to_tmp(group, m_x, monty_ws);
641 }
642
643 BigInt z2 = fe_sqr(group, m_z, monty_ws);
644 z2 = invert_element(group, z2, monty_ws);
645
646 BigInt r;
647 fe_mul(group, r, m_x, z2, monty_ws);
648 from_rep(group, r, monty_ws);
649 return r;
650}
651
653 if(is_zero()) {
654 throw Invalid_State("Cannot convert zero point to affine");
655 }
656
657 const auto& group = m_curve.group();
658 secure_vector<word> monty_ws;
659
660 if(is_affine()) {
661 return from_rep_to_tmp(group, m_y, monty_ws);
662 }
663
664 const BigInt z2 = fe_sqr(group, m_z, monty_ws);
665 const BigInt z3 = fe_mul(group, m_z, z2, monty_ws);
666 const BigInt z3_inv = invert_element(group, z3, monty_ws);
667
668 BigInt r;
669 fe_mul(group, r, m_y, z3_inv, monty_ws);
670 from_rep(group, r, monty_ws);
671 return r;
672}
673
675 /*
676 Is the point still on the curve?? (If everything is correct, the
677 point is always on its curve; then the function will return true.
678 If somehow the state is corrupted, which suggests a fault attack
679 (or internal computational error), then return false.
680 */
681 if(is_zero()) {
682 return true;
683 }
684
685 const auto& group = m_curve.group();
686 secure_vector<word> monty_ws;
687
688 const BigInt y2 = from_rep_to_tmp(group, fe_sqr(group, m_y, monty_ws), monty_ws);
689 const BigInt x3 = fe_mul(group, m_x, fe_sqr(group, m_x, monty_ws), monty_ws);
690 const BigInt ax = fe_mul(group, m_x, group.monty_a(), monty_ws);
691 const BigInt z2 = fe_sqr(group, m_z, monty_ws);
692
693 const BigInt& monty_b = group.monty_b();
694
695 // Is z equal to 1 (in Montgomery form)?
696 if(m_z == z2) {
697 if(y2 != from_rep_to_tmp(group, x3 + ax + monty_b, monty_ws)) {
698 return false;
699 }
700 }
701
702 const BigInt z3 = fe_mul(group, m_z, z2, monty_ws);
703 const BigInt ax_z4 = fe_mul(group, ax, fe_sqr(group, z2, monty_ws), monty_ws);
704 const BigInt b_z6 = fe_mul(group, monty_b, fe_sqr(group, z3, monty_ws), monty_ws);
705
706 if(y2 != from_rep_to_tmp(group, x3 + ax_z4 + b_z6, monty_ws)) {
707 return false;
708 }
709
710 return true;
711}
712
714 if(this->is_zero()) {
715 return false;
716 }
717
718 const auto& group = m_curve.group();
719
720 /*
721 * The trick used below doesn't work for curves with cofactors
722 */
723 if(group.has_cofactor()) {
724 return group.mod_order().reduce(this->get_affine_x()) == v;
725 }
726
727 /*
728 * Note we're working with the projective coordinate directly here!
729 * Nominally we're comparing v with the affine x coordinate.
730 *
731 * return group.mod_order(this->get_affine_x()) == v;
732 *
733 * However by instead projecting r to an identical z as the x
734 * coordinate, we can compare without having to perform an
735 * expensive inversion in the field.
736 *
737 * That is, given (x*z2) and r, instead of checking if
738 * (x*z2)*z2^-1 == r,
739 * we check if
740 * (x*z2) == (r*z2)
741 */
743 BigInt vr = v;
744 to_rep(group, vr, ws);
745 BigInt z2;
746 BigInt v_z2;
747 fe_sqr(group, z2, this->get_z(), ws);
748 fe_mul(group, v_z2, vr, z2, ws);
749
750 /*
751 * Since (typically) the group order is slightly less than the size
752 * of the field elements, its possible the signer had to reduce the
753 * r component. If they did not reduce r, then this value is correct.
754 *
755 * Due to the Hasse bound, this case occurs almost always; the
756 * probability that a reduction was actually required is
757 * approximately 1 in 2^(n/2) where n is the bit length of the curve.
758 */
759 if(this->get_x() == v_z2) {
760 return true;
761 }
762
763 if(group.order_is_less_than_p()) {
764 vr = v + group.order();
765 if(vr < group.p()) {
766 to_rep(group, vr, ws);
767 fe_mul(group, v_z2, vr, z2, ws);
768
769 if(this->get_x() == v_z2) {
770 return true;
771 }
772 }
773 }
774
775 // Reject:
776 return false;
777}
778
779// swaps the states of *this and other
780void EC_Point::swap(EC_Point& other) noexcept {
781 m_curve.swap(other.m_curve);
782 m_x.swap(other.m_x);
783 m_y.swap(other.m_y);
784 m_z.swap(other.m_z);
785}
786
787bool EC_Point::operator==(const EC_Point& other) const {
788 if(m_curve != other.m_curve) {
789 return false;
790 }
791
792 // If this is zero, only equal if other is also zero
793 if(is_zero()) {
794 return other.is_zero();
795 }
796
797 return (get_affine_x() == other.get_affine_x() && get_affine_y() == other.get_affine_y());
798}
799
800// encoding and decoding
801std::vector<uint8_t> EC_Point::encode(EC_Point_Format format) const {
802 if(is_zero()) {
803 return std::vector<uint8_t>(1); // single 0 byte
804 }
805
806 const size_t p_bytes = m_curve.group().p_bytes();
807
808 const BigInt x = get_affine_x();
809 const BigInt y = get_affine_y();
810
811 const size_t parts = (format == EC_Point_Format::Compressed) ? 1 : 2;
812
813 std::vector<uint8_t> result(1 + parts * p_bytes);
814 BufferStuffer stuffer(result);
815
816 if(format == EC_Point_Format::Uncompressed) {
817 stuffer.append(0x04);
818 x.serialize_to(stuffer.next(p_bytes));
819 y.serialize_to(stuffer.next(p_bytes));
820 } else if(format == EC_Point_Format::Compressed) {
821 stuffer.append(0x02 | static_cast<uint8_t>(y.get_bit(0)));
822 x.serialize_to(stuffer.next(p_bytes));
823 } else if(format == EC_Point_Format::Hybrid) {
824 stuffer.append(0x06 | static_cast<uint8_t>(y.get_bit(0)));
825 x.serialize_to(stuffer.next(p_bytes));
826 y.serialize_to(stuffer.next(p_bytes));
827 } else {
828 throw Invalid_Argument("EC2OSP illegal point encoding");
829 }
830
831 return result;
832}
833
834namespace {
835
836BigInt decompress_point(bool y_mod_2, const BigInt& x, const BigInt& p, const BigInt& a, const BigInt& b) {
837 const BigInt g = ((x * x + a) * x + b) % p;
838
839 BigInt z = sqrt_modulo_prime(g, p);
840
841 if(z < 0) {
842 throw Decoding_Error("Error during EC point decompression");
843 }
844
845 if(z.get_bit(0) != y_mod_2) {
846 z = p - z;
847 }
848
849 return z;
850}
851
852} // namespace
853
854EC_Point OS2ECP(std::span<const uint8_t> data, const CurveGFp& curve) {
855 return OS2ECP(data.data(), data.size(), curve);
856}
857
858EC_Point OS2ECP(const uint8_t data[], size_t data_len, const CurveGFp& curve) {
859 if(data_len == 1 && data[0] == 0) {
860 // SEC1 standard representation of the point at infinity
861 return EC_Point(curve);
862 }
863
864 const auto [g_x, g_y] = OS2ECP(data, data_len, curve.get_p(), curve.get_a(), curve.get_b());
865
866 EC_Point point(curve, g_x, g_y);
867
868 if(!point.on_the_curve()) {
869 throw Decoding_Error("OS2ECP: Decoded point was not on the curve");
870 }
871
872 return point;
873}
874
875std::pair<BigInt, BigInt> OS2ECP(const uint8_t pt[], size_t pt_len, const BigInt& p, const BigInt& a, const BigInt& b) {
876 if(pt_len <= 1) {
877 throw Decoding_Error("OS2ECP invalid point encoding");
878 }
879
880 const uint8_t pc = pt[0];
881 const size_t p_bytes = p.bytes();
882
883 BigInt x;
884 BigInt y;
885
886 if(pc == 2 || pc == 3) {
887 if(pt_len != 1 + p_bytes) {
888 throw Decoding_Error("OS2ECP invalid point encoding");
889 }
890 x = BigInt::decode(&pt[1], pt_len - 1);
891
892 const bool y_mod_2 = ((pc & 0x01) == 1);
893 y = decompress_point(y_mod_2, x, p, a, b);
894 } else if(pc == 4) {
895 if(pt_len != 1 + 2 * p_bytes) {
896 throw Decoding_Error("OS2ECP invalid point encoding");
897 }
898
899 x = BigInt::decode(&pt[1], p_bytes);
900 y = BigInt::decode(&pt[p_bytes + 1], p_bytes);
901 } else if(pc == 6 || pc == 7) {
902 if(pt_len != 1 + 2 * p_bytes) {
903 throw Decoding_Error("OS2ECP invalid point encoding");
904 }
905
906 x = BigInt::decode(&pt[1], p_bytes);
907 y = BigInt::decode(&pt[p_bytes + 1], p_bytes);
908
909 const bool y_mod_2 = ((pc & 0x01) == 1);
910
911 if(decompress_point(y_mod_2, x, p, a, b) != y) {
912 throw Decoding_Error("OS2ECP: Decoding error in hybrid format");
913 }
914 } else {
915 throw Decoding_Error("OS2ECP: Unknown format type " + std::to_string(static_cast<int>(pc)));
916 }
917
918 if(x >= p || y >= p) {
919 throw Decoding_Error("OS2ECP invalid point encoding");
920 }
921
922 return std::make_pair(x, y);
923}
924
925} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:114
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
void swap(BigInt &other) noexcept
Definition bigint.h:191
static BigInt decode(const uint8_t buf[], size_t length)
Definition bigint.h:857
static BigInt random_integer(RandomNumberGenerator &rng, const BigInt &min, const BigInt &max)
Definition big_rand.cpp:43
size_t size() const
Definition bigint.h:609
BigInt & mod_add(const BigInt &y, const BigInt &mod, secure_vector< word > &ws)
Definition big_ops2.cpp:45
void serialize_to(std::span< uint8_t > out) const
Definition bigint.cpp:399
BigInt & mod_sub(const BigInt &y, const BigInt &mod, secure_vector< word > &ws)
Definition big_ops2.cpp:94
size_t bits() const
Definition bigint.cpp:311
static secure_vector< uint8_t > encode_1363(const BigInt &n, size_t bytes)
Definition bigint.h:895
const word * _data() const
Definition bigint.h:936
static BigInt from_s32(int32_t n)
Definition bigint.cpp:41
bool is_zero() const
Definition bigint.h:457
bool is_negative() const
Definition bigint.h:559
size_t bytes() const
Definition bigint.cpp:298
bool get_bit(size_t n) const
Definition bigint.h:496
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:133
constexpr void append(std::span< const uint8_t > buffer)
Definition stl_util.h:168
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:141
CurveGFp(const CurveGFp &)=default
const BigInt & get_a() const
Definition ec_point.cpp:36
size_t get_p_words() const
Definition ec_point.cpp:48
const BigInt & get_p() const
Definition ec_point.cpp:44
const BigInt & get_b() const
Definition ec_point.cpp:40
size_t p_words() const
EC_Point & negate()
Definition ec_point.h:134
void swap(EC_Point &other) noexcept
Definition ec_point.cpp:780
void randomize_repr(RandomNumberGenerator &rng)
Definition ec_point.cpp:130
BigInt get_affine_x() const
Definition ec_point.cpp:630
secure_vector< uint8_t > x_bytes() const
Definition ec_point.cpp:605
secure_vector< uint8_t > xy_bytes() const
Definition ec_point.cpp:621
void add(const EC_Point &other, std::vector< BigInt > &workspace)
Definition ec_point.cpp:265
void add_affine(const EC_Point &other, std::vector< BigInt > &workspace)
Definition ec_point.cpp:174
void mult2(std::vector< BigInt > &workspace)
Definition ec_point.cpp:387
void mult2i(size_t i, std::vector< BigInt > &workspace)
Definition ec_point.cpp:367
EC_Point & operator-=(const EC_Point &rhs)
Definition ec_point.cpp:479
bool is_zero() const
Definition ec_point.h:164
const BigInt & get_z() const
Definition ec_point.h:253
bool on_the_curve() const
Definition ec_point.cpp:674
EC_Point & operator+=(const EC_Point &rhs)
Definition ec_point.cpp:473
EC_Point zero() const
Definition ec_point.cpp:109
bool is_affine() const
Definition ec_point.cpp:600
bool operator==(const EC_Point &other) const
Definition ec_point.cpp:787
EC_Point mul(const BigInt &scalar) const
Definition ec_point.cpp:496
void force_affine()
Definition ec_point.cpp:583
static void force_all_affine(std::span< EC_Point > points, secure_vector< word > &ws)
Definition ec_point.cpp:519
BigInt get_affine_y() const
Definition ec_point.cpp:652
EC_Point & operator*=(const BigInt &scalar)
Definition ec_point.cpp:491
secure_vector< uint8_t > y_bytes() const
Definition ec_point.cpp:613
const BigInt & get_x() const
Definition ec_point.h:239
EC_Point()=default
std::vector< uint8_t > encode(EC_Point_Format format) const
Definition ec_point.cpp:801
bool _is_x_eq_to_v_mod_order(const BigInt &v) const
Definition ec_point.cpp:713
virtual bool is_seeded() const =0
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
Definition ct_utils.h:813
BigInt inverse_mod_public_prime(const BigInt &x, const BigInt &p)
Definition mod_inv.cpp:291
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69
BigInt sqrt_modulo_prime(const BigInt &a, const BigInt &p)
Definition numthry.cpp:26
std::conditional_t< HasNative64BitRegisters, std::uint64_t, uint32_t > word
Definition types.h:119
EC_Point OS2ECP(std::span< const uint8_t > data, const CurveGFp &curve)
Definition ec_point.cpp:854