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