/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 |