Coverage Report

Created: 2026-05-06 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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