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