Botan 3.7.1
Crypto and TLS for C&
point_mul.cpp
Go to the documentation of this file.
1/*
2* (C) 2015,2018 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/internal/point_mul.h>
8
9#include <botan/reducer.h>
10#include <botan/rng.h>
11#include <botan/internal/ct_utils.h>
12#include <botan/internal/rounding.h>
13
14namespace Botan {
15
16namespace {
17
18size_t blinding_size(const BigInt& group_order) {
19 return (group_order.bits() + 1) / 2;
20}
21
22BigInt blinding_mask(const BigInt& group_order, RandomNumberGenerator& rng) {
23 if(rng.is_seeded()) {
24 BigInt mask(rng, blinding_size(group_order));
25 mask.set_bit(0);
26 return mask;
27 } else {
28 return 1;
29 }
30}
31
32} // namespace
33
34EC_Point multi_exponentiate(const EC_Point& x, const BigInt& z1, const EC_Point& y, const BigInt& z2) {
36 return xy_mul.multi_exp(z1, z2);
37}
38
40 m_base_point(base), m_mod_order(mod_order), m_p_words(base.get_curve().get_p_words()) {
41 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
42
43 const size_t order_bits = mod_order.get_modulus().bits();
44
45 const size_t T_bits = round_up(order_bits + blinding_size(mod_order.get_modulus()), WINDOW_BITS) / WINDOW_BITS;
46
47 std::vector<EC_Point> T(WINDOW_SIZE * T_bits);
48
49 EC_Point g = base;
50 EC_Point g2, g4;
51
52 for(size_t i = 0; i != T_bits; i++) {
53 g2 = g;
54 g2.mult2(ws);
55 g4 = g2;
56 g4.mult2(ws);
57
58 T[7 * i + 0] = g;
59 T[7 * i + 1] = std::move(g2);
60 T[7 * i + 2] = T[7 * i + 1].plus(T[7 * i + 0], ws); // g2+g
61 T[7 * i + 3] = g4;
62 T[7 * i + 4] = T[7 * i + 3].plus(T[7 * i + 0], ws); // g4+g
63 T[7 * i + 5] = T[7 * i + 3].plus(T[7 * i + 1], ws); // g4+g2
64 T[7 * i + 6] = T[7 * i + 3].plus(T[7 * i + 2], ws); // g4+g2+g
65
66 g.swap(g4);
67 g.mult2(ws);
68 }
69
70 EC_Point::force_all_affine(T, ws[0].get_word_vector());
71
72 m_W.resize(T.size() * 2 * m_p_words);
73
74 word* p = &m_W[0];
75 for(size_t i = 0; i != T.size(); ++i) {
76 T[i].get_x().encode_words(p, m_p_words);
77 p += m_p_words;
78 T[i].get_y().encode_words(p, m_p_words);
79 p += m_p_words;
80 }
81}
82
85 const BigInt& group_order,
86 std::vector<BigInt>& ws) const {
87 if(k.is_negative()) {
88 throw Invalid_Argument("EC_Point_Base_Point_Precompute scalar must be positive");
89 }
90
91 // Instead of reducing k mod group order should we alter the mask size??
92 BigInt scalar = m_mod_order.reduce(k);
93
94 if(rng.is_seeded()) {
95 // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
96 scalar += group_order * blinding_mask(group_order, rng);
97 } else {
98 /*
99 When we don't have an RNG we cannot do scalar blinding. Instead use the
100 same trick as OpenSSL and add one or two copies of the order to normalize
101 the length of the scalar at order.bits()+1. This at least ensures the loop
102 bound does not leak information about the high bits of the scalar.
103 */
104 scalar += group_order;
105 if(scalar.bits() == group_order.bits()) {
106 scalar += group_order;
107 }
108 BOTAN_DEBUG_ASSERT(scalar.bits() == group_order.bits() + 1);
109 }
110
111 const size_t windows = round_up(scalar.bits(), WINDOW_BITS) / WINDOW_BITS;
112
113 const size_t elem_size = 2 * m_p_words;
114
115 BOTAN_ASSERT(windows <= m_W.size() / (3 * elem_size), "Precomputed sufficient values for scalar mult");
116
117 EC_Point R = m_base_point.zero();
118
119 if(ws.size() < EC_Point::WORKSPACE_SIZE) {
120 ws.resize(EC_Point::WORKSPACE_SIZE);
121 }
122
123 // the precomputed multiples are not secret so use std::vector
124 std::vector<word> Wt(elem_size);
125
126 for(size_t i = 0; i != windows; ++i) {
127 const size_t window = windows - i - 1;
128 const size_t base_addr = (WINDOW_SIZE * window) * elem_size;
129
130 const word w = scalar.get_substring(WINDOW_BITS * window, WINDOW_BITS);
131
132 const auto w_is_1 = CT::Mask<word>::is_equal(w, 1);
133 const auto w_is_2 = CT::Mask<word>::is_equal(w, 2);
134 const auto w_is_3 = CT::Mask<word>::is_equal(w, 3);
135 const auto w_is_4 = CT::Mask<word>::is_equal(w, 4);
136 const auto w_is_5 = CT::Mask<word>::is_equal(w, 5);
137 const auto w_is_6 = CT::Mask<word>::is_equal(w, 6);
138 const auto w_is_7 = CT::Mask<word>::is_equal(w, 7);
139
140 for(size_t j = 0; j != elem_size; ++j) {
141 const word w1 = w_is_1.if_set_return(m_W[base_addr + 0 * elem_size + j]);
142 const word w2 = w_is_2.if_set_return(m_W[base_addr + 1 * elem_size + j]);
143 const word w3 = w_is_3.if_set_return(m_W[base_addr + 2 * elem_size + j]);
144 const word w4 = w_is_4.if_set_return(m_W[base_addr + 3 * elem_size + j]);
145 const word w5 = w_is_5.if_set_return(m_W[base_addr + 4 * elem_size + j]);
146 const word w6 = w_is_6.if_set_return(m_W[base_addr + 5 * elem_size + j]);
147 const word w7 = w_is_7.if_set_return(m_W[base_addr + 6 * elem_size + j]);
148
149 Wt[j] = w1 | w2 | w3 | w4 | w5 | w6 | w7;
150 }
151
152 R.add_affine(&Wt[0], m_p_words, &Wt[m_p_words], m_p_words, ws);
153
154 if(i == 0 && rng.is_seeded()) {
155 /*
156 * Since we start with the top bit of the exponent we know the
157 * first window must have a non-zero element, and thus R is
158 * now a point other than the point at infinity.
159 */
160 BOTAN_DEBUG_ASSERT(w != 0);
161 R.randomize_repr(rng, ws[0].get_word_vector());
162 }
163 }
164
166
167 return R;
168}
169
172 std::vector<BigInt>& ws) :
173 m_curve(ipoint.get_curve()), m_p_words(m_curve.get_p_words()), m_window_bits(4) {
174 if(ws.size() < EC_Point::WORKSPACE_SIZE) {
175 ws.resize(EC_Point::WORKSPACE_SIZE);
176 }
177
178 auto point = ipoint;
179 point.randomize_repr(rng);
180
181 std::vector<EC_Point> U(static_cast<size_t>(1) << m_window_bits);
182 U[0] = point.zero();
183 U[1] = point;
184
185 for(size_t i = 2; i < U.size(); i += 2) {
186 U[i] = U[i / 2].double_of(ws);
187 U[i + 1] = U[i].plus(point, ws);
188 }
189
190 // Hack to handle Blinded_Point_Multiply
191 if(rng.is_seeded()) {
192 // Skipping zero point since it can't be randomized
193 for(size_t i = 1; i != U.size(); ++i) {
194 U[i].randomize_repr(rng);
195 }
196 }
197
198 m_T.resize(U.size() * 3 * m_p_words);
199
200 word* p = &m_T[0];
201 for(size_t i = 0; i != U.size(); ++i) {
202 U[i].get_x().encode_words(p, m_p_words);
203 U[i].get_y().encode_words(p + m_p_words, m_p_words);
204 U[i].get_z().encode_words(p + 2 * m_p_words, m_p_words);
205 p += 3 * m_p_words;
206 }
207}
208
209EC_Point EC_Point_Var_Point_Precompute::mul(const BigInt& k,
211 const BigInt& group_order,
212 std::vector<BigInt>& ws) const {
213 if(k.is_negative()) {
214 throw Invalid_Argument("EC_Point_Var_Point_Precompute scalar must be positive");
215 }
216 if(ws.size() < EC_Point::WORKSPACE_SIZE) {
217 ws.resize(EC_Point::WORKSPACE_SIZE);
218 }
219
220 // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
221 const BigInt scalar = k + group_order * blinding_mask(group_order, rng);
222
223 const size_t elem_size = 3 * m_p_words;
224 const size_t window_elems = static_cast<size_t>(1) << m_window_bits;
225
226 size_t windows = round_up(scalar.bits(), m_window_bits) / m_window_bits;
227 EC_Point R(m_curve);
228 secure_vector<word> e(elem_size);
229
230 if(windows > 0) {
231 windows--;
232
233 const uint32_t w = scalar.get_substring(windows * m_window_bits, m_window_bits);
234
235 clear_mem(e.data(), e.size());
236 for(size_t i = 1; i != window_elems; ++i) {
237 const auto wmask = CT::Mask<word>::is_equal(w, i);
238
239 for(size_t j = 0; j != elem_size; ++j) {
240 e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
241 }
242 }
243
244 R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws);
245
246 /*
247 Randomize after adding the first nibble as before the addition R
248 is zero, and we cannot effectively randomize the point
249 representation of the zero point.
250 */
251 R.randomize_repr(rng, ws[0].get_word_vector());
252 }
253
254 while(windows) {
255 R.mult2i(m_window_bits, ws);
256
257 const uint32_t w = scalar.get_substring((windows - 1) * m_window_bits, m_window_bits);
258
259 clear_mem(e.data(), e.size());
260 for(size_t i = 1; i != window_elems; ++i) {
261 const auto wmask = CT::Mask<word>::is_equal(w, i);
262
263 for(size_t j = 0; j != elem_size; ++j) {
264 e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
265 }
266 }
267
268 R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws);
269
270 windows--;
271 }
272
274
275 return R;
276}
277
278EC_Point_Multi_Point_Precompute::EC_Point_Multi_Point_Precompute(const EC_Point& x, const EC_Point& y) {
279 if(x.on_the_curve() == false || y.on_the_curve() == false) {
280 m_M.push_back(x.zero());
281 return;
282 }
283
284 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
285
286 EC_Point x2 = x;
287 x2.mult2(ws);
288
289 const EC_Point x3(x2.plus(x, ws));
290
291 EC_Point y2 = y;
292 y2.mult2(ws);
293
294 const EC_Point y3(y2.plus(y, ws));
295
296 m_M.reserve(15);
297
298 m_M.push_back(x);
299 m_M.push_back(x2);
300 m_M.push_back(x3);
301
302 m_M.push_back(y);
303 m_M.push_back(y.plus(x, ws));
304 m_M.push_back(y.plus(x2, ws));
305 m_M.push_back(y.plus(x3, ws));
306
307 m_M.push_back(y2);
308 m_M.push_back(y2.plus(x, ws));
309 m_M.push_back(y2.plus(x2, ws));
310 m_M.push_back(y2.plus(x3, ws));
311
312 m_M.push_back(y3);
313 m_M.push_back(y3.plus(x, ws));
314 m_M.push_back(y3.plus(x2, ws));
315 m_M.push_back(y3.plus(x3, ws));
316
317 bool no_infinity = true;
318 for(auto& pt : m_M) {
319 if(pt.is_zero()) {
320 no_infinity = false;
321 }
322 }
323
324 if(no_infinity) {
325 EC_Point::force_all_affine(m_M, ws[0].get_word_vector());
326 }
327
328 m_no_infinity = no_infinity;
329}
330
331EC_Point EC_Point_Multi_Point_Precompute::multi_exp(const BigInt& z1, const BigInt& z2) const {
332 if(m_M.size() == 1) {
333 return m_M[0];
334 }
335
336 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
337
338 const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2);
339
340 EC_Point H = m_M[0].zero();
341
342 for(size_t i = 0; i != z_bits; i += 2) {
343 if(i > 0) {
344 H.mult2i(2, ws);
345 }
346
347 const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
348 const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);
349
350 const uint32_t z12 = (4 * z2_b) + z1_b;
351
352 // This function is not intended to be const time
353 if(z12) {
354 if(m_no_infinity) {
355 H.add_affine(m_M[z12 - 1], ws);
356 } else {
357 H.add(m_M[z12 - 1], ws);
358 }
359 }
360 }
361
362 if(z1.is_negative() != z2.is_negative()) {
363 H.negate();
364 }
365
366 return H;
367}
368
369} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:98
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
size_t bits() const
Definition bigint.cpp:295
bool is_negative() const
Definition bigint.h:560
uint32_t get_substring(size_t offset, size_t length) const
Definition bigint.cpp:227
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:453
EC_Point_Base_Point_Precompute(const EC_Point &base_point, const Modular_Reducer &mod_order)
Definition point_mul.cpp:39
EC_Point mul(const BigInt &k, RandomNumberGenerator &rng, const BigInt &group_order, std::vector< BigInt > &ws) const
Definition point_mul.cpp:83
EC_Point multi_exp(const BigInt &k1, const BigInt &k2) const
EC_Point_Var_Point_Precompute(const EC_Point &point, RandomNumberGenerator &rng, std::vector< BigInt > &ws)
EC_Point & negate()
Definition ec_point.h:132
void swap(EC_Point &other) noexcept
Definition ec_point.cpp:741
EC_Point plus(const EC_Point &other, std::vector< BigInt > &workspace) const
Definition ec_point.h:359
void randomize_repr(RandomNumberGenerator &rng)
Definition ec_point.cpp:120
void add(const EC_Point &other, std::vector< BigInt > &workspace)
Definition ec_point.h:274
void add_affine(const EC_Point &other, std::vector< BigInt > &workspace)
Definition ec_point.h:316
void mult2(std::vector< BigInt > &workspace)
Definition ec_point.cpp:351
void mult2i(size_t i, std::vector< BigInt > &workspace)
Definition ec_point.cpp:331
bool on_the_curve() const
Definition ec_point.cpp:636
EC_Point zero() const
Definition ec_point.cpp:99
static void force_all_affine(std::span< EC_Point > points, secure_vector< word > &ws)
Definition ec_point.cpp:483
const BigInt & get_modulus() const
Definition reducer.h:22
BigInt reduce(const BigInt &x) const
Definition reducer.cpp:54
virtual bool is_seeded() const =0
FE_25519 T
Definition ge.cpp:34
constexpr size_t round_up(size_t n, size_t align_to)
Definition rounding.h:25
EC_Point multi_exponentiate(const EC_Point &p1, const BigInt &z1, const EC_Point &p2, const BigInt &z2)
Definition point_mul.cpp:34
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:121