Botan 3.6.1
Crypto and TLS for C&
pcurves_impl.h
Go to the documentation of this file.
1/*
2* (C) 2024 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_util.h>
14#include <botan/internal/stl_util.h>
15#include <vector>
16
17#if defined(BOTAN_HAS_XMD)
18 #include <botan/internal/xmd.h>
19#endif
20
21namespace Botan {
22
23namespace {
24
25template <typename Params>
26class MontgomeryRep final {
27 public:
28 using Self = MontgomeryRep<Params>;
29
30 static constexpr auto P = Params::P;
31 static constexpr size_t N = Params::N;
32 typedef typename Params::W W;
33
34 static_assert(N > 0 && (Params::P[0] & 1) == 1, "Invalid Montgomery modulus");
35
36 static constexpr auto P_dash = monty_inverse(P[0]);
37
38 static constexpr auto R1 = montygomery_r(P);
39 static constexpr auto R2 = mul_mod(R1, R1, P);
40 static constexpr auto R3 = mul_mod(R1, R2, P);
41
42 constexpr static std::array<W, N> one() { return R1; }
43
44 constexpr static std::array<W, N> redc(const std::array<W, 2 * N>& z) {
45 if constexpr(P_dash == 1) {
46 return monty_redc_pdash1(z, P);
47 } else {
48 return monty_redc(z, P, P_dash);
49 }
50 }
51
52 constexpr static std::array<W, N> to_rep(const std::array<W, N>& x) {
53 std::array<W, 2 * N> z;
54 comba_mul<N>(z.data(), x.data(), R2.data());
55 return Self::redc(z);
56 }
57
58 constexpr static std::array<W, N> wide_to_rep(const std::array<W, 2 * N>& x) {
59 auto redc_x = Self::redc(x);
60 std::array<W, 2 * N> z;
61 comba_mul<N>(z.data(), redc_x.data(), R3.data());
62 return Self::redc(z);
63 }
64
65 constexpr static std::array<W, N> from_rep(const std::array<W, N>& z) {
66 std::array<W, 2 * N> ze = {};
67 copy_mem(std::span{ze}.template first<N>(), z);
68 return Self::redc(ze);
69 }
70};
71
72template <typename Rep>
73class IntMod final {
74 private:
75 static constexpr auto P = Rep::P;
76 static constexpr size_t N = Rep::N;
77 typedef typename Rep::W W;
78
79 static constexpr auto P_MINUS_2 = p_minus<2>(P);
80
81 public:
82 static constexpr size_t BITS = count_bits(P);
83 static constexpr size_t BYTES = (BITS + 7) / 8;
84
85 static constexpr auto P_MOD_4 = P[0] % 4;
86
87 using Self = IntMod<Rep>;
88
89 // Default value is zero
90 constexpr IntMod() : m_val({}) {}
91
92 IntMod(const Self& other) = default;
93 IntMod(Self&& other) = default;
94 IntMod& operator=(const Self& other) = default;
95 IntMod& operator=(Self&& other) = default;
96
97 static constexpr Self zero() { return Self(std::array<W, N>{0}); }
98
99 static constexpr Self one() { return Self(Rep::one()); }
100
101 static constexpr Self from_word(W x) {
102 std::array<W, 1> v{x};
103 return Self::from_words(v);
104 }
105
106 template <size_t L>
107 static constexpr Self from_words(std::array<W, L> w) {
108 if constexpr(L == N) {
109 return Self(Rep::to_rep(w));
110 } else {
111 static_assert(L < N);
112 std::array<W, N> ew = {};
113 copy_mem(std::span{ew}.template first<L>(), w);
114 return Self(Rep::to_rep(ew));
115 }
116 }
117
118 constexpr CT::Choice is_zero() const { return CT::all_zeros(m_val.data(), m_val.size()).as_choice(); }
119
120 constexpr CT::Choice is_nonzero() const { return !is_zero(); }
121
122 constexpr CT::Choice is_one() const { return (*this == Self::one()); }
123
124 constexpr CT::Choice is_even() const {
125 auto v = Rep::from_rep(m_val);
126 return !CT::Choice::from_int(v[0] & 0x01);
127 }
128
129 friend constexpr Self operator+(const Self& a, const Self& b) {
130 std::array<W, N> t;
131 W carry = bigint_add<W, N>(t, a.value(), b.value());
132
133 std::array<W, N> r;
134 bigint_monty_maybe_sub<N>(r.data(), carry, t.data(), P.data());
135 return Self(r);
136 }
137
138 friend constexpr Self operator-(const Self& a, const Self& b) { return a + b.negate(); }
139
140 /// Return (*this) divided by 2
141 Self div2() const {
142 // The inverse of 2 modulo P is (P/2)+1; this avoids a constexpr time
143 // general inversion, which some compilers can't handle
144 constexpr auto INV_2 = p_div_2_plus_1(Rep::P);
145
146 // We could multiply by INV_2 but there is a better way ...
147
148 std::array<W, N> t = value();
149 W borrow = shift_right<1>(t);
150
151 // If value was odd, add (P/2)+1
152 bigint_cnd_add(borrow, t.data(), N, INV_2.data(), N);
153
154 return Self(t);
155 }
156
157 /// Return (*this) multiplied by 2
158 Self mul2() const {
159 std::array<W, N> t = value();
160 W carry = shift_left<1>(t);
161
162 std::array<W, N> r;
163 bigint_monty_maybe_sub<N>(r.data(), carry, t.data(), P.data());
164 return Self(r);
165 }
166
167 /// Return (*this) multiplied by 3
168 constexpr Self mul3() const { return mul2() + (*this); }
169
170 /// Return (*this) multiplied by 4
171 constexpr Self mul4() const { return mul2().mul2(); }
172
173 /// Return (*this) multiplied by 8
174 constexpr Self mul8() const { return mul2().mul2().mul2(); }
175
176 friend constexpr Self operator*(const Self& a, const Self& b) {
177 std::array<W, 2 * N> z;
178 comba_mul<N>(z.data(), a.data(), b.data());
179 return Self(Rep::redc(z));
180 }
181
182 constexpr Self& operator*=(const Self& other) {
183 std::array<W, 2 * N> z;
184 comba_mul<N>(z.data(), data(), other.data());
185 m_val = Rep::redc(z);
186 return (*this);
187 }
188
189 // if cond is true, sets x to nx
190 static constexpr void conditional_assign(Self& x, CT::Choice cond, const Self& nx) {
191 const W mask = CT::Mask<W>::from_choice(cond).value();
192
193 for(size_t i = 0; i != N; ++i) {
194 x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
195 }
196 }
197
198 // if cond is true, sets x to nx, y to ny
199 static constexpr void conditional_assign(Self& x, Self& y, CT::Choice cond, const Self& nx, const Self& ny) {
200 const W mask = CT::Mask<W>::from_choice(cond).value();
201
202 for(size_t i = 0; i != N; ++i) {
203 x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
204 y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
205 }
206 }
207
208 // if cond is true, sets x to nx, y to ny, z to nz
209 static constexpr void conditional_assign(
210 Self& x, Self& y, Self& z, CT::Choice cond, const Self& nx, const Self& ny, const Self& nz) {
211 const W mask = CT::Mask<W>::from_choice(cond).value();
212
213 for(size_t i = 0; i != N; ++i) {
214 x.m_val[i] = choose(mask, nx.m_val[i], x.m_val[i]);
215 y.m_val[i] = choose(mask, ny.m_val[i], y.m_val[i]);
216 z.m_val[i] = choose(mask, nz.m_val[i], z.m_val[i]);
217 }
218 }
219
220 constexpr Self square() const {
221 std::array<W, 2 * N> z;
222 comba_sqr<N>(z.data(), this->data());
223 return Self(Rep::redc(z));
224 }
225
226 constexpr void square_n(size_t n) {
227 std::array<W, 2 * N> z;
228 for(size_t i = 0; i != n; ++i) {
229 comba_sqr<N>(z.data(), this->data());
230 m_val = Rep::redc(z);
231 }
232 }
233
234 // Negation modulo p
235 constexpr Self negate() const {
236 auto x_is_zero = CT::all_zeros(this->data(), N);
237
238 std::array<W, N> r;
239 bigint_sub3(r.data(), P.data(), N, this->data(), N);
240 x_is_zero.if_set_zero_out(r.data(), N);
241 return Self(r);
242 }
243
244 constexpr Self pow_vartime(const std::array<W, N>& exp) const {
245 constexpr size_t WindowBits = (Self::BITS <= 256) ? 4 : 5;
246 constexpr size_t WindowElements = (1 << WindowBits) - 1;
247
248 constexpr size_t Windows = (Self::BITS + WindowBits - 1) / WindowBits;
249
250 std::array<Self, WindowElements> tbl;
251
252 tbl[0] = (*this);
253
254 for(size_t i = 1; i != WindowElements; ++i) {
255 if(i % 2 == 1) {
256 tbl[i] = tbl[i / 2].square();
257 } else {
258 tbl[i] = tbl[i - 1] * tbl[0];
259 }
260 }
261
262 auto r = Self::one();
263
264 const size_t w0 = read_window_bits<WindowBits>(std::span{exp}, (Windows - 1) * WindowBits);
265
266 if(w0 > 0) {
267 r = tbl[w0 - 1];
268 }
269
270 for(size_t i = 1; i != Windows; ++i) {
271 r.square_n(WindowBits);
272
273 const size_t w = read_window_bits<WindowBits>(std::span{exp}, (Windows - i - 1) * WindowBits);
274
275 if(w > 0) {
276 r *= tbl[w - 1];
277 }
278 }
279
280 return r;
281 }
282
283 /**
284 * Returns the modular inverse, or 0 if no modular inverse exists.
285 *
286 * If the modulus is prime the only value that has no modular inverse is 0.
287 *
288 * This uses Fermat's little theorem, and so assumes that p is prime
289 */
290 constexpr Self invert() const { return pow_vartime(Self::P_MINUS_2); }
291
292 /**
293 * Return the modular square root if it exists
294 */
295 constexpr std::pair<Self, CT::Choice> sqrt() const {
296 if constexpr(Self::P_MOD_4 == 3) {
297 constexpr auto P_PLUS_1_OVER_4 = p_plus_1_over_4(P);
298 auto z = pow_vartime(P_PLUS_1_OVER_4);
299 const CT::Choice correct = (z.square() == *this);
300 Self::conditional_assign(z, !correct, Self::zero());
301 return {z, correct};
302 } else {
303 // Shanks-Tonelli, following I.4 in RFC 9380
304
305 /*
306 Constants:
307 1. c1, the largest integer such that 2^c1 divides q - 1.
308 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
309 3. c3 = (c2 - 1) / 2 # Integer arithmetic
310 4. c4, a non-square value in F
311 5. c5 = c4^c2 in F
312 */
313 constexpr auto C1_C2 = shanks_tonelli_c1c2(Self::P);
314 constexpr std::array<W, N> C3 = shanks_tonelli_c3(C1_C2.second);
315 constexpr std::array<W, N> P_MINUS_1_OVER_2 = p_minus_1_over_2(Self::P);
316 constexpr Self C4 = shanks_tonelli_c4<Self>(P_MINUS_1_OVER_2);
317 constexpr Self C5 = C4.pow_vartime(C1_C2.second);
318
319 const Self& x = (*this);
320
321 auto z = x.pow_vartime(C3);
322 auto t = z.square();
323 t *= x;
324 z *= x;
325 auto b = t;
326 auto c = C5;
327
328 for(size_t i = C1_C2.first; i >= 2; i--) {
329 b.square_n(i - 2);
330 const auto e = b.is_one();
331 Self::conditional_assign(z, !e, z * c);
332 c.square_n(1);
333 Self::conditional_assign(t, !e, t * c);
334 b = t;
335 }
336
337 const CT::Choice correct = (z.square() == *this);
338 Self::conditional_assign(z, !correct, Self::zero());
339 return {z, correct};
340 }
341 }
342
343 constexpr CT::Choice operator==(const Self& other) const {
344 return CT::is_equal(this->data(), other.data(), N).as_choice();
345 }
346
347 constexpr CT::Choice operator!=(const Self& other) const {
348 return CT::is_not_equal(this->data(), other.data(), N).as_choice();
349 }
350
351 constexpr std::array<W, Self::N> to_words() const { return Rep::from_rep(m_val); }
352
353 constexpr void serialize_to(std::span<uint8_t, Self::BYTES> bytes) const {
354 auto v = Rep::from_rep(m_val);
355 std::reverse(v.begin(), v.end());
356
357 if constexpr(Self::BYTES == N * WordInfo<W>::bytes) {
358 store_be(bytes, v);
359 } else {
360 // Remove leading zero bytes
361 const auto padded_bytes = store_be(v);
362 constexpr size_t extra = N * WordInfo<W>::bytes - Self::BYTES;
363 copy_mem(bytes, std::span{padded_bytes}.template subspan<extra, Self::BYTES>());
364 }
365 }
366
367 template <size_t L>
368 std::array<W, L> stash_value() const {
369 static_assert(L >= N);
370 std::array<W, L> stash = {};
371 for(size_t i = 0; i != N; ++i) {
372 stash[i] = m_val[i];
373 }
374 return stash;
375 }
376
377 template <size_t L>
378 static Self from_stash(const std::array<W, L>& stash) {
379 static_assert(L >= N);
380 std::array<W, N> val = {};
381 for(size_t i = 0; i != N; ++i) {
382 val[i] = stash[i];
383 }
384 return Self(val);
385 }
386
387 // Returns nullopt if the input is an encoding greater than or equal P
388 static std::optional<Self> deserialize(std::span<const uint8_t> bytes) {
389 // We could allow either short inputs or longer zero padded
390 // inputs here, however it seems best to avoid non-canonical
391 // representations unless required
392 if(bytes.size() != Self::BYTES) {
393 return {};
394 }
395
396 const auto words = bytes_to_words<W, N, BYTES>(bytes.first<Self::BYTES>());
397
398 if(!bigint_ct_is_lt(words.data(), N, P.data(), N).as_bool()) {
399 return {};
400 }
401
402 return Self::from_words(words);
403 }
404
405 // Reduces large input modulo the order
406 template <size_t L>
407 static constexpr Self from_wide_bytes(std::span<const uint8_t, L> bytes) {
408 static_assert(8 * L <= 2 * Self::BITS);
409 std::array<uint8_t, 2 * BYTES> padded_bytes = {};
410 copy_mem(std::span{padded_bytes}.template last<L>(), bytes);
411 return Self(Rep::wide_to_rep(bytes_to_words<W, 2 * N, 2 * BYTES>(std::span{padded_bytes})));
412 }
413
414 // Reduces large input modulo the order
415 static constexpr std::optional<Self> from_wide_bytes_varlen(std::span<const uint8_t> bytes) {
416 if(8 * bytes.size() > 2 * Self::BITS) {
417 return {};
418 }
419 std::array<uint8_t, 2 * BYTES> padded_bytes = {};
420 copy_mem(std::span{padded_bytes}.last(bytes.size()), bytes);
421 return Self(Rep::wide_to_rep(bytes_to_words<W, 2 * N, 2 * BYTES>(std::span{padded_bytes})));
422 }
423
424 static Self random(RandomNumberGenerator& rng) {
425 constexpr size_t MAX_ATTEMPTS = 1000;
426
427 std::array<uint8_t, Self::BYTES> buf;
428
429 for(size_t i = 0; i != MAX_ATTEMPTS; ++i) {
430 rng.randomize(buf);
431
432 // Zero off high bits that if set would certainly cause us
433 // to be out of range
434 if constexpr(Self::BITS % 8 != 0) {
435 constexpr uint8_t mask = 0xFF >> (8 - (Self::BITS % 8));
436 buf[0] &= mask;
437 }
438
439 if(auto s = Self::deserialize(buf)) {
440 if(s.value().is_nonzero().as_bool()) {
441 return s.value();
442 }
443 }
444 }
445
446 throw Internal_Error("Failed to generate random Scalar within bounded number of attempts");
447 }
448
449 static consteval Self constant(int8_t x) {
450 std::array<W, 1> v;
451 v[0] = (x >= 0) ? x : -x;
452 auto s = Self::from_words(v);
453 return (x >= 0) ? s : s.negate();
454 }
455
456 constexpr void _const_time_poison() const { CT::poison(m_val); }
457
458 constexpr void _const_time_unpoison() const { CT::unpoison(m_val); }
459
460 private:
461 constexpr const std::array<W, N>& value() const { return m_val; }
462
463 constexpr const W* data() const { return m_val.data(); }
464
465 explicit constexpr IntMod(std::array<W, N> v) : m_val(v) {}
466
467 std::array<W, N> m_val;
468};
469
470template <typename FieldElement, typename Params>
471class AffineCurvePoint {
472 public:
473 // We can't pass a FieldElement directly because FieldElement is
474 // not "structural" due to having private members, so instead
475 // recreate it here from the words.
476 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
477 static constexpr FieldElement B = FieldElement::from_words(Params::BW);
478
479 static constexpr size_t BYTES = 1 + 2 * FieldElement::BYTES;
480 static constexpr size_t COMPRESSED_BYTES = 1 + FieldElement::BYTES;
481
482 using Self = AffineCurvePoint<FieldElement, Params>;
483
484 constexpr AffineCurvePoint(const FieldElement& x, const FieldElement& y) : m_x(x), m_y(y) {}
485
486 constexpr AffineCurvePoint() : m_x(FieldElement::zero()), m_y(FieldElement::zero()) {}
487
488 static constexpr Self identity() { return Self(FieldElement::zero(), FieldElement::zero()); }
489
490 constexpr CT::Choice is_identity() const { return x().is_zero() && y().is_zero(); }
491
492 AffineCurvePoint(const Self& other) = default;
493 AffineCurvePoint(Self&& other) = default;
494 AffineCurvePoint& operator=(const Self& other) = default;
495 AffineCurvePoint& operator=(Self&& other) = default;
496
497 constexpr Self negate() const { return Self(x(), y().negate()); }
498
499 constexpr void serialize_to(std::span<uint8_t, Self::BYTES> bytes) const {
500 BOTAN_STATE_CHECK(this->is_identity().as_bool() == false);
501 BufferStuffer pack(bytes);
502 pack.append(0x04);
503 x().serialize_to(pack.next<FieldElement::BYTES>());
504 y().serialize_to(pack.next<FieldElement::BYTES>());
505 BOTAN_DEBUG_ASSERT(pack.full());
506 }
507
508 constexpr void serialize_compressed_to(std::span<uint8_t, Self::COMPRESSED_BYTES> bytes) const {
509 BOTAN_STATE_CHECK(this->is_identity().as_bool() == false);
510 const uint8_t hdr = CT::Mask<uint8_t>::from_choice(y().is_even()).select(0x02, 0x03);
511
512 BufferStuffer pack(bytes);
513 pack.append(hdr);
514 x().serialize_to(pack.next<FieldElement::BYTES>());
515 BOTAN_DEBUG_ASSERT(pack.full());
516 }
517
518 constexpr void serialize_x_to(std::span<uint8_t, FieldElement::BYTES> bytes) const {
519 BOTAN_STATE_CHECK(this->is_identity().as_bool() == false);
520 x().serialize_to(bytes);
521 }
522
523 /**
524 * If idx is zero then return the identity element. Otherwise return pts[idx - 1]
525 *
526 * Returns the identity element also if idx is out of range
527 */
528 static constexpr auto ct_select(std::span<const Self> pts, size_t idx) {
529 auto result = Self::identity();
530
531 // Intentionally wrapping; set to maximum size_t if idx == 0
532 const size_t idx1 = static_cast<size_t>(idx - 1);
533 for(size_t i = 0; i != pts.size(); ++i) {
534 const auto found = CT::Mask<size_t>::is_equal(idx1, i).as_choice();
535 result.conditional_assign(found, pts[i]);
536 }
537
538 return result;
539 }
540
541 static constexpr FieldElement x3_ax_b(const FieldElement& x) { return (x.square() + Self::A) * x + Self::B; }
542
543 static std::optional<Self> deserialize(std::span<const uint8_t> bytes) {
544 if(bytes.size() == Self::BYTES) {
545 if(bytes[0] == 0x04) {
546 auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES));
547 auto y = FieldElement::deserialize(bytes.subspan(1 + FieldElement::BYTES, FieldElement::BYTES));
548
549 if(x && y) {
550 const auto lhs = (*y).square();
551 const auto rhs = Self::x3_ax_b(*x);
552 if((lhs == rhs).as_bool()) {
553 return Self(*x, *y);
554 }
555 }
556 } else if(bytes[0] == 0x06 || bytes[0] == 0x07) {
557 // Deprecated "hybrid" encoding
558 const CT::Choice y_is_even = CT::Mask<uint8_t>::is_equal(bytes[0], 0x06).as_choice();
559 auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES));
560 auto y = FieldElement::deserialize(bytes.subspan(1 + FieldElement::BYTES, FieldElement::BYTES));
561
562 if(x && y && (y_is_even == y->is_even()).as_bool()) {
563 const auto lhs = (*y).square();
564 const auto rhs = Self::x3_ax_b(*x);
565 if((lhs == rhs).as_bool()) {
566 return Self(*x, *y);
567 }
568 }
569 }
570 } else if(bytes.size() == Self::COMPRESSED_BYTES) {
571 if(bytes[0] == 0x02 || bytes[0] == 0x03) {
572 const CT::Choice y_is_even = CT::Mask<uint8_t>::is_equal(bytes[0], 0x02).as_choice();
573
574 if(auto x = FieldElement::deserialize(bytes.subspan(1, FieldElement::BYTES))) {
575 auto [y, is_square] = x3_ax_b(*x).sqrt();
576
577 if(is_square.as_bool()) {
578 const auto flip_y = y_is_even != y.is_even();
579 FieldElement::conditional_assign(y, flip_y, y.negate());
580 return Self(*x, y);
581 }
582 }
583 }
584 } else if(bytes.size() == 1 && bytes[0] == 0x00) {
585 // See SEC1 section 2.3.4
586 return Self::identity();
587 }
588
589 return {};
590 }
591
592 constexpr const FieldElement& x() const { return m_x; }
593
594 constexpr const FieldElement& y() const { return m_y; }
595
596 constexpr void conditional_assign(CT::Choice cond, const Self& pt) {
597 FieldElement::conditional_assign(m_x, m_y, cond, pt.x(), pt.y());
598 }
599
600 constexpr void _const_time_poison() const { CT::poison_all(m_x, m_y); }
601
602 constexpr void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y); }
603
604 private:
605 FieldElement m_x;
606 FieldElement m_y;
607};
608
609template <typename FieldElement, typename Params>
610class ProjectiveCurvePoint {
611 public:
612 // We can't pass a FieldElement directly because FieldElement is
613 // not "structural" due to having private members, so instead
614 // recreate it here from the words.
615 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
616
617 static constexpr bool A_is_zero = A.is_zero().as_bool();
618 static constexpr bool A_is_minus_3 = (A == FieldElement::constant(-3)).as_bool();
619
620 using Self = ProjectiveCurvePoint<FieldElement, Params>;
621 using AffinePoint = AffineCurvePoint<FieldElement, Params>;
622
623 static constexpr Self from_affine(const AffinePoint& pt) {
624 if(pt.is_identity().as_bool()) {
625 return Self::identity();
626 } else {
627 return ProjectiveCurvePoint(pt.x(), pt.y());
628 }
629 }
630
631 static constexpr Self identity() { return Self(FieldElement::zero(), FieldElement::one(), FieldElement::zero()); }
632
633 constexpr ProjectiveCurvePoint() :
634 m_x(FieldElement::zero()), m_y(FieldElement::one()), m_z(FieldElement::zero()) {}
635
636 constexpr ProjectiveCurvePoint(const FieldElement& x, const FieldElement& y) :
637 m_x(x), m_y(y), m_z(FieldElement::one()) {}
638
639 constexpr ProjectiveCurvePoint(const FieldElement& x, const FieldElement& y, const FieldElement& z) :
640 m_x(x), m_y(y), m_z(z) {}
641
642 ProjectiveCurvePoint(const Self& other) = default;
643 ProjectiveCurvePoint(Self&& other) = default;
644 ProjectiveCurvePoint& operator=(const Self& other) = default;
645 ProjectiveCurvePoint& operator=(Self&& other) = default;
646
647 friend constexpr Self operator+(const Self& a, const Self& b) { return Self::add(a, b); }
648
649 friend constexpr Self operator+(const Self& a, const AffinePoint& b) { return Self::add_mixed(a, b); }
650
651 friend constexpr Self operator+(const AffinePoint& a, const Self& b) { return Self::add_mixed(b, a); }
652
653 constexpr Self& operator+=(const Self& other) {
654 (*this) = (*this) + other;
655 return (*this);
656 }
657
658 constexpr Self& operator+=(const AffinePoint& other) {
659 (*this) = (*this) + other;
660 return (*this);
661 }
662
663 friend constexpr Self operator-(const Self& a, const Self& b) { return a + b.negate(); }
664
665 constexpr CT::Choice is_identity() const { return z().is_zero(); }
666
667 constexpr void conditional_assign(CT::Choice cond, const Self& pt) {
668 FieldElement::conditional_assign(m_x, m_y, m_z, cond, pt.x(), pt.y(), pt.z());
669 }
670
671 constexpr static Self add_mixed(const Self& a, const AffinePoint& b) {
672 const auto a_is_identity = a.is_identity();
673 const auto b_is_identity = b.is_identity();
674 if((a_is_identity && b_is_identity).as_bool()) {
675 return Self::identity();
676 }
677
678 /*
679 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
680
681 Cost: 8M + 3S + 6add + 1*2
682 */
683
684 const auto Z1Z1 = a.z().square();
685 const auto U2 = b.x() * Z1Z1;
686 const auto S2 = b.y() * a.z() * Z1Z1;
687 const auto H = U2 - a.x();
688 const auto r = S2 - a.y();
689
690 // If r == H == 0 then we are in the doubling case
691 // For a == -b we compute the correct result because
692 // H will be zero, leading to Z3 being zero also
693 if((r.is_zero() && H.is_zero()).as_bool()) {
694 return a.dbl();
695 }
696
697 const auto HH = H.square();
698 const auto HHH = H * HH;
699 const auto V = a.x() * HH;
700 const auto t2 = r.square();
701 const auto t3 = V + V;
702 const auto t4 = t2 - HHH;
703 auto X3 = t4 - t3;
704 const auto t5 = V - X3;
705 const auto t6 = a.y() * HHH;
706 const auto t7 = r * t5;
707 auto Y3 = t7 - t6;
708 auto Z3 = a.z() * H;
709
710 // if a is identity then return b
711 FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), b.y(), FieldElement::one());
712
713 // if b is identity then return a
714 FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z());
715
716 return Self(X3, Y3, Z3);
717 }
718
719 constexpr static Self add(const Self& a, const Self& b) {
720 const auto a_is_identity = a.is_identity();
721 const auto b_is_identity = b.is_identity();
722
723 if((a_is_identity && b_is_identity).as_bool()) {
724 return Self::identity();
725 }
726
727 /*
728 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
729
730 Cost: 12M + 4S + 6add + 1*2
731 */
732
733 const auto Z1Z1 = a.z().square();
734 const auto Z2Z2 = b.z().square();
735 const auto U1 = a.x() * Z2Z2;
736 const auto U2 = b.x() * Z1Z1;
737 const auto S1 = a.y() * b.z() * Z2Z2;
738 const auto S2 = b.y() * a.z() * Z1Z1;
739 const auto H = U2 - U1;
740 const auto r = S2 - S1;
741
742 // If a == -b then H == 0 && r != 0, in which case
743 // at the end we'll set z = a.z * b.z * H = 0, resulting
744 // in the correct output (point at infinity)
745 if((r.is_zero() && H.is_zero()).as_bool()) {
746 return a.dbl();
747 }
748
749 const auto HH = H.square();
750 const auto HHH = H * HH;
751 const auto V = U1 * HH;
752 const auto t2 = r.square();
753 const auto t3 = V + V;
754 const auto t4 = t2 - HHH;
755 auto X3 = t4 - t3;
756 const auto t5 = V - X3;
757 const auto t6 = S1 * HHH;
758 const auto t7 = r * t5;
759 auto Y3 = t7 - t6;
760 const auto t8 = b.z() * H;
761 auto Z3 = a.z() * t8;
762
763 // if a is identity then return b
764 FieldElement::conditional_assign(X3, Y3, Z3, a_is_identity, b.x(), b.y(), b.z());
765
766 // if b is identity then return a
767 FieldElement::conditional_assign(X3, Y3, Z3, b_is_identity, a.x(), a.y(), a.z());
768
769 return Self(X3, Y3, Z3);
770 }
771
772 constexpr Self dbl_n(size_t n) const {
773 /*
774 Repeated doubling using an adaptation of Algorithm 3.23 in
775 "Guide To Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone)
776
777 Curiously the book gives the algorithm only for A == -3, but
778 the largest gains come from applying it to the generic A case,
779 where it saves 2 squarings per iteration.
780
781 For A == 0
782 Pay 1*2 + 1half to save n*(1*4 + 1*8)
783
784 For A == -3:
785 Pay 2S + 1*2 + 1half to save n*(1A + 1*4 + 1*8) + 1M
786
787 For generic A:
788 Pay 2S + 1*2 + 1half to save n*(2S + 1*4 + 1*8)
789 */
790
791 if constexpr(Self::A_is_zero) {
792 auto nx = x();
793 auto ny = y().mul2();
794 auto nz = z();
795
796 while(n > 0) {
797 const auto ny2 = ny.square();
798 const auto ny4 = ny2.square();
799 const auto t1 = nx.square().mul3();
800 const auto t2 = nx * ny2;
801 nx = t1.square() - t2.mul2();
802 nz *= ny;
803 ny = t1 * (t2 - nx).mul2() - ny4;
804 n--;
805 }
806 return Self(nx, ny.div2(), nz);
807 } else {
808 auto nx = x();
809 auto ny = y().mul2();
810 auto nz = z();
811 auto w = nz.square().square();
812
813 if constexpr(!Self::A_is_minus_3) {
814 w *= A;
815 }
816
817 while(n > 0) {
818 const auto ny2 = ny.square();
819 const auto ny4 = ny2.square();
820 FieldElement t1;
821 if constexpr(Self::A_is_minus_3) {
822 t1 = (nx.square() - w).mul3();
823 } else {
824 t1 = nx.square().mul3() + w;
825 }
826 const auto t2 = nx * ny2;
827 nx = t1.square() - t2.mul2();
828 nz *= ny;
829 ny = t1 * (t2 - nx).mul2() - ny4;
830 n--;
831 if(n > 0) {
832 w *= ny4;
833 }
834 }
835 return Self(nx, ny.div2(), nz);
836 }
837 }
838
839 constexpr Self dbl() const {
840 /*
841 Using https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1998-cmo-2
842
843 Cost (generic A): 4M + 6S + 4A + 2*2 + 1*3 + 1*4 + 1*8
844 Cost (A == -3): 4M + 4S + 5A + 2*2 + 1*3 + 1*4 + 1*8
845 Cost (A == 0): 3M + 4S + 3A + 2*2 + 1*3 + 1*4 + 1*8
846 */
847
848 FieldElement m = FieldElement::zero();
849
850 if constexpr(Self::A_is_minus_3) {
851 /*
852 if a == -3 then
853 3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2)
854
855 Cost: 1M + 1S + 2A + 1*3
856 */
857 const auto z2 = z().square();
858 m = (x() - z2).mul3() * (x() + z2);
859 } else if constexpr(Self::A_is_zero) {
860 // If a == 0 then 3*x^2 + a*z^4 == 3*x^2
861 // Cost: 1S + 1*3
862 m = x().square().mul3();
863 } else {
864 // Cost: 1M + 3S + 1A + 1*3
865 const auto z2 = z().square();
866 m = x().square().mul3() + A * z2.square();
867 }
868
869 // Remaining cost: 3M + 3S + 3A + 2*2 + 1*4 + 1*8
870 const auto y2 = y().square();
871 const auto s = x().mul4() * y2;
872 const auto nx = m.square() - s.mul2();
873 const auto ny = m * (s - nx) - y2.square().mul8();
874 const auto nz = y().mul2() * z();
875
876 return Self(nx, ny, nz);
877 }
878
879 constexpr Self negate() const { return Self(x(), y().negate(), z()); }
880
881 void randomize_rep(RandomNumberGenerator& rng) {
882 if(!rng.is_seeded()) {
883 return;
884 }
885
886 auto r = FieldElement::random(rng);
887
888 auto r2 = r.square();
889 auto r3 = r2 * r;
890
891 m_x *= r2;
892 m_y *= r3;
893 m_z *= r;
894 }
895
896 constexpr const FieldElement& x() const { return m_x; }
897
898 constexpr const FieldElement& y() const { return m_y; }
899
900 constexpr const FieldElement& z() const { return m_z; }
901
902 constexpr void _const_time_poison() const { CT::poison_all(m_x, m_y, m_z); }
903
904 constexpr void _const_time_unpoison() const { CT::unpoison_all(m_x, m_y, m_z); }
905
906 private:
907 FieldElement m_x;
908 FieldElement m_y;
909 FieldElement m_z;
910};
911
912template <StringLiteral PS,
913 StringLiteral AS,
914 StringLiteral BS,
915 StringLiteral NS,
916 StringLiteral GXS,
917 StringLiteral GYS,
918 int8_t ZI = 0>
919class EllipticCurveParameters {
920 public:
921 typedef word W;
922
923 static constexpr auto PW = hex_to_words<W>(PS.value);
924 static constexpr auto NW = hex_to_words<W>(NS.value);
925 static constexpr auto AW = hex_to_words<W>(AS.value);
926 static constexpr auto BW = hex_to_words<W>(BS.value);
927 static constexpr auto GXW = hex_to_words<W>(GXS.value);
928 static constexpr auto GYW = hex_to_words<W>(GYS.value);
929
930 static constexpr int8_t Z = ZI;
931};
932
933template <WordType WI, size_t NI, std::array<WI, NI> PI>
934struct IntParams {
935 public:
936 typedef WI W;
937 static constexpr size_t N = NI;
938 static constexpr auto P = PI;
939};
940
941template <typename Params, template <typename FieldParamsT> typename FieldRep = MontgomeryRep>
942class EllipticCurve {
943 public:
944 typedef typename Params::W W;
945
946 static constexpr auto PW = Params::PW;
947 static constexpr auto NW = Params::NW;
948 static constexpr auto AW = Params::AW;
949
950 // Simplifying assumption
951 static_assert(PW.size() == NW.size());
952
953 class ScalarParams final : public IntParams<W, NW.size(), NW> {};
954
955 using Scalar = IntMod<MontgomeryRep<ScalarParams>>;
956
957 class FieldParams final : public IntParams<W, PW.size(), PW> {};
958
959 using FieldElement = IntMod<FieldRep<FieldParams>>;
960
961 using AffinePoint = AffineCurvePoint<FieldElement, Params>;
962 using ProjectivePoint = ProjectiveCurvePoint<FieldElement, Params>;
963
964 static constexpr size_t OrderBits = Scalar::BITS;
965 static constexpr size_t PrimeFieldBits = FieldElement::BITS;
966
967 static constexpr FieldElement A = FieldElement::from_words(Params::AW);
968 static constexpr FieldElement B = FieldElement::from_words(Params::BW);
969
970 static constexpr AffinePoint G =
971 AffinePoint(FieldElement::from_words(Params::GXW), FieldElement::from_words(Params::GYW));
972
973 static constexpr FieldElement SSWU_Z = FieldElement::constant(Params::Z);
974
975 static constexpr bool ValidForSswuHash =
976 (Params::Z != 0 && A.is_nonzero().as_bool() && B.is_nonzero().as_bool() && FieldElement::P_MOD_4 == 3);
977
978 static constexpr bool OrderIsLessThanField = bigint_cmp(NW.data(), NW.size(), PW.data(), PW.size()) == -1;
979};
980
981template <typename C>
982concept curve_supports_fe_invert2 = requires(const typename C::FieldElement& fe) {
983 { C::fe_invert2(fe) } -> std::same_as<typename C::FieldElement>;
984};
985
986/**
987* Blinded Scalar
988*
989* This randomizes the scalar representation by computing s + n*k
990* where n is the group order and k is a random value
991*/
992template <typename C, size_t WindowBits>
993class BlindedScalarBits final {
994 private:
995 typedef typename C::W W;
996
997 static constexpr bool BlindingEnabled = true;
998
999 // For blinding use 1/4 the order, rounded up to the next word
1000 static constexpr size_t BlindingBits =
1001 ((C::OrderBits / 4 + WordInfo<W>::bits - 1) / WordInfo<W>::bits) * WordInfo<W>::bits;
1002
1003 static_assert(BlindingBits % WordInfo<W>::bits == 0);
1004 static_assert(BlindingBits < C::Scalar::BITS);
1005
1006 public:
1007 static constexpr size_t Bits = C::Scalar::BITS + (BlindingEnabled ? BlindingBits : 0);
1008 static constexpr size_t Bytes = (Bits + 7) / 8;
1009
1010 BlindedScalarBits(const typename C::Scalar& scalar, RandomNumberGenerator& rng) {
1011 if constexpr(BlindingEnabled) {
1012 constexpr size_t mask_words = BlindingBits / WordInfo<W>::bits;
1013 constexpr size_t mask_bytes = mask_words * WordInfo<W>::bytes;
1014
1015 constexpr size_t n_words = C::NW.size();
1016
1017 uint8_t maskb[mask_bytes] = {0};
1018 if(rng.is_seeded()) {
1019 rng.randomize(maskb, mask_bytes);
1020 } else {
1021 // If we don't have an RNG we don't have many good options. We
1022 // could just omit the blinding entirely, but this changes the
1023 // size of the blinded scalar, which we're expecting otherwise is
1024 // knowable at compile time. So generate a mask by XORing the
1025 // bytes of the scalar together. At worst, it's equivalent to
1026 // omitting the blinding entirely.
1027
1028 std::array<uint8_t, C::Scalar::BYTES> sbytes;
1029 scalar.serialize_to(sbytes);
1030 for(size_t i = 0; i != sbytes.size(); ++i) {
1031 maskb[i % mask_bytes] ^= sbytes[i];
1032 }
1033 }
1034
1035 W mask[n_words] = {0};
1036 load_le(mask, maskb, mask_words);
1037 mask[mask_words - 1] |= WordInfo<W>::top_bit;
1038 mask[0] |= 1;
1039
1040 W mask_n[2 * n_words] = {0};
1041
1042 const auto sw = scalar.to_words();
1043
1044 // Compute masked scalar s + k*n
1045 comba_mul<n_words>(mask_n, mask, C::NW.data());
1046 bigint_add2_nc(mask_n, 2 * n_words, sw.data(), sw.size());
1047
1048 std::reverse(mask_n, mask_n + 2 * n_words);
1049 m_bytes = store_be<std::vector<uint8_t>>(mask_n);
1050 } else {
1051 static_assert(Bytes == C::Scalar::BYTES);
1052 m_bytes.resize(Bytes);
1053 scalar.serialize_to(std::span{m_bytes}.template first<Bytes>());
1054 }
1055
1056 CT::poison(m_bytes.data(), m_bytes.size());
1057 }
1058
1059 size_t get_window(size_t offset) const {
1060 // Extract a WindowBits sized window out of s, depending on offset.
1061 return read_window_bits<WindowBits>(std::span{m_bytes}, offset);
1062 }
1063
1064 ~BlindedScalarBits() {
1065 secure_scrub_memory(m_bytes.data(), m_bytes.size());
1066 CT::unpoison(m_bytes.data(), m_bytes.size());
1067 }
1068
1069 private:
1070 // TODO this could be a fixed size array
1071 std::vector<uint8_t> m_bytes;
1072};
1073
1074template <typename C, size_t WindowBits>
1075class UnblindedScalarBits final {
1076 public:
1077 static constexpr size_t Bits = C::Scalar::BITS;
1078
1079 UnblindedScalarBits(const typename C::Scalar& scalar) { scalar.serialize_to(std::span{m_bytes}); }
1080
1081 size_t get_window(size_t offset) const {
1082 // Extract a WindowBits sized window out of s, depending on offset.
1083 return read_window_bits<WindowBits>(std::span{m_bytes}, offset);
1084 }
1085
1086 private:
1087 std::array<uint8_t, C::Scalar::BYTES> m_bytes;
1088};
1089
1090template <typename C>
1091inline auto invert_field_element(const typename C::FieldElement& fe) {
1092 if constexpr(curve_supports_fe_invert2<C>) {
1093 return C::fe_invert2(fe) * fe;
1094 } else {
1095 return fe.invert();
1096 }
1097}
1098
1099/// Convert a projective point into affine
1100template <typename C>
1101auto to_affine(const typename C::ProjectivePoint& pt) {
1102 // Not strictly required right? - default should work as long
1103 // as (0,0) is identity and invert returns 0 on 0
1104 if(pt.is_identity().as_bool()) {
1105 return C::AffinePoint::identity();
1106 }
1107
1108 if constexpr(curve_supports_fe_invert2<C>) {
1109 const auto z2_inv = C::fe_invert2(pt.z());
1110 const auto z3_inv = z2_inv.square() * pt.z();
1111 return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv);
1112 } else {
1113 const auto z_inv = invert_field_element<C>(pt.z());
1114 const auto z2_inv = z_inv.square();
1115 const auto z3_inv = z_inv * z2_inv;
1116 return typename C::AffinePoint(pt.x() * z2_inv, pt.y() * z3_inv);
1117 }
1118}
1119
1120/// Convert a projective point into affine and return x coordinate only
1121template <typename C>
1122auto to_affine_x(const typename C::ProjectivePoint& pt) {
1123 if constexpr(curve_supports_fe_invert2<C>) {
1124 return pt.x() * C::fe_invert2(pt.z());
1125 } else {
1126 return to_affine<C>(pt).x();
1127 }
1128}
1129
1130/**
1131* Batch projective->affine conversion
1132*/
1133template <typename C>
1134auto to_affine_batch(std::span<const typename C::ProjectivePoint> projective) {
1135 typedef typename C::AffinePoint AffinePoint;
1136 typedef typename C::FieldElement FieldElement;
1137
1138 const size_t N = projective.size();
1139 std::vector<AffinePoint> affine(N, AffinePoint::identity());
1140
1141 bool any_identity = false;
1142 for(size_t i = 0; i != N; ++i) {
1143 if(projective[i].is_identity().as_bool()) {
1144 any_identity = true;
1145 // If any of the elements are the identity we fall back to
1146 // performing the conversion without a batch
1147 break;
1148 }
1149 }
1150
1151 if(N <= 2 || any_identity) {
1152 // If there are identity elements, using the batch inversion gets
1153 // tricky. It can be done, but this should be a rare situation so
1154 // just punt to the serial conversion if it occurs
1155 for(size_t i = 0; i != N; ++i) {
1156 affine[i] = to_affine<C>(projective[i]);
1157 }
1158 } else {
1159 std::vector<FieldElement> c(N);
1160
1161 /*
1162 Batch projective->affine using Montgomery's trick
1163
1164 See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography"
1165 (Hankerson, Menezes, Vanstone)
1166 */
1167
1168 c[0] = projective[0].z();
1169 for(size_t i = 1; i != N; ++i) {
1170 c[i] = c[i - 1] * projective[i].z();
1171 }
1172
1173 auto s_inv = invert_field_element<C>(c[N - 1]);
1174
1175 for(size_t i = N - 1; i > 0; --i) {
1176 const auto& p = projective[i];
1177
1178 const auto z_inv = s_inv * c[i - 1];
1179 const auto z2_inv = z_inv.square();
1180 const auto z3_inv = z_inv * z2_inv;
1181
1182 s_inv = s_inv * p.z();
1183
1184 affine[i] = AffinePoint(p.x() * z2_inv, p.y() * z3_inv);
1185 }
1186
1187 const auto z2_inv = s_inv.square();
1188 const auto z3_inv = s_inv * z2_inv;
1189 affine[0] = AffinePoint(projective[0].x() * z2_inv, projective[0].y() * z3_inv);
1190 }
1191
1192 return affine;
1193}
1194
1195/**
1196* Base point precomputation table
1197*
1198* This algorithm works by precomputing a set of points such that
1199* the online phase of the point multiplication can be effected by
1200* a sequence of point additions.
1201*
1202* The tables, even for W = 1, are large and costly to precompute, so
1203* this is only used for the base point.
1204*
1205* The online phase of the algorithm uess `ceil(SB/W)` additions,
1206* and no point doublings. The table is of size
1207* `ceil(SB + W - 1)/W * ((1 << W) - 1)`
1208* where SB is the bit length of the (blinded) scalar.
1209*
1210* Each window of the scalar is associated with a window in the table.
1211* The table windows are unique to that offset within the scalar.
1212*
1213* The simplest version to understand is when W = 1. There the table
1214* consists of [P, 2*P, 4*P, ..., 2^N*P] where N is the bit length of
1215* the group order. The online phase consists of conditionally adding
1216* table[i] depending on if bit i of the scalar is set or not.
1217*
1218* When W = 2, the scalar is examined 2 bits at a time, and the table
1219* for a window index `I` is [(2^I)*P, (2^(I+1))*P, (2^I+2^(I+1))*P].
1220*
1221* This extends similarly for larger W
1222*
1223* At a certain point, the side channel silent table lookup becomes the
1224* dominating cost
1225*
1226* For all W, each window in the table has an implicit element of
1227* the identity element which is used if the scalar bits were all zero.
1228* This is omitted to save space; AffinePoint::ct_select is designed
1229* to assist in this by returning the identity element if its index
1230* argument is zero, or otherwise it returns table[idx - 1]
1231*/
1232template <typename C, size_t W>
1233class PrecomputedBaseMulTable final {
1234 public:
1235 typedef typename C::Scalar Scalar;
1236 typedef typename C::AffinePoint AffinePoint;
1237 typedef typename C::ProjectivePoint ProjectivePoint;
1238
1239 static constexpr size_t WindowBits = W;
1240 static_assert(WindowBits >= 1 && WindowBits <= 8);
1241
1242 using BlindedScalar = BlindedScalarBits<C, WindowBits>;
1243
1244 static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits;
1245
1246 static_assert(Windows > 1);
1247
1248 // 2^W elements, less the identity element
1249 static constexpr size_t WindowElements = (1 << WindowBits) - 1;
1250
1251 static constexpr size_t TableSize = Windows * WindowElements;
1252
1253 PrecomputedBaseMulTable(const AffinePoint& p) : m_table{} {
1254 std::vector<ProjectivePoint> table;
1255 table.reserve(TableSize);
1256
1257 auto accum = ProjectivePoint::from_affine(p);
1258
1259 for(size_t i = 0; i != TableSize; i += WindowElements) {
1260 table.push_back(accum);
1261
1262 for(size_t j = 1; j != WindowElements; ++j) {
1263 if(j % 2 == 1) {
1264 table.emplace_back(table[i + j / 2].dbl());
1265 } else {
1266 table.emplace_back(table[i + j - 1] + table[i]);
1267 }
1268 }
1269
1270 accum = table[i + (WindowElements / 2)].dbl();
1271 }
1272
1273 m_table = to_affine_batch<C>(table);
1274 }
1275
1276 ProjectivePoint mul(const Scalar& s, RandomNumberGenerator& rng) const {
1277 const BlindedScalar bits(s, rng);
1278
1279 // TODO: C++23 - use std::mdspan to access m_table
1280 auto table = std::span{m_table};
1281
1282 auto accum = [&]() {
1283 const size_t w_0 = bits.get_window(0);
1284 const auto tbl_0 = table.first(WindowElements);
1285 auto pt = ProjectivePoint::from_affine(AffinePoint::ct_select(tbl_0, w_0));
1286 CT::poison(pt);
1287 pt.randomize_rep(rng);
1288 return pt;
1289 }();
1290
1291 for(size_t i = 1; i != Windows; ++i) {
1292 const size_t w_i = bits.get_window(WindowBits * i);
1293 const auto tbl_i = table.subspan(WindowElements * i, WindowElements);
1294
1295 /*
1296 None of these additions can be doublings, because in each iteration, the
1297 discrete logarithms of the points we're selecting out of the table are
1298 larger than the largest possible dlog of accum.
1299 */
1300 accum += AffinePoint::ct_select(tbl_i, w_i);
1301
1302 if(i <= 3) {
1303 accum.randomize_rep(rng);
1304 }
1305 }
1306
1307 CT::unpoison(accum);
1308 return accum;
1309 }
1310
1311 private:
1312 std::vector<AffinePoint> m_table;
1313};
1314
1315/**
1316* Precomputed point multiplication table
1317*
1318* This is a standard fixed window multiplication using W-bit wide window.
1319*/
1320template <typename C, size_t W>
1321class WindowedMulTable final {
1322 public:
1323 typedef typename C::Scalar Scalar;
1324 typedef typename C::AffinePoint AffinePoint;
1325 typedef typename C::ProjectivePoint ProjectivePoint;
1326
1327 static constexpr size_t WindowBits = W;
1328 static_assert(WindowBits >= 1 && WindowBits <= 8);
1329
1330 using BlindedScalar = BlindedScalarBits<C, WindowBits>;
1331
1332 static constexpr size_t Windows = (BlindedScalar::Bits + WindowBits - 1) / WindowBits;
1333
1334 static_assert(Windows > 1);
1335
1336 // 2^W elements, less the identity element
1337 static constexpr size_t TableSize = (1 << WindowBits) - 1;
1338
1339 WindowedMulTable(const AffinePoint& p) : m_table{} {
1340 std::vector<ProjectivePoint> table;
1341 table.reserve(TableSize);
1342
1343 table.push_back(ProjectivePoint::from_affine(p));
1344 for(size_t i = 1; i != TableSize; ++i) {
1345 if(i % 2 == 1) {
1346 table.push_back(table[i / 2].dbl());
1347 } else {
1348 table.push_back(table[i - 1] + table[0]);
1349 }
1350 }
1351
1352 m_table = to_affine_batch<C>(table);
1353 }
1354
1355 ProjectivePoint mul(const Scalar& s, RandomNumberGenerator& rng) const {
1356 const BlindedScalar bits(s, rng);
1357
1358 auto accum = [&]() {
1359 const size_t w_0 = bits.get_window((Windows - 1) * WindowBits);
1360 // Guaranteed because we set the high bit of the randomizer
1361 BOTAN_DEBUG_ASSERT(w_0 != 0);
1362 auto pt = ProjectivePoint::from_affine(AffinePoint::ct_select(m_table, w_0));
1363 CT::poison(pt);
1364 pt.randomize_rep(rng);
1365 return pt;
1366 }();
1367
1368 for(size_t i = 1; i != Windows; ++i) {
1369 accum = accum.dbl_n(WindowBits);
1370 const size_t w_i = bits.get_window((Windows - i - 1) * WindowBits);
1371
1372 /*
1373 This point addition cannot be a doubling (except once)
1374
1375 Consider the sequence of points that are operated on, and specifically
1376 their discrete logarithms. We start out at the point at infinity
1377 (dlog 0) and then add the initial window which is precisely P*w_0
1378
1379 We then perform WindowBits doublings, so accum's dlog at the point
1380 of the addition in the first iteration of the loop (when i == 1) is
1381 at least 2^W * w_0.
1382
1383 Since we know w_0 > 0, then in every iteration of the loop, accums
1384 dlog will always be greater than the dlog of the table element we
1385 just looked up (something between 0 and 2^W-1), and thus the
1386 addition into accum cannot be a doubling.
1387
1388 However due to blinding this argument fails, since we perform
1389 multiplications using a scalar that is larger than the group
1390 order. In this case it's possible that the dlog of accum becomes
1391 `order + x` (or, effectively, `x`) and `x` is smaller than 2^W.
1392 In this case, a doubling may occur. Future iterations of the loop
1393 cannot be doublings by the same argument above. Since the blinding
1394 factor is always less than the group order (substantially so),
1395 it is not possible for the dlog of accum to overflow a second time.
1396 */
1397 accum += AffinePoint::ct_select(m_table, w_i);
1398
1399 if(i <= 3) {
1400 accum.randomize_rep(rng);
1401 }
1402 }
1403
1404 CT::unpoison(accum);
1405 return accum;
1406 }
1407
1408 private:
1409 std::vector<AffinePoint> m_table;
1410};
1411
1412/**
1413* Effect 2-ary multiplication ie x*G + y*H
1414*
1415* This is done using a windowed variant of what is usually called
1416* Shamir's trick.
1417*
1418* The W = 1 case is simple; we precompute an extra point GH = G + H,
1419* and then examine 1 bit in each of x and y. If one or the other bits
1420* are set then add G or H resp. If both bits are set, add GH.
1421*
1422* The example below is a precomputed table for W=2. The flattened table
1423* begins at (x_i,y_i) = (1,0), i.e. the identity element is omitted.
1424* The indices in each cell refer to the cell's location in m_table.
1425*
1426* x-> 0 1 2 3
1427* 0 |/ (ident) |0 x |1 2x |2 3x |
1428* 1 |3 y |4 x+y |5 2x+y |6 3x+y |
1429* y = 2 |7 2y |8 x+2y |9 2(x+y) |10 3x+2y |
1430* 3 |11 3y |12 x+3y |13 2x+3y |14 3x+3y |
1431*/
1432template <typename C, size_t W>
1433class WindowedMul2Table final {
1434 public:
1435 // We look at W bits of each scalar per iteration
1436 static_assert(W >= 1 && W <= 4);
1437
1438 typedef typename C::Scalar Scalar;
1439 typedef typename C::AffinePoint AffinePoint;
1440 typedef typename C::ProjectivePoint ProjectivePoint;
1441
1442 static constexpr size_t WindowBits = W;
1443
1444 static constexpr size_t Windows = (Scalar::BITS + WindowBits - 1) / WindowBits;
1445
1446 static constexpr size_t WindowSize = (1 << WindowBits);
1447
1448 // 2^(2*W) elements, less the identity element
1449 static constexpr size_t TableSize = (1 << (2 * WindowBits)) - 1;
1450
1451 WindowedMul2Table(const AffinePoint& x, const AffinePoint& y) {
1452 std::vector<ProjectivePoint> table;
1453 table.reserve(TableSize);
1454
1455 for(size_t i = 0; i != TableSize; ++i) {
1456 const size_t t_i = (i + 1);
1457 const size_t x_i = t_i % WindowSize;
1458 const size_t y_i = (t_i >> WindowBits) % WindowSize;
1459
1460 // Returns x_i * x + y_i * y
1461 auto next_tbl_e = [&]() {
1462 if(x_i % 2 == 0 && y_i % 2 == 0) {
1463 // Where possible using doubling (eg indices 1, 7, 9 in
1464 // the table above)
1465 return table[(t_i / 2) - 1].dbl();
1466 } else if(x_i > 0 && y_i > 0) {
1467 // A combination of x and y
1468 return table[x_i - 1] + table[(y_i << WindowBits) - 1];
1469 } else if(x_i > 0 && y_i == 0) {
1470 // A multiple of x without a y component
1471 if(x_i == 1) {
1472 // Just x
1473 return ProjectivePoint::from_affine(x);
1474 } else {
1475 // x * x_{i-1}
1476 return x + table[x_i - 1 - 1];
1477 }
1478 } else if(x_i == 0 && y_i > 0) {
1479 if(y_i == 1) {
1480 // Just y
1481 return ProjectivePoint::from_affine(y);
1482 } else {
1483 // y * y_{i-1}
1484 return y + table[((y_i - 1) << WindowBits) - 1];
1485 }
1486 } else {
1488 }
1489 };
1490
1491 table.emplace_back(next_tbl_e());
1492 }
1493
1494 m_table = to_affine_batch<C>(table);
1495 }
1496
1497 /**
1498 * Variable time 2-ary multiplication
1499 *
1500 * A common use of 2-ary multiplication is when verifying the commitments
1501 * of an elliptic curve signature. Since in this case the inputs are all
1502 * public, there is no problem with variable time computation.
1503 *
1504 * It may be useful to offer a constant time (+blinded) variant of this in
1505 * the future for handling secret inputs, for example when computing
1506 * Pedersen commitments
1507 *
1508 * TODO for variable time computation we could make use of a wNAF
1509 * representation instead
1510 */
1511 ProjectivePoint mul2_vartime(const Scalar& s1, const Scalar& s2) const {
1512 const UnblindedScalarBits<C, W> bits1(s1);
1513 const UnblindedScalarBits<C, W> bits2(s2);
1514
1515 auto accum = ProjectivePoint::identity();
1516
1517 for(size_t i = 0; i != Windows; ++i) {
1518 if(i > 0) {
1519 accum = accum.dbl_n(WindowBits);
1520 }
1521
1522 const size_t w_1 = bits1.get_window((Windows - i - 1) * WindowBits);
1523 const size_t w_2 = bits2.get_window((Windows - i - 1) * WindowBits);
1524
1525 const size_t window = w_1 + (w_2 << WindowBits);
1526
1527 if(window > 0) {
1528 accum += m_table[window - 1];
1529 }
1530 }
1531
1532 return accum;
1533 }
1534
1535 private:
1536 std::vector<AffinePoint> m_table;
1537};
1538
1539// SSWU constant C1 - (B / (Z * A))
1540template <typename C>
1541const auto& SSWU_C2()
1542 requires C::ValidForSswuHash
1543{
1544 // This could use a variable time inversion
1545 static const typename C::FieldElement C2 = C::B * invert_field_element<C>(C::SSWU_Z * C::A);
1546 return C2;
1547}
1548
1549// SSWU constant C1 - (-B / A)
1550template <typename C>
1551const auto& SSWU_C1()
1552 requires C::ValidForSswuHash
1553{
1554 // We derive it from C2 to avoid a second inversion
1555 static const typename C::FieldElement C1 = (SSWU_C2<C>() * C::SSWU_Z).negate();
1556 return C1;
1557}
1558
1559template <typename C>
1560inline auto map_to_curve_sswu(const typename C::FieldElement& u) -> typename C::AffinePoint {
1561 CT::poison(u);
1562 const auto z_u2 = C::SSWU_Z * u.square(); // z * u^2
1563 const auto z2_u4 = z_u2.square();
1564 const auto tv1 = invert_field_element<C>(z2_u4 + z_u2);
1565 auto x1 = SSWU_C1<C>() * (C::FieldElement::one() + tv1);
1566 C::FieldElement::conditional_assign(x1, tv1.is_zero(), SSWU_C2<C>());
1567 const auto gx1 = C::AffinePoint::x3_ax_b(x1);
1568
1569 const auto x2 = z_u2 * x1;
1570 const auto gx2 = C::AffinePoint::x3_ax_b(x2);
1571
1572 // Will be zero if gx1 is not a square
1573 const auto [gx1_sqrt, gx1_is_square] = gx1.sqrt();
1574
1575 auto x = x2;
1576 // By design one of gx1 and gx2 must be a quadratic residue
1577 auto y = gx2.sqrt().first;
1578
1579 C::FieldElement::conditional_assign(x, y, gx1_is_square, x1, gx1_sqrt);
1580
1581 const auto flip_y = y.is_even() != u.is_even();
1582 C::FieldElement::conditional_assign(y, flip_y, y.negate());
1583
1584 auto pt = typename C::AffinePoint(x, y);
1585
1586 CT::unpoison(pt);
1587 return pt;
1588}
1589
1590template <typename C, bool RO>
1591inline auto hash_to_curve_sswu(std::string_view hash, std::span<const uint8_t> pw, std::span<const uint8_t> dst) {
1592 static_assert(C::ValidForSswuHash);
1593#if defined(BOTAN_HAS_XMD)
1594
1595 constexpr size_t SecurityLevel = (C::OrderBits + 1) / 2;
1596 constexpr size_t L = (C::PrimeFieldBits + SecurityLevel + 7) / 8;
1597 constexpr size_t Cnt = RO ? 2 : 1;
1598
1599 std::array<uint8_t, L * Cnt> xmd;
1600 expand_message_xmd(hash, xmd, pw, dst);
1601
1602 if constexpr(RO) {
1603 const auto u0 = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(xmd.data(), L));
1604 const auto u1 = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(xmd.data() + L, L));
1605
1606 auto accum = C::ProjectivePoint::from_affine(map_to_curve_sswu<C>(u0));
1607 accum += map_to_curve_sswu<C>(u1);
1608 return accum;
1609 } else {
1610 const auto u = C::FieldElement::from_wide_bytes(std::span<const uint8_t, L>(xmd.data(), L));
1611 return map_to_curve_sswu<C>(u);
1612 }
1613#else
1614 throw Not_Implemented("Hash to curve not available due to missing XMD");
1615#endif
1616}
1617
1618} // namespace
1619
1620} // namespace Botan
1621
1622#endif
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:98
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:137
static constexpr Choice from_int(T v)
Definition ct_utils.h:292
static constexpr Mask< T > from_choice(Choice c)
Definition ct_utils.h:394
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:434
static FE_25519 invert(const FE_25519 &a)
void randomize(std::span< uint8_t > output)
Definition rng.h:53
virtual bool is_seeded() const =0
int(* final)(unsigned char *, CTX *)
constexpr void pack(const Polynomial< PolyTrait, D > &p, BufferStuffer &stuffer, MapFnT map)
constexpr void unpoison_all(Ts &&... ts)
Definition ct_utils.h:201
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:784
constexpr void poison_all(Ts &&... ts)
Definition ct_utils.h:195
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:759
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:64
constexpr CT::Mask< T > all_zeros(const T elem[], size_t len)
Definition ct_utils.h:746
constexpr void poison(const T *p, size_t n)
Definition ct_utils.h:53
constexpr auto bigint_add(std::span< W, N > z, std::span< const W, N > x, std::span< const W, N > y) -> W
Definition mp_core.h:257
constexpr W shift_left(std::array< W, N > &x)
Definition mp_core.h:861
constexpr void comba_sqr(W z[2 *N], const W x[N])
Definition mp_core.h:984
BigInt operator*(const BigInt &x, const BigInt &y)
Definition big_ops3.cpp:46
constexpr void comba_mul(W z[2 *N], const W x[N], const W y[N])
Definition mp_core.h:948
BigInt square(const BigInt &x)
Definition numthry.cpp:157
OctetString operator+(const OctetString &k1, const OctetString &k2)
Definition symkey.cpp:99
constexpr T choose(T mask, T a, T b)
Definition bit_ops.h:193
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:341
constexpr auto monty_inverse(W a) -> W
Definition mp_core.h:832
FE_25519 fe
Definition ed25519_fe.h:140
BigInt operator-(const BigInt &x, const BigInt &y)
Definition bigint.h:1094
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition alg_id.cpp:69
void secure_scrub_memory(void *ptr, size_t n)
Definition os_utils.cpp:83
constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:592
void expand_message_xmd(std::string_view hash_fn, std::span< uint8_t > output, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep)
Definition xmd.cpp:16
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition alg_id.cpp:54
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
Definition mp_core.h:374
constexpr W bigint_cnd_add(W cnd, W x[], size_t x_size, const W y[], size_t y_size)
Definition mp_core.h:42
void carry(int64_t &h0, int64_t &h1)
std::vector< T, Alloc > & operator+=(std::vector< T, Alloc > &out, const std::vector< T, Alloc2 > &in)
Definition secmem.h:80
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:521
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:639
const SIMD_8x32 & b
constexpr auto hex_to_words(const char(&s)[N])
Definition mp_core.h:890
constexpr auto bigint_add2_nc(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:206
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:146
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:773
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:875
constexpr auto operator*=(Strong< T1, Tags... > &a, T2 b)