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