Coverage Report

Created: 2026-04-01 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/math/numbertheory/barrett.cpp
Line
Count
Source
1
/*
2
* (C) 2025 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6
7
#include <botan/internal/barrett.h>
8
9
#include <botan/internal/ct_utils.h>
10
#include <botan/internal/divide.h>
11
#include <botan/internal/mp_core.h>
12
13
namespace Botan {
14
15
Barrett_Reduction::Barrett_Reduction(const BigInt& m, BigInt mu, size_t mw) :
16
484
      m_modulus(m), m_mu(std::move(mu)), m_mod_words(mw), m_modulus_bits(m.bits()) {
17
   // Give some extra space for Karatsuba
18
484
   m_modulus.grow_to(m_mod_words + 8);
19
484
   m_mu.grow_to(m_mod_words + 8);
20
484
}
21
22
377
Barrett_Reduction Barrett_Reduction::for_secret_modulus(const BigInt& mod) {
23
377
   BOTAN_ARG_CHECK(!mod.is_zero(), "Modulus cannot be zero");
24
377
   BOTAN_ARG_CHECK(!mod.is_negative(), "Modulus cannot be negative");
25
26
377
   size_t mod_words = mod.sig_words();
27
28
   // Compute mu = floor(2^{2k} / m)
29
377
   const size_t mu_bits = 2 * WordInfo<word>::bits * mod_words;
30
377
   return Barrett_Reduction(mod, ct_divide_pow2k(mu_bits, mod), mod_words);
31
377
}
32
33
107
Barrett_Reduction Barrett_Reduction::for_public_modulus(const BigInt& mod) {
34
107
   BOTAN_ARG_CHECK(!mod.is_zero(), "Modulus cannot be zero");
35
107
   BOTAN_ARG_CHECK(!mod.is_negative(), "Modulus cannot be negative");
36
37
107
   size_t mod_words = mod.sig_words();
38
39
   // Compute mu = floor(2^{2k} / m)
40
107
   const size_t mu_bits = 2 * WordInfo<word>::bits * mod_words;
41
107
   return Barrett_Reduction(mod, vartime_divide_pow2k(mu_bits, mod), mod_words);
42
107
}
43
44
namespace {
45
46
/*
47
* Barrett Reduction
48
*
49
* This function assumes that the significant size of x_words (ie the number of
50
* words with a value other than zero) is at most 2 * mod_words. In any case, any
51
* larger value cannot be reduced using Barrett reduction; callers should have
52
* already checked for this.
53
*/
54
BigInt barrett_reduce(
55
135k
   size_t mod_words, const BigInt& modulus, const BigInt& mu, std::span<const word> x_words, secure_vector<word>& ws) {
56
135k
   BOTAN_ASSERT_NOMSG(modulus.sig_words() == mod_words);
57
58
   // Caller must expand input to be at least this size
59
135k
   BOTAN_ASSERT_NOMSG(x_words.size() >= 2 * mod_words);
60
61
   // Normally mod_words + 1 but can be + 2 if the modulus is a power of 2
62
135k
   const size_t mu_words = mu.sig_words();
63
135k
   BOTAN_ASSERT_NOMSG(mu_words <= mod_words + 2);
64
65
135k
   if(ws.size() < 2 * (mod_words + 2)) {
66
59.6k
      ws.resize(2 * (mod_words + 2));
67
59.6k
   }
68
69
135k
   CT::poison(x_words);
70
71
   /*
72
   * Following the notation of Handbook of Applied Cryptography
73
   * Algorithm 14.42 "Barrett modular reduction", page 604
74
   * <https://cacr.uwaterloo.ca/hac/about/chap14.pdf>
75
   *
76
   * Using `mu` for μ in the code
77
   */
78
79
   // Compute q1 = floor(x / 2^(k - 1)) which is equivalent to ignoring the low (k-1) words
80
81
   // 2 * mod_words + 1 is sufficient, extra is to enable Karatsuba
82
135k
   secure_vector<word> r(2 * mu_words + 2);
83
84
135k
   copy_mem(r.data(), x_words.data() + (mod_words - 1), mod_words + 1);
85
86
   // Now compute q2 = q1 * μ
87
88
   // We allocate more size than required since this allows Karatsuba more often;
89
   // just `mu_words + (mod_words + 1)` is sufficient
90
135k
   const size_t q2_size = 2 * mu_words + 2;
91
92
135k
   secure_vector<word> q2(q2_size);
93
94
135k
   bigint_mul(
95
135k
      q2.data(), q2.size(), r.data(), r.size(), mod_words + 1, mu._data(), mu.size(), mu_words, ws.data(), ws.size());
96
97
   // Compute r2 = (floor(q2 / b^(k+1)) * m) mod 2^(k+1)
98
   // The division/floor is again effected by just ignoring the low k + 1 words
99
135k
   bigint_mul(r.data(),
100
135k
              r.size(),
101
135k
              &q2[mod_words + 1],  // ignoring the low mod_words + 1 words of the first product
102
135k
              q2.size() - (mod_words + 1),
103
135k
              mod_words + 1,
104
135k
              modulus._data(),
105
135k
              modulus.size(),
106
135k
              mod_words,
107
135k
              ws.data(),
108
135k
              ws.size());
109
110
   // Clear the high words of the product, equivalent to computing mod 2^(k+1)
111
   // TODO add masked mul to avoid computing high bits at all
112
135k
   clear_mem(std::span{r}.subspan(mod_words + 1));
113
114
   // Compute r = r1 - r2
115
116
   // The return value of bigint_sub_abs isn't quite right for what we need here so first compare
117
135k
   const int32_t relative_size = bigint_cmp(r.data(), mod_words + 1, x_words.data(), mod_words + 1);
118
119
135k
   bigint_sub_abs(r.data(), r.data(), x_words.data(), mod_words + 1, ws.data());
120
121
   /*
122
   If r is negative then we have to set r to r + 2^(k+1)
123
124
   However for r negative computing this sum is equivalent to computing 2^(k+1) - r
125
   */
126
135k
   word borrow = 0;
127
545k
   for(size_t i = 0; i != mod_words + 1; ++i) {
128
409k
      ws[i] = word_sub(static_cast<word>(0), r[i], &borrow);
129
409k
   }
130
135k
   ws[mod_words + 1] = word_sub(static_cast<word>(1), r[mod_words + 1], &borrow);
131
132
   // If relative_size > 0 then assign r to 2^(k+1) - r
133
135k
   CT::Mask<word>::is_equal(static_cast<word>(relative_size), 1).select_n(r.data(), ws.data(), r.data(), mod_words + 2);
134
135
   /*
136
   * Per HAC Note 14.44 (ii) "step 4 is repeated at most twice since 0 ≤ r < 3m"
137
   */
138
135k
   const size_t bound = 2;
139
140
135k
   BOTAN_ASSERT_NOMSG(r.size() >= mod_words + 1);
141
407k
   for(size_t i = 0; i != bound; ++i) {
142
271k
      borrow = bigint_sub3(ws.data(), r.data(), mod_words + 1, modulus._data(), mod_words);
143
271k
      CT::Mask<word>::is_zero(borrow).select_n(r.data(), ws.data(), r.data(), mod_words + 1);
144
271k
   }
145
146
135k
   CT::unpoison(q2);
147
135k
   CT::unpoison(r);
148
135k
   CT::unpoison(ws);
149
135k
   CT::unpoison(x_words);
150
151
135k
   return BigInt::_from_words(r);
152
135k
}
153
154
121k
CT::Choice acceptable_barrett_input(const BigInt& x, const BigInt& modulus) {
155
121k
   auto x_is_positive = CT::Choice::from_int(static_cast<uint32_t>(x.is_positive()));
156
121k
   auto x_lt_mod = bigint_ct_is_lt(x._data(), x.size(), modulus._data(), modulus.sig_words()).as_choice();
157
121k
   return x_is_positive && x_lt_mod;
158
121k
}
159
160
}  // namespace
161
162
45.0k
BigInt Barrett_Reduction::multiply(const BigInt& x, const BigInt& y) const {
163
45.0k
   BOTAN_ARG_CHECK(acceptable_barrett_input(x, m_modulus).as_bool(), "Invalid x param for Barrett multiply");
164
45.0k
   BOTAN_ARG_CHECK(acceptable_barrett_input(y, m_modulus).as_bool(), "Invalid y param for Barrett multiply");
165
166
45.0k
   secure_vector<word> ws(2 * (m_mod_words + 2));
167
45.0k
   secure_vector<word> xy(2 * m_mod_words);
168
169
45.0k
   bigint_mul(xy.data(),
170
45.0k
              xy.size(),
171
45.0k
              x._data(),
172
45.0k
              x.size(),
173
45.0k
              std::min(x.size(), m_mod_words),
174
45.0k
              y._data(),
175
45.0k
              y.size(),
176
45.0k
              std::min(y.size(), m_mod_words),
177
45.0k
              ws.data(),
178
45.0k
              ws.size());
179
180
45.0k
   return barrett_reduce(m_mod_words, m_modulus, m_mu, xy, ws);
181
45.0k
}
182
183
31.1k
BigInt Barrett_Reduction::square(const BigInt& x) const {
184
31.1k
   BOTAN_ARG_CHECK(acceptable_barrett_input(x, m_modulus).as_bool(), "Invalid x param for Barrett square");
185
186
31.1k
   secure_vector<word> ws(2 * (m_mod_words + 2));
187
31.1k
   secure_vector<word> x2(2 * m_mod_words);
188
189
31.1k
   bigint_sqr(x2.data(), x2.size(), x._data(), x.size(), std::min(x.size(), m_mod_words), ws.data(), ws.size());
190
191
31.1k
   return barrett_reduce(m_mod_words, m_modulus, m_mu, x2, ws);
192
31.1k
}
193
194
59.6k
BigInt Barrett_Reduction::reduce(const BigInt& x) const {
195
59.6k
   BOTAN_ARG_CHECK(x.is_positive(), "Argument must be positive");
196
197
59.6k
   const size_t x_sw = x.sig_words();
198
59.6k
   BOTAN_ARG_CHECK(x_sw <= 2 * m_mod_words, "Argument is too large for Barrett reduction");
199
200
59.6k
   x.grow_to(2 * m_mod_words);
201
202
59.6k
   secure_vector<word> ws;
203
59.6k
   return barrett_reduce(m_mod_words, m_modulus, m_mu, x._as_span(), ws);
204
59.6k
}
205
206
}  // namespace Botan