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