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