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