Botan 3.11.1
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}
48
50 return m_block_size;
51}
52
53bool CTR_BE::valid_iv_length(size_t iv_len) const {
54 return (iv_len <= m_block_size);
55}
56
57size_t CTR_BE::buffer_size() const {
58 return m_pad.size();
59}
60
62 return m_cipher->key_spec();
63}
64
65std::unique_ptr<StreamCipher> CTR_BE::new_object() const {
66 return std::make_unique<CTR_BE>(m_cipher->new_object(), m_ctr_size);
67}
68
70 return m_cipher->has_keying_material();
71}
72
73void CTR_BE::key_schedule(std::span<const uint8_t> key) {
74 m_cipher->set_key(key);
75
76 // Set a default all-zeros IV
77 set_iv(nullptr, 0);
78}
79
80std::string CTR_BE::name() const {
81 if(m_ctr_size == m_block_size) {
82 return fmt("CTR-BE({})", m_cipher->name());
83 } else {
84 return fmt("CTR-BE({},{})", m_cipher->name(), m_ctr_size);
85 }
86}
87
88void CTR_BE::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) {
90
91 const uint8_t* pad_bits = m_pad.data();
92 const size_t pad_size = m_pad.size();
93
94 /* Consume any already computed keystream in m_pad */
95
96 if(m_pad_pos > 0) {
97 const size_t avail = pad_size - m_pad_pos;
98 const size_t take = std::min(length, avail);
99 xor_buf(out, in, pad_bits + m_pad_pos, take);
100 length -= take;
101 in += take;
102 out += take;
103 m_pad_pos += take;
104
105 if(take == avail) {
106 add_counter(m_ctr_blocks);
107 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
108 m_pad_pos = 0;
109 }
110 }
111
112 /* Bulk processing */
113
114 [[maybe_unused]] const bool can_use_bs16_ctr4_fastpath = m_block_size == 16 && m_ctr_size == 4 && pad_size % 64 == 0;
115
116#if defined(BOTAN_HAS_CTR_BE_AVX2)
117 if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::AVX2)) {
118 const size_t consumed = ctr_proc_bs16_ctr4_avx2(in, out, length);
119 in += consumed;
120 out += consumed;
121 length -= consumed;
122 }
123#endif
124
125#if defined(BOTAN_HAS_CTR_BE_SIMD32)
126 if(length >= pad_size && can_use_bs16_ctr4_fastpath && CPUID::has(CPUID::Feature::SIMD_4X32)) {
127 const size_t consumed = ctr_proc_bs16_ctr4_simd32(in, out, length);
128 in += consumed;
129 out += consumed;
130 length -= consumed;
131 }
132#endif
133
134 while(length >= pad_size) {
135 xor_buf(out, in, pad_bits, pad_size);
136 length -= pad_size;
137 in += pad_size;
138 out += pad_size;
139
140 add_counter(m_ctr_blocks);
141 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
142 }
143
144 /* Now if length > 0 then we have some remaining text, and m_pad is full - consume as required */
145 if(length > 0) {
146 xor_buf(out, in, pad_bits, length);
147 m_pad_pos = length;
148 }
149}
150
151void CTR_BE::generate_keystream(uint8_t out[], size_t length) {
153
154 const size_t avail = m_pad.size() - m_pad_pos;
155 const size_t take = std::min(length, avail);
156 copy_mem(out, &m_pad[m_pad_pos], take);
157 length -= take;
158 out += take;
159 m_pad_pos += take;
160
161 while(length >= m_pad.size()) {
162 add_counter(m_ctr_blocks);
163 m_cipher->encrypt_n(m_counter.data(), out, m_ctr_blocks);
164
165 length -= m_pad.size();
166 out += m_pad.size();
167 }
168
169 if(m_pad_pos == m_pad.size()) {
170 add_counter(m_ctr_blocks);
171 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
172 m_pad_pos = 0;
173 }
174
175 copy_mem(out, m_pad.data(), length);
176 m_pad_pos += length;
177 BOTAN_ASSERT_NOMSG(m_pad_pos < m_pad.size());
178}
179
180void CTR_BE::set_iv_bytes(const uint8_t iv[], size_t iv_len) {
181 if(!valid_iv_length(iv_len)) {
182 throw Invalid_IV_Length(name(), iv_len);
183 }
184
185 m_iv.resize(m_block_size);
186 zeroise(m_iv);
187 copy_mem(m_iv.data(), iv, iv_len);
188
189 seek(0);
190}
191
192void CTR_BE::add_counter(const uint64_t counter) {
193 const size_t ctr_size = m_ctr_size;
194 const size_t ctr_blocks = m_ctr_blocks;
195 const size_t BS = m_block_size;
196
197 if(ctr_size == 4) {
198 const size_t off = (BS - 4);
199 const uint32_t low32 = static_cast<uint32_t>(counter + load_be<uint32_t>(&m_counter[off], 0));
200
201 for(size_t i = 0; i != ctr_blocks; ++i) {
202 store_be(uint32_t(low32 + i), &m_counter[i * BS + off]);
203 }
204 } else if(ctr_size == 8) {
205 const size_t off = (BS - 8);
206 const uint64_t low64 = counter + load_be<uint64_t>(&m_counter[off], 0);
207
208 for(size_t i = 0; i != ctr_blocks; ++i) {
209 store_be(uint64_t(low64 + i), &m_counter[i * BS + off]);
210 }
211 } else if(ctr_size == 16) {
212 const size_t off = (BS - 16);
213 uint64_t b0 = load_be<uint64_t>(&m_counter[off], 0);
214 uint64_t b1 = load_be<uint64_t>(&m_counter[off], 1);
215 b1 += counter;
216 b0 += (b1 < counter) ? 1 : 0; // carry
217
218 for(size_t i = 0; i != ctr_blocks; ++i) {
219 store_be(b0, &m_counter[i * BS + off]);
220 store_be(b1, &m_counter[i * BS + off + 8]);
221 b1 += 1;
222 if(b1 == 0) {
223 b0 += 1; // carry
224 }
225 }
226 } else {
227 for(size_t i = 0; i != ctr_blocks; ++i) {
228 uint64_t local_counter = counter;
229 uint16_t carry = static_cast<uint8_t>(local_counter);
230 for(size_t j = 0; (carry > 0 || local_counter > 0) && j != ctr_size; ++j) {
231 const size_t off = i * BS + (BS - 1 - j);
232 const uint16_t cnt = static_cast<uint16_t>(m_counter[off]) + carry;
233 m_counter[off] = static_cast<uint8_t>(cnt);
234 local_counter = (local_counter >> 8);
235 carry = (cnt >> 8) + static_cast<uint8_t>(local_counter);
236 }
237 }
238 }
239}
240
241void CTR_BE::seek(uint64_t offset) {
243
244 const uint64_t base_counter = m_ctr_blocks * (offset / m_counter.size());
245
246 zeroise(m_counter);
247 BOTAN_ASSERT_NOMSG(m_counter.size() >= m_iv.size());
248 copy_mem(m_counter.data(), m_iv.data(), m_iv.size());
249
250 const size_t BS = m_block_size;
251
252 // Set m_counter blocks to IV, IV + 1, ... IV + n
253
254 if(m_ctr_size == 4 && BS >= 8) {
255 const uint32_t low32 = load_be<uint32_t>(&m_counter[BS - 4], 0);
256
257 if(m_ctr_blocks >= 4 && is_power_of_2(m_ctr_blocks)) {
258 size_t written = 1;
259 while(written < m_ctr_blocks) {
260 copy_mem(&m_counter[written * BS], &m_counter[0], BS * written); // NOLINT(*container-data-pointer)
261 written *= 2;
262 }
263 } else {
264 for(size_t i = 1; i != m_ctr_blocks; ++i) {
265 copy_mem(&m_counter[i * BS], &m_counter[0], BS - 4); // NOLINT(*container-data-pointer)
266 }
267 }
268
269 for(size_t i = 1; i != m_ctr_blocks; ++i) {
270 const uint32_t c = static_cast<uint32_t>(low32 + i);
271 store_be(c, &m_counter[(BS - 4) + i * BS]);
272 }
273 } else {
274 // do everything sequentially:
275 for(size_t i = 1; i != m_ctr_blocks; ++i) {
276 copy_mem(&m_counter[i * BS], &m_counter[(i - 1) * BS], BS);
277
278 for(size_t j = 0; j != m_ctr_size; ++j) {
279 uint8_t& c = m_counter[i * BS + (BS - 1 - j)];
280 c += 1;
281 if(c > 0) {
282 break;
283 }
284 }
285 }
286 }
287
288 if(base_counter > 0) {
289 add_counter(base_counter);
290 }
291
292 m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
293 m_pad_pos = offset % m_counter.size();
294}
295} // 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:49
bool has_keying_material() const override
Definition ctr.cpp:69
size_t buffer_size() const override
Definition ctr.cpp:57
Key_Length_Specification key_spec() const override
Definition ctr.cpp:61
bool valid_iv_length(size_t iv_len) const override
Definition ctr.cpp:53
void seek(uint64_t offset) override
Definition ctr.cpp:241
std::unique_ptr< StreamCipher > new_object() const override
Definition ctr.cpp:65
std::string name() const override
Definition ctr.cpp:80
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:145
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:124
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:133
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
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:341
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504