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