Botan 3.9.0
Crypto and TLS for C&
pcurves_impl.h
Go to the documentation of this file.
1/*
2* (C) 2024,2025 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#ifndef BOTAN_PCURVES_IMPL_H_
8#define BOTAN_PCURVES_IMPL_H_
9
10#include <botan/rng.h>
11#include <botan/internal/ct_utils.h>
12#include <botan/internal/loadstor.h>
13#include <botan/internal/pcurves_algos.h>
14#include <botan/internal/pcurves_mul.h>
15#include <botan/internal/pcurves_util.h>
16#include <botan/internal/stl_util.h>
17#include <concepts>
18#include <vector>
19
20namespace Botan {
21
22/*
23This file implements a system for compile-time instantiation of elliptic curve arithmetic.
24
25All computations including point multiplication are implemented to be constant time,
26with the exception of any function which includes "vartime" or equivalent in its
27name. Randomization techniques (scalar blinding, point rerandomization) are also
28used, largely to guard against situations where a compiler inserts a conditional jump
29where not expected.
30
31A specific elliptic curve is created by creating a set of EllipticCurveParameters,
32which are templatized over the relevant constants (p, a, b, etc) and then
33passing that set of parameters to an EllipticCurve template.
34
35For a simple example of how these are used see pcurves_brainpool256r1.cpp
36
37The system also includes various hooks which allow for specialized representations of
38field elements (for curves where a modular reduction technique faster than Montgomery
39is available) and to provide pre-computed addition chains for field and scalar
40inversions. See pcurves_secp256r1.cpp or pcurves_secp256k1.cpp for examples with all
41the bells and whistles.
42*/
43
44/**
45* Montomgomery Representation of Integers
46*
47* Integers modulo a prime (IntMod, see below) use some representation that
48* allows for fast arithmetic.
49*
50* The default representation used is Montgomery arithmetic. Curves with
51* specialized fields (eg Mersenne primes, Solinas primes, or Crandall primes)
52* provide a different type as the FieldRep parameter to the EllipticCurve
53* template.
54*
55* Since the curve parameters are public and known at compile time, we can
56* similarly compute the Montgomery parameters at compile time.
57*/
58template <typename Params>
59class MontgomeryRep final {
60 public:
62
63 static constexpr auto P = Params::P;
64 static constexpr size_t N = Params::N;
65 typedef typename Params::W W;
66
67 static_assert(N > 0 && (Params::P[0] & 1) == 1, "Invalid Montgomery modulus");
68
69 static constexpr auto P_dash = monty_inverse(P[0]);
70
71 static constexpr auto R1 = montygomery_r(P);
72 static constexpr auto R2 = mul_mod(R1, R1, P);
73 static constexpr auto R3 = mul_mod(R1, R2, P);
74
75 /**
76 * Return the constant one, pre-converted into Montgomery form
77 */
78 constexpr static std::array<W, N> one() { return R1; }
79
80 /**
81 * Modular reduction
82 */
83 constexpr static std::array<W, N> redc(const std::array<W, 2 * N>& z) {
84 if constexpr(P_dash == 1) {
85 return monty_redc_pdash1(z, P);
86 } else {
87 return monty_redc(z, P, P_dash);
88 }
89 }
90
91 /**
92 * Convert an integer into Montgomery representation
93 */
94 constexpr static std::array<W, N> to_rep(const std::array<W, N>& x) {
95 std::array<W, 2 * N> z; // NOLINT(*-member-init)
96 comba_mul<N>(z.data(), x.data(), R2.data());
97 return Self::redc(z);
98 }
99
100 /**
101 * Wide reduction modulo the prime
102 *
103 * Modular reduces an input of up to twice the length of the modulus, and
104 * converts it into Montgomery form.
105 */
106 constexpr static std::array<W, N> wide_to_rep(const std::array<W, 2 * N>& x) {
107 auto redc_x = Self::redc(x);
108 std::array<W, 2 * N> z; // NOLINT(*-member-init)
109 comba_mul<N>(z.data(), redc_x.data(), R3.data());
110 return Self::redc(z);
111 }
112
113 /**
114 * Convert an integer out of Montgomery representation
115 */
116 constexpr static std::array<W, N> from_rep(const std::array<W, N>& z) {
117 std::array<W, 2 * N> ze = {};
118 copy_mem(std::span{ze}.template first<N>(), z);
119 return Self::redc(ze);
120 }
121};
122
123/**
124* Integers Modulo (a Prime)
125*
126* This is used to store and manipulate integers modulo the field (for the affine
127* x/y or Jacobian x/y/z coordinates) and group order (for scalar arithmetic).
128*
129* This class is parameterized by Rep which handles the modular reduction step,
130* as well (if required) any conversions into or out of the inner
131* representation. This is primarily for Montgomery arithmetic; specialized
132* reduction methods instead keep the integer in the "standard" form.
133*
134* _Most_ of the code in this class does work for arbitrary moduli. However
135* at least div2 and invert make assumptions that the modulus is prime.
136*
137* Any function that does not contain "vartime" or equivalent in the name is
138* written such that it does not leak information about its arguments via control
139* flow or memory access patterns.
140*/
141template <typename Rep>
142class IntMod final {
143 private:
144 static constexpr auto P = Rep::P;
145 static constexpr size_t N = Rep::N;
146 typedef typename Rep::W W;
147
148 static constexpr auto P_MINUS_2 = p_minus<2>(P);
149
150 public:
151 static constexpr size_t BITS = count_bits(P);
152 static constexpr size_t BYTES = (BITS + 7) / 8;
153
154 static constexpr auto P_MOD_4 = P[0] % 4;
155
157
158 // Default value is zero
159 constexpr IntMod() : m_val({}) {}
160
161 IntMod(const Self& other) = default;
162 IntMod(Self&& other) = default;
163 IntMod& operator=(const Self& other) = default;
164 IntMod& operator=(Self&& other) = default;
165 ~IntMod() = default;
166
167 /**
168 * Return integer zero
169 *
170 * Note this assumes that the representation of zero is an all zero
171 * sequence of words. This is true for both Montgomery and standard
172 * representations.
173 */
174 static constexpr Self zero() { return Self(std::array<W, N>{0}); }
175
176 /**
177 * Return integer one
178 */
179 static constexpr Self one() { return Self(Rep::one()); }
180
181 /**
182 * Consume an array of words and convert it to an IntMod
183 *
184 * This handles the Montgomery conversion, if required.
185 *
186 * Note that this function assumes that `w` represents an integer that is
187 * less than the modulus.
188 */
189 template <size_t L>
190 static constexpr Self from_words(std::array<W, L> w) {
191 if constexpr(L == N) {
192 return Self(Rep::to_rep(w));
193 } else {
194 static_assert(L < N);
195 std::array<W, N> ew = {};
196 copy_mem(std::span{ew}.template first<L>(), w);
197 return Self(Rep::to_rep(ew));
198 }
199 }
200
201 /**
202 * Check in constant time if this is equal to zero
203 */
204 constexpr CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_val.size()).as_choice(); }
205
206 /**
207 * Check in constant time if this not equal to zero
208 */
209 constexpr CT::Choice is_nonzero() const { return !is_zero(); }
210
211 /**
212 * Check in constant time if this equal to one
213 */
214 constexpr CT::Choice is_one() const { return (*this == Self::one()); }
215
216 /**
217 * Check in constant time if this is an even integer
218 */
219 constexpr CT::Choice is_even() const {
220 auto v = Rep::from_rep(m_val);
221 return !CT::Choice::from_int(v[0] & 0x01);
222 }
223
224 /**
225 * Return either this or -this depending on which is even
226 */
227 constexpr Self correct_sign(CT::Choice even) const {
228 const auto flip = (even != this->is_even());
229 return Self::choose(flip, this->negate(), *this);
230 }
231
232 /**
233 * Return x or y depending on if choice is set or not
234 */
235 static constexpr Self choose(CT::Choice choice, const Self& x, const Self& y) {
236 auto r = y;
237 r.conditional_assign(choice, x);
238 return r;
239 }
240
241 /**
242 * Modular addition; return c = a + b
243 */
244 friend constexpr Self operator+(const Self& a, const Self& b) {
245 std::array<W, N> t; // NOLINT(*-member-init)
246
247 W carry = 0;
248 for(size_t i = 0; i != N; ++i) {
249 t[i] = word_add(a.m_val[i], b.m_val[i], &carry);
250 }
251
252 std::array<W, N> r; // NOLINT(*-member-init)
253 bigint_monty_maybe_sub<N>(r.data(), carry, t.data(), P.data());
254 return Self(r);
255 }
256
257 /**
258 * Modular subtraction; return c = a - b
259 */
260 friend constexpr Self operator-(const Self& a, const Self& b) {
261 std::array<W, N> r; // NOLINT(*-member-init)
262 W carry = 0;
263 for(size_t i = 0; i != N; ++i) {
264 r[i] = word_sub(a.m_val[i], b.m_val[i], &carry);
265 }
266
267 bigint_cnd_add(carry, r.data(), P.data(), N);
268 return Self(r);
269 }
270
271 /**
272 * Return the value of this divided by 2
273 */
274 Self div2() const {
275 // The inverse of 2 modulo P is (P/2)+1; this avoids a constexpr time
276 // general inversion, which some compilers can't handle
277 constexpr auto INV_2 = p_div_2_plus_1(Rep::P);
278
279 // We could multiply by INV_2 but there is a better way ...
280
281 std::array<W, N> t = value();
282 W borrow = shift_right<1>(t);
283
284 // If value was odd, add (P/2)+1
285 bigint_cnd_add(borrow, t.data(), INV_2.data(), N);
286
287 return Self(t);
288 }
289
290 /// Return (*this) multiplied by 2
291 constexpr Self mul2() const {
292 std::array<W, N> t = value();
293 W carry = shift_left<1>(t);
294
295 std::array<W, N> r; // NOLINT(*-member-init)
296 bigint_monty_maybe_sub<N>(r.data(), carry, t.data(), P.data());
297 return Self(r);
298 }
299
300 /// Return (*this) multiplied by 3
301 constexpr Self mul3() const { return mul2() + (*this); }
302
303 /// Return (*this) multiplied by 4
304 constexpr Self mul4() const { return mul2().mul2(); }
305
306 /// Return (*this) multiplied by 8
307 constexpr Self mul8() const { return mul2().mul2().mul2(); }
308
309 /**
310 * Modular multiplication; return c = a * b
311 */
312 friend constexpr Self operator*(const Self& a, const Self& b) {
313 std::array<W, 2 * N> z; // NOLINT(*-member-init)
314 comba_mul<N>(z.data(), a.data(), b.data());
315 return Self(Rep::redc(z));
316 }
317
318 /**
319 * Modular multiplication; set this to this * other
320 */
321 constexpr Self& operator*=(const Self& other) {
322 std::array<W, 2 * N> z; // NOLINT(*-member-init)
323 comba_mul<N>(z.data(), data(), other.data());
324 m_val = Rep::redc(z);
325 return (*this);
326 }
327
328 /**
329 * Conditional assignment
330 *
331 * If `cond` is true, sets *this to `nx`
332 */
333 constexpr void conditional_assign(CT::Choice cond, const Self& nx) {
334 const W mask = CT::Mask<W>::from_choice(cond).value();
335
336 for(size_t i = 0; i != N; ++i) {
337 m_val[i] = Botan::choose(mask, nx.m_val[i], m_val[i]);
338 }
339 }
340
341 /**
342 * Conditional assignment
343 *
344 * If `cond` is true, sets `x` to `nx` and `y` to `ny`
345 */
346 static constexpr void conditional_assign(Self& x, Self& y, CT::Choice cond, const Self& nx, const Self& ny) {
347 const W mask = CT::Mask<W>::from_choice(cond).value();
348
349 for(size_t i = 0; i != N; ++i) {
350 x.m_val[i] = Botan::choose(mask, nx.m_val[i], x.m_val[i]);
351 y.m_val[i] = Botan::choose(mask, ny.m_val[i], y.m_val[i]);
352 }
353 }
354
355 /**
356 * Conditional assignment
357 *
358 * If `cond` is true, sets `x` to `nx`, `y` to `ny`, and `z` to `nz`
359 */
360 static constexpr void conditional_assign(
361 Self& x, Self& y, Self& z, CT::Choice cond, const Self& nx, const Self& ny, const Self& nz) {
362 const W mask = CT::Mask<W>::from_choice(cond).value();
363
364 for(size_t i = 0; i != N; ++i) {
365 x.m_val[i] = Botan::choose(mask, nx.m_val[i], x.m_val[i]);
366 y.m_val[i] = Botan::choose(mask, ny.m_val[i], y.m_val[i]);
367 z.m_val[i] = Botan::choose(mask, nz.m_val[i], z.m_val[i]);
368 }
369 }
370
371 /**
372 * Conditional swap
373 *
374 * If `cond` is true, swaps the values of `x` and `y`
375 */
376 static constexpr void conditional_swap(CT::Choice cond, Self& x, Self& y) {
377 const W mask = CT::Mask<W>::from_choice(cond).value();
378
379 for(size_t i = 0; i != N; ++i) {
380 auto nx = Botan::choose(mask, y.m_val[i], x.m_val[i]);
381 auto ny = Botan::choose(mask, x.m_val[i], y.m_val[i]);
382 x.m_val[i] = nx;
383 y.m_val[i] = ny;
384 }
385 }
386
387 /**
388 * Modular squaring
389 *
390 * Returns the square of this after modular reduction
391 */
392 constexpr Self square() const {
393 std::array<W, 2 * N> z; // NOLINT(*-member-init)
394 comba_sqr<N>(z.data(), this->data());
395 return Self(Rep::redc(z));
396 }
397
398 /**
399 * Repeated modular squaring
400 *
401 * Returns the nth square of this
402 *
403 * (Alternate view, returns this raised to the 2^nth power)
404 */
405 constexpr void square_n(size_t n) {
406 std::array<W, 2 * N> z; // NOLINT(*-member-init)
407 for(size_t i = 0; i != n; ++i) {
408 comba_sqr<N>(z.data(), this->data());
409 m_val = Rep::redc(z);
410 }
411 }
412
413 /**
414 * Modular negation
415 *
416 * Returns the additive inverse of (*this)
417 */
418 constexpr Self negate() const {
419 const W x_is_zero = ~CT::all_zeros(this->data(), N).value();
420
421 std::array<W, N> r; // NOLINT(*-member-init)
422 W carry = 0;
423 for(size_t i = 0; i != N; ++i) {
424 r[i] = word_sub(P[i] & x_is_zero, m_val[i], &carry);
425 }
426
427 return Self(r);
428 }
429
430 /**
431 * Modular Exponentiation (Variable Time)
432 *
433 * This function is variable time with respect to the exponent. It should
434 * only be used when exp is not secret. In the current code, `exp` is
435 * always a compile-time constant.
436 *
437 * This function should not leak any information about this, since the
438 * value being operated on may be a secret.
439 *
440 * TODO: this interface should be changed so that the exponent is always a
441 * compile-time constant; this should allow some interesting optimizations.
442 */
443 constexpr Self pow_vartime(const std::array<W, N>& exp) const {
444 constexpr size_t WindowBits = (Self::BITS <= 256) ? 4 : 5;
445 constexpr size_t WindowElements = (1 << WindowBits) - 1;
446
447 constexpr size_t Windows = (Self::BITS + WindowBits - 1) / WindowBits;
448
449 /*
450 A simple fixed width window modular multiplication.
451
452 TODO: investigate using sliding window here
453 */
454
455 std::array<Self, WindowElements> tbl;
456
457 tbl[0] = (*this);
458
459 for(size_t i = 1; i != WindowElements; ++i) {
460 // Conditional ok: table indexes are public here
461 if(i % 2 == 1) {
462 tbl[i] = tbl[i / 2].square();
463 } else {
464 tbl[i] = tbl[i - 1] * tbl[0];
465 }
466 }
467
468 auto r = Self::one();
469
470 const size_t w0 = read_window_bits<WindowBits>(std::span{exp}, (Windows - 1) * WindowBits);
471
472 // Conditional ok: this function is variable time
473 if(w0 > 0) {
474 r = tbl[w0 - 1];
475 }
476
477 for(size_t i = 1; i != Windows; ++i) {
478 r.square_n(WindowBits);
479
480 const size_t w = read_window_bits<WindowBits>(std::span{exp}, (Windows - i - 1) * WindowBits);
481
482 // Conditional ok: this function is variable time
483 if(w > 0) {
484 r *= tbl[w - 1];
485 }
486 }
487
488 return r;
489 }
490
491 /**
492 * Returns the modular inverse, or 0 if no modular inverse exists.
493 *
494 * If the modulus is prime the only value that has no modular inverse is 0.
495 *
496 * This uses Fermat's little theorem, and so assumes that p is prime
497 *
498 * Since P is public, P-2 is as well, thus using a variable time modular
499 * exponentiation routine is safe.
500 *
501 * This function is only used if the curve does not provide an addition
502 * chain for specific inversions (see for example pcurves_secp256r1.cpp)
503 */
504 constexpr Self invert() const { return pow_vartime(Self::P_MINUS_2); }
505
506 /**
507 * Helper for variable time BEEA
508 *
509 * Note this function assumes that its arguments are in the standard
510 * domain, not the Montgomery domain. invert_vartime converts its argument
511 * out of Montgomery, and then back to Montgomery when returning the result.
512 */
513 static constexpr void _invert_vartime_div2_helper(Self& a, Self& x) {
514 constexpr auto INV_2 = p_div_2_plus_1(Rep::P);
515
516 // Conditional ok: this function is variable time
517 while((a.m_val[0] & 1) != 1) {
518 shift_right<1>(a.m_val);
519
520 W borrow = shift_right<1>(x.m_val);
521
522 // Conditional ok: this function is variable time
523 if(borrow) {
524 bigint_add2(x.m_val.data(), N, INV_2.data(), N);
525 }
526 }
527 }
528
529 /**
530 * Returns the modular inverse, or 0 if no modular inverse exists.
531 *
532 * This function assumes that the modulus is prime
533 *
534 * This function does something a bit nasty and converts from the normal
535 * representation (for scalars, Montgomery) into the "standard"
536 * representation. This relies on the fact that we aren't doing any
537 * multiplications within this function, just additions, subtractions,
538 * division by 2, and comparisons.
539 *
540 * The reason is there is no good way to compare integers in the Montgomery
541 * domain; we could convert out for each comparison but this is slower than
542 * just doing a constant-time inversion.
543 *
544 * This is loosely based on the algorithm BoringSSL uses in
545 * BN_mod_inverse_odd, which is a variant of the Binary Extended Euclidean
546 * algorithm. It is optimized somewhat by taking advantage of a couple of
547 * observations.
548 *
549 * In the first two iterations, the control flow is known because `a` is
550 * less than the modulus and not zero, and we know that the modulus is
551 * odd. So we peel out those iterations. This also avoids having to
552 * initialize `a` with the modulus, because we instead set it directly to
553 * what the first loop iteration would have updated it to. This ensures
554 * that all values are always less than or equal to the modulus.
555 *
556 * Then we take advantage of the fact that in each iteration of the loop,
557 * at the end we update either b/x or a/y, but never both. In the next
558 * iteration of the loop, we attempt to modify b/x or a/y depending on the
559 * low zero bits of b or a. But if a or b were not updated in the previous
560 * iteration than they will still be odd, and nothing will happen. Instead
561 * update just the pair we need to update, right after writing to b/x or
562 * a/y resp.
563 */
564 constexpr Self invert_vartime() const {
565 // Conditional ok: this function is variable time
566 if(this->is_zero().as_bool()) {
567 return Self::zero();
568 }
569
570 auto x = Self(std::array<W, N>{1}); // 1 in standard domain
571 auto b = Self(this->to_words()); // *this in standard domain
572
573 // First loop iteration
575
576 auto a = b.negate();
577 // y += x but y is zero at the outset
578 auto y = x;
579
580 // First half of second loop iteration
582
583 for(;;) {
584 // Conditional ok: this function is variable time
585 if(a.m_val == b.m_val) {
586 // At this point it should be that a == b == 1
587 auto r = y.negate();
588
589 // Convert back to Montgomery if required
590 r.m_val = Rep::to_rep(r.m_val);
591 return r;
592 }
593
594 auto nx = x + y;
595
596 /*
597 * Otherwise either b > a or a > b
598 *
599 * If b > a we want to set b to b - a
600 * Otherwise we want to set a to a - b
601 *
602 * Compute r = b - a and check if it underflowed
603 * If it did not then we are in the b > a path
604 */
605 std::array<W, N> r; // NOLINT(*-member-init)
606 word carry = bigint_sub3(r.data(), b.data(), N, a.data(), N);
607
608 // Conditional ok: this function is variable time
609 if(carry == 0) {
610 // b > a
611 b.m_val = r;
612 x = nx;
614 } else {
615 // We know this can't underflow because a > b
616 bigint_sub3(r.data(), a.data(), N, b.data(), N);
617 a.m_val = r;
618 y = nx;
620 }
621 }
622 }
623
624 /**
625 * Return the modular square root if it exists
626 *
627 * The CT::Option will be unset if the square root does not exist
628 */
629 constexpr CT::Option<Self> sqrt() const {
630 if constexpr(Self::P_MOD_4 == 3) {
631 // The easy case for square root is when p == 3 (mod 4)
632
633 constexpr auto P_PLUS_1_OVER_4 = p_plus_1_over_4(P);
634 auto z = pow_vartime(P_PLUS_1_OVER_4);
635
636 // Zero out the return value if it would otherwise be incorrect
637 const CT::Choice correct = (z.square() == *this);
638 z.conditional_assign(!correct, Self::zero());
639 return CT::Option<Self>(z, correct);
640 } else {
641 // Shanks-Tonelli, following I.4 in RFC 9380
642
643 /*
644 Constants:
645 1. c1, the largest integer such that 2^c1 divides q - 1.
646 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
647 3. c3 = (c2 - 1) / 2 # Integer arithmetic
648 4. c4, a non-square value in F
649 5. c5 = c4^c2 in F
650 */
651 constexpr auto C1_C2 = shanks_tonelli_c1c2(Self::P);
652 constexpr std::array<W, N> C3 = shanks_tonelli_c3(C1_C2.second);
653 constexpr std::array<W, N> P_MINUS_1_OVER_2 = p_minus_1_over_2(Self::P);
654 constexpr Self C4 = shanks_tonelli_c4<Self>(P_MINUS_1_OVER_2);
655 constexpr Self C5 = C4.pow_vartime(C1_C2.second);
656
657 const Self& x = (*this);
658
659 auto z = x.pow_vartime(C3);
660 auto t = z.square();
661 t *= x;
662 z *= x;
663 auto b = t;
664 auto c = C5;
665
666 for(size_t i = C1_C2.first; i >= 2; i--) {
667 b.square_n(i - 2);
668 const CT::Choice e = b.is_one();
669 z.conditional_assign(!e, z * c);
670 c.square_n(1);
671 t.conditional_assign(!e, t * c);
672 b = t;
673 }
674
675 // Zero out the return value if it would otherwise be incorrect
676 const CT::Choice correct = (z.square() == *this);
677 z.conditional_assign(!correct, Self::zero());
678 return CT::Option<Self>(z, correct);
679 }
680 }
681
682 /**
683 * Constant time integer equality test
684 *
685 * Since both this and other are in Montgomery representation (if applicable),
686 * we can always compare the words directly, without having to convert out.
687 */
688 constexpr CT::Choice operator==(const Self& other) const {
689 return CT::is_equal(this->data(), other.data(), N).as_choice();
690 }
691
692 /**
693 * Constant time integer inequality test
694 */
695 constexpr CT::Choice operator!=(const Self& other) const { return !(*this == other); }
696
697 /**
698 * Convert the integer to standard representation and return the sequence of words
699 */
700 constexpr std::array<W, Self::N> to_words() const { return Rep::from_rep(m_val); }
701
702 /**
703 * Serialize the integer to a bytestring
704 */
705 constexpr void serialize_to(std::span<uint8_t, Self::BYTES> bytes) const {
706 auto v = Rep::from_rep(m_val);
707 std::reverse(v.begin(), v.end());
708
709 if constexpr(Self::BYTES == N * WordInfo<W>::bytes) {
710 store_be(bytes, v);
711 } else {
712 // Remove leading zero bytes
713 const auto padded_bytes = store_be(v);
714 constexpr size_t extra = N * WordInfo<W>::bytes - Self::BYTES;
715 copy_mem(bytes, std::span{padded_bytes}.template subspan<extra, Self::BYTES>());
716 }
717 }
718
719 /**
720 * Store the raw words to an array
721 *
722 * See pcurves_wrap.h for why/where this is used
723 */
724 template <size_t L>
725 std::array<W, L> stash_value() const {
726 static_assert(L >= N);
727 std::array<W, L> stash = {};
728 for(size_t i = 0; i != N; ++i) {
729 stash[i] = m_val[i];
730 }
731 return stash;
732 }
733
734 /**
735 * Restore the value previously stashed
736 *
737 * See pcurves_wrap.h for why/where this is used
738 */
739 template <size_t L>
740 static Self from_stash(const std::array<W, L>& stash) {
741 static_assert(L >= N);
742 std::array<W, N> val = {};
743 for(size_t i = 0; i != N; ++i) {
744 val[i] = stash[i];
745 }
746 return Self(val);
747 }
748
749 /**
750 * Deserialize an integer from a bytestring
751 *
752 * Returns nullopt if the input is an encoding greater than or equal P
753 *
754 * This function also requires that the bytestring be exactly of the expected
755 * length; short bytestrings, or a long bytestring with leading zero bytes, are
756 * also rejected.
757 */
758 static std::optional<Self> deserialize(std::span<const uint8_t> bytes) {
759 // Conditional ok: input length is public
760 if(bytes.size() != Self::BYTES) {
761 return {};
762 }
763
764 const auto words = bytes_to_words<W, N, BYTES>(bytes.first<Self::BYTES>());
765
766 // Conditional acceptable: std::optional is implicitly not constant time
767 if(!bigint_ct_is_lt(words.data(), N, P.data(), N).as_bool()) {
768 return {};
769 }
770
771 // Safe because we checked above that words is an integer < P
772 return Self::from_words(words);
773 }
774
775 /**
776 * Modular reduce a larger input
777 *
778 * This takes a bytestring that is at most twice the length of the modulus, and
779 * modular reduces it.
780 */
781 template <size_t L>
782 static constexpr Self from_wide_bytes(std::span<const uint8_t, L> bytes) {
783 static_assert(8 * L <= 2 * Self::BITS);
784 std::array<uint8_t, 2 * BYTES> padded_bytes = {};
785 copy_mem(std::span{padded_bytes}.template last<L>(), bytes);
786 return Self(Rep::wide_to_rep(bytes_to_words<W, 2 * N, 2 * BYTES>(std::span{padded_bytes})));
787 }
788
789 /**
790 * Modular reduce a larger input
791 *
792 * This takes a bytestring that is at most twice the length of the modulus, and
793 * modular reduces it.
794 */
795 static constexpr std::optional<Self> from_wide_bytes_varlen(std::span<const uint8_t> bytes) {
796 // Conditional ok: input length is public
797 if(bytes.size() > 2 * Self::BYTES) {
798 return {};
799 }
800
801 std::array<uint8_t, 2 * Self::BYTES> padded_bytes = {};
802 copy_mem(std::span{padded_bytes}.last(bytes.size()), bytes);
803 return Self(Rep::wide_to_rep(bytes_to_words<W, 2 * N, 2 * BYTES>(std::span{padded_bytes})));
804 }
805
806 /**
807 * Return a random integer value in [1,p)
808 *
809 * This uses rejection sampling. This could have alternatively been implemented
810 * by oversampling the random number generator and then performing a wide
811 * reduction. The main reason that approach is avoided here is because it makes
812 * testing ECDSA-style known answer tests more difficult.
813 *
814 * This function avoids returning zero since in almost all contexts where a
815 * random integer is desired we want a random integer in Z_p*
816 */
818 constexpr size_t MAX_ATTEMPTS = 1000;
819
820 std::array<uint8_t, Self::BYTES> buf{};
821
822 for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
823 rng.randomize(buf);
824
825 // Zero off high bits that if set would certainly cause us
826 // to be out of range
827 if constexpr(Self::BITS % 8 != 0) {
828 constexpr uint8_t mask = 0xFF >> (8 - (Self::BITS % 8));
829 buf[0] &= mask;
830 }
831
832 // Conditionals ok: rejection sampling reveals only values we didn't use
833 if(auto s = Self::deserialize(buf)) {
834 if(s.value().is_nonzero().as_bool()) {
835 return s.value();
836 }
837 }
838 }
839
840 throw Internal_Error("Failed to generate random Scalar within bounded number of attempts");
841 }
842
843 /**
844 * Create a small compile time constant
845 *
846 * Notice this function is consteval, and so can only be called at compile time
847 */
848 static consteval Self constant(int8_t x) {
849 std::array<W, 1> v{};
850 v[0] = (x >= 0) ? x : -x;
851 auto s = Self::from_words(v);
852 return (x >= 0) ? s : s.negate();
853 }
854
855 constexpr void _const_time_poison() const { CT::poison(m_val); }
856
857 constexpr void _const_time_unpoison() const { CT::unpoison(m_val); }
858
859 private:
860 constexpr const std::array<W, N>& value() const { return m_val; }
861
862 constexpr const W* data() const { return m_val.data(); }
863
864 explicit constexpr IntMod(std::array<W, N> v) : m_val(v) {}
865
866 std::array<W, N> m_val;
867};
868
869/**
870* Affine Curve Point
871*
872* This contains a pair of integers (x,y) which satisfy the curve equation
873*/
874template <typename FieldElement, typename Params>
875class AffineCurvePoint final {
876 public:
877 static constexpr size_t BYTES = 1 + 2 * FieldElement::BYTES;
878
880
881 // Note this constructor does not check the validity of the x/y pair
882 // This must be verified prior to this constructor being called
883 constexpr AffineCurvePoint(const FieldElement& x, const FieldElement& y) : m_x(x), m_y(y) {}
884
885 constexpr AffineCurvePoint() : m_x(FieldElement::zero()), m_y(FieldElement::zero()) {}
886
887 static constexpr Self identity() { return Self(FieldElement::zero(), FieldElement::zero()); }
888
889 // Helper for ct_select of pcurves_generic
890 static constexpr Self identity(const Self& /*unused*/) {
891 return Self(FieldElement::zero(), FieldElement::zero());
892 }
893
894 constexpr CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); }
895
896 AffineCurvePoint(const Self& other) = default;
897 AffineCurvePoint(Self&& other) = default;
898 AffineCurvePoint& operator=(const Self& other) = default;
899 AffineCurvePoint& operator=(Self&& other) = default;
900 ~AffineCurvePoint() = default;
901
902 constexpr Self negate() const { return Self(x(), y().negate()); }
903
904 /**
905 * Serialize the point in uncompressed format
906 */
907 constexpr void serialize_to(std::span<uint8_t, Self::BYTES> bytes) const {
908 BOTAN_STATE_CHECK(this->is_identity().as_bool() == false);
909 BufferStuffer pack(bytes);
910 pack.append(0x04);
911 x().serialize_to(pack.next<FieldElement::BYTES>());
912 y().serialize_to(pack.next<FieldElement::BYTES>());
913 BOTAN_DEBUG_ASSERT(pack.full());
914 }
915
916 /**
917 * If idx is zero then return the identity element. Otherwise return pts[idx - 1]
918 *
919 * Returns the identity element also if idx is out of range
920 */
921 static constexpr auto ct_select(std::span<const Self> pts, size_t idx) {
922 auto result = Self::identity(pts[0]);
923
924 // Intentionally wrapping; set to maximum size_t if idx == 0
925 const size_t idx1 = static_cast<size_t>(idx - 1);
926 for(size_t i = 0; i != pts.size(); ++i) {
927 const auto found = CT::Mask<size_t>::is_equal(idx1, i).as_choice();
928 result.conditional_assign(found, pts[i]);
929 }
930
931 return result;
932 }
933
934 /**
935 * Return the affine x coordinate
936 */
937 constexpr const FieldElement& x() const { return m_x; }
938
939 /**
940 * Return the affine y coordinate
941 */
942 constexpr const FieldElement& y() const { return m_y; }
943
944 /**
945 * Conditional assignment of an affine point
946 */
947 constexpr void conditional_assign(CT::Choice cond, const Self& pt) {
948 FieldElement::conditional_assign(m_x, m_y, cond, pt.x(), pt.y());
949 }
950
951 constexpr void _const_time_poison() const { CT::poison_all(m_x, m_y); }
952
953 constexpr void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y); }
954
955 private:
956 FieldElement m_x;
957 FieldElement m_y;
958};
959
960/**
961* Projective curve point
962*
963* This uses Jacobian coordinates
964*/
965template <typename FieldElement, typename Params>
967 public:
968 // We can't pass a FieldElement directly because FieldElement is
969 // not "structural" due to having private members, so instead
970 // recreate it here from the words.
971 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
972
973 static constexpr bool A_is_zero = A.is_zero().as_bool();
974 static constexpr bool A_is_minus_3 = (A == FieldElement::constant(-3)).as_bool();
975
978
979 /**
980 * Convert a point from affine to projective form
981 */
982 static constexpr Self from_affine(const AffinePoint& pt) {
983 /*
984 * If the point is the identity element (x=0, y=0) then instead of
985 * creating (x, y, 1) = (0, 0, 1) we want our projective identity
986 * encoding of (0, 1, 0)
987 *
988 * Which we can achieve by a conditional swap of y and z if the
989 * affine point is the identity.
990 */
991
992 auto x = pt.x();
993 auto y = pt.y();
994 auto z = FieldElement::one();
995
996 FieldElement::conditional_swap(pt.is_identity(), y, z);
997
998 return ProjectiveCurvePoint(x, y, z);
999 }
1000
1001 /**
1002 * Return the identity element
1003 */
1004 static constexpr Self identity() { return Self(FieldElement::zero(), FieldElement::one(), FieldElement::zero()); }
1005
1006 /**
1007 * Default constructor: the identity element
1008 */
1010 m_x(FieldElement::zero()), m_y(FieldElement::one()), m_z(FieldElement::zero()) {}
1011
1012 /**
1013 * Affine constructor: take x/y coordinates
1014 */
1015 constexpr ProjectiveCurvePoint(const FieldElement& x, const FieldElement& y) :
1016 m_x(x), m_y(y), m_z(FieldElement::one()) {}
1017
1018 /**
1019 * Projective constructor: take x/y/z coordinates
1020 */
1021 constexpr ProjectiveCurvePoint(const FieldElement& x, const FieldElement& y, const FieldElement& z) :
1022 m_x(x), m_y(y), m_z(z) {}
1023
1024 ProjectiveCurvePoint(const Self& other) = default;
1025 ProjectiveCurvePoint(Self&& other) = default;
1026 ProjectiveCurvePoint& operator=(const Self& other) = default;
1029
1030 friend constexpr Self operator+(const Self& a, const Self& b) { return Self::add(a, b); }
1031
1032 friend constexpr Self operator+(const Self& a, const AffinePoint& b) { return Self::add_mixed(a, b); }
1033
1034 friend constexpr Self operator+(const AffinePoint& a, const Self& b) { return Self::add_mixed(b, a); }
1035
1036 constexpr Self& operator+=(const Self& other) {
1037 (*this) = (*this) + other;
1038 return (*this);
1039 }
1040
1041 constexpr Self& operator+=(const AffinePoint& other) {
1042 (*this) = (*this) + other;
1043 return (*this);
1044 }
1045
1046 friend constexpr Self operator-(const Self& a, const Self& b) { return a + b.negate(); }
1047
1048 constexpr CT::Choice is_identity() const { return z().is_zero(); }
1049
1050 constexpr void conditional_assign(CT::Choice cond, const Self& pt) {
1051 FieldElement::conditional_assign(m_x, m_y, m_z, cond, pt.x(), pt.y(), pt.z());
1052 }
1053
1054 /**
1055 * Mixed (projective + affine) point addition
1056 */
1057 constexpr static Self add_mixed(const Self& a, const AffinePoint& b) {
1058 return point_add_mixed<Self, AffinePoint, FieldElement>(a, b, FieldElement::one());
1059 }
1060
1061 // Either add or subtract based on the CT::Choice
1062 constexpr static Self add_or_sub(const Self& a, const AffinePoint& b, CT::Choice sub) {
1063 return point_add_or_sub_mixed<Self, AffinePoint, FieldElement>(a, b, sub, FieldElement::one());
1064 }
1065
1066 /**
1067 * Projective point addition
1068 */
1069 constexpr static Self add(const Self& a, const Self& b) { return point_add<Self, FieldElement>(a, b); }
1070
1071 /**
1072 * Iterated point doubling
1073 */
1074 constexpr Self dbl_n(size_t n) const {
1075 if constexpr(Self::A_is_minus_3) {
1076 return dbl_n_a_minus_3(*this, n);
1077 } else if constexpr(Self::A_is_zero) {
1078 return dbl_n_a_zero(*this, n);
1079 } else {
1080 return dbl_n_generic(*this, A, n);
1081 }
1082 }
1083
1084 /**
1085 * Point doubling
1086 */
1087 constexpr Self dbl() const {
1088 if constexpr(Self::A_is_minus_3) {
1089 return dbl_a_minus_3(*this);
1090 } else if constexpr(Self::A_is_zero) {
1091 return dbl_a_zero(*this);
1092 } else {
1093 return dbl_generic(*this, A);
1094 }
1095 }
1096
1097 /**
1098 * Point negation
1099 */
1100 constexpr Self negate() const { return Self(x(), y().negate(), z()); }
1101
1102 /**
1103 * Randomize the point representation
1104 *
1105 * Projective coordinates are redundant; if (x,y,z) is a projective
1106 * point then so is (x*r^2,y*r^3,z*r) for any non-zero r.
1107 */
1109 // In certain contexts we may be called with a Null_RNG; in that case the
1110 // caller is accepting that randomization will not occur
1111
1112 // Conditional ok: caller's RNG state (seeded vs not) is presumed public
1113 if(rng.is_seeded()) {
1114 auto r = FieldElement::random(rng);
1115
1116 auto r2 = r.square();
1117 auto r3 = r2 * r;
1118
1119 m_x *= r2;
1120 m_y *= r3;
1121 m_z *= r;
1122 }
1123 }
1124
1125 /**
1126 * Return the projective x coordinate
1127 */
1128 constexpr const FieldElement& x() const { return m_x; }
1129
1130 /**
1131 * Return the projective y coordinate
1132 */
1133 constexpr const FieldElement& y() const { return m_y; }
1134
1135 /**
1136 * Return the projective z coordinate
1137 */
1138 constexpr const FieldElement& z() const { return m_z; }
1139
1140 constexpr void _const_time_poison() const { CT::poison_all(m_x, m_y, m_z); }
1141
1142 constexpr void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y, m_z); }
1143
1144 private:
1145 FieldElement m_x;
1146 FieldElement m_y;
1147 FieldElement m_z;
1148};
1149
1150/**
1151* Elliptic Curve Parameters
1152*
1153* These are constructed using compile time strings which contain the relevant values
1154* (P, A, B, the group order, and the group generator x/y coordinates)
1155*/
1156template <StringLiteral PS,
1157 StringLiteral AS,
1158 StringLiteral BS,
1159 StringLiteral NS,
1160 StringLiteral GXS,
1161 StringLiteral GYS,
1162 int8_t ZI = 0>
1164 public:
1165 typedef word W;
1166
1167 static constexpr auto PW = hex_to_words<W>(PS.value);
1168 static constexpr auto NW = hex_to_words<W>(NS.value);
1169 static constexpr auto AW = hex_to_words<W>(AS.value);
1170 static constexpr auto BW = hex_to_words<W>(BS.value);
1171 static constexpr auto GXW = hex_to_words<W>(GXS.value);
1172 static constexpr auto GYW = hex_to_words<W>(GYS.value);
1173
1174 static constexpr int8_t Z = ZI;
1175};
1176
1177/**
1178* This exists soley as a hack which somewhat reduces symbol lengths
1179*/
1180template <WordType WI, size_t NI, std::array<WI, NI> PI>
1182 public:
1183 typedef WI W;
1184 static constexpr size_t N = NI;
1185 static constexpr auto P = PI;
1186};
1187
1188/**
1189* Elliptic Curve
1190*
1191* Takes as input a set of parameters, and instantiates the elliptic curve
1192*/
1193template <typename Params, template <typename FieldParamsT> typename FieldRep = MontgomeryRep>
1195 public:
1196 typedef typename Params::W W;
1197
1198 typedef W WordType;
1199
1200 static constexpr auto PW = Params::PW;
1201 static constexpr auto NW = Params::NW;
1202 static constexpr auto AW = Params::AW;
1203
1204 // Simplifying assumption
1205 static_assert(PW.size() == NW.size());
1206
1207 static constexpr size_t Words = PW.size();
1208
1209 class ScalarParams final : public IntParams<W, Words, NW> {};
1210
1212
1213 class FieldParams final : public IntParams<W, Words, PW> {};
1214
1216
1219
1220 static constexpr size_t OrderBits = Scalar::BITS;
1221 static constexpr size_t PrimeFieldBits = FieldElement::BITS;
1222
1223 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
1224 static constexpr FieldElement B = FieldElement::from_words(Params::BW);
1225
1226 static_assert(B.is_nonzero().as_bool(), "B must be non-zero");
1227
1228 static constexpr AffinePoint G =
1230
1231 static constexpr FieldElement SSWU_Z = FieldElement::constant(Params::Z);
1232
1233 static constexpr bool ValidForSswuHash =
1234 (Params::Z != 0 && A.is_nonzero().as_bool() && B.is_nonzero().as_bool() && FieldElement::P_MOD_4 == 3);
1235
1236 static constexpr bool OrderIsLessThanField = bigint_cmp(NW.data(), Words, PW.data(), Words) == -1;
1237
1238 /**
1239 * Return (x^3 + A*x + B) mod p
1240 */
1241 static constexpr FieldElement x3_ax_b(const FieldElement& x) { return (x.square() + A) * x + B; }
1242};
1243
1244/**
1245* Blinded Scalar
1246*
1247* This randomizes the scalar representation by computing s + n*k,
1248* where n is the group order and k is a random value
1249*
1250* Note that the field arithmetic and point multiplication algorithms
1251* implemented in this file are already constant time; blinding is used here as
1252* an additional precaution to guard against compilers introducing conditional
1253* jumps where not expected.
1254*
1255* If you would like a "go faster" button, change the BlindingEnabled variable
1256* below to false.
1257*/
1258template <typename C, size_t WindowBits>
1260 private:
1261 typedef typename C::W W;
1262
1263 static constexpr bool BlindingEnabled = true;
1264
1265 // Decide size of scalar blinding factor based on bitlength of the scalar
1266 //
1267 // This can return any value between 0 and the scalar bit length, as long
1268 // as it is a multiple of the word size.
1269 //
1270 // TODO(Botan4) this function should be consteval but cannot currently to a bug
1271 // in older versions of Clang. Change to consteval when minimum Clang is bumped.
1272 static constexpr size_t blinding_bits(size_t sb) {
1273 constexpr size_t wb = WordInfo<W>::bits;
1274
1275 static_assert(wb == 32 || wb == 64, "Unexpected W size");
1276
1277 if(sb == 521) {
1278 /*
1279 Treat P-521 as if it was a 512 bit field; otherwise it is penalized
1280 by the below computation, using either 160 or 192 bits of blinding
1281 (depending on wb), vs 128 bits used for 512 bit groups.
1282 */
1283 return blinding_bits(512);
1284 } else {
1285 // For blinding use 1/4 the order, rounded up to the next word
1286 return ((sb / 4 + wb - 1) / wb) * wb;
1287 }
1288 }
1289
1290 static constexpr size_t BlindingBits = blinding_bits(C::OrderBits);
1291
1292 static_assert(BlindingBits % WordInfo<W>::bits == 0);
1293 static_assert(BlindingBits < C::Scalar::BITS);
1294
1295 public:
1296 static constexpr size_t Bits = C::Scalar::BITS + (BlindingEnabled ? BlindingBits : 0);
1297 static constexpr size_t Bytes = (Bits + 7) / 8;
1298
1299 constexpr size_t bits() const { return Bits; }
1300
1301 BlindedScalarBits(const typename C::Scalar& scalar, RandomNumberGenerator& rng) {
1302 if constexpr(BlindingEnabled) {
1303 constexpr size_t mask_words = BlindingBits / WordInfo<W>::bits;
1304 constexpr size_t mask_bytes = mask_words * WordInfo<W>::bytes;
1305
1306 constexpr size_t n_words = C::Words;
1307
1308 uint8_t maskb[mask_bytes] = {0};
1309 if(rng.is_seeded()) {
1310 rng.randomize(maskb, mask_bytes);
1311 } else {
1312 // If we don't have an RNG we don't have many good options. We
1313 // could just omit the blinding entirely, but this changes the
1314 // size of the blinded scalar, which we're expecting otherwise is
1315 // knowable at compile time. So generate a mask by XORing the
1316 // bytes of the scalar together. At worst, it's equivalent to
1317 // omitting the blinding entirely.
1318
1319 std::array<uint8_t, C::Scalar::BYTES> sbytes = {};
1320 scalar.serialize_to(sbytes);
1321 for(size_t i = 0; i != sbytes.size(); ++i) {
1322 maskb[i % mask_bytes] ^= sbytes[i];
1323 }
1324 }
1325
1326 W mask[n_words] = {0};
1327 load_le(mask, maskb, mask_words);
1328 mask[mask_words - 1] |= WordInfo<W>::top_bit;
1329 mask[0] |= 1;
1330
1331 W mask_n[2 * n_words] = {0};
1332
1333 const auto sw = scalar.to_words();
1334
1335 // Compute masked scalar s + k*n
1336 comba_mul<n_words>(mask_n, mask, C::NW.data());
1337 bigint_add2(mask_n, 2 * n_words, sw.data(), sw.size());
1338
1339 std::reverse(mask_n, mask_n + 2 * n_words);
1340 m_bytes = store_be<std::vector<uint8_t>>(mask_n);
1341 } else {
1342 static_assert(Bytes == C::Scalar::BYTES);
1343 m_bytes.resize(Bytes);
1344 scalar.serialize_to(std::span{m_bytes}.template first<Bytes>());
1345 }
1346
1347 CT::poison(m_bytes.data(), m_bytes.size());
1348 }
1349
1350 size_t get_window(size_t offset) const {
1351 // Extract a WindowBits sized window out of s, depending on offset.
1352 return read_window_bits<WindowBits>(std::span{m_bytes}, offset);
1353 }
1354
1356 secure_scrub_memory(m_bytes.data(), m_bytes.size());
1357 CT::unpoison(m_bytes.data(), m_bytes.size());
1358 }
1359
1360 BlindedScalarBits(const BlindedScalarBits& other) = delete;
1364
1365 private:
1366 // TODO this could be a fixed size array
1367 std::vector<uint8_t> m_bytes;
1368};
1369
1370template <typename C, size_t WindowBits>
1372 public:
1373 static constexpr size_t Bits = C::Scalar::BITS;
1374
1375 explicit UnblindedScalarBits(const typename C::Scalar& scalar) { scalar.serialize_to(std::span{m_bytes}); }
1376
1377 size_t get_window(size_t offset) const {
1378 // Extract a WindowBits sized window out of s, depending on offset.
1379 return read_window_bits<WindowBits>(std::span{m_bytes}, offset);
1380 }
1381
1382 private:
1383 std::array<uint8_t, C::Scalar::BYTES> m_bytes;
1384};
1385
1386template <typename C, size_t W>
1388 public:
1389 typedef typename C::Scalar Scalar;
1390 typedef typename C::AffinePoint AffinePoint;
1391 typedef typename C::ProjectivePoint ProjectivePoint;
1392
1393 static constexpr size_t WindowBits = W;
1394 static_assert(WindowBits >= 1 && WindowBits <= 8);
1395
1397
1398 static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits;
1399
1401 m_table(basemul_setup<C, WindowBits>(p, BlindedScalar::Bits)) {}
1402
1404 const BlindedScalar scalar(s, rng);
1405 return basemul_exec<C, WindowBits>(m_table, scalar, rng);
1406 }
1407
1408 private:
1409 std::vector<AffinePoint> m_table;
1410};
1411
1412/**
1413* Precomputed point multiplication table
1414*
1415* This is a standard fixed window multiplication using W-bit wide window.
1416*/
1417template <typename C, size_t W>
1418class WindowedMulTable final {
1419 public:
1420 typedef typename C::Scalar Scalar;
1421 typedef typename C::AffinePoint AffinePoint;
1422 typedef typename C::ProjectivePoint ProjectivePoint;
1423
1424 static constexpr size_t WindowBits = W;
1425 static_assert(WindowBits >= 1 && WindowBits <= 8);
1426
1428
1429 static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits;
1430
1431 static_assert(Windows > 1);
1432
1433 // 2^W elements, less the identity element
1434 static constexpr size_t TableSize = (1 << WindowBits) - 1;
1435
1436 explicit WindowedMulTable(const AffinePoint& p) : m_table(varpoint_setup<C, TableSize>(p)) {}
1437
1439 const BlindedScalar bits(s, rng);
1440 return varpoint_exec<C, WindowBits>(m_table, bits, rng);
1441 }
1442
1443 private:
1444 std::vector<AffinePoint> m_table;
1445};
1446
1447/**
1448* Precomputed point multiplication table with Booth
1449*/
1450template <typename C, size_t W>
1452 public:
1453 typedef typename C::Scalar Scalar;
1454 typedef typename C::AffinePoint AffinePoint;
1455 typedef typename C::ProjectivePoint ProjectivePoint;
1456
1457 static constexpr size_t TableBits = W;
1458 static_assert(TableBits >= 1 && TableBits <= 7);
1459
1460 static constexpr size_t WindowBits = TableBits + 1;
1461
1463
1464 static constexpr size_t compute_full_windows(size_t sb, size_t wb) {
1465 if(sb % wb == 0) {
1466 return (sb - 1) / wb;
1467 } else {
1468 return sb / wb;
1469 }
1470 }
1471
1473
1474 static constexpr size_t compute_initial_shift(size_t sb, size_t wb) {
1475 if(sb % wb == 0) {
1476 return wb;
1477 } else {
1478 return sb - (sb / wb) * wb;
1479 }
1480 }
1481
1483
1484 static_assert(FullWindows * WindowBits + InitialShift == BlindedScalar::Bits + 1);
1485 static_assert(InitialShift > 0);
1486
1487 // 2^W elements [1*P, 2*P, ..., 2^W*P]
1488 static constexpr size_t TableSize = 1 << TableBits;
1489
1490 explicit WindowedBoothMulTable(const AffinePoint& p) : m_table(varpoint_setup<C, TableSize>(p)) {}
1491
1493 const BlindedScalar bits(s, rng);
1494
1495 auto accum = ProjectivePoint::identity();
1496 CT::poison(accum);
1497
1498 for(size_t i = 0; i != FullWindows; ++i) {
1499 const size_t idx = BlindedScalar::Bits - InitialShift - WindowBits * i;
1500
1501 const size_t w_i = bits.get_window(idx);
1502 const auto [tidx, tneg] = booth_recode<WindowBits>(w_i);
1503
1504 // Conditional ok: loop iteration count is public
1505 if(i == 0) {
1506 accum = ProjectivePoint::from_affine(m_table.ct_select(tidx));
1507 accum.conditional_assign(tneg, accum.negate());
1508 } else {
1509 accum = ProjectivePoint::add_or_sub(accum, m_table.ct_select(tidx), tneg);
1510 }
1511
1512 accum = accum.dbl_n(WindowBits);
1513
1514 // Conditional ok: loop iteration count is public
1515 if(i <= 3) {
1516 accum.randomize_rep(rng);
1517 }
1518 }
1519
1520 // final window (note one bit shorter than previous reads)
1521 const size_t w_l = bits.get_window(0) & ((1 << WindowBits) - 1);
1522 const auto [tidx, tneg] = booth_recode<WindowBits>(w_l << 1);
1523 accum = ProjectivePoint::add_or_sub(accum, m_table.ct_select(tidx), tneg);
1524
1525 CT::unpoison(accum);
1526 return accum;
1527 }
1528
1529 private:
1530 template <size_t B, std::unsigned_integral T>
1531 static constexpr std::pair<size_t, CT::Choice> booth_recode(T x) {
1532 static_assert(B < sizeof(T) * 8 - 2, "Invalid B");
1533
1534 auto s_mask = CT::Mask<T>::expand(x >> B);
1535 const T neg_x = (1 << (B + 1)) - x - 1;
1536 T d = s_mask.select(neg_x, x);
1537 d = (d >> 1) + (d & 1);
1538
1539 return std::make_pair(static_cast<size_t>(d), s_mask.as_choice());
1540 }
1541
1542 AffinePointTable<C> m_table;
1543};
1544
1545template <typename C, size_t W>
1547 public:
1548 // We look at W bits of each scalar per iteration
1549 static_assert(W >= 1 && W <= 4);
1550
1551 typedef typename C::Scalar Scalar;
1552 typedef typename C::AffinePoint AffinePoint;
1553 typedef typename C::ProjectivePoint ProjectivePoint;
1554
1555 WindowedMul2Table(const AffinePoint& p, const AffinePoint& q) : m_table(mul2_setup<C, W>(p, q)) {}
1556
1557 /**
1558 * Constant time 2-ary multiplication
1559 */
1560 ProjectivePoint mul2(const Scalar& s1, const Scalar& s2, RandomNumberGenerator& rng) const {
1561 using BlindedScalar = BlindedScalarBits<C, W>;
1562 BlindedScalar bits1(s1, rng);
1563 BlindedScalar bits2(s2, rng);
1564
1565 return mul2_exec<C, W>(m_table, bits1, bits2, rng);
1566 }
1567
1568 private:
1569 AffinePointTable<C> m_table;
1570};
1571
1572template <typename C, size_t W>
1573class VartimeMul2Table final {
1574 public:
1575 // We look at W bits of each scalar per iteration
1576 static_assert(W >= 1 && W <= 4);
1577
1578 static constexpr size_t WindowBits = W;
1579
1580 using Scalar = typename C::Scalar;
1581 using AffinePoint = typename C::AffinePoint;
1582 using ProjectivePoint = typename C::ProjectivePoint;
1583
1585 m_table(to_affine_batch<C>(mul2_setup<C, W>(p, q))) {}
1586
1587 /**
1588 * Variable time 2-ary multiplication
1589 *
1590 * A common use of 2-ary multiplication is when verifying the commitments
1591 * of an elliptic curve signature. Since in this case the inputs are all
1592 * public, there is no problem with variable time computation.
1593 *
1594 * TODO in the future we could use joint sparse form here.
1595 */
1596 ProjectivePoint mul2_vartime(const Scalar& s1, const Scalar& s2) const {
1597 constexpr size_t Windows = (Scalar::BITS + WindowBits - 1) / WindowBits;
1598
1599 const UnblindedScalarBits<C, W> bits1(s1);
1600 const UnblindedScalarBits<C, W> bits2(s2);
1601
1602 bool s1_is_zero = s1.is_zero().as_bool();
1603 bool s2_is_zero = s2.is_zero().as_bool();
1604
1605 // Conditional ok: this function is variable time
1606 if(s1_is_zero && s2_is_zero) {
1607 return ProjectivePoint::identity();
1608 }
1609
1610 auto [w_0, first_nonempty_window] = [&]() {
1611 for(size_t i = 0; i != Windows; ++i) {
1612 const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits);
1613 const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits);
1614 const size_t window = w_1 + (w_2 << WindowBits);
1615 // Conditional ok: this function is variable time
1616 if(window > 0) {
1617 return std::make_pair(window, i);
1618 }
1619 }
1620 // We checked for s1 == s2 == 0 above, so we must see a window eventually
1622 }();
1623
1624 BOTAN_ASSERT_NOMSG(w_0 > 0);
1625 auto accum = ProjectivePoint::from_affine(m_table[w_0 - 1]);
1626
1627 for(size_t i = first_nonempty_window + 1; i < Windows; ++i) {
1628 accum = accum.dbl_n(WindowBits);
1629
1630 const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits);
1631 const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits);
1632
1633 const size_t window = w_1 + (w_2 << WindowBits);
1634
1635 // Conditional ok: this function is variable time
1636 if(window > 0) {
1637 accum += m_table[window - 1];
1638 }
1639 }
1640
1641 return accum;
1642 }
1643
1644 private:
1645 std::vector<AffinePoint> m_table;
1646};
1647
1648/**
1649* SSWU constant C2 - (B / (Z * A))
1650*
1651* See RFC 9380 section 6.6.2
1652*/
1653template <typename C>
1654const auto& SSWU_C2()
1655 requires C::ValidForSswuHash
1656{
1657 // TODO(Botan4) Make this a constexpr once compilers have caught up
1658 static const typename C::FieldElement C2 = C::B * invert_field_element<C>(C::SSWU_Z * C::A);
1659 return C2;
1660}
1661
1662/**
1663* SSWU constant C1 - (-B / A)
1664*
1665* See RFC 9380 section 6.6.2
1666*/
1667template <typename C>
1668const auto& SSWU_C1()
1669 requires C::ValidForSswuHash
1670{
1671 // TODO(Botan4) Make this a constexpr
1672 // We derive it from C2 to avoid a second inversion
1673 static const typename C::FieldElement C1 = (SSWU_C2<C>() * C::SSWU_Z).negate();
1674 return C1;
1675}
1676
1677/**
1678* Map to curve (SSWU)
1679*
1680* See RFC 9380 ("Hashing to Elliptic Curves") section 6.6.2
1681*/
1682template <typename C>
1683inline auto map_to_curve_sswu(const typename C::FieldElement& u) -> typename C::AffinePoint {
1684 CT::poison(u);
1685 const auto z_u2 = C::SSWU_Z * u.square(); // z * u^2
1686 const auto z2_u4 = z_u2.square();
1687 const auto tv1 = invert_field_element<C>(z2_u4 + z_u2);
1688 auto x1 = SSWU_C1<C>() * (C::FieldElement::one() + tv1);
1689 x1.conditional_assign(tv1.is_zero(), SSWU_C2<C>());
1690 const auto x2 = z_u2 * x1;
1691
1692 // By design one of gx1 and gx2 must be a quadratic residue
1695
1696 const auto use_y1 = y1.has_value();
1697
1698 auto x = C::FieldElement::choose(use_y1, x1, x2);
1699 auto y = C::FieldElement::choose(use_y1, y1.value_or(C::FieldElement::zero()), y2.value_or(C::FieldElement::zero()));
1700
1701 auto pt = typename C::AffinePoint(x, y.correct_sign(u.is_even()));
1702
1703 CT::unpoison(pt);
1704 return pt;
1705}
1706
1707/**
1708* Hash to curve (SSWU); RFC 9380
1709*
1710* This is the Simplified Shallue-van de Woestijne-Ulas (SSWU) map.
1711*
1712* The parameter expand_message models the function of RFC 9380 and is provided
1713* by higher levels. For the curves implemented here it will typically be XMD,
1714* but could also be an XOF (expand_message_xof) or a MHF like Argon2.
1715*
1716* For details see RFC 9380 sections 3, 5.2 and 6.6.2.
1717*/
1718template <typename C, bool RO, std::invocable<std::span<uint8_t>> ExpandMsg>
1719 requires C::ValidForSswuHash
1720inline auto hash_to_curve_sswu(const ExpandMsg& expand_message)
1721 -> std::conditional_t<RO, typename C::ProjectivePoint, typename C::AffinePoint> {
1722 constexpr size_t SecurityLevel = (C::OrderBits + 1) / 2;
1723 constexpr size_t L = (C::PrimeFieldBits + SecurityLevel + 7) / 8;
1724 constexpr size_t Cnt = RO ? 2 : 1;
1725
1726 std::array<uint8_t, L * Cnt> uniform_bytes = {};
1727 expand_message(uniform_bytes);
1728
1729 if constexpr(RO) {
1730 const auto u0 = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(uniform_bytes.data(), L));
1731 const auto u1 = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(uniform_bytes.data() + L, L));
1732
1733 auto accum = C::ProjectivePoint::from_affine(map_to_curve_sswu<C>(u0));
1734 accum += map_to_curve_sswu<C>(u1);
1735 return accum;
1736 } else {
1737 const auto u = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(uniform_bytes.data(), L));
1738 return map_to_curve_sswu<C>(u);
1739 }
1740}
1741
1742} // namespace Botan
1743
1744#endif
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:163
constexpr const FieldElement & y() const
static constexpr size_t BYTES
AffineCurvePoint & operator=(const Self &other)=default
static constexpr Self identity(const Self &)
static constexpr Self identity()
constexpr const FieldElement & x() const
constexpr AffineCurvePoint(const FieldElement &x, const FieldElement &y)
static constexpr auto ct_select(std::span< const Self > pts, size_t idx)
AffineCurvePoint & operator=(Self &&other)=default
constexpr Self negate() const
constexpr CT::Choice is_identity() const
constexpr void conditional_assign(CT::Choice cond, const Self &pt)
constexpr void _const_time_unpoison() const
AffineCurvePoint(const Self &other)=default
AffineCurvePoint(Self &&other)=default
constexpr void serialize_to(std::span< uint8_t, Self::BYTES > bytes) const
constexpr void _const_time_poison() const
AffineCurvePoint< FieldElement, Params > Self
BlindedScalarBits(const BlindedScalarBits &other)=delete
BlindedScalarBits & operator=(BlindedScalarBits &&other)=delete
size_t get_window(size_t offset) const
BlindedScalarBits(BlindedScalarBits &&other)=delete
BlindedScalarBits(const typename C::Scalar &scalar, RandomNumberGenerator &rng)
constexpr size_t bits() const
BlindedScalarBits & operator=(const BlindedScalarBits &other)=delete
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:134
static constexpr Choice from_int(T v)
Definition ct_utils.h:314
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:420
static constexpr Mask< T > from_choice(Choice c)
Definition ct_utils.h:430
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:470
constexpr Choice has_value() const
Return true if this Option contains a value.
Definition ct_utils.h:692
constexpr T value_or(T other) const
Definition ct_utils.h:716
static constexpr int8_t Z
static constexpr auto GXW
static constexpr auto GYW
static constexpr auto NW
static constexpr size_t PrimeFieldBits
static constexpr FieldElement SSWU_Z
static constexpr auto PW
IntMod< MontgomeryRep< ScalarParams > > Scalar
static constexpr bool ValidForSswuHash
static constexpr FieldElement x3_ax_b(const FieldElement &x)
static constexpr FieldElement A
static constexpr bool OrderIsLessThanField
static constexpr size_t Words
AffineCurvePoint< FieldElement, Params > AffinePoint
ProjectiveCurvePoint< FieldElement, Params > ProjectivePoint
static constexpr size_t OrderBits
static constexpr auto AW
static constexpr AffinePoint G
IntMod< FieldRep< FieldParams > > FieldElement
static constexpr FieldElement B
constexpr Self & operator*=(const Self &other)
Self div2() const
static constexpr void conditional_assign(Self &x, Self &y, CT::Choice cond, const Self &nx, const Self &ny)
friend constexpr Self operator*(const Self &a, const Self &b)
static constexpr std::optional< Self > from_wide_bytes_varlen(std::span< const uint8_t > bytes)
constexpr void serialize_to(std::span< uint8_t, Self::BYTES > bytes) const
constexpr CT::Choice is_zero() const
static constexpr void _invert_vartime_div2_helper(Self &a, Self &x)
std::array< W, L > stash_value() const
constexpr CT::Choice is_nonzero() const
~IntMod()=default
constexpr void _const_time_unpoison() const
friend constexpr Self operator-(const Self &a, const Self &b)
IntMod(const Self &other)=default
constexpr Self correct_sign(CT::Choice even) const
IntMod< Rep > Self
constexpr void conditional_assign(CT::Choice cond, const Self &nx)
constexpr Self negate() const
constexpr Self invert_vartime() const
static consteval Self constant(int8_t x)
constexpr std::array< W, Self::N > to_words() const
static constexpr Self zero()
static Self from_stash(const std::array< W, L > &stash)
IntMod(Self &&other)=default
static std::optional< Self > deserialize(std::span< const uint8_t > bytes)
constexpr Self mul8() const
Return (*this) multiplied by 8.
constexpr Self pow_vartime(const std::array< W, N > &exp) const
constexpr void square_n(size_t n)
constexpr CT::Option< Self > sqrt() const
constexpr void _const_time_poison() const
friend constexpr Self operator+(const Self &a, const Self &b)
static constexpr Self one()
static constexpr Self choose(CT::Choice choice, const Self &x, const Self &y)
constexpr CT::Choice operator==(const Self &other) const
constexpr Self invert() const
constexpr Self mul3() const
Return (*this) multiplied by 3.
static constexpr void conditional_assign(Self &x, Self &y, Self &z, CT::Choice cond, const Self &nx, const Self &ny, const Self &nz)
constexpr CT::Choice is_even() const
IntMod & operator=(const Self &other)=default
static constexpr Self from_words(std::array< W, L > w)
IntMod & operator=(Self &&other)=default
constexpr Self mul4() const
Return (*this) multiplied by 4.
constexpr Self mul2() const
Return (*this) multiplied by 2.
static constexpr Self from_wide_bytes(std::span< const uint8_t, L > bytes)
static Self random(RandomNumberGenerator &rng)
constexpr CT::Choice is_one() const
static constexpr void conditional_swap(CT::Choice cond, Self &x, Self &y)
constexpr Self square() const
constexpr CT::Choice operator!=(const Self &other) const
constexpr IntMod()
static constexpr std::array< W, N > wide_to_rep(const std::array< W, 2 *N > &x)
static constexpr auto P_dash
static constexpr std::array< W, N > from_rep(const std::array< W, N > &z)
static constexpr size_t N
static constexpr auto R3
static constexpr auto P
MontgomeryRep< Params > Self
static constexpr auto R1
static constexpr std::array< W, N > one()
static constexpr auto R2
static constexpr std::array< W, N > to_rep(const std::array< W, N > &x)
static constexpr std::array< W, N > redc(const std::array< W, 2 *N > &z)
static constexpr size_t Windows
static constexpr size_t WindowBits
PrecomputedBaseMulTable(const AffinePoint &p)
BlindedScalarBits< C, WindowBits > BlindedScalar
C::ProjectivePoint ProjectivePoint
ProjectivePoint mul(const Scalar &s, RandomNumberGenerator &rng) const
static constexpr Self add_or_sub(const Self &a, const AffinePoint &b, CT::Choice sub)
ProjectiveCurvePoint< FieldElement, Params > Self
ProjectiveCurvePoint(const Self &other)=default
friend constexpr Self operator+(const AffinePoint &a, const Self &b)
ProjectiveCurvePoint(Self &&other)=default
static constexpr bool A_is_zero
ProjectiveCurvePoint & operator=(Self &&other)=default
constexpr const FieldElement & x() const
friend constexpr Self operator-(const Self &a, const Self &b)
friend constexpr Self operator+(const Self &a, const AffinePoint &b)
static constexpr bool A_is_minus_3
ProjectiveCurvePoint & operator=(const Self &other)=default
static constexpr Self from_affine(const AffinePoint &pt)
static constexpr Self add(const Self &a, const Self &b)
constexpr Self & operator+=(const AffinePoint &other)
constexpr void conditional_assign(CT::Choice cond, const Self &pt)
constexpr void _const_time_unpoison() const
constexpr const FieldElement & z() const
constexpr const FieldElement & y() const
void randomize_rep(RandomNumberGenerator &rng)
constexpr CT::Choice is_identity() const
constexpr Self negate() const
friend constexpr Self operator+(const Self &a, const Self &b)
static constexpr FieldElement A
AffineCurvePoint< FieldElement, Params > AffinePoint
constexpr ProjectiveCurvePoint(const FieldElement &x, const FieldElement &y)
static constexpr Self add_mixed(const Self &a, const AffinePoint &b)
static constexpr Self identity()
constexpr void _const_time_poison() const
constexpr Self dbl_n(size_t n) const
constexpr ProjectiveCurvePoint(const FieldElement &x, const FieldElement &y, const FieldElement &z)
constexpr Self dbl() const
constexpr Self & operator+=(const Self &other)
void randomize(std::span< uint8_t > output)
Definition rng.h:71
virtual bool is_seeded() const =0
static constexpr size_t Bits
UnblindedScalarBits(const typename C::Scalar &scalar)
size_t get_window(size_t offset) const
static constexpr size_t WindowBits
typename C::AffinePoint AffinePoint
VartimeMul2Table(const AffinePoint &p, const AffinePoint &q)
typename C::Scalar Scalar
typename C::ProjectivePoint ProjectivePoint
ProjectivePoint mul2_vartime(const Scalar &s1, const Scalar &s2) const
static constexpr size_t TableSize
WindowedBoothMulTable(const AffinePoint &p)
C::ProjectivePoint ProjectivePoint
static constexpr size_t WindowBits
BlindedScalarBits< C, WindowBits+1 > BlindedScalar
static constexpr size_t InitialShift
ProjectivePoint mul(const Scalar &s, RandomNumberGenerator &rng) const
static constexpr size_t FullWindows
static constexpr size_t compute_full_windows(size_t sb, size_t wb)
static constexpr size_t TableBits
static constexpr size_t compute_initial_shift(size_t sb, size_t wb)
ProjectivePoint mul2(const Scalar &s1, const Scalar &s2, RandomNumberGenerator &rng) const
C::ProjectivePoint ProjectivePoint
WindowedMul2Table(const AffinePoint &p, const AffinePoint &q)
C::ProjectivePoint ProjectivePoint
static constexpr size_t Windows
WindowedMulTable(const AffinePoint &p)
BlindedScalarBits< C, WindowBits > BlindedScalar
static constexpr size_t TableSize
C::AffinePoint AffinePoint
ProjectivePoint mul(const Scalar &s, RandomNumberGenerator &rng) const
static constexpr size_t WindowBits
constexpr void poison_all(const Ts &... ts)
Definition ct_utils.h:199
constexpr void unpoison_all(const Ts &... ts)
Definition ct_utils.h:205
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:826
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:65
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
Definition ct_utils.h:813
constexpr void poison(const T *p, size_t n)
Definition ct_utils.h:54
C::ProjectivePoint varpoint_exec(const AffinePointTable< C > &table, const BlindedScalar &scalar, RandomNumberGenerator &rng)
constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:96
auto map_to_curve_sswu(const typename C::FieldElement &u) -> typename C::AffinePoint
consteval std::array< W, N > shanks_tonelli_c3(const std::array< W, N > &c2)
auto to_affine_batch(std::span< const typename C::ProjectivePoint > projective)
consteval std::array< W, N > p_plus_1_over_4(const std::array< W, N > &p)
constexpr auto bytes_to_words(std::span< const uint8_t, L > bytes)
constexpr W shift_left(std::array< W, N > &x)
Definition mp_core.h:612
constexpr auto word_sub(W x, W y, W *carry) -> W
Definition mp_asmi.h:280
consteval std::array< W, N > p_minus_1_over_2(const std::array< W, N > &p)
constexpr CT::Option< typename C::FieldElement > sqrt_field_element(const typename C::FieldElement &fe)
constexpr auto word_add(W x, W y, W *carry) -> W
Definition mp_asmi.h:191
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:145
constexpr ProjectivePoint dbl_n_generic(const ProjectivePoint &pt, const FieldElement &A, size_t n)
constexpr void comba_sqr(W z[2 *N], const W x[N])
Definition mp_core.h:735
consteval auto shanks_tonelli_c4(const std::array< W, N > &p_minus_1_over_2) -> Z
constexpr size_t read_window_bits(std::span< const W, N > words, size_t offset)
Definition mp_core.h:952
constexpr ProjectivePoint dbl_a_minus_3(const ProjectivePoint &pt)
constexpr void comba_mul(W z[2 *N], const W x[N], const W y[N])
Definition mp_core.h:699
const auto & SSWU_C1()
C::ProjectivePoint mul2_exec(const AffinePointTable< C > &table, const BlindedScalar &x, const BlindedScalar &y, RandomNumberGenerator &rng)
consteval std::array< W, N > p_div_2_plus_1(const std::array< W, N > &p)
constexpr auto monty_redc(const std::array< W, 2 *N > &z, const std::array< W, N > &p, W p_dash) -> std::array< W, N >
constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:194
constexpr auto monty_inverse(W a) -> W
Definition mp_core.h:585
constexpr ProjectivePoint dbl_n_a_zero(const ProjectivePoint &pt, size_t n)
constexpr ProjectivePoint dbl_a_zero(const ProjectivePoint &pt)
C::ProjectivePoint basemul_exec(std::span< const typename C::AffinePoint > table, const BlindedScalar &scalar, RandomNumberGenerator &rng)
std::vector< typename C::ProjectivePoint > mul2_setup(const typename C::AffinePoint &p, const typename C::AffinePoint &q)
auto hash_to_curve_sswu(const ExpandMsg &expand_message) -> std::conditional_t< RO, typename C::ProjectivePoint, typename C::AffinePoint >
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:24
consteval std::array< W, N > mul_mod(const std::array< W, N > &x, const std::array< W, N > &y, const std::array< W, N > &p)
constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:428
constexpr ProjectivePoint point_add_mixed(const ProjectivePoint &a, const AffinePoint &b, const FieldElement &one)
constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size)
Definition mp_core.h:47
constexpr ProjectivePoint point_add_or_sub_mixed(const ProjectivePoint &a, const AffinePoint &b, CT::Choice sub, const FieldElement &one)
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
Definition mp_core.h:227
constexpr auto invert_field_element(const typename C::FieldElement &fe)
void carry(int64_t &h0, int64_t &h1)
BOTAN_FORCE_INLINE constexpr T choose(T mask, T a, T b)
Definition bit_ops.h:196
constexpr ProjectivePoint dbl_n_a_minus_3(const ProjectivePoint &pt, size_t n)
AffinePointTable< C > varpoint_setup(const typename C::AffinePoint &p)
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:495
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 >
Definition mp_core.h:475
constexpr auto hex_to_words(const char(&s)[N])
Definition mp_core.h:641
constexpr ProjectivePoint dbl_generic(const ProjectivePoint &pt, const FieldElement &A)
consteval std::array< W, N > p_minus(const std::array< W, N > &p)
std::vector< typename C::AffinePoint > basemul_setup(const typename C::AffinePoint &p, size_t max_scalar_bits)
constexpr ProjectivePoint point_add(const ProjectivePoint &a, const ProjectivePoint &b)
std::conditional_t< HasNative64BitRegisters, std::uint64_t, uint32_t > word
Definition types.h:119
consteval size_t count_bits(const std::array< W, N > &p)
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto monty_redc_pdash1(const std::array< W, 2 *N > &z, const std::array< W, N > &p) -> std::array< W, N >
consteval std::array< W, N > montygomery_r(const std::array< W, N > &p)
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:626
consteval std::pair< size_t, std::array< W, N > > shanks_tonelli_c1c2(const std::array< W, N > &p)
const auto & SSWU_C2()
static constexpr auto P
static constexpr size_t N