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