Coverage Report

Created: 2026-10-28 06:31

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