/src/botan/src/lib/math/numbertheory/monty.cpp
Line | Count | Source |
1 | | /* |
2 | | * (C) 2018,2024,2025 Jack Lloyd |
3 | | * |
4 | | * Botan is released under the Simplified BSD License (see license.txt) |
5 | | */ |
6 | | |
7 | | #include <botan/internal/monty.h> |
8 | | |
9 | | #include <botan/exceptn.h> |
10 | | #include <botan/mem_ops.h> |
11 | | #include <botan/internal/barrett.h> |
12 | | #include <botan/internal/mp_core.h> |
13 | | #include <array> |
14 | | |
15 | | namespace Botan { |
16 | | |
17 | | namespace { |
18 | | |
19 | | // If the modulus is at most this many words, then use the stack instead |
20 | | // of a heap variable for some temporary values |
21 | | constexpr size_t MontgomeryUseStackLimit = 32; |
22 | | |
23 | | } // namespace |
24 | | |
25 | 11.6k | Montgomery_Params::Data::Data(const BigInt& p, const Barrett_Reduction& mod_p) { |
26 | 11.6k | if(p.is_even() || p < 3) { |
27 | 0 | throw Invalid_Argument("Montgomery_Params invalid modulus"); |
28 | 0 | } |
29 | | |
30 | 11.6k | m_p = p; |
31 | 11.6k | m_p_words = m_p.sig_words(); |
32 | 11.6k | m_p_dash = monty_inverse(m_p.word_at(0)); |
33 | | |
34 | 11.6k | const BigInt r = BigInt::power_of_2(m_p_words * WordInfo<word>::bits); |
35 | | |
36 | 11.6k | m_r1 = mod_p.reduce(r); |
37 | 11.6k | m_r2 = mod_p.square(m_r1); |
38 | 11.6k | m_r3 = mod_p.multiply(m_r1, m_r2); |
39 | | |
40 | | // Barrett should be at least zero prefixing up to modulus size |
41 | 11.6k | BOTAN_ASSERT_NOMSG(m_r1.size() >= m_p_words); |
42 | 11.6k | BOTAN_ASSERT_NOMSG(m_r2.size() >= m_p_words); |
43 | 11.6k | BOTAN_ASSERT_NOMSG(m_r3.size() >= m_p_words); |
44 | 11.6k | } |
45 | | |
46 | | Montgomery_Params::Montgomery_Params(const BigInt& p, const Barrett_Reduction& mod_p) : |
47 | 11.6k | m_data(std::make_shared<Data>(p, mod_p)) {} |
48 | | |
49 | | Montgomery_Params::Montgomery_Params(const BigInt& p) : |
50 | 2.17k | Montgomery_Params(p, Barrett_Reduction::for_secret_modulus(p)) {} |
51 | | |
52 | 189k | bool Montgomery_Params::operator==(const Montgomery_Params& other) const { |
53 | 189k | if(this->m_data == other.m_data) { |
54 | 189k | return true; |
55 | 189k | } |
56 | | |
57 | 0 | return (this->m_data->p() == other.m_data->p()); |
58 | 189k | } |
59 | | |
60 | 377 | BigInt Montgomery_Params::redc(const BigInt& x, secure_vector<word>& ws) const { |
61 | 377 | const size_t p_size = this->p_words(); |
62 | | |
63 | 377 | if(ws.size() < p_size) { |
64 | 258 | ws.resize(p_size); |
65 | 258 | } |
66 | | |
67 | 377 | BigInt z = x; |
68 | 377 | z.grow_to(2 * p_size); |
69 | | |
70 | 377 | bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); |
71 | | |
72 | 377 | return z; |
73 | 377 | } |
74 | | |
75 | 1.78M | BigInt Montgomery_Params::mul(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const { |
76 | 1.78M | const size_t p_size = this->p_words(); |
77 | 1.78M | BigInt z = BigInt::with_capacity(2 * p_size); |
78 | 1.78M | this->mul(z, x, y, ws); |
79 | 1.78M | return z; |
80 | 1.78M | } |
81 | | |
82 | 7.10M | void Montgomery_Params::mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) const { |
83 | 7.10M | const size_t p_size = this->p_words(); |
84 | | |
85 | 7.10M | if(ws.size() < 2 * p_size) { |
86 | 4.84k | ws.resize(2 * p_size); |
87 | 4.84k | } |
88 | | |
89 | 7.10M | BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size); |
90 | 7.10M | BOTAN_DEBUG_ASSERT(y.sig_words() <= p_size); |
91 | | |
92 | 7.10M | if(z.size() < 2 * p_size) { |
93 | 589k | z.grow_to(2 * p_size); |
94 | 589k | } |
95 | | |
96 | 7.10M | bigint_mul(z.mutable_data(), |
97 | 7.10M | z.size(), |
98 | 7.10M | x._data(), |
99 | 7.10M | x.size(), |
100 | 7.10M | std::min(p_size, x.size()), |
101 | 7.10M | y._data(), |
102 | 7.10M | y.size(), |
103 | 7.10M | std::min(p_size, y.size()), |
104 | 7.10M | ws.data(), |
105 | 7.10M | ws.size()); |
106 | | |
107 | 7.10M | bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); |
108 | 7.10M | } |
109 | | |
110 | 1.36M | void Montgomery_Params::mul(BigInt& z, const BigInt& x, std::span<const word> y, secure_vector<word>& ws) const { |
111 | 1.36M | const size_t p_size = this->p_words(); |
112 | | |
113 | 1.36M | if(ws.size() < 2 * p_size) { |
114 | 0 | ws.resize(2 * p_size); |
115 | 0 | } |
116 | 1.36M | if(z.size() < 2 * p_size) { |
117 | 0 | z.grow_to(2 * p_size); |
118 | 0 | } |
119 | | |
120 | 1.36M | BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size); |
121 | | |
122 | 1.36M | bigint_mul(z.mutable_data(), |
123 | 1.36M | z.size(), |
124 | 1.36M | x._data(), |
125 | 1.36M | x.size(), |
126 | 1.36M | std::min(p_size, x.size()), |
127 | 1.36M | y.data(), |
128 | 1.36M | y.size(), |
129 | 1.36M | std::min(p_size, y.size()), |
130 | 1.36M | ws.data(), |
131 | 1.36M | ws.size()); |
132 | | |
133 | 1.36M | bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); |
134 | 1.36M | } |
135 | | |
136 | 34.0k | void Montgomery_Params::mul_by(BigInt& x, const BigInt& y, secure_vector<word>& ws) const { |
137 | 34.0k | const size_t p_size = this->p_words(); |
138 | | |
139 | 34.0k | if(ws.size() < 4 * p_size) { |
140 | 17.0k | ws.resize(4 * p_size); |
141 | 17.0k | } |
142 | | |
143 | 34.0k | word* z_data = ws.data(); |
144 | 34.0k | word* ws_data = &ws[2 * p_size]; |
145 | | |
146 | 34.0k | BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size); |
147 | | |
148 | 34.0k | bigint_mul(z_data, |
149 | 34.0k | 2 * p_size, |
150 | 34.0k | x._data(), |
151 | 34.0k | x.size(), |
152 | 34.0k | std::min(p_size, x.size()), |
153 | 34.0k | y._data(), |
154 | 34.0k | y.size(), |
155 | 34.0k | std::min(p_size, y.size()), |
156 | 34.0k | ws_data, |
157 | 34.0k | 2 * p_size); |
158 | | |
159 | 34.0k | bigint_monty_redc_inplace(z_data, this->p()._data(), p_size, this->p_dash(), ws_data, 2 * p_size); |
160 | | |
161 | 34.0k | if(x.size() < 2 * p_size) { |
162 | 10.4k | x.grow_to(2 * p_size); |
163 | 10.4k | } |
164 | 34.0k | copy_mem(x.mutable_data(), z_data, 2 * p_size); |
165 | 34.0k | } |
166 | | |
167 | 308 | BigInt Montgomery_Params::sqr(const BigInt& x, secure_vector<word>& ws) const { |
168 | 308 | BOTAN_DEBUG_ASSERT(x.sig_words() <= this->p_words()); |
169 | 308 | return this->sqr(std::span{x._data(), x.size()}, ws); |
170 | 308 | } |
171 | | |
172 | 308 | BigInt Montgomery_Params::sqr(std::span<const word> x, secure_vector<word>& ws) const { |
173 | 308 | const size_t p_size = this->p_words(); |
174 | 308 | BigInt z = BigInt::with_capacity(2 * p_size); |
175 | 308 | this->sqr(z, x, ws); |
176 | 308 | return z; |
177 | 308 | } |
178 | | |
179 | 2.63M | void Montgomery_Params::sqr(BigInt& z, const BigInt& x, secure_vector<word>& ws) const { |
180 | 2.63M | this->sqr(z, std::span{x._data(), x.size()}, ws); |
181 | 2.63M | } |
182 | | |
183 | 2.97M | void Montgomery_Params::sqr(BigInt& z, std::span<const word> x, secure_vector<word>& ws) const { |
184 | 2.97M | const size_t p_size = this->p_words(); |
185 | | |
186 | 2.97M | if(ws.size() < 2 * p_size) { |
187 | 59 | ws.resize(2 * p_size); |
188 | 59 | } |
189 | | |
190 | 2.97M | if(z.size() < 2 * p_size) { |
191 | 62.5k | z.grow_to(2 * p_size); |
192 | 62.5k | } |
193 | | |
194 | 2.97M | bigint_sqr(z.mutable_data(), z.size(), x.data(), x.size(), std::min(p_size, x.size()), ws.data(), ws.size()); |
195 | | |
196 | 2.97M | bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size()); |
197 | 2.97M | } |
198 | | |
199 | | Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, secure_vector<word> words) : |
200 | 70.5k | m_params(params), m_v(std::move(words)) { |
201 | 70.5k | BOTAN_ASSERT_NOMSG(m_v.size() == m_params.p_words()); |
202 | 70.5k | } |
203 | | |
204 | 11.6k | Montgomery_Int Montgomery_Int::one(const Montgomery_Params& params) { |
205 | 11.6k | return Montgomery_Int(params, params.R1(), false); |
206 | 11.6k | } |
207 | | |
208 | 0 | Montgomery_Int Montgomery_Int::from_wide_int(const Montgomery_Params& params, const BigInt& x) { |
209 | 0 | secure_vector<word> ws; |
210 | 0 | auto redc_x = params.mul(params.redc(x, ws), params.R3(), ws); |
211 | 0 | return Montgomery_Int(params, redc_x, false); |
212 | 0 | } |
213 | | |
214 | | Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, const BigInt& v, bool redc_needed) : |
215 | 23.3k | m_params(params), m_v(m_params.p_words()) { |
216 | 23.3k | BOTAN_ASSERT_NOMSG(v < m_params.p()); |
217 | | |
218 | 23.3k | const size_t p_size = m_params.p_words(); |
219 | | |
220 | 23.3k | auto v_span = v._as_span(); |
221 | | |
222 | 23.3k | if(v_span.size() > p_size) { |
223 | | // Safe to truncate the span since we already checked v < p |
224 | 20.9k | v_span = v_span.first(p_size); |
225 | 20.9k | } |
226 | | |
227 | 23.3k | BOTAN_ASSERT_NOMSG(m_v.size() >= v_span.size()); |
228 | | |
229 | 23.3k | copy_mem(std::span{m_v}.first(v_span.size()), v_span); |
230 | | |
231 | 23.3k | if(redc_needed) { |
232 | 11.6k | secure_vector<word> ws; |
233 | 11.6k | this->mul_by(m_params.R2()._as_span().first(p_size), ws); |
234 | 11.6k | } |
235 | 23.3k | } |
236 | | |
237 | | Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, std::span<const word> words) : |
238 | 5.95k | m_params(params), m_v(words.begin(), words.end()) { |
239 | 5.95k | BOTAN_ARG_CHECK(m_v.size() == m_params.p_words(), "Invalid input span"); |
240 | 5.95k | } |
241 | | |
242 | 0 | std::vector<uint8_t> Montgomery_Int::serialize() const { |
243 | 0 | return value().serialize(); |
244 | 0 | } |
245 | | |
246 | 16.1k | BigInt Montgomery_Int::value() const { |
247 | 16.1k | secure_vector<word> ws(m_params.p_words()); |
248 | | |
249 | 16.1k | secure_vector<word> z = m_v; |
250 | 16.1k | z.resize(2 * m_params.p_words()); // zero extend |
251 | | |
252 | 16.1k | bigint_monty_redc_inplace( |
253 | 16.1k | z.data(), m_params.p()._data(), m_params.p_words(), m_params.p_dash(), ws.data(), ws.size()); |
254 | | |
255 | 16.1k | return BigInt::_from_words(z); |
256 | 16.1k | } |
257 | | |
258 | 0 | Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const { |
259 | 0 | BOTAN_STATE_CHECK(other.m_params == m_params); |
260 | |
|
261 | 0 | const size_t p_size = m_params.p_words(); |
262 | 0 | BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size); |
263 | |
|
264 | 0 | secure_vector<word> z(2 * p_size); |
265 | |
|
266 | 0 | word* r = std::span{z}.first(p_size).data(); |
267 | 0 | word* t = std::span{z}.last(p_size).data(); |
268 | | |
269 | | // t = this + other |
270 | 0 | const word carry = bigint_add3(t, m_v.data(), p_size, other.m_v.data(), p_size); |
271 | | |
272 | | // Conditionally subtract r = t - p |
273 | 0 | bigint_monty_maybe_sub(p_size, r, carry, t, m_params.p()._data()); |
274 | |
|
275 | 0 | z.resize(p_size); // truncate leaving only r |
276 | 0 | return Montgomery_Int(m_params, std::move(z)); |
277 | 0 | } |
278 | | |
279 | 0 | Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const { |
280 | 0 | BOTAN_STATE_CHECK(other.m_params == m_params); |
281 | |
|
282 | 0 | const size_t p_size = m_params.p_words(); |
283 | 0 | BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size); |
284 | |
|
285 | 0 | secure_vector<word> t(p_size); |
286 | 0 | const word borrow = bigint_sub3(t.data(), m_v.data(), p_size, other.m_v.data(), p_size); |
287 | |
|
288 | 0 | bigint_cnd_add(borrow, t.data(), m_params.p()._data(), p_size); |
289 | |
|
290 | 0 | return Montgomery_Int(m_params, std::move(t)); |
291 | 0 | } |
292 | | |
293 | 70.5k | Montgomery_Int Montgomery_Int::mul(const Montgomery_Int& other, secure_vector<word>& ws) const { |
294 | 70.5k | BOTAN_STATE_CHECK(other.m_params == m_params); |
295 | | |
296 | 70.5k | const size_t p_size = m_params.p_words(); |
297 | 70.5k | BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size); |
298 | | |
299 | 70.5k | if(ws.size() < 2 * p_size) { |
300 | 0 | ws.resize(2 * p_size); |
301 | 0 | } |
302 | | |
303 | 70.5k | secure_vector<word> z(2 * p_size); |
304 | | |
305 | 70.5k | bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.m_v.data(), p_size, p_size, ws.data(), ws.size()); |
306 | | |
307 | 70.5k | bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size()); |
308 | 70.5k | z.resize(p_size); // truncate off high zero words |
309 | | |
310 | 70.5k | return Montgomery_Int(m_params, std::move(z)); |
311 | 70.5k | } |
312 | | |
313 | 118k | Montgomery_Int& Montgomery_Int::mul_by(const Montgomery_Int& other, secure_vector<word>& ws) { |
314 | 118k | BOTAN_STATE_CHECK(other.m_params == m_params); |
315 | 118k | return this->mul_by(std::span{other.m_v}, ws); |
316 | 118k | } |
317 | | |
318 | 2.32M | Montgomery_Int& Montgomery_Int::mul_by(std::span<const word> other, secure_vector<word>& ws) { |
319 | 2.32M | const size_t p_size = m_params.p_words(); |
320 | 2.32M | BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.size() == p_size); |
321 | | |
322 | 2.32M | if(ws.size() < 2 * p_size) { |
323 | 11.6k | ws.resize(2 * p_size); |
324 | 11.6k | } |
325 | | |
326 | 2.32M | auto do_mul_by = [&](std::span<word> z) { |
327 | 2.32M | bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.data(), p_size, p_size, ws.data(), ws.size()); |
328 | | |
329 | 2.32M | bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size()); |
330 | | |
331 | 2.32M | copy_mem(m_v, z.first(p_size)); |
332 | 2.32M | }; |
333 | | |
334 | 2.32M | if(p_size <= MontgomeryUseStackLimit) { |
335 | 1.04M | std::array<word, 2 * MontgomeryUseStackLimit> z{}; |
336 | 1.04M | do_mul_by(z); |
337 | 1.27M | } else { |
338 | 1.27M | secure_vector<word> z(2 * p_size); |
339 | 1.27M | do_mul_by(z); |
340 | 1.27M | } |
341 | | |
342 | 2.32M | return (*this); |
343 | 2.32M | } |
344 | | |
345 | 2.40M | Montgomery_Int& Montgomery_Int::square_this_n_times(secure_vector<word>& ws, size_t n) { |
346 | 2.40M | const size_t p_size = m_params.p_words(); |
347 | 2.40M | BOTAN_ASSERT_NOMSG(m_v.size() == p_size); |
348 | | |
349 | 2.40M | if(ws.size() < 2 * p_size) { |
350 | 0 | ws.resize(2 * p_size); |
351 | 0 | } |
352 | | |
353 | 2.40M | auto do_sqr_n = [&](std::span<word> z) { |
354 | 11.7M | for(size_t i = 0; i != n; ++i) { |
355 | 9.32M | bigint_sqr(z.data(), 2 * p_size, m_v.data(), p_size, p_size, ws.data(), ws.size()); |
356 | | |
357 | 9.32M | bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size()); |
358 | | |
359 | 9.32M | copy_mem(m_v, std::span{z}.first(p_size)); |
360 | 9.32M | } |
361 | 2.40M | }; |
362 | | |
363 | 2.40M | if(p_size <= MontgomeryUseStackLimit) { |
364 | 1.11M | std::array<word, 2 * MontgomeryUseStackLimit> z{}; |
365 | 1.11M | do_sqr_n(z); |
366 | 1.29M | } else { |
367 | 1.29M | secure_vector<word> z(2 * p_size); |
368 | 1.29M | do_sqr_n(z); |
369 | 1.29M | } |
370 | | |
371 | 2.40M | return (*this); |
372 | 2.40M | } |
373 | | |
374 | 70.5k | Montgomery_Int Montgomery_Int::square(secure_vector<word>& ws) const { |
375 | 70.5k | auto z = (*this); |
376 | 70.5k | z.square_this_n_times(ws, 1); |
377 | 70.5k | return z; |
378 | 70.5k | } |
379 | | |
380 | | } // namespace Botan |