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