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