Botan 3.9.0
Crypto and TLS for C&
pcurves_util.h
Go to the documentation of this file.
1/*
2* (C) 2024,2025 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#ifndef BOTAN_PCURVES_UTIL_H_
8#define BOTAN_PCURVES_UTIL_H_
9
10#include <botan/internal/mp_core.h>
11#include <array>
12
13namespace Botan {
14
15template <WordType W, size_t N, size_t XN>
16inline consteval std::array<W, N> reduce_mod(const std::array<W, XN>& x, const std::array<W, N>& p) {
17 std::array<W, N + 1> r = {0};
18 std::array<W, N + 1> t = {0};
19
20 const size_t x_bits = XN * WordInfo<W>::bits;
21
22 for(size_t i = 0; i != x_bits; ++i) {
23 const size_t b = x_bits - 1 - i;
24
25 const size_t b_word = b / WordInfo<W>::bits;
26 const size_t b_bit = b % WordInfo<W>::bits;
27 const bool x_b = (x[b_word] >> b_bit) & 1;
28
30 // Conditional ok: this function is consteval
31 if(x_b) {
32 r[0] += 1;
33 }
34
35 const W carry = bigint_sub3(t.data(), r.data(), N + 1, p.data(), N);
36
37 // Conditional ok: this function is consteval
38 if(carry == 0) {
39 std::swap(r, t);
40 }
41 }
42
43 std::array<W, N> rs = {};
44 copy_mem(rs, std::span{r}.template first<N>());
45 return rs;
46}
47
48template <WordType W, size_t N>
49inline consteval std::array<W, N> montygomery_r(const std::array<W, N>& p) {
50 std::array<W, N + 1> x = {};
51 x[N] = 1;
52 return reduce_mod(x, p);
53}
54
55template <WordType W, size_t N>
56inline consteval std::array<W, N> mul_mod(const std::array<W, N>& x,
57 const std::array<W, N>& y,
58 const std::array<W, N>& p) {
59 std::array<W, 2 * N> z = {};
60 comba_mul<N>(z.data(), x.data(), y.data());
61 return reduce_mod(z, p);
62}
63
64template <WordType W, size_t N>
65inline constexpr auto monty_redc_pdash1(const std::array<W, 2 * N>& z, const std::array<W, N>& p) -> std::array<W, N> {
66 static_assert(N >= 1);
67
68 std::array<W, N> ws; // NOLINT(*-member-init);
69 std::array<W, N> r; // NOLINT(*-member-init)
70
71 word3<W> accum;
72
73 accum.add(z[0]);
74
75 ws[0] = accum.monty_step_pdash1();
76
77 for(size_t i = 1; i != N; ++i) {
78 for(size_t j = 0; j < i; ++j) {
79 accum.mul(ws[j], p[i - j]);
80 }
81
82 accum.add(z[i]);
83
84 ws[i] = accum.monty_step_pdash1();
85 }
86
87 for(size_t i = 0; i != N - 1; ++i) {
88 for(size_t j = i + 1; j != N; ++j) {
89 accum.mul(ws[j], p[N + i - j]);
90 }
91
92 accum.add(z[N + i]);
93
94 ws[i] = accum.extract();
95 }
96
97 accum.add(z[2 * N - 1]);
98
99 ws[N - 1] = accum.extract();
100 // w1 is the final part, which is not stored in the workspace
101 const W w1 = accum.extract();
102
103 bigint_monty_maybe_sub<N>(r.data(), w1, ws.data(), p.data());
104
105 return r;
106}
107
108template <WordType W, size_t N>
109inline constexpr auto monty_redc(const std::array<W, 2 * N>& z, const std::array<W, N>& p, W p_dash)
110 -> std::array<W, N> {
111 static_assert(N >= 1);
112
113 std::array<W, N> ws; // NOLINT(*-member-init)
114 std::array<W, N> r; // NOLINT(*-member-init)
115
116 // Conditional ok: the parameter size is public
117 if(!std::is_constant_evaluated()) {
118 // This range ensures we cover fields of 256, 384 and 512 bits for both 32 and 64 bit words
119 if constexpr(N == 4) {
120 bigint_monty_redc_4(r.data(), z.data(), p.data(), p_dash, ws.data());
121 return r;
122 } else if constexpr(N == 6) {
123 bigint_monty_redc_6(r.data(), z.data(), p.data(), p_dash, ws.data());
124 return r;
125 } else if constexpr(N == 8) {
126 bigint_monty_redc_8(r.data(), z.data(), p.data(), p_dash, ws.data());
127 return r;
128 } else if constexpr(N == 12) {
129 bigint_monty_redc_12(r.data(), z.data(), p.data(), p_dash, ws.data());
130 return r;
131 } else if constexpr(N == 16) {
132 bigint_monty_redc_16(r.data(), z.data(), p.data(), p_dash, ws.data());
133 return r;
134 }
135 }
136
137 word3<W> accum;
138
139 accum.add(z[0]);
140
141 ws[0] = accum.monty_step(p[0], p_dash);
142
143 for(size_t i = 1; i != N; ++i) {
144 for(size_t j = 0; j < i; ++j) {
145 accum.mul(ws[j], p[i - j]);
146 }
147
148 accum.add(z[i]);
149
150 ws[i] = accum.monty_step(p[0], p_dash);
151 }
152
153 for(size_t i = 0; i != N - 1; ++i) {
154 for(size_t j = i + 1; j != N; ++j) {
155 accum.mul(ws[j], p[N + i - j]);
156 }
157
158 accum.add(z[N + i]);
159
160 ws[i] = accum.extract();
161 }
162
163 accum.add(z[2 * N - 1]);
164
165 ws[N - 1] = accum.extract();
166 // w1 is the final part, which is not stored in the workspace
167 const W w1 = accum.extract();
168
169 bigint_monty_maybe_sub<N>(r.data(), w1, ws.data(), p.data());
170
171 return r;
172}
173
174template <uint8_t X, WordType W, size_t N>
175inline consteval std::array<W, N> p_minus(const std::array<W, N>& p) {
176 // TODO combine into p_plus_x_over_y<-1, 1>
177 static_assert(X > 0);
178 std::array<W, N> r{};
179 W x = X;
180 bigint_sub3(r.data(), p.data(), N, &x, 1);
181 std::reverse(r.begin(), r.end());
182 return r;
183}
184
185template <WordType W, size_t N>
186inline consteval std::array<W, N> p_plus_1_over_4(const std::array<W, N>& p) {
187 const W one = 1;
188 std::array<W, N> r{};
189 bigint_add3(r.data(), p.data(), N, &one, 1);
191 std::reverse(r.begin(), r.end());
192 return r;
193}
194
195template <WordType W, size_t N>
196inline consteval std::array<W, N> p_minus_1_over_2(const std::array<W, N>& p) {
197 const W one = 1;
198 std::array<W, N> r{};
199 bigint_sub3(r.data(), p.data(), N, &one, 1);
201 std::reverse(r.begin(), r.end());
202 return r;
203}
204
205template <WordType W, size_t N>
206inline consteval std::array<W, N> p_div_2_plus_1(const std::array<W, N>& p) {
207 const W one = 1;
208 std::array<W, N> r = p;
210 bigint_add2(r.data(), N, &one, 1);
211 return r;
212}
213
214template <WordType W, size_t N>
215inline consteval std::pair<size_t, std::array<W, N>> shanks_tonelli_c1c2(const std::array<W, N>& p) {
216 size_t c1 = 0;
217 auto c2 = p;
218
219 // This assumes p % 2 == 1
220 shift_right<1>(c2);
221 c1++;
222
223 for(;;) {
224 // If we found another one bit past the first, stop
225 // Conditional ok: this function is consteval
226 if(c2[0] % 2 == 1) {
227 break;
228 }
229 shift_right<1>(c2);
230 c1++;
231 }
232
233 std::reverse(c2.begin(), c2.end());
234 return {c1, c2};
235}
236
237template <WordType W, size_t N>
238inline consteval std::array<W, N> shanks_tonelli_c3(const std::array<W, N>& c2) {
239 auto c3 = c2;
240 std::reverse(c3.begin(), c3.end());
241 shift_right<1>(c3);
242 std::reverse(c3.begin(), c3.end());
243 return c3;
244}
245
246template <typename Z, WordType W, size_t N>
247consteval auto shanks_tonelli_c4(const std::array<W, N>& p_minus_1_over_2) -> Z {
248 const auto one = Z::one();
249
250 // This is a silly performance hack; the first non-quadratic root in P-224
251 // is 11 so if we start the search there we save a little time.
252 auto z = Z::constant(11);
253
254 for(;;) {
255 auto c = z.pow_vartime(p_minus_1_over_2);
256
257 auto is_square = c.is_zero() || c.is_one();
258
259 // Conditional ok: this function is consteval
260 if(!is_square.as_bool()) {
261 return z;
262 }
263
264 z = z + one;
265 }
266}
267
268template <WordType W, size_t N>
269inline consteval size_t count_bits(const std::array<W, N>& p) {
270 auto get_bit = [&](size_t i) {
271 const size_t w = i / WordInfo<W>::bits;
272 const size_t b = i % WordInfo<W>::bits;
273 return static_cast<uint8_t>((p[w] >> b) & 0x01);
274 };
275
276 size_t b = WordInfo<W>::bits * N;
277
278 // Conditional ok: this function is consteval
279 while(get_bit(b - 1) == 0) {
280 b -= 1;
281 }
282
283 return b;
284}
285
286template <WordType W, size_t N, size_t L>
287inline constexpr auto bytes_to_words(std::span<const uint8_t, L> bytes) {
288 static_assert(L <= WordInfo<W>::bytes * N);
289
290 std::array<W, N> r = {};
291
292 constexpr size_t full_words = L / WordInfo<W>::bytes;
293 constexpr size_t extra_bytes = L % WordInfo<W>::bytes;
294
295 static_assert(full_words + (extra_bytes ? 1 : 0) <= N);
296
297 for(size_t i = 0; i != full_words; ++i) {
298 r[i] = load_be<W>(bytes.data(), full_words - 1 - i);
299 }
300
301 if constexpr(extra_bytes > 0) {
302 constexpr size_t shift = extra_bytes * 8;
304
305 for(size_t i = 0; i != extra_bytes; ++i) {
306 const W b0 = bytes[WordInfo<W>::bytes * full_words + i];
307 r[0] |= (b0 << (8 * (extra_bytes - 1 - i)));
308 }
309 }
310
311 return r;
312}
313
314} // namespace Botan
315
316#endif
constexpr void add(W x)
Definition mp_asmi.h:520
constexpr W monty_step(W p0, W p_dash)
Definition mp_asmi.h:537
constexpr W extract()
Definition mp_asmi.h:529
constexpr void mul(W x, W y)
Definition mp_asmi.h:455
constexpr W monty_step_pdash1()
Definition mp_asmi.h:546
constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:96
consteval std::array< W, N > shanks_tonelli_c3(const std::array< W, N > &c2)
consteval std::array< W, N > p_plus_1_over_4(const std::array< W, N > &p)
constexpr auto bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:122
constexpr auto bytes_to_words(std::span< const uint8_t, L > bytes)
constexpr W shift_left(std::array< W, N > &x)
Definition mp_core.h:612
consteval std::array< W, N > p_minus_1_over_2(const std::array< W, N > &p)
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:145
consteval auto shanks_tonelli_c4(const std::array< W, N > &p_minus_1_over_2) -> Z
BOTAN_FUZZER_API void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6])
constexpr void comba_mul(W z[2 *N], const W x[N], const W y[N])
Definition mp_core.h:699
consteval std::array< W, N > p_div_2_plus_1(const std::array< W, N > &p)
constexpr auto monty_redc(const std::array< W, 2 *N > &z, const std::array< W, N > &p, W p_dash) -> std::array< W, N >
constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:194
consteval std::array< W, N > mul_mod(const std::array< W, N > &x, const std::array< W, N > &y, const std::array< W, N > &p)
consteval std::array< W, N > reduce_mod(const std::array< W, XN > &x, const std::array< W, N > &p)
BOTAN_FUZZER_API void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4])
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
Definition mp_core.h:227
BOTAN_FUZZER_API void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12])
void carry(int64_t &h0, int64_t &h1)
BOTAN_FUZZER_API void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16])
consteval std::array< W, N > p_minus(const std::array< W, N > &p)
consteval size_t count_bits(const std::array< W, N > &p)
BOTAN_FUZZER_API void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8])
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504
constexpr auto monty_redc_pdash1(const std::array< W, 2 *N > &z, const std::array< W, N > &p) -> std::array< W, N >
consteval std::array< W, N > montygomery_r(const std::array< W, N > &p)
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:626
consteval std::pair< size_t, std::array< W, N > > shanks_tonelli_c1c2(const std::array< W, N > &p)