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