Botan 3.6.1
Crypto and TLS for C&
pcurves_util.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_UTIL_H_
8#define BOTAN_PCURVES_UTIL_H_
9
10#include <botan/internal/mp_core.h>
11#include <array>
12
13namespace Botan {
14
15namespace {
16
17template <WordType W, size_t N, size_t XN>
18inline consteval std::array<W, N> reduce_mod(const std::array<W, XN>& x, const std::array<W, N>& p) {
19 std::array<W, N + 1> r = {0};
20 std::array<W, N + 1> t = {0};
21
22 const size_t x_bits = XN * WordInfo<W>::bits;
23
24 for(size_t i = 0; i != x_bits; ++i) {
25 const size_t b = x_bits - 1 - i;
26
27 const size_t b_word = b / WordInfo<W>::bits;
28 const size_t b_bit = b % WordInfo<W>::bits;
29 const bool x_b = (x[b_word] >> b_bit) & 1;
30
32 if(x_b) {
33 r[0] += 1;
34 }
35
36 const W carry = bigint_sub3(t.data(), r.data(), N + 1, p.data(), N);
37
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 = {0};
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;
69
70 word3<W> accum;
71
72 accum.add(z[0]);
73
74 ws[0] = accum.monty_step_pdash1();
75
76 for(size_t i = 1; i != N; ++i) {
77 for(size_t j = 0; j < i; ++j) {
78 accum.mul(ws[j], p[i - j]);
79 }
80
81 accum.add(z[i]);
82
83 ws[i] = accum.monty_step_pdash1();
84 }
85
86 for(size_t i = 0; i != N - 1; ++i) {
87 for(size_t j = i + 1; j != N; ++j) {
88 accum.mul(ws[j], p[N + i - j]);
89 }
90
91 accum.add(z[N + i]);
92
93 ws[i] = accum.extract();
94 }
95
96 accum.add(z[2 * N - 1]);
97
98 ws[N - 1] = accum.extract();
99 // w1 is the final part, which is not stored in the workspace
100 const W w1 = accum.extract();
101
102 std::array<W, N> r;
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;
114
115 word3<W> accum;
116
117 accum.add(z[0]);
118
119 ws[0] = accum.monty_step(p[0], p_dash);
120
121 for(size_t i = 1; i != N; ++i) {
122 for(size_t j = 0; j < i; ++j) {
123 accum.mul(ws[j], p[i - j]);
124 }
125
126 accum.add(z[i]);
127
128 ws[i] = accum.monty_step(p[0], p_dash);
129 }
130
131 for(size_t i = 0; i != N - 1; ++i) {
132 for(size_t j = i + 1; j != N; ++j) {
133 accum.mul(ws[j], p[N + i - j]);
134 }
135
136 accum.add(z[N + i]);
137
138 ws[i] = accum.extract();
139 }
140
141 accum.add(z[2 * N - 1]);
142
143 ws[N - 1] = accum.extract();
144 // w1 is the final part, which is not stored in the workspace
145 const W w1 = accum.extract();
146
147 std::array<W, N> r;
148 bigint_monty_maybe_sub<N>(r.data(), w1, ws.data(), p.data());
149
150 return r;
151}
152
153template <uint8_t X, WordType W, size_t N>
154inline consteval std::array<W, N> p_minus(const std::array<W, N>& p) {
155 // TODO combine into p_plus_x_over_y<-1, 1>
156 static_assert(X > 0);
157 std::array<W, N> r;
158 W x = X;
159 bigint_sub3(r.data(), p.data(), N, &x, 1);
160 std::reverse(r.begin(), r.end());
161 return r;
162}
163
164template <WordType W, size_t N>
165inline consteval std::array<W, N> p_plus_1_over_4(const std::array<W, N>& p) {
166 const W one = 1;
167 std::array<W, N> r;
168 bigint_add3_nc(r.data(), p.data(), N, &one, 1);
170 std::reverse(r.begin(), r.end());
171 return r;
172}
173
174template <WordType W, size_t N>
175inline consteval std::array<W, N> p_minus_1_over_2(const std::array<W, N>& p) {
176 const W one = 1;
177 std::array<W, N> r;
178 bigint_sub3(r.data(), p.data(), N, &one, 1);
180 std::reverse(r.begin(), r.end());
181 return r;
182}
183
184template <WordType W, size_t N>
185inline consteval std::array<W, N> p_div_2_plus_1(const std::array<W, N>& p) {
186 const W one = 1;
187 std::array<W, N> r = p;
189 bigint_add2_nc(r.data(), N, &one, 1);
190 return r;
191}
192
193template <WordType W, size_t N>
194inline consteval std::pair<size_t, std::array<W, N>> shanks_tonelli_c1c2(const std::array<W, N>& p) {
195 size_t c1 = 0;
196 auto c2 = p;
197
198 // This assumes p % 2 == 1
199 shift_right<1>(c2);
200 c1++;
201
202 for(;;) {
203 // If we found another one bit past the first, stop
204 if(c2[0] % 2 == 1) {
205 break;
206 }
207 shift_right<1>(c2);
208 c1++;
209 }
210
211 std::reverse(c2.begin(), c2.end());
212 return {c1, c2};
213}
214
215template <WordType W, size_t N>
216inline consteval std::array<W, N> shanks_tonelli_c3(const std::array<W, N>& c2) {
217 auto c3 = c2;
218 std::reverse(c3.begin(), c3.end());
219 shift_right<1>(c3);
220 std::reverse(c3.begin(), c3.end());
221 return c3;
222}
223
224template <typename Z, WordType W, size_t N>
225consteval auto shanks_tonelli_c4(const std::array<W, N>& p_minus_1_over_2) -> Z {
226 const auto one = Z::one();
227
228 // This is a silly performance hack; the first non-quadratic root in P-224
229 // is 11 so if we start the search there we save a little time.
230 auto z = Z::from_word(11);
231
232 for(;;) {
233 auto c = z.pow_vartime(p_minus_1_over_2);
234
235 auto is_square = c.is_zero() || c.is_one();
236
237 if(!is_square.as_bool()) {
238 return z;
239 }
240
241 z = z + one;
242 }
243}
244
245template <WordType W, size_t N>
246inline consteval size_t count_bits(const std::array<W, N>& p) {
247 auto get_bit = [&](size_t i) {
248 const size_t w = i / WordInfo<W>::bits;
249 const size_t b = i % WordInfo<W>::bits;
250 return static_cast<uint8_t>((p[w] >> b) & 0x01);
251 };
252
253 size_t b = WordInfo<W>::bits * N;
254
255 while(get_bit(b - 1) == 0) {
256 b -= 1;
257 }
258
259 return b;
260}
261
262template <WordType W, size_t N, size_t L>
263inline constexpr auto bytes_to_words(std::span<const uint8_t, L> bytes) {
264 static_assert(L <= WordInfo<W>::bytes * N);
265
266 std::array<W, N> r = {};
267
268 constexpr size_t full_words = L / WordInfo<W>::bytes;
269 constexpr size_t extra_bytes = L % WordInfo<W>::bytes;
270
271 static_assert(full_words + (extra_bytes ? 1 : 0) <= N);
272
273 for(size_t i = 0; i != full_words; ++i) {
274 r[i] = load_be<W>(bytes.data(), full_words - 1 - i);
275 }
276
277 if constexpr(extra_bytes > 0) {
278 constexpr size_t shift = extra_bytes * 8;
280
281 for(size_t i = 0; i != extra_bytes; ++i) {
282 const W b0 = bytes[WordInfo<W>::bytes * full_words + i];
283 r[0] |= (b0 << (8 * (extra_bytes - 1 - i)));
284 }
285 }
286
287 return r;
288}
289
290// Extract a WindowBits sized window out of s, depending on offset.
291template <size_t WindowBits, typename W, size_t N>
292constexpr size_t read_window_bits(std::span<const W, N> words, size_t offset) {
293 static_assert(WindowBits >= 1 && WindowBits <= 7);
294
295 const uint8_t WindowMask = static_cast<uint8_t>(1 << WindowBits) - 1;
296
297 const size_t W_bits = sizeof(W) * 8;
298 const auto bit_shift = offset % W_bits;
299 const auto word_offset = words.size() - 1 - (offset / W_bits);
300
301 const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0;
302
303 const auto w0 = words[word_offset];
304
305 if(single_byte_window) {
306 return (w0 >> bit_shift) & WindowMask;
307 } else {
308 // Otherwise we must join two words and extract the result
309 const auto w1 = words[word_offset - 1];
310 const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift)));
311 return combined & WindowMask;
312 }
313}
314
315} // namespace
316
317} // namespace Botan
318
319#endif
FE_25519 Z
Definition ge.cpp:27
FE_25519 X
Definition ge.cpp:25
constexpr W shift_left(std::array< W, N > &x)
Definition mp_core.h:861
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 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)
const SIMD_8x32 & b
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 load_be(ParamTs &&... params)
Definition loadstor.h:530
constexpr W shift_right(std::array< W, N > &x)
Definition mp_core.h:875
constexpr auto bigint_add3_nc(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:232