Botan 3.13.0
Crypto and TLS for C&
monty.cpp
Go to the documentation of this file.
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#include <functional>
15
16namespace Botan {
17
18namespace {
19
20// If the modulus is at most this many words, then use the stack instead
21// of a heap variable for some temporary values
22constexpr size_t MontgomeryUseStackLimit = 32;
23
24/*
25* The typed std::less<T*> specialization gives a strict total order on pointers
26* of the same type even when they belong to different allocations (while direct
27* < and > on raw pointers are only specified within a single array object).
28* The transparent std::less<> forwards to <, so it is unsuitable here.
29*/
30bool ranges_overlap(const word* a, size_t na, const word* b, size_t nb) {
31 if(na == 0 || nb == 0) {
32 return false;
33 }
34 const std::less<const word*> lt; // NOLINT(modernize-use-transparent-functors)
35 return lt(a, b + nb) && lt(b, a + na);
36}
37
38} // namespace
39
40Montgomery_Params::Data::Data(const BigInt& p, const Barrett_Reduction& mod_p) {
41 if(p.is_even() || p < 3) {
42 throw Invalid_Argument("Montgomery_Params invalid modulus");
43 }
44
45 m_p = p;
46 m_p_words = m_p.sig_words();
47 m_p_dash = monty_inverse(m_p.word_at(0));
48
49 const BigInt r = BigInt::power_of_2(m_p_words * WordInfo<word>::bits);
50
51 m_r1 = mod_p.reduce(r);
52 m_r2 = mod_p.square(m_r1);
53 m_r3 = mod_p.multiply(m_r1, m_r2);
54
55 // Barrett should be at least zero prefixing up to modulus size
56 BOTAN_ASSERT_NOMSG(m_r1.size() >= m_p_words);
57 BOTAN_ASSERT_NOMSG(m_r2.size() >= m_p_words);
58 BOTAN_ASSERT_NOMSG(m_r3.size() >= m_p_words);
59}
60
62 m_data(std::make_shared<Data>(p, mod_p)) {}
63
66
68 if(this->m_data == other.m_data) {
69 return true;
70 }
71
72 return (this->m_data->p() == other.m_data->p());
73}
74
76 const size_t p_size = this->p_words();
77
78 if(ws.size() < p_size) {
79 ws.resize(p_size);
80 }
81
82 BigInt z = x;
83 z.grow_to(2 * p_size);
84
85 bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
86
87 return z;
88}
89
91 const size_t p_size = this->p_words();
92 BigInt z = BigInt::with_capacity(2 * p_size);
93 this->mul(z, x, y, ws);
94 return z;
95}
96
97void Montgomery_Params::mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) const {
98 BOTAN_ARG_CHECK(&z != &x && &z != &y, "Montgomery_Params::mul output must not alias inputs");
99
100 const size_t p_size = this->p_words();
101
102 if(ws.size() < 2 * p_size) {
103 ws.resize(2 * p_size);
104 }
105
106 BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size);
107 BOTAN_DEBUG_ASSERT(y.sig_words() <= p_size);
108
109 if(z.size() < 2 * p_size) {
110 z.grow_to(2 * p_size);
111 }
112
114 z.size(),
115 x._data(),
116 x.size(),
117 std::min(p_size, x.size()),
118 y._data(),
119 y.size(),
120 std::min(p_size, y.size()),
121 ws.data(),
122 ws.size());
123
124 bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
125}
126
127void Montgomery_Params::mul(BigInt& z, const BigInt& x, std::span<const word> y, secure_vector<word>& ws) const {
128 BOTAN_ARG_CHECK(&z != &x, "Montgomery_Params::mul output must not alias x");
129 BOTAN_ARG_CHECK(!ranges_overlap(z._data(), z.size(), y.data(), y.size()),
130 "Montgomery_Params::mul output must not overlap y");
131
132 const size_t p_size = this->p_words();
133
134 if(ws.size() < 2 * p_size) {
135 ws.resize(2 * p_size);
136 }
137 if(z.size() < 2 * p_size) {
138 z.grow_to(2 * p_size);
139 }
140
141 BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size);
142
144 z.size(),
145 x._data(),
146 x.size(),
147 std::min(p_size, x.size()),
148 y.data(),
149 y.size(),
150 std::min(p_size, y.size()),
151 ws.data(),
152 ws.size());
153
154 bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
155}
156
158 const size_t p_size = this->p_words();
159
160 if(ws.size() < 4 * p_size) {
161 ws.resize(4 * p_size);
162 }
163
164 word* z_data = ws.data();
165 word* ws_data = &ws[2 * p_size];
166
167 BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size);
168
169 bigint_mul(z_data,
170 2 * p_size,
171 x._data(),
172 x.size(),
173 std::min(p_size, x.size()),
174 y._data(),
175 y.size(),
176 std::min(p_size, y.size()),
177 ws_data,
178 2 * p_size);
179
180 bigint_monty_redc_inplace(z_data, this->p()._data(), p_size, this->p_dash(), ws_data, 2 * p_size);
181
182 if(x.size() < 2 * p_size) {
183 x.grow_to(2 * p_size);
184 }
185 copy_mem(x.mutable_data(), z_data, 2 * p_size);
186}
187
189 BOTAN_DEBUG_ASSERT(x.sig_words() <= this->p_words());
190 return this->sqr(std::span{x._data(), x.size()}, ws);
191}
192
193BigInt Montgomery_Params::sqr(std::span<const word> x, secure_vector<word>& ws) const {
194 const size_t p_size = this->p_words();
195 BigInt z = BigInt::with_capacity(2 * p_size);
196 this->sqr(z, x, ws);
197 return z;
198}
199
201 BOTAN_ARG_CHECK(&z != &x, "Montgomery_Params::sqr output must not alias input");
202 this->sqr(z, std::span{x._data(), x.size()}, ws);
203}
204
205void Montgomery_Params::sqr(BigInt& z, std::span<const word> x, secure_vector<word>& ws) const {
206 BOTAN_ARG_CHECK(!ranges_overlap(z._data(), z.size(), x.data(), x.size()),
207 "Montgomery_Params::sqr output must not overlap input");
208
209 const size_t p_size = this->p_words();
210
211 if(ws.size() < 2 * p_size) {
212 ws.resize(2 * p_size);
213 }
214
215 if(z.size() < 2 * p_size) {
216 z.grow_to(2 * p_size);
217 }
218
219 bigint_sqr(z.mutable_data(), z.size(), x.data(), x.size(), std::min(p_size, x.size()), ws.data(), ws.size());
220
221 bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
222}
223
224Montgomery_Int::Montgomery_Int(const Montgomery_Params& params) : m_params(params), m_v(m_params.p_words()) {}
225
227 m_params(params), m_v(std::move(words)) {
228 BOTAN_ASSERT_NOMSG(m_v.size() == m_params.p_words());
229}
230
232 return Montgomery_Int(params, params.R1(), false);
233}
234
237 auto redc_x = params.mul(params.redc(x, ws), params.R3(), ws);
238 return Montgomery_Int(params, redc_x, false);
239}
240
241Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, const BigInt& v, bool redc_needed) :
242 m_params(params), m_v(m_params.p_words()) {
243 BOTAN_ARG_CHECK(v.signum() >= 0 && v < m_params.p(), "Input out of range");
244
245 const size_t p_size = m_params.p_words();
246
247 auto v_span = v._as_span();
248
249 if(v_span.size() > p_size) {
250 // Safe to truncate the span since we already checked v < p
251 v_span = v_span.first(p_size);
252 }
253
254 BOTAN_ASSERT_NOMSG(m_v.size() >= v_span.size());
255
256 copy_mem(std::span{m_v}.first(v_span.size()), v_span);
257
258 if(redc_needed) {
260 this->mul_by(m_params.R2()._as_span().first(p_size), ws);
261 }
262}
263
264Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, std::span<const word> words) :
265 m_params(params), m_v(words.begin(), words.end()) {
266 BOTAN_ARG_CHECK(m_v.size() == m_params.p_words(), "Invalid input span");
267}
268
269std::vector<uint8_t> Montgomery_Int::serialize() const {
270 return value().serialize();
271}
272
274 secure_vector<word> ws(m_params.p_words());
275
276 secure_vector<word> z = m_v;
277 z.resize(2 * m_params.p_words()); // zero extend
278
280 z.data(), m_params.p()._data(), m_params.p_words(), m_params.p_dash(), ws.data(), ws.size());
281
282 return BigInt::_from_words(z);
283}
284
286 BOTAN_STATE_CHECK(other.m_params == m_params);
287
288 const size_t p_size = m_params.p_words();
289 BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size);
290
291 secure_vector<word> z(2 * p_size);
292
293 word* r = std::span{z}.first(p_size).data();
294 word* t = std::span{z}.last(p_size).data();
295
296 // t = this + other
297 const word carry = bigint_add3(t, m_v.data(), p_size, other.m_v.data(), p_size);
298
299 // Conditionally subtract r = t - p
300 bigint_monty_maybe_sub(p_size, r, carry, t, m_params.p()._data());
301
302 z.resize(p_size); // truncate leaving only r
303 return Montgomery_Int(m_params, std::move(z));
304}
305
307 BOTAN_STATE_CHECK(other.m_params == m_params);
308
309 const size_t p_size = m_params.p_words();
310 BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size);
311
312 secure_vector<word> t(p_size);
313 const word borrow = bigint_sub3(t.data(), m_v.data(), p_size, other.m_v.data(), p_size);
314
315 bigint_cnd_add(borrow, t.data(), m_params.p()._data(), p_size);
316
317 return Montgomery_Int(m_params, std::move(t));
318}
319
321 BOTAN_STATE_CHECK(other.m_params == m_params);
322
323 const size_t p_size = m_params.p_words();
324 BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size);
325
326 if(ws.size() < 2 * p_size) {
327 ws.resize(2 * p_size);
328 }
329
330 secure_vector<word> z(2 * p_size);
331
332 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());
333
334 bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size());
335 z.resize(p_size); // truncate off high zero words
336
337 return Montgomery_Int(m_params, std::move(z));
338}
339
341 BOTAN_STATE_CHECK(other.m_params == m_params);
342 return this->mul_by(std::span{other.m_v}, ws);
343}
344
346 const size_t p_size = m_params.p_words();
347 BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.size() == p_size);
348
349 if(ws.size() < 2 * p_size) {
350 ws.resize(2 * p_size);
351 }
352
353 auto do_mul_by = [&](std::span<word> z) {
354 bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.data(), p_size, p_size, ws.data(), ws.size());
355
356 bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size());
357
358 copy_mem(m_v, z.first(p_size));
359 };
360
361 if(p_size <= MontgomeryUseStackLimit) {
362 std::array<word, 2 * MontgomeryUseStackLimit> z{};
363 do_mul_by(z);
364 } else {
365 secure_vector<word> z(2 * p_size);
366 do_mul_by(z);
367 }
368
369 return (*this);
370}
371
373 const size_t p_size = m_params.p_words();
374 BOTAN_ASSERT_NOMSG(m_v.size() == p_size);
375
376 if(ws.size() < 2 * p_size) {
377 ws.resize(2 * p_size);
378 }
379
380 auto do_sqr_n = [&](std::span<word> z) {
381 for(size_t i = 0; i != n; ++i) {
382 bigint_sqr(z.data(), 2 * p_size, m_v.data(), p_size, p_size, ws.data(), ws.size());
383
384 bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size());
385
386 copy_mem(m_v, std::span{z}.first(p_size));
387 }
388 };
389
390 if(p_size <= MontgomeryUseStackLimit) {
391 std::array<word, 2 * MontgomeryUseStackLimit> z{};
392 do_sqr_n(z);
393 } else {
394 secure_vector<word> z(2 * p_size);
395 do_sqr_n(z);
396 }
397
398 return (*this);
399}
400
402 auto z = (*this);
403 z.square_this_n_times(ws, 1);
404 return z;
405}
406
407} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
size_t sig_words() const
Definition bigint.h:687
word * mutable_data()
Definition bigint.h:712
size_t size() const
Definition bigint.h:681
void grow_to(size_t n) const
Definition bigint.h:738
static BigInt _from_words(secure_vector< word > &words)
Definition bigint.h:1052
int signum() const
Definition bigint.h:493
static BigInt power_of_2(size_t n)
Definition bigint.h:906
const word * _data() const
Definition bigint.h:1033
T serialize(size_t len) const
Definition bigint.h:790
static BigInt with_capacity(size_t n)
Definition bigint.cpp:51
std::span< const word > _as_span() const
Definition bigint.h:1023
static Montgomery_Int from_wide_int(const Montgomery_Params &params, const BigInt &x)
Definition monty.cpp:235
Montgomery_Int square(secure_vector< word > &ws) const
Definition monty.cpp:401
static Montgomery_Int one(const Montgomery_Params &params)
Definition monty.cpp:231
Montgomery_Int operator-(const Montgomery_Int &other) const
Definition monty.cpp:306
Montgomery_Int(const Montgomery_Params &params)
Definition monty.cpp:224
Montgomery_Int operator+(const Montgomery_Int &other) const
Definition monty.cpp:285
BigInt value() const
Definition monty.cpp:273
Montgomery_Int & square_this_n_times(secure_vector< word > &ws, size_t n)
Definition monty.cpp:372
Montgomery_Int mul(const Montgomery_Int &other, secure_vector< word > &ws) const
Definition monty.cpp:320
Montgomery_Int & mul_by(const Montgomery_Int &other, secure_vector< word > &ws)
Definition monty.cpp:340
std::vector< uint8_t > serialize() const
Definition monty.cpp:269
BigInt redc(const BigInt &x, secure_vector< word > &ws) const
Definition monty.cpp:75
BigInt sqr(const BigInt &x, secure_vector< word > &ws) const
Definition monty.cpp:188
void mul(BigInt &z, const BigInt &x, const BigInt &y, secure_vector< word > &ws) const
Definition monty.cpp:97
size_t p_words() const
Definition monty.h:51
bool operator==(const Montgomery_Params &other) const
Definition monty.cpp:67
void mul_by(BigInt &x, const BigInt &y, secure_vector< word > &ws) const
Definition monty.cpp:157
Montgomery_Params(const BigInt &p, const Barrett_Reduction &mod_p)
Definition monty.cpp:61
const BigInt & R3() const
Definition monty.h:47
const BigInt & R1() const
Definition monty.h:43
word p_dash() const
Definition monty.h:49
const BigInt & p() const
Definition monty.h:41
constexpr auto bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:120
void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size)
Definition mp_karat.cpp:327
constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W
Definition mp_core.h:192
constexpr auto monty_inverse(W a) -> W
Definition mp_core.h:703
void bigint_mul(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, const word y[], size_t y_size, size_t y_sw, word workspace[], size_t ws_size)
Definition mp_karat.cpp:283
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
void bigint_monty_redc_inplace(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size)
Definition mp_core.h:948
constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size)
Definition mp_core.h:45
constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[])
Definition mp_core.h:225
void carry(int64_t &h0, int64_t &h1)
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
std::conditional_t< HasNative64BitRegisters, std::uint64_t, uint32_t > word
The native machine word, used as the limb type for multiprecision integers.
Definition types.h:131