Botan 3.13.0
Crypto and TLS for C&
cbc.cpp
Go to the documentation of this file.
1/*
2* CBC Mode
3* (C) 1999-2007,2013,2017 Jack Lloyd
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5* (C) 2018 Ribose Inc
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/cbc.h>
11
12#include <botan/exceptn.h>
13#include <botan/mem_ops.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/int_utils.h>
16#include <botan/internal/mode_pad.h>
17
18namespace Botan {
19
20CBC_Mode::CBC_Mode(std::unique_ptr<BlockCipher> cipher, std::unique_ptr<BlockCipherModePaddingMethod> padding) :
21 m_cipher(std::move(cipher)), m_padding(std::move(padding)), m_block_size(m_cipher->block_size()) {
22 if(m_padding && !m_padding->valid_blocksize(m_block_size)) {
23 throw Invalid_Argument(fmt("Padding {} cannot be used with {} in CBC mode", m_padding->name(), m_cipher->name()));
24 }
25}
26
28 m_cipher->clear();
29 reset();
30}
31
33 m_state.clear();
34}
35
36std::string CBC_Mode::name() const {
37 if(m_padding) {
38 return fmt("{}/CBC/{}", cipher().name(), padding().name());
39 } else {
40 return fmt("{}/CBC/CTS", cipher().name());
41 }
42}
43
45 return cipher().block_size();
46}
47
49 return cipher().parallel_bytes();
50}
51
55
57 return block_size();
58}
59
60bool CBC_Mode::valid_nonce_length(size_t n) const {
61 return (n == 0 || n == block_size());
62}
63
65 return m_cipher->has_keying_material();
66}
67
68void CBC_Mode::key_schedule(std::span<const uint8_t> key) {
69 m_cipher->set_key(key);
70 m_state.clear();
71}
72
73void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
74 if(!valid_nonce_length(nonce_len)) {
75 throw Invalid_IV_Length(name(), nonce_len);
76 }
77
78 /*
79 * A nonce of zero length means carry the last ciphertext value over
80 * as the new IV, as unfortunately some protocols require this. If
81 * this is the first message then we use an IV of all zeros.
82 */
83 if(nonce_len > 0) {
84 m_state.assign(nonce, nonce + nonce_len);
85 } else if(m_state.empty()) {
86 m_state.resize(m_cipher->block_size());
87 }
88 // else leave the state alone
89}
90
92 return 0;
93}
94
95size_t CBC_Encryption::output_length(size_t input_length) const {
96 return padding().output_length(input_length, block_size());
97}
98
99size_t CBC_Encryption::process_msg(uint8_t buf[], size_t sz) {
100 BOTAN_STATE_CHECK(state().empty() == false);
101 const size_t BS = block_size();
102
103 BOTAN_ARG_CHECK(sz % BS == 0, "CBC input is not full blocks");
104 const size_t blocks = sz / BS;
105
106 if(blocks > 0) {
107 xor_buf(&buf[0], state_ptr(), BS);
108 cipher().encrypt(&buf[0]);
109
110 for(size_t i = 1; i != blocks; ++i) {
111 xor_buf(&buf[BS * i], &buf[BS * (i - 1)], BS);
112 cipher().encrypt(&buf[BS * i]);
113 }
114
115 state().assign(&buf[BS * (blocks - 1)], &buf[BS * blocks]);
116 }
117
118 return sz;
119}
120
121void CBC_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
122 BOTAN_STATE_CHECK(state().empty() == false);
123 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
124
125 const size_t BS = block_size();
126
127 const size_t output_bytes =
128 add_or_throw(offset, padding().output_length(buffer.size() - offset, BS), "CBC input too large");
129 const size_t bytes_in_final_block = (buffer.size() - offset) % BS;
130 buffer.resize(output_bytes);
131 padding().add_padding(std::span(buffer).subspan(offset), bytes_in_final_block, BS);
132
133 // With NoPadding a non-block-multiple input reaches here un-padded; reject it
134 // as an argument error rather than tripping the assertion.
135 BOTAN_ARG_CHECK(buffer.size() % BS == offset % BS, "CBC input is not full blocks (NoPadding)");
136
137 update(buffer, offset);
138}
139
141 return (n == block_size());
142}
143
145 return block_size() + 1;
146}
147
148size_t CTS_Encryption::output_length(size_t input_length) const {
149 return input_length; // no ciphertext expansion in CTS
150}
151
152void CTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
153 BOTAN_STATE_CHECK(state().empty() == false);
154 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
155 uint8_t* buf = buffer.data() + offset;
156 const size_t sz = buffer.size() - offset;
157
158 const size_t BS = block_size();
159
160 if(sz < BS + 1) {
161 throw Encoding_Error(name() + ": insufficient data to encrypt");
162 }
163
164 if(sz % BS == 0) {
165 update(buffer, offset);
166
167 // swap last two blocks
168 for(size_t i = 0; i != BS; ++i) {
169 std::swap(buffer[buffer.size() - BS + i], buffer[buffer.size() - 2 * BS + i]);
170 }
171 } else {
172 const size_t full_blocks = ((sz / BS) - 1) * BS;
173 const size_t final_bytes = sz - full_blocks;
174 BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
175
176 secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
177 buffer.resize(full_blocks + offset);
178 update(buffer, offset);
179
180 xor_buf(last.data(), state_ptr(), BS);
181 cipher().encrypt(last.data());
182
183 for(size_t i = 0; i != final_bytes - BS; ++i) {
184 last[i] ^= last[i + BS];
185 last[i + BS] ^= last[i];
186 }
187
188 cipher().encrypt(last.data());
189
190 buffer += last;
191 }
192}
193
194size_t CBC_Decryption::output_length(size_t input_length) const {
195 return input_length; // precise for CTS, worst case otherwise
196}
197
199 return block_size();
200}
201
202size_t CBC_Decryption::process_msg(uint8_t buf[], size_t sz) {
203 BOTAN_STATE_CHECK(state().empty() == false);
204
205 const size_t BS = block_size();
206
207 BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
208 size_t blocks = sz / BS;
209
210 while(blocks > 0) {
211 const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());
212
213 cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS);
214
215 xor_buf(m_tempbuf.data(), state_ptr(), BS);
216 xor_buf(&m_tempbuf[BS], buf, to_proc - BS);
217 copy_mem(state_ptr(), buf + (to_proc - BS), BS);
218
219 copy_mem(buf, m_tempbuf.data(), to_proc);
220
221 buf += to_proc;
222 blocks -= to_proc / BS;
223 }
224
225 return sz;
226}
227
228void CBC_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
229 BOTAN_STATE_CHECK(state().empty() == false);
230 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
231 const size_t sz = buffer.size() - offset;
232
233 const size_t BS = block_size();
234
235 if(sz == 0 || sz % BS != 0) {
236 throw Decoding_Error(name() + ": Ciphertext not a multiple of block size");
237 }
238
239 update(buffer, offset);
240
241 const size_t pad_bytes = BS - padding().unpad(std::span{buffer}.last(BS));
242 buffer.resize(buffer.size() - pad_bytes); // remove padding
243 if(pad_bytes == 0 && padding().name() != "NoPadding") {
244 clear_mem(std::span{buffer}.subspan(offset));
245 throw Decoding_Error("Invalid CBC padding");
246 }
247}
248
251 zeroise(m_tempbuf);
252}
253
255 return (n == block_size());
256}
257
259 return block_size() + 1;
260}
261
262void CTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
263 BOTAN_STATE_CHECK(state().empty() == false);
264 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
265 const size_t sz = buffer.size() - offset;
266 uint8_t* buf = buffer.data() + offset;
267
268 const size_t BS = block_size();
269
270 if(sz < BS + 1) {
271 throw Encoding_Error(name() + ": insufficient data to decrypt");
272 }
273
274 if(sz % BS == 0) {
275 // swap last two blocks
276
277 for(size_t i = 0; i != BS; ++i) {
278 std::swap(buffer[buffer.size() - BS + i], buffer[buffer.size() - 2 * BS + i]);
279 }
280
281 update(buffer, offset);
282 } else {
283 const size_t full_blocks = ((sz / BS) - 1) * BS;
284 const size_t final_bytes = sz - full_blocks;
285 BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
286
287 secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
288 buffer.resize(full_blocks + offset);
289 update(buffer, offset);
290
291 cipher().decrypt(last.data());
292
293 xor_buf(last.data(), &last[BS], final_bytes - BS);
294
295 for(size_t i = 0; i != final_bytes - BS; ++i) {
296 std::swap(last[i], last[i + BS]);
297 }
298
299 cipher().decrypt(last.data());
300 xor_buf(last.data(), state_ptr(), BS);
301
302 buffer += last;
303 }
304}
305
306} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
virtual size_t output_length(size_t input_length, size_t block_size) const
Definition mode_pad.cpp:17
size_t unpad(std::span< const uint8_t > last_block) const
Definition mode_pad.cpp:60
virtual void add_padding(std::span< uint8_t > buffer, size_t final_block_bytes, size_t block_size) const
Definition mode_pad.cpp:50
void encrypt(const uint8_t in[], uint8_t out[]) const
void decrypt(const uint8_t in[], uint8_t out[]) const
virtual void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const =0
virtual size_t block_size() const =0
size_t parallel_bytes() const
size_t minimum_final_size() const override
Definition cbc.cpp:198
size_t output_length(size_t input_length) const override
Definition cbc.cpp:194
void reset() override
Definition cbc.cpp:249
size_t minimum_final_size() const override
Definition cbc.cpp:91
size_t output_length(size_t input_length) const override
Definition cbc.cpp:95
std::string name() const final
Definition cbc.cpp:36
size_t update_granularity() const final
Definition cbc.cpp:44
size_t ideal_granularity() const final
Definition cbc.cpp:48
const BlockCipherModePaddingMethod & padding() const
Definition cbc.h:47
size_t block_size() const
Definition cbc.h:52
bool valid_nonce_length(size_t n) const override
Definition cbc.cpp:60
CBC_Mode(std::unique_ptr< BlockCipher > cipher, std::unique_ptr< BlockCipherModePaddingMethod > padding)
Definition cbc.cpp:20
void reset() override
Definition cbc.cpp:32
size_t default_nonce_length() const final
Definition cbc.cpp:56
const BlockCipher & cipher() const
Definition cbc.h:45
void clear() final
Definition cbc.cpp:27
secure_vector< uint8_t > & state()
Definition cbc.h:54
uint8_t * state_ptr()
Definition cbc.h:56
Key_Length_Specification key_spec() const final
Definition cbc.cpp:52
bool has_keying_material() const final
Definition cbc.cpp:64
bool valid_nonce_length(size_t n) const override
Definition cbc.cpp:254
size_t minimum_final_size() const override
Definition cbc.cpp:258
size_t output_length(size_t input_length) const override
Definition cbc.cpp:148
size_t minimum_final_size() const override
Definition cbc.cpp:144
bool valid_nonce_length(size_t n) const override
Definition cbc.cpp:140
void update(T &buffer, size_t offset=0)
virtual Key_Length_Specification key_spec() const =0
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:241
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
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118