7#ifndef BOTAN_PCURVES_IMPL_H_
8#define BOTAN_PCURVES_IMPL_H_
11#include <botan/internal/ct_utils.h>
12#include <botan/internal/loadstor.h>
13#include <botan/internal/pcurves_util.h>
14#include <botan/internal/stl_util.h>
17#if defined(BOTAN_HAS_XMD)
18 #include <botan/internal/xmd.h>
25template <
typename Params>
26class MontgomeryRep
final {
28 using Self = MontgomeryRep<Params>;
30 static constexpr auto P = Params::P;
31 static constexpr size_t N = Params::N;
32 typedef typename Params::W W;
34 static_assert(N > 0 && (Params::P[0] & 1) == 1,
"Invalid Montgomery modulus");
38 static constexpr auto R1 = montygomery_r(P);
39 static constexpr auto R2 = mul_mod(R1, R1, P);
40 static constexpr auto R3 = mul_mod(R1, R2, P);
42 constexpr static std::array<W, N> one() {
return R1; }
44 constexpr static std::array<W, N> redc(
const std::array<W, 2 * N>& z) {
45 if constexpr(P_dash == 1) {
46 return monty_redc_pdash1(z, P);
48 return monty_redc(z, P, P_dash);
52 constexpr static std::array<W, N> to_rep(
const std::array<W, N>& x) {
53 std::array<W, 2 * N> z;
58 constexpr static std::array<W, N> wide_to_rep(
const std::array<W, 2 * N>& x) {
59 auto redc_x = Self::redc(x);
60 std::array<W, 2 * N> z;
65 constexpr static std::array<W, N> from_rep(
const std::array<W, N>& z) {
66 std::array<W, 2 * N> ze = {};
67 copy_mem(std::span{ze}.template first<N>(), z);
68 return Self::redc(ze);
72template <
typename Rep>
75 static constexpr auto P = Rep::P;
76 static constexpr size_t N = Rep::N;
77 typedef typename Rep::W W;
79 static constexpr auto P_MINUS_2 = p_minus<2>(P);
82 static constexpr size_t BITS = count_bits(P);
83 static constexpr size_t BYTES = (BITS + 7) / 8;
85 static constexpr auto P_MOD_4 = P[0] % 4;
87 using Self = IntMod<Rep>;
90 constexpr IntMod() : m_val({}) {}
92 IntMod(
const Self& other) =
default;
93 IntMod(Self&& other) =
default;
94 IntMod& operator=(
const Self& other) =
default;
95 IntMod& operator=(Self&& other) =
default;
97 static constexpr Self zero() {
return Self(std::array<W, N>{0}); }
99 static constexpr Self one() {
return Self(Rep::one()); }
101 static constexpr Self from_word(W x) {
102 std::array<W, 1> v{x};
103 return Self::from_words(v);
107 static constexpr Self from_words(std::array<W, L> w) {
108 if constexpr(L == N) {
109 return Self(Rep::to_rep(w));
111 static_assert(L < N);
112 std::array<W, N> ew = {};
113 copy_mem(std::span{ew}.template first<L>(), w);
114 return Self(Rep::to_rep(ew));
118 constexpr CT::Choice is_zero()
const {
return CT::all_zeros(m_val.data(), m_val.size()).as_choice(); }
120 constexpr CT::Choice is_nonzero()
const {
return !is_zero(); }
122 constexpr CT::Choice is_one()
const {
return (*
this == Self::one()); }
124 constexpr CT::Choice is_even()
const {
125 auto v = Rep::from_rep(m_val);
129 friend constexpr Self
operator+(
const Self& a,
const Self&
b) {
138 friend constexpr Self
operator-(
const Self& a,
const Self&
b) {
return a +
b.negate(); }
144 constexpr auto INV_2 = p_div_2_plus_1(Rep::P);
148 std::array<W, N> t = value();
159 std::array<W, N> t = value();
168 constexpr Self mul3()
const {
return mul2() + (*this); }
171 constexpr Self mul4()
const {
return mul2().mul2(); }
174 constexpr Self mul8()
const {
return mul2().mul2().mul2(); }
176 friend constexpr Self
operator*(
const Self& a,
const Self&
b) {
177 std::array<W, 2 * N> z;
179 return Self(Rep::redc(z));
182 constexpr Self&
operator*=(
const Self& other) {
183 std::array<W, 2 * N> z;
185 m_val = Rep::redc(z);
190 static constexpr void conditional_assign(Self& x, CT::Choice cond,
const Self& nx) {
193 for(
size_t i = 0; i != N; ++i) {
194 x.m_val[i] =
choose(mask, nx.m_val[i], x.m_val[i]);
199 static constexpr void conditional_assign(Self& x, Self& y, CT::Choice cond,
const Self& nx,
const Self& ny) {
202 for(
size_t i = 0; i != N; ++i) {
203 x.m_val[i] =
choose(mask, nx.m_val[i], x.m_val[i]);
204 y.m_val[i] =
choose(mask, ny.m_val[i], y.m_val[i]);
209 static constexpr void conditional_assign(
210 Self& x, Self& y, Self& z, CT::Choice cond,
const Self& nx,
const Self& ny,
const Self& nz) {
213 for(
size_t i = 0; i != N; ++i) {
214 x.m_val[i] =
choose(mask, nx.m_val[i], x.m_val[i]);
215 y.m_val[i] =
choose(mask, ny.m_val[i], y.m_val[i]);
216 z.m_val[i] =
choose(mask, nz.m_val[i], z.m_val[i]);
220 constexpr Self
square()
const {
221 std::array<W, 2 * N> z;
223 return Self(Rep::redc(z));
226 constexpr void square_n(
size_t n) {
227 std::array<W, 2 * N> z;
228 for(
size_t i = 0; i != n; ++i) {
230 m_val = Rep::redc(z);
235 constexpr Self negate()
const {
239 bigint_sub3(r.data(), P.data(), N, this->data(), N);
240 x_is_zero.if_set_zero_out(r.data(), N);
244 constexpr Self pow_vartime(
const std::array<W, N>& exp)
const {
245 constexpr size_t WindowBits = (Self::BITS <= 256) ? 4 : 5;
246 constexpr size_t WindowElements = (1 << WindowBits) - 1;
248 constexpr size_t Windows = (Self::BITS + WindowBits - 1) / WindowBits;
250 std::array<Self, WindowElements> tbl;
254 for(
size_t i = 1; i != WindowElements; ++i) {
256 tbl[i] = tbl[i / 2].square();
258 tbl[i] = tbl[i - 1] * tbl[0];
262 auto r = Self::one();
264 const size_t w0 = read_window_bits<WindowBits>(std::span{exp}, (Windows - 1) * WindowBits);
270 for(
size_t i = 1; i != Windows; ++i) {
271 r.square_n(WindowBits);
273 const size_t w = read_window_bits<WindowBits>(std::span{exp}, (Windows - i - 1) * WindowBits);
290 constexpr Self invert()
const {
return pow_vartime(Self::P_MINUS_2); }
295 constexpr std::pair<Self, CT::Choice> sqrt()
const {
296 if constexpr(Self::P_MOD_4 == 3) {
297 constexpr auto P_PLUS_1_OVER_4 = p_plus_1_over_4(P);
298 auto z = pow_vartime(P_PLUS_1_OVER_4);
299 const CT::Choice correct = (z.square() == *
this);
300 Self::conditional_assign(z, !correct, Self::zero());
313 constexpr auto C1_C2 = shanks_tonelli_c1c2(Self::P);
314 constexpr std::array<W, N> C3 = shanks_tonelli_c3(C1_C2.second);
315 constexpr std::array<W, N> P_MINUS_1_OVER_2 = p_minus_1_over_2(Self::P);
316 constexpr Self C4 = shanks_tonelli_c4<Self>(P_MINUS_1_OVER_2);
317 constexpr Self C5 = C4.pow_vartime(C1_C2.second);
319 const Self& x = (*this);
321 auto z = x.pow_vartime(C3);
328 for(
size_t i = C1_C2.first; i >= 2; i--) {
330 const auto e =
b.is_one();
331 Self::conditional_assign(z, !e, z * c);
333 Self::conditional_assign(t, !e, t * c);
337 const CT::Choice correct = (z.square() == *
this);
338 Self::conditional_assign(z, !correct, Self::zero());
343 constexpr CT::Choice
operator==(
const Self& other)
const {
344 return CT::is_equal(this->data(), other.data(), N).as_choice();
347 constexpr CT::Choice
operator!=(
const Self& other)
const {
351 constexpr std::array<W, Self::N> to_words()
const {
return Rep::from_rep(m_val); }
353 constexpr void serialize_to(std::span<uint8_t, Self::BYTES> bytes)
const {
354 auto v = Rep::from_rep(m_val);
355 std::reverse(v.begin(), v.end());
357 if constexpr(Self::BYTES == N * WordInfo<W>::bytes) {
361 const auto padded_bytes =
store_be(v);
362 constexpr size_t extra = N * WordInfo<W>::bytes - Self::BYTES;
363 copy_mem(bytes, std::span{padded_bytes}.template subspan<extra, Self::BYTES>());
368 std::array<W, L> stash_value()
const {
369 static_assert(L >= N);
370 std::array<W, L> stash = {};
371 for(
size_t i = 0; i != N; ++i) {
378 static Self from_stash(
const std::array<W, L>& stash) {
379 static_assert(L >= N);
380 std::array<W, N> val = {};
381 for(
size_t i = 0; i != N; ++i) {
388 static std::optional<Self> deserialize(std::span<const uint8_t> bytes) {
392 if(bytes.size() != Self::BYTES) {
396 const auto words = bytes_to_words<W, N, BYTES>(bytes.first<Self::BYTES>());
402 return Self::from_words(words);
407 static constexpr Self from_wide_bytes(std::span<const uint8_t, L> bytes) {
408 static_assert(8 * L <= 2 * Self::BITS);
409 std::array<uint8_t, 2 * BYTES> padded_bytes = {};
410 copy_mem(std::span{padded_bytes}.template last<L>(), bytes);
411 return Self(Rep::wide_to_rep(bytes_to_words<W, 2 * N, 2 * BYTES>(std::span{padded_bytes})));
415 static constexpr std::optional<Self> from_wide_bytes_varlen(std::span<const uint8_t> bytes) {
416 if(8 * bytes.size() > 2 * Self::BITS) {
419 std::array<uint8_t, 2 * BYTES> padded_bytes = {};
420 copy_mem(std::span{padded_bytes}.last(bytes.size()), bytes);
421 return Self(Rep::wide_to_rep(bytes_to_words<W, 2 * N, 2 * BYTES>(std::span{padded_bytes})));
424 static Self random(RandomNumberGenerator& rng) {
425 constexpr size_t MAX_ATTEMPTS = 1000;
427 std::array<uint8_t, Self::BYTES> buf;
429 for(
size_t i = 0; i != MAX_ATTEMPTS; ++i) {
434 if constexpr(Self::BITS % 8 != 0) {
435 constexpr uint8_t mask = 0xFF >> (8 - (Self::BITS % 8));
439 if(
auto s = Self::deserialize(buf)) {
440 if(s.value().is_nonzero().as_bool()) {
446 throw Internal_Error(
"Failed to generate random Scalar within bounded number of attempts");
449 static consteval Self constant(int8_t x) {
451 v[0] = (x >= 0) ? x : -x;
452 auto s = Self::from_words(v);
453 return (x >= 0) ? s : s.negate();
456 constexpr void _const_time_poison()
const {
CT::poison(m_val); }
458 constexpr void _const_time_unpoison()
const {
CT::unpoison(m_val); }
461 constexpr const std::array<W, N>& value()
const {
return m_val; }
463 constexpr const W* data()
const {
return m_val.data(); }
465 explicit constexpr IntMod(std::array<W, N> v) : m_val(v) {}
467 std::array<W, N> m_val;
470template <
typename FieldElement,
typename Params>
471class AffineCurvePoint {
476 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
477 static constexpr FieldElement B = FieldElement::from_words(Params::BW);
479 static constexpr size_t BYTES = 1 + 2 * FieldElement::BYTES;
480 static constexpr size_t COMPRESSED_BYTES = 1 + FieldElement::BYTES;
482 using Self = AffineCurvePoint<FieldElement, Params>;
484 constexpr AffineCurvePoint(
const FieldElement& x,
const FieldElement& y) : m_x(x), m_y(y) {}
486 constexpr AffineCurvePoint() : m_x(FieldElement::zero()), m_y(FieldElement::zero()) {}
488 static constexpr Self identity() {
return Self(FieldElement::zero(), FieldElement::zero()); }
490 constexpr CT::Choice is_identity()
const {
return x().is_zero() && y().is_zero(); }
492 AffineCurvePoint(
const Self& other) =
default;
493 AffineCurvePoint(Self&& other) =
default;
494 AffineCurvePoint& operator=(
const Self& other) =
default;
495 AffineCurvePoint& operator=(Self&& other) =
default;
497 constexpr Self negate()
const {
return Self(x(), y().negate()); }
499 constexpr void serialize_to(std::span<uint8_t, Self::BYTES> bytes)
const {
501 BufferStuffer
pack(bytes);
503 x().serialize_to(
pack.next<FieldElement::BYTES>());
504 y().serialize_to(
pack.next<FieldElement::BYTES>());
508 constexpr void serialize_compressed_to(std::span<uint8_t, Self::COMPRESSED_BYTES> bytes)
const {
512 BufferStuffer
pack(bytes);
514 x().serialize_to(
pack.next<FieldElement::BYTES>());
518 constexpr void serialize_x_to(std::span<uint8_t, FieldElement::BYTES> bytes)
const {
520 x().serialize_to(bytes);
528 static constexpr auto ct_select(std::span<const Self> pts,
size_t idx) {
529 auto result = Self::identity();
532 const size_t idx1 =
static_cast<size_t>(idx - 1);
533 for(
size_t i = 0; i != pts.size(); ++i) {
535 result.conditional_assign(found, pts[i]);
541 static constexpr FieldElement x3_ax_b(
const FieldElement& x) {
return (x.square() + Self::A) * x + Self::B; }
543 static std::optional<Self> deserialize(std::span<const uint8_t> bytes) {
544 if(bytes.size() == Self::BYTES) {
545 if(bytes[0] == 0x04) {
546 auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES));
547 auto y = FieldElement::deserialize(bytes.subspan(1 + FieldElement::BYTES, FieldElement::BYTES));
550 const auto lhs = (*y).square();
551 const auto rhs = Self::x3_ax_b(*x);
552 if((lhs == rhs).as_bool()) {
556 }
else if(bytes[0] == 0x06 || bytes[0] == 0x07) {
559 auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES));
560 auto y = FieldElement::deserialize(bytes.subspan(1 + FieldElement::BYTES, FieldElement::BYTES));
562 if(x && y && (y_is_even == y->is_even()).as_bool()) {
563 const auto lhs = (*y).square();
564 const auto rhs = Self::x3_ax_b(*x);
565 if((lhs == rhs).as_bool()) {
570 }
else if(bytes.size() == Self::COMPRESSED_BYTES) {
571 if(bytes[0] == 0x02 || bytes[0] == 0x03) {
574 if(
auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES))) {
575 auto [y, is_square] = x3_ax_b(*x).sqrt();
577 if(is_square.as_bool()) {
578 const auto flip_y = y_is_even != y.is_even();
579 FieldElement::conditional_assign(y, flip_y, y.negate());
584 }
else if(bytes.size() == 1 && bytes[0] == 0x00) {
586 return Self::identity();
592 constexpr const FieldElement& x()
const {
return m_x; }
594 constexpr const FieldElement& y()
const {
return m_y; }
596 constexpr void conditional_assign(CT::Choice cond,
const Self& pt) {
597 FieldElement::conditional_assign(m_x, m_y, cond, pt.x(), pt.y());
600 constexpr void _const_time_poison()
const {
CT::poison_all(m_x, m_y); }
609template <
typename FieldElement,
typename Params>
610class ProjectiveCurvePoint {
615 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
617 static constexpr bool A_is_zero = A.is_zero().as_bool();
618 static constexpr bool A_is_minus_3 = (A == FieldElement::constant(-3)).as_bool();
620 using Self = ProjectiveCurvePoint<FieldElement, Params>;
621 using AffinePoint = AffineCurvePoint<FieldElement, Params>;
623 static constexpr Self from_affine(
const AffinePoint& pt) {
624 if(pt.is_identity().as_bool()) {
625 return Self::identity();
627 return ProjectiveCurvePoint(pt.x(), pt.y());
631 static constexpr Self identity() {
return Self(FieldElement::zero(), FieldElement::one(), FieldElement::zero()); }
633 constexpr ProjectiveCurvePoint() :
634 m_x(FieldElement::zero()), m_y(FieldElement::one()), m_z(FieldElement::zero()) {}
636 constexpr ProjectiveCurvePoint(
const FieldElement& x,
const FieldElement& y) :
637 m_x(x), m_y(y), m_z(FieldElement::one()) {}
639 constexpr ProjectiveCurvePoint(
const FieldElement& x,
const FieldElement& y,
const FieldElement& z) :
640 m_x(x), m_y(y), m_z(z) {}
642 ProjectiveCurvePoint(
const Self& other) =
default;
643 ProjectiveCurvePoint(Self&& other) =
default;
644 ProjectiveCurvePoint& operator=(
const Self& other) =
default;
645 ProjectiveCurvePoint& operator=(Self&& other) =
default;
647 friend constexpr Self
operator+(
const Self& a,
const Self&
b) {
return Self::add(a,
b); }
649 friend constexpr Self
operator+(
const Self& a,
const AffinePoint&
b) {
return Self::add_mixed(a,
b); }
651 friend constexpr Self
operator+(
const AffinePoint& a,
const Self&
b) {
return Self::add_mixed(
b, a); }
653 constexpr Self&
operator+=(
const Self& other) {
654 (*this) = (*this) + other;
658 constexpr Self&
operator+=(
const AffinePoint& other) {
659 (*this) = (*this) + other;
663 friend constexpr Self
operator-(
const Self& a,
const Self&
b) {
return a +
b.negate(); }
665 constexpr CT::Choice is_identity()
const {
return z().is_zero(); }
667 constexpr void conditional_assign(CT::Choice cond,
const Self& pt) {
668 FieldElement::conditional_assign(m_x, m_y, m_z, cond, pt.x(), pt.y(), pt.z());
671 constexpr static Self add_mixed(
const Self& a,
const AffinePoint&
b) {
672 const auto a_is_identity = a.is_identity();
673 const auto b_is_identity =
b.is_identity();
674 if((a_is_identity && b_is_identity).as_bool()) {
675 return Self::identity();
684 const auto Z1Z1 = a.z().square();
685 const auto U2 =
b.x() * Z1Z1;
686 const auto S2 =
b.y() * a.z() * Z1Z1;
687 const auto H = U2 - a.x();
688 const auto r = S2 - a.y();
693 if((r.is_zero() && H.is_zero()).as_bool()) {
697 const auto HH = H.square();
698 const auto HHH = H * HH;
699 const auto V = a.x() * HH;
700 const auto t2 = r.square();
701 const auto t3 = V + V;
702 const auto t4 = t2 - HHH;
704 const auto t5 = V - X3;
705 const auto t6 = a.y() * HHH;
706 const auto t7 = r * t5;
711 FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity,
b.x(),
b.y(), FieldElement::one());
714 FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z());
716 return Self(X3, Y3, Z3);
719 constexpr static Self add(
const Self& a,
const Self&
b) {
720 const auto a_is_identity = a.is_identity();
721 const auto b_is_identity =
b.is_identity();
723 if((a_is_identity && b_is_identity).as_bool()) {
724 return Self::identity();
733 const auto Z1Z1 = a.z().square();
734 const auto Z2Z2 =
b.z().square();
735 const auto U1 = a.x() * Z2Z2;
736 const auto U2 =
b.x() * Z1Z1;
737 const auto S1 = a.y() *
b.z() * Z2Z2;
738 const auto S2 =
b.y() * a.z() * Z1Z1;
739 const auto H = U2 - U1;
740 const auto r = S2 - S1;
745 if((r.is_zero() && H.is_zero()).as_bool()) {
749 const auto HH = H.square();
750 const auto HHH = H * HH;
751 const auto V = U1 * HH;
752 const auto t2 = r.square();
753 const auto t3 = V + V;
754 const auto t4 = t2 - HHH;
756 const auto t5 = V - X3;
757 const auto t6 = S1 * HHH;
758 const auto t7 = r * t5;
760 const auto t8 =
b.z() * H;
761 auto Z3 = a.z() * t8;
764 FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity,
b.x(),
b.y(),
b.z());
767 FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z());
769 return Self(X3, Y3, Z3);
772 constexpr Self dbl_n(
size_t n)
const {
791 if constexpr(Self::A_is_zero) {
793 auto ny = y().mul2();
797 const auto ny2 = ny.square();
798 const auto ny4 = ny2.square();
799 const auto t1 = nx.square().mul3();
800 const auto t2 = nx * ny2;
801 nx = t1.square() - t2.mul2();
803 ny = t1 * (t2 - nx).mul2() - ny4;
806 return Self(nx, ny.div2(), nz);
809 auto ny = y().mul2();
811 auto w = nz.square().square();
813 if constexpr(!Self::A_is_minus_3) {
818 const auto ny2 = ny.square();
819 const auto ny4 = ny2.square();
821 if constexpr(Self::A_is_minus_3) {
822 t1 = (nx.square() - w).mul3();
824 t1 = nx.square().mul3() + w;
826 const auto t2 = nx * ny2;
827 nx = t1.square() - t2.mul2();
829 ny = t1 * (t2 - nx).mul2() - ny4;
835 return Self(nx, ny.div2(), nz);
839 constexpr Self dbl()
const {
848 FieldElement m = FieldElement::zero();
850 if constexpr(Self::A_is_minus_3) {
857 const auto z2 = z().square();
858 m = (x() - z2).mul3() * (x() + z2);
859 }
else if constexpr(Self::A_is_zero) {
862 m = x().square().mul3();
865 const auto z2 = z().square();
866 m = x().square().mul3() + A * z2.square();
870 const auto y2 = y().square();
871 const auto s = x().mul4() * y2;
872 const auto nx = m.square() - s.mul2();
873 const auto ny = m * (s - nx) - y2.square().mul8();
874 const auto nz = y().mul2() * z();
876 return Self(nx, ny, nz);
879 constexpr Self negate()
const {
return Self(x(), y().negate(), z()); }
881 void randomize_rep(RandomNumberGenerator& rng) {
882 if(!rng.is_seeded()) {
886 auto r = FieldElement::random(rng);
888 auto r2 = r.square();
896 constexpr const FieldElement& x()
const {
return m_x; }
898 constexpr const FieldElement& y()
const {
return m_y; }
900 constexpr const FieldElement& z()
const {
return m_z; }
902 constexpr void _const_time_poison()
const {
CT::poison_all(m_x, m_y, m_z); }
904 constexpr void _const_time_unpoison()
const {
CT::unpoison_all(m_x, m_y, m_z); }
912template <StringLiteral PS,
919class EllipticCurveParameters {
930 static constexpr int8_t Z = ZI;
933template <WordType WI,
size_t NI, std::array<WI, NI> PI>
937 static constexpr size_t N = NI;
938 static constexpr auto P = PI;
941template <
typename Params,
template <
typename FieldParamsT>
typename FieldRep = MontgomeryRep>
944 typedef typename Params::W W;
946 static constexpr auto PW = Params::PW;
947 static constexpr auto NW = Params::NW;
948 static constexpr auto AW = Params::AW;
951 static_assert(PW.size() == NW.size());
953 class ScalarParams
final :
public IntParams<W, NW.size(), NW> {};
955 using Scalar = IntMod<MontgomeryRep<ScalarParams>>;
957 class FieldParams
final :
public IntParams<W, PW.size(), PW> {};
959 using FieldElement = IntMod<FieldRep<FieldParams>>;
961 using AffinePoint = AffineCurvePoint<FieldElement, Params>;
962 using ProjectivePoint = ProjectiveCurvePoint<FieldElement, Params>;
964 static constexpr size_t OrderBits = Scalar::BITS;
965 static constexpr size_t PrimeFieldBits = FieldElement::BITS;
967 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
968 static constexpr FieldElement B = FieldElement::from_words(Params::BW);
970 static constexpr AffinePoint G =
971 AffinePoint(FieldElement::from_words(Params::GXW), FieldElement::from_words(Params::GYW));
973 static constexpr FieldElement SSWU_Z = FieldElement::constant(Params::Z);
975 static constexpr bool ValidForSswuHash =
976 (Params::Z != 0 && A.is_nonzero().as_bool() && B.is_nonzero().as_bool() && FieldElement::P_MOD_4 == 3);
978 static constexpr bool OrderIsLessThanField =
bigint_cmp(NW.data(), NW.size(), PW.data(), PW.size()) == -1;
983 { C::fe_invert2(
fe) } -> std::same_as<typename C::FieldElement>;
992template <
typename C,
size_t WindowBits>
993class BlindedScalarBits
final {
995 typedef typename C::W W;
997 static constexpr bool BlindingEnabled =
true;
1000 static constexpr size_t BlindingBits =
1004 static_assert(BlindingBits < C::Scalar::BITS);
1007 static constexpr size_t Bits = C::Scalar::BITS + (BlindingEnabled ? BlindingBits : 0);
1008 static constexpr size_t Bytes = (Bits + 7) / 8;
1011 if constexpr(BlindingEnabled) {
1015 constexpr size_t n_words = C::NW.size();
1017 uint8_t maskb[mask_bytes] = {0};
1028 std::array<uint8_t, C::Scalar::BYTES> sbytes;
1029 scalar.serialize_to(sbytes);
1030 for(
size_t i = 0; i != sbytes.size(); ++i) {
1031 maskb[i % mask_bytes] ^= sbytes[i];
1035 W mask[n_words] = {0};
1036 load_le(mask, maskb, mask_words);
1037 mask[mask_words - 1] |= WordInfo<W>::top_bit;
1040 W mask_n[2 * n_words] = {0};
1042 const auto sw = scalar.to_words();
1048 std::reverse(mask_n, mask_n + 2 * n_words);
1051 static_assert(Bytes == C::Scalar::BYTES);
1052 m_bytes.resize(Bytes);
1053 scalar.serialize_to(std::span{m_bytes}.template first<Bytes>());
1059 size_t get_window(
size_t offset)
const {
1061 return read_window_bits<WindowBits>(std::span{m_bytes}, offset);
1064 ~BlindedScalarBits() {
1071 std::vector<uint8_t> m_bytes;
1074template <
typename C,
size_t WindowBits>
1075class UnblindedScalarBits
final {
1077 static constexpr size_t Bits = C::Scalar::BITS;
1079 UnblindedScalarBits(
const typename C::Scalar& scalar) { scalar.serialize_to(std::span{m_bytes}); }
1081 size_t get_window(
size_t offset)
const {
1083 return read_window_bits<WindowBits>(std::span{m_bytes}, offset);
1087 std::array<uint8_t, C::Scalar::BYTES> m_bytes;
1090template <
typename C>
1091inline auto invert_field_element(
const typename C::FieldElement&
fe) {
1092 if constexpr(curve_supports_fe_invert2<C>) {
1093 return C::fe_invert2(
fe) *
fe;
1100template <
typename C>
1101auto to_affine(
const typename C::ProjectivePoint& pt) {
1104 if(pt.is_identity().as_bool()) {
1105 return C::AffinePoint::identity();
1108 if constexpr(curve_supports_fe_invert2<C>) {
1109 const auto z2_inv = C::fe_invert2(pt.z());
1110 const auto z3_inv = z2_inv.square() * pt.z();
1111 return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv);
1113 const auto z_inv = invert_field_element<C>(pt.z());
1114 const auto z2_inv = z_inv.square();
1115 const auto z3_inv = z_inv * z2_inv;
1116 return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv);
1121template <
typename C>
1122auto to_affine_x(
const typename C::ProjectivePoint& pt) {
1123 if constexpr(curve_supports_fe_invert2<C>) {
1124 return pt.x() * C::fe_invert2(pt.z());
1126 return to_affine<C>(pt).x();
1133template <
typename C>
1134auto to_affine_batch(std::span<const typename C::ProjectivePoint> projective) {
1135 typedef typename C::AffinePoint AffinePoint;
1136 typedef typename C::FieldElement FieldElement;
1138 const size_t N = projective.size();
1139 std::vector<AffinePoint> affine(N, AffinePoint::identity());
1141 bool any_identity =
false;
1142 for(
size_t i = 0; i != N; ++i) {
1143 if(projective[i].is_identity().as_bool()) {
1144 any_identity =
true;
1151 if(N <= 2 || any_identity) {
1155 for(
size_t i = 0; i != N; ++i) {
1156 affine[i] = to_affine<C>(projective[i]);
1159 std::vector<FieldElement> c(N);
1168 c[0] = projective[0].z();
1169 for(
size_t i = 1; i != N; ++i) {
1170 c[i] = c[i - 1] * projective[i].z();
1173 auto s_inv = invert_field_element<C>(c[N - 1]);
1175 for(
size_t i = N - 1; i > 0; --i) {
1176 const auto& p = projective[i];
1178 const auto z_inv = s_inv * c[i - 1];
1179 const auto z2_inv = z_inv.square();
1180 const auto z3_inv = z_inv * z2_inv;
1182 s_inv = s_inv * p.z();
1184 affine[i] = AffinePoint(p.x() * z2_inv, p.y() * z3_inv);
1187 const auto z2_inv = s_inv.square();
1188 const auto z3_inv = s_inv * z2_inv;
1189 affine[0] = AffinePoint(projective[0].x() * z2_inv, projective[0].y() * z3_inv);
1232template <
typename C,
size_t W>
1233class PrecomputedBaseMulTable
final {
1235 typedef typename C::Scalar Scalar;
1236 typedef typename C::AffinePoint AffinePoint;
1237 typedef typename C::ProjectivePoint ProjectivePoint;
1239 static constexpr size_t WindowBits = W;
1240 static_assert(WindowBits >= 1 && WindowBits <= 8);
1242 using BlindedScalar = BlindedScalarBits<C, WindowBits>;
1244 static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits;
1246 static_assert(Windows > 1);
1249 static constexpr size_t WindowElements = (1 << WindowBits) - 1;
1251 static constexpr size_t TableSize = Windows * WindowElements;
1253 PrecomputedBaseMulTable(
const AffinePoint& p) : m_table{} {
1254 std::vector<ProjectivePoint> table;
1255 table.reserve(TableSize);
1257 auto accum = ProjectivePoint::from_affine(p);
1259 for(
size_t i = 0; i != TableSize; i += WindowElements) {
1260 table.push_back(accum);
1262 for(
size_t j = 1; j != WindowElements; ++j) {
1264 table.emplace_back(table[i + j / 2].dbl());
1266 table.emplace_back(table[i + j - 1] + table[i]);
1270 accum = table[i + (WindowElements / 2)].dbl();
1273 m_table = to_affine_batch<C>(table);
1276 ProjectivePoint mul(
const Scalar& s, RandomNumberGenerator& rng)
const {
1277 const BlindedScalar bits(s, rng);
1280 auto table = std::span{m_table};
1282 auto accum = [&]() {
1283 const size_t w_0 = bits.get_window(0);
1284 const auto tbl_0 = table.first(WindowElements);
1285 auto pt = ProjectivePoint::from_affine(AffinePoint::ct_select(tbl_0, w_0));
1287 pt.randomize_rep(rng);
1291 for(
size_t i = 1; i != Windows; ++i) {
1292 const size_t w_i = bits.get_window(WindowBits * i);
1293 const auto tbl_i = table.subspan(WindowElements * i, WindowElements);
1300 accum += AffinePoint::ct_select(tbl_i, w_i);
1303 accum.randomize_rep(rng);
1312 std::vector<AffinePoint> m_table;
1320template <
typename C,
size_t W>
1321class WindowedMulTable
final {
1323 typedef typename C::Scalar Scalar;
1324 typedef typename C::AffinePoint AffinePoint;
1325 typedef typename C::ProjectivePoint ProjectivePoint;
1327 static constexpr size_t WindowBits = W;
1328 static_assert(WindowBits >= 1 && WindowBits <= 8);
1330 using BlindedScalar = BlindedScalarBits<C, WindowBits>;
1332 static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits;
1334 static_assert(Windows > 1);
1337 static constexpr size_t TableSize = (1 << WindowBits) - 1;
1339 WindowedMulTable(
const AffinePoint& p) : m_table{} {
1340 std::vector<ProjectivePoint> table;
1341 table.reserve(TableSize);
1343 table.push_back(ProjectivePoint::from_affine(p));
1344 for(
size_t i = 1; i != TableSize; ++i) {
1346 table.push_back(table[i / 2].dbl());
1348 table.push_back(table[i - 1] + table[0]);
1352 m_table = to_affine_batch<C>(table);
1355 ProjectivePoint mul(
const Scalar& s, RandomNumberGenerator& rng)
const {
1356 const BlindedScalar bits(s, rng);
1358 auto accum = [&]() {
1359 const size_t w_0 = bits.get_window((Windows - 1) * WindowBits);
1362 auto pt = ProjectivePoint::from_affine(AffinePoint::ct_select(m_table, w_0));
1364 pt.randomize_rep(rng);
1368 for(
size_t i = 1; i != Windows; ++i) {
1369 accum = accum.dbl_n(WindowBits);
1370 const size_t w_i = bits.get_window((Windows - i - 1) * WindowBits);
1397 accum += AffinePoint::ct_select(m_table, w_i);
1400 accum.randomize_rep(rng);
1409 std::vector<AffinePoint> m_table;
1432template <
typename C,
size_t W>
1433class WindowedMul2Table
final {
1436 static_assert(W >= 1 && W <= 4);
1438 typedef typename C::Scalar Scalar;
1439 typedef typename C::AffinePoint AffinePoint;
1440 typedef typename C::ProjectivePoint ProjectivePoint;
1442 static constexpr size_t WindowBits = W;
1444 static constexpr size_t Windows = (Scalar::BITS + WindowBits - 1) / WindowBits;
1446 static constexpr size_t WindowSize = (1 << WindowBits);
1449 static constexpr size_t TableSize = (1 << (2 * WindowBits)) - 1;
1451 WindowedMul2Table(
const AffinePoint& x,
const AffinePoint& y) {
1452 std::vector<ProjectivePoint> table;
1453 table.reserve(TableSize);
1455 for(
size_t i = 0; i != TableSize; ++i) {
1456 const size_t t_i = (i + 1);
1457 const size_t x_i = t_i % WindowSize;
1458 const size_t y_i = (t_i >> WindowBits) % WindowSize;
1461 auto next_tbl_e = [&]() {
1462 if(x_i % 2 == 0 && y_i % 2 == 0) {
1465 return table[(t_i / 2) - 1].dbl();
1466 }
else if(x_i > 0 && y_i > 0) {
1468 return table[x_i - 1] + table[(y_i << WindowBits) - 1];
1469 }
else if(x_i > 0 && y_i == 0) {
1473 return ProjectivePoint::from_affine(x);
1476 return x + table[x_i - 1 - 1];
1478 }
else if(x_i == 0 && y_i > 0) {
1481 return ProjectivePoint::from_affine(y);
1484 return y + table[((y_i - 1) << WindowBits) - 1];
1491 table.emplace_back(next_tbl_e());
1494 m_table = to_affine_batch<C>(table);
1511 ProjectivePoint mul2_vartime(
const Scalar& s1,
const Scalar& s2)
const {
1512 const UnblindedScalarBits<C, W> bits1(s1);
1513 const UnblindedScalarBits<C, W> bits2(s2);
1515 auto accum = ProjectivePoint::identity();
1517 for(
size_t i = 0; i != Windows; ++i) {
1519 accum = accum.dbl_n(WindowBits);
1522 const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits);
1523 const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits);
1525 const size_t window = w_1 + (w_2 << WindowBits);
1528 accum += m_table[window - 1];
1536 std::vector<AffinePoint> m_table;
1540template <
typename C>
1541const auto& SSWU_C2()
1542 requires C::ValidForSswuHash
1545 static const typename C::FieldElement C2 = C::B * invert_field_element<C>(C::SSWU_Z * C::A);
1550template <
typename C>
1551const auto& SSWU_C1()
1552 requires C::ValidForSswuHash
1555 static const typename C::FieldElement C1 = (SSWU_C2<C>() * C::SSWU_Z).negate();
1559template <
typename C>
1560inline auto map_to_curve_sswu(
const typename C::FieldElement& u) ->
typename C::AffinePoint {
1562 const auto z_u2 = C::SSWU_Z * u.square();
1563 const auto z2_u4 = z_u2.square();
1564 const auto tv1 = invert_field_element<C>(z2_u4 + z_u2);
1565 auto x1 = SSWU_C1<C>() * (C::FieldElement::one() + tv1);
1566 C::FieldElement::conditional_assign(x1, tv1.is_zero(), SSWU_C2<C>());
1567 const auto gx1 = C::AffinePoint::x3_ax_b(x1);
1569 const auto x2 = z_u2 * x1;
1570 const auto gx2 = C::AffinePoint::x3_ax_b(x2);
1573 const auto [gx1_sqrt, gx1_is_square] = gx1.sqrt();
1577 auto y = gx2.sqrt().first;
1579 C::FieldElement::conditional_assign(x, y, gx1_is_square, x1, gx1_sqrt);
1581 const auto flip_y = y.is_even() != u.is_even();
1582 C::FieldElement::conditional_assign(y, flip_y, y.negate());
1584 auto pt =
typename C::AffinePoint(x, y);
1590template <
typename C,
bool RO>
1591inline auto hash_to_curve_sswu(std::string_view hash, std::span<const uint8_t> pw, std::span<const uint8_t> dst) {
1592 static_assert(C::ValidForSswuHash);
1593#if defined(BOTAN_HAS_XMD)
1595 constexpr size_t SecurityLevel = (C::OrderBits + 1) / 2;
1596 constexpr size_t L = (C::PrimeFieldBits + SecurityLevel + 7) / 8;
1597 constexpr size_t Cnt = RO ? 2 : 1;
1599 std::array<uint8_t, L * Cnt> xmd;
1603 const auto u0 = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(xmd.data(), L));
1604 const auto u1 = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(xmd.data() + L, L));
1606 auto accum = C::ProjectivePoint::from_affine(map_to_curve_sswu<C>(u0));
1607 accum += map_to_curve_sswu<C>(u1);
1610 const auto u = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(xmd.data(), L));
1611 return map_to_curve_sswu<C>(u);
1614 throw Not_Implemented(
"Hash to curve not available due to missing XMD");
#define BOTAN_DEBUG_ASSERT(expr)
#define BOTAN_STATE_CHECK(expr)
#define BOTAN_ASSERT_UNREACHABLE()
static constexpr Choice from_int(T v)
static constexpr Mask< T > from_choice(Choice c)
static constexpr Mask< T > is_equal(T x, T y)
static FE_25519 invert(const FE_25519 &a)
void randomize(std::span< uint8_t > output)
virtual bool is_seeded() const =0
int(* final)(unsigned char *, CTX *)
constexpr void pack(const Polynomial< PolyTrait, D > &p, BufferStuffer &stuffer, MapFnT map)
constexpr void unpoison_all(Ts &&... ts)
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
constexpr void poison_all(Ts &&... ts)
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
constexpr void unpoison(const T *p, size_t n)
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
constexpr void poison(const T *p, size_t n)
constexpr auto bigint_add(std::span< W, N > z, std::span< const W, N > x, std::span< const W, N > y) -> W
constexpr W shift_left(std::array< W, N > &x)
constexpr void comba_sqr(W z[2 *N], const W x[N])
BigInt operator*(const BigInt &x, const BigInt &y)
constexpr void comba_mul(W z[2 *N], const W x[N], const W y[N])
BigInt square(const BigInt &x)
OctetString operator+(const OctetString &k1, const OctetString &k2)
constexpr T choose(T mask, T a, T b)
constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
constexpr auto monty_inverse(W a) -> W
BigInt operator-(const BigInt &x, const BigInt &y)
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
void secure_scrub_memory(void *ptr, size_t n)
constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size)
void expand_message_xmd(std::string_view hash_fn, std::span< uint8_t > output, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep)
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
constexpr W bigint_cnd_add(W cnd, W x[], size_t x_size, const W y[], size_t y_size)
void carry(int64_t &h0, int64_t &h1)
std::vector< T, Alloc > & operator+=(std::vector< T, Alloc > &out, const std::vector< T, Alloc2 > &in)
constexpr auto load_le(ParamTs &&... params)
constexpr auto bigint_ct_is_lt(const W x[], size_t x_size, const W y[], size_t y_size, bool lt_or_equal=false) -> CT::Mask< W >
constexpr auto hex_to_words(const char(&s)[N])
constexpr auto bigint_add2_nc(W x[], size_t x_size, const W y[], size_t y_size) -> W
constexpr void copy_mem(T *out, const T *in, size_t n)
constexpr auto store_be(ParamTs &&... params)
constexpr W shift_right(std::array< W, N > &x)
constexpr auto operator*=(Strong< T1, Tags... > &a, T2 b)