Botan 3.13.0
Crypto and TLS for C&
ctr.cpp
Go to the documentation of this file.
1/*
2* Counter mode
3* (C) 1999-2011,2014 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/ctr.h>
9
10#include <botan/exceptn.h>
11#include <botan/internal/bit_ops.h>
12#include <botan/internal/fmt.h>
13#include <botan/internal/loadstor.h>
14
15#if defined(BOTAN_HAS_CTR_BE_AVX2) || defined(BOTAN_HAS_CTR_BE_SIMD32)
16 #include <botan/internal/cpuid.h>
17#endif
18
19namespace Botan {
20
21CTR_BE::CTR_BE(std::unique_ptr<BlockCipher> cipher) :
22 m_cipher(std::move(cipher)),
23 m_block_size(m_cipher->block_size()),
24 m_ctr_size(m_block_size),
25 m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
26 m_counter(m_cipher->parallel_bytes()),
27 m_pad(m_counter.size()),
28 m_pad_pos(0) {}
29
30CTR_BE::CTR_BE(std::unique_ptr<BlockCipher> cipher, size_t ctr_size) :
31 m_cipher(std::move(cipher)),
32 m_block_size(m_cipher->block_size()),
33 m_ctr_size(ctr_size),
34 m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
35 m_counter(m_cipher->parallel_bytes()),
36 m_pad(m_counter.size()),
37 m_pad_pos(0) {
38 BOTAN_ARG_CHECK(m_ctr_size >= 4 && m_ctr_size <= m_block_size, "Invalid CTR-BE counter size");
39}
40
42 m_cipher->clear();
43 zeroise(m_pad);
44 zeroise(m_counter);
45 zap(m_iv);
46 m_pad_pos = 0;
47 m_bytes_remaining = 0;
48}
49
50std::optional<uint64_t> CTR_BE::remaining_keystream_bytes() const {
51 if(!has_keying_material() || m_ctr_size >= sizeof(uint64_t)) {
52 return std::nullopt;
53 }
54 return m_bytes_remaining;
55}
56
58 return m_block_size;
59}
60
61bool CTR_BE::valid_iv_length(size_t iv_len) const {
62 return (iv_len <= m_block_size);
63}
64
65size_t CTR_BE::buffer_size() const {
66 return m_pad.size();
67}
68
70 return m_cipher->key_spec();
71}
72
73std::unique_ptr<StreamCipher> CTR_BE::new_object() const {
74 return std::make_unique<CTR_BE>(m_cipher->new_object(), m_ctr_size);
75}
76
78 return m_cipher->has_keying_material();
79}
80
81void CTR_BE::key_schedule(std::span<const uint8_t> key) {
82 m_cipher->set_key(key);
83
84 // Set a default all-zeros IV
85 set_iv(nullptr, 0);
86}
87
88std::string CTR_BE::name() const {
89 if(m_ctr_size == m_block_size) {
90 return fmt("CTR-BE({})", m_cipher->name());
91 } else {
92 return fmt("CTR-BE({},{})", m_cipher->name(), m_ctr_size);
93 }
94}
95
96void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) {
98
99 if(m_ctr_size < sizeof(uint64_t)) {
100 if(length > m_bytes_remaining) {
101 throw Invalid_State(fmt("CTR_BE with {}-byte counter has exhausted its keystream", m_ctr_size));
102 }
103 m_bytes_remaining -= length;
104 }
105
106 const uint8_t* pad_bits = m_pad.data();
107 const size_t pad_size = m_pad.size();
108
109 /* Consume any already computed keystream in m_pad */
110
111 if(m_pad_pos > 0) {
112 const size_t avail = pad_size - m_pad_pos;
113 const size_t take = std::min(length, avail);
114 xor_buf(out, in, pad_bits + m_pad_pos, take);
115 length -= take;
116 in += take;
117 out += take;
118 m_pad_pos += take;
119
120 if(take == avail) {
121 add_counter(m_ctr_blocks);
122 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
123 m_pad_pos = 0;
124 }
125 }
126
127 /* Bulk processing */
128
129 [[maybe_unused]] const bool can_use_bs16_ctr4_fastpath = m_block_size == 16 && m_ctr_size == 4 && pad_size % 64 == 0;
130
131#if defined(BOTAN_HAS_CTR_BE_AVX2)
132 if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::AVX2)) {
133 const size_t consumed = ctr_proc_bs16_ctr4_avx2(in, out, length);
134 in += consumed;
135 out += consumed;
136 length -= consumed;
137 }
138#endif
139
140#if defined(BOTAN_HAS_CTR_BE_SIMD32)
141 if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::SIMD_4X32)) {
142 const size_t consumed = ctr_proc_bs16_ctr4_simd32(in, out, length);
143 in += consumed;
144 out += consumed;
145 length -= consumed;
146 }
147#endif
148
149 while(length >= pad_size) {
150 xor_buf(out, in, pad_bits, pad_size);
151 length -= pad_size;
152 in += pad_size;
153 out += pad_size;
154
155 add_counter(m_ctr_blocks);
156 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
157 }
158
159 /* Now if length > 0 then we have some remaining text, and m_pad is full - consume as required */
160 if(length > 0) {
161 xor_buf(out, in, pad_bits, length);
162 m_pad_pos = length;
163 }
164}
165
166void CTR_BE::generate_keystream(uint8_t out[], size_t length) {
168
169 if(m_ctr_size < sizeof(uint64_t)) {
170 if(length > m_bytes_remaining) {
171 throw Invalid_State(fmt("CTR_BE with {}-byte counter has exhausted its keystream", m_ctr_size));
172 }
173 m_bytes_remaining -= length;
174 }
175
176 const size_t avail = m_pad.size() - m_pad_pos;
177 const size_t take = std::min(length, avail);
178 copy_mem(out, &m_pad[m_pad_pos], take);
179 length -= take;
180 out += take;
181 m_pad_pos += take;
182
183 while(length >= m_pad.size()) {
184 add_counter(m_ctr_blocks);
185 m_cipher->encrypt_n(m_counter.data(), out, m_ctr_blocks);
186
187 length -= m_pad.size();
188 out += m_pad.size();
189 }
190
191 if(m_pad_pos == m_pad.size()) {
192 add_counter(m_ctr_blocks);
193 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
194 m_pad_pos = 0;
195 }
196
197 copy_mem(out, m_pad.data(), length);
198 m_pad_pos += length;
199 BOTAN_ASSERT_NOMSG(m_pad_pos < m_pad.size());
200}
201
202void CTR_BE::set_iv_bytes(const uint8_t iv[], size_t iv_len) {
203 if(!valid_iv_length(iv_len)) {
204 throw Invalid_IV_Length(name(), iv_len);
205 }
206
207 m_iv.resize(m_block_size);
208 zeroise(m_iv);
209 copy_mem(m_iv.data(), iv, iv_len);
210
211 seek(0);
212}
213
214void CTR_BE::add_counter(const uint64_t counter) {
215 const size_t ctr_size = m_ctr_size;
216 const size_t ctr_blocks = m_ctr_blocks;
217 const size_t BS = m_block_size;
218
219 if(ctr_size == 4) {
220 const size_t off = (BS - 4);
221 const uint32_t low32 = static_cast<uint32_t>(counter + load_be<uint32_t>(&m_counter[off], 0));
222
223 for(size_t i = 0; i != ctr_blocks; ++i) {
224 store_be(uint32_t(low32 + i), &m_counter[i * BS + off]);
225 }
226 } else if(ctr_size == 8) {
227 const size_t off = (BS - 8);
228 const uint64_t low64 = counter + load_be<uint64_t>(&m_counter[off], 0);
229
230 for(size_t i = 0; i != ctr_blocks; ++i) {
231 store_be(uint64_t(low64 + i), &m_counter[i * BS + off]);
232 }
233 } else if(ctr_size == 16) {
234 const size_t off = (BS - 16);
235 uint64_t b0 = load_be<uint64_t>(&m_counter[off], 0);
236 uint64_t b1 = load_be<uint64_t>(&m_counter[off], 1);
237 b1 += counter;
238 b0 += (b1 < counter) ? 1 : 0; // carry
239
240 for(size_t i = 0; i != ctr_blocks; ++i) {
241 store_be(b0, &m_counter[i * BS + off]);
242 store_be(b1, &m_counter[i * BS + off + 8]);
243 b1 += 1;
244 if(b1 == 0) {
245 b0 += 1; // carry
246 }
247 }
248 } else {
249 for(size_t i = 0; i != ctr_blocks; ++i) {
250 uint64_t local_counter = counter;
251 uint16_t carry = static_cast<uint8_t>(local_counter);
252 for(size_t j = 0; (carry > 0 || local_counter > 0) && j != ctr_size; ++j) {
253 const size_t off = i * BS + (BS - 1 - j);
254 const uint16_t cnt = static_cast<uint16_t>(m_counter[off]) + carry;
255 m_counter[off] = static_cast<uint8_t>(cnt);
256 local_counter = (local_counter >> 8);
257 carry = (cnt >> 8) + static_cast<uint8_t>(local_counter);
258 }
259 }
260 }
261}
262
263void CTR_BE::seek(uint64_t offset) {
265
266 if(m_ctr_size < sizeof(uint64_t)) {
267 const uint64_t requested_block = offset / m_block_size;
268 const uint64_t max_blocks = uint64_t{1} << (8 * m_ctr_size);
269 if(requested_block >= max_blocks) {
270 throw Invalid_Argument(fmt("CTR_BE::seek offset {} exceeds {}-byte counter range", offset, m_ctr_size));
271 }
272
273 m_bytes_remaining = max_blocks * m_block_size - offset;
274 }
275
276 const uint64_t base_counter = m_ctr_blocks * (offset / m_counter.size());
277
278 zeroise(m_counter);
279 BOTAN_ASSERT_NOMSG(m_counter.size() >= m_iv.size());
280 copy_mem(m_counter.data(), m_iv.data(), m_iv.size());
281
282 const size_t BS = m_block_size;
283
284 // Set m_counter blocks to IV, IV + 1, ... IV + n
285
286 if(m_ctr_size == 4 && BS >= 8) {
287 const uint32_t low32 = load_be<uint32_t>(&m_counter[BS - 4], 0);
288
289 if(m_ctr_blocks >= 4 && is_power_of_2(m_ctr_blocks)) {
290 size_t written = 1;
291 while(written < m_ctr_blocks) {
292 copy_mem(&m_counter[written * BS], &m_counter[0], BS * written); // NOLINT(*container-data-pointer)
293 written *= 2;
294 }
295 } else {
296 for(size_t i = 1; i != m_ctr_blocks; ++i) {
297 copy_mem(&m_counter[i * BS], &m_counter[0], BS - 4); // NOLINT(*container-data-pointer)
298 }
299 }
300
301 for(size_t i = 1; i != m_ctr_blocks; ++i) {
302 const uint32_t c = static_cast<uint32_t>(low32 + i);
303 store_be(c, &m_counter[(BS - 4) + i * BS]);
304 }
305 } else {
306 // do everything sequentially:
307 for(size_t i = 1; i != m_ctr_blocks; ++i) {
308 copy_mem(&m_counter[i * BS], &m_counter[(i - 1) * BS], BS);
309
310 for(size_t j = 0; j != m_ctr_size; ++j) {
311 uint8_t& c = m_counter[i * BS + (BS - 1 - j)];
312 c += 1;
313 if(c > 0) {
314 break;
315 }
316 }
317 }
318 }
319
320 if(base_counter > 0) {
321 add_counter(base_counter);
322 }
323
324 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
325 m_pad_pos = offset % m_counter.size();
326}
327} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
static bool has(CPUID::Feature feat)
Definition cpuid.h:94
void clear() override
Definition ctr.cpp:41
size_t default_iv_length() const override
Definition ctr.cpp:57
std::optional< uint64_t > remaining_keystream_bytes() const override
Definition ctr.cpp:50
bool has_keying_material() const override
Definition ctr.cpp:77
size_t buffer_size() const override
Definition ctr.cpp:65
Key_Length_Specification key_spec() const override
Definition ctr.cpp:69
bool valid_iv_length(size_t iv_len) const override
Definition ctr.cpp:61
void seek(uint64_t offset) override
Definition ctr.cpp:263
std::unique_ptr< StreamCipher > new_object() const override
Definition ctr.cpp:73
std::string name() const override
Definition ctr.cpp:88
CTR_BE(std::unique_ptr< BlockCipher > cipher)
Definition ctr.cpp:21
void set_iv(const uint8_t iv[], size_t iv_len)
void cipher(const uint8_t in[], uint8_t out[], size_t len)
void assert_key_material_set() const
Definition sym_algo.h:180
BOTAN_FORCE_INLINE constexpr bool is_power_of_2(T arg)
Definition bit_ops.h:62
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:241
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:261
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
void carry(int64_t &h0, int64_t &h1)
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504