Botan 3.5.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(point.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 std::vector<EC_Point> U(static_cast<size_t>(1) << m_window_bits);
170 U[0] = point.zero();
171 U[1] = point;
172
173 for(size_t i = 2; i < U.size(); i += 2) {
174 U[i] = U[i / 2].double_of(ws);
175 U[i + 1] = U[i].plus(point, ws);
176 }
177
178 // Hack to handle Blinded_Point_Multiply
179 if(rng.is_seeded()) {
180 // Skipping zero point since it can't be randomized
181 for(size_t i = 1; i != U.size(); ++i) {
182 U[i].randomize_repr(rng);
183 }
184 }
185
186 m_T.resize(U.size() * 3 * m_p_words);
187
188 word* p = &m_T[0];
189 for(size_t i = 0; i != U.size(); ++i) {
190 U[i].get_x().encode_words(p, m_p_words);
191 U[i].get_y().encode_words(p + m_p_words, m_p_words);
192 U[i].get_z().encode_words(p + 2 * m_p_words, m_p_words);
193 p += 3 * m_p_words;
194 }
195}
196
199 const BigInt& group_order,
200 std::vector<BigInt>& ws) const {
201 if(k.is_negative()) {
202 throw Invalid_Argument("EC_Point_Var_Point_Precompute scalar must be positive");
203 }
204 if(ws.size() < EC_Point::WORKSPACE_SIZE) {
205 ws.resize(EC_Point::WORKSPACE_SIZE);
206 }
207
208 // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
209 const BigInt mask(rng, blinding_size(group_order), false);
210 const BigInt scalar = k + group_order * mask;
211
212 const size_t elem_size = 3 * m_p_words;
213 const size_t window_elems = static_cast<size_t>(1) << m_window_bits;
214
215 size_t windows = round_up(scalar.bits(), m_window_bits) / m_window_bits;
216 EC_Point R(m_curve);
217 secure_vector<word> e(elem_size);
218
219 if(windows > 0) {
220 windows--;
221
222 const uint32_t w = scalar.get_substring(windows * m_window_bits, m_window_bits);
223
224 clear_mem(e.data(), e.size());
225 for(size_t i = 1; i != window_elems; ++i) {
226 const auto wmask = CT::Mask<word>::is_equal(w, i);
227
228 for(size_t j = 0; j != elem_size; ++j) {
229 e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
230 }
231 }
232
233 R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws);
234
235 /*
236 Randomize after adding the first nibble as before the addition R
237 is zero, and we cannot effectively randomize the point
238 representation of the zero point.
239 */
240 R.randomize_repr(rng, ws[0].get_word_vector());
241 }
242
243 while(windows) {
244 R.mult2i(m_window_bits, ws);
245
246 const uint32_t w = scalar.get_substring((windows - 1) * m_window_bits, m_window_bits);
247
248 clear_mem(e.data(), e.size());
249 for(size_t i = 1; i != window_elems; ++i) {
250 const auto wmask = CT::Mask<word>::is_equal(w, i);
251
252 for(size_t j = 0; j != elem_size; ++j) {
253 e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
254 }
255 }
256
257 R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2 * m_p_words], m_p_words, ws);
258
259 windows--;
260 }
261
263
264 return R;
265}
266
268 if(x.on_the_curve() == false || y.on_the_curve() == false) {
269 m_M.push_back(x.zero());
270 return;
271 }
272
273 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
274
275 EC_Point x2 = x;
276 x2.mult2(ws);
277
278 const EC_Point x3(x2.plus(x, ws));
279
280 EC_Point y2 = y;
281 y2.mult2(ws);
282
283 const EC_Point y3(y2.plus(y, ws));
284
285 m_M.reserve(15);
286
287 m_M.push_back(x);
288 m_M.push_back(x2);
289 m_M.push_back(x3);
290
291 m_M.push_back(y);
292 m_M.push_back(y.plus(x, ws));
293 m_M.push_back(y.plus(x2, ws));
294 m_M.push_back(y.plus(x3, ws));
295
296 m_M.push_back(y2);
297 m_M.push_back(y2.plus(x, ws));
298 m_M.push_back(y2.plus(x2, ws));
299 m_M.push_back(y2.plus(x3, ws));
300
301 m_M.push_back(y3);
302 m_M.push_back(y3.plus(x, ws));
303 m_M.push_back(y3.plus(x2, ws));
304 m_M.push_back(y3.plus(x3, ws));
305
306 bool no_infinity = true;
307 for(auto& pt : m_M) {
308 if(pt.is_zero()) {
309 no_infinity = false;
310 }
311 }
312
313 if(no_infinity) {
314 EC_Point::force_all_affine(m_M, ws[0].get_word_vector());
315 }
316
317 m_no_infinity = no_infinity;
318}
319
321 if(m_M.size() == 1) {
322 return m_M[0];
323 }
324
325 std::vector<BigInt> ws(EC_Point::WORKSPACE_SIZE);
326
327 const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2);
328
329 EC_Point H = m_M[0].zero();
330
331 for(size_t i = 0; i != z_bits; i += 2) {
332 if(i > 0) {
333 H.mult2i(2, ws);
334 }
335
336 const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
337 const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);
338
339 const uint32_t z12 = (4 * z2_b) + z1_b;
340
341 // This function is not intended to be const time
342 if(z12) {
343 if(m_no_infinity) {
344 H.add_affine(m_M[z12 - 1], ws);
345 } else {
346 H.add(m_M[z12 - 1], ws);
347 }
348 }
349 }
350
351 if(z1.is_negative() != z2.is_negative()) {
352 H.negate();
353 }
354
355 return H;
356}
357
358} // namespace Botan
#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:559
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:250
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_Point_Precompute(const EC_Point &g1, const EC_Point &g2)
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 mul(const BigInt &k, RandomNumberGenerator &rng, const BigInt &group_order, std::vector< BigInt > &ws) const
EC_Point & negate()
Definition ec_point.h:133
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:344
void randomize_repr(RandomNumberGenerator &rng)
Definition ec_point.cpp:37
void add(const EC_Point &other, std::vector< BigInt > &workspace)
Definition ec_point.h:263
void add_affine(const EC_Point &other, std::vector< BigInt > &workspace)
Definition ec_point.h:301
void mult2(std::vector< BigInt > &workspace)
Definition ec_point.cpp:257
EC_Point double_of(std::vector< BigInt > &workspace) const
Definition ec_point.h:355
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:195
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
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
size_t round_up(size_t n, size_t align_to)
Definition rounding.h:25