Botan 3.13.0
Crypto and TLS for C&
ccm.cpp
Go to the documentation of this file.
1/*
2* CCM Mode Encryption
3* (C) 2013,2018 Jack Lloyd
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/ccm.h>
10
11#include <botan/exceptn.h>
12#include <botan/mem_ops.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/int_utils.h>
16#include <botan/internal/loadstor.h>
17
18namespace Botan {
19
20// 128-bit cipher is intrinsic to CCM definition
21static const size_t CCM_BS = 16;
22
23/*
24* CCM_Mode Constructor
25*/
26CCM_Mode::CCM_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size, size_t L) :
27 m_tag_size(tag_size), m_L(L), m_cipher(std::move(cipher)) {
28 if(m_cipher->block_size() != CCM_BS) {
29 throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
30 }
31
32 if(L < 2 || L > 8) {
33 throw Invalid_Argument(fmt("Invalid CCM L value {}", L));
34 }
35
36 if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) {
37 throw Invalid_Argument(fmt("Invalid CCM tag length {}", tag_size));
38 }
39}
40
42 m_cipher->clear();
43 m_ad_buf.clear();
44 reset();
45}
46
48 m_nonce.clear();
49 m_msg_buf.clear();
50}
51
52std::string CCM_Mode::name() const {
53 return fmt("{}/CCM({},{})", m_cipher->name(), tag_size(), L());
54}
55
56bool CCM_Mode::valid_nonce_length(size_t length) const {
57 return (length == (15 - L()));
58}
59
61 return (15 - L());
62}
63
65 return 1;
66}
67
69 // Completely arbitrary
70 return m_cipher->parallel_bytes();
71}
72
74 return true;
75}
76
78 return m_cipher->key_spec();
79}
80
82 return m_cipher->has_keying_material();
83}
84
85void CCM_Mode::key_schedule(std::span<const uint8_t> key) {
86 m_cipher->set_key(key);
87 // Clear any per-message state; AD is preserved per AEAD contract
88 // (CCM advertises associated_data_requires_key() == false).
89 reset();
90}
91
92void CCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
93 BOTAN_ARG_CHECK(idx == 0, "CCM: cannot handle non-zero index in set_associated_data_n");
94 BOTAN_STATE_CHECK(m_nonce.empty());
95
96 m_ad_buf.clear();
97
98 if(!ad.empty()) {
99 // FIXME: support larger AD using length encoding rules
100 BOTAN_ARG_CHECK(ad.size() < (0xFFFF - 0xFF), "Supported CCM AD length");
101
102 m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(ad.size())));
103 m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(ad.size())));
104 m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end());
105 while(m_ad_buf.size() % CCM_BS != 0) {
106 m_ad_buf.push_back(0); // pad with zeros to full block size
107 }
108 }
109}
110
111void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
112 BOTAN_STATE_CHECK(m_nonce.empty());
113
114 if(!valid_nonce_length(nonce_len)) {
115 throw Invalid_IV_Length(name(), nonce_len);
116 }
117
118 m_nonce.assign(nonce, nonce + nonce_len);
119 m_msg_buf.clear();
120}
121
122size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz) {
123 BOTAN_STATE_CHECK(!m_nonce.empty());
124 m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
125
126 // CCM message length is limited to 2^(8*L) - 1 bytes
127 if(L() < 8) {
128 const uint64_t max_msg_len = (static_cast<uint64_t>(1) << (8 * L())) - 1;
129 if(m_msg_buf.size() > max_msg_len) {
130 throw Invalid_State("CCM message length exceeds the limit for L");
131 }
132 }
133
134 return 0; // no output until finished
135}
136
137void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) {
138 const size_t len_bytes = L();
139
140 BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
141
142 for(size_t i = 0; i != len_bytes; ++i) {
143 out[len_bytes - 1 - i] = get_byte_var(sizeof(uint64_t) - 1 - i, len);
144 }
145
146 if(len_bytes < 8 && (len >> (len_bytes * 8)) > 0) {
147 throw Encoding_Error("CCM message length too long to encode in L field");
148 }
149}
150
152 for(size_t i = 0; i != C.size(); ++i) {
153 uint8_t& b = C[C.size() - i - 1];
154 b += 1;
155 if(b > 0) {
156 break;
157 }
158 }
159}
160
162 if(m_nonce.size() != 15 - L()) {
163 throw Invalid_State("CCM mode must set nonce");
164 }
165 secure_vector<uint8_t> B0(CCM_BS);
166
167 const uint8_t b_flags =
168 static_cast<uint8_t>((!m_ad_buf.empty() ? 64 : 0) + (((tag_size() / 2) - 1) << 3) + (L() - 1));
169
170 B0[0] = b_flags;
171 copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
172 encode_length(sz, &B0[m_nonce.size() + 1]);
173
174 return B0;
175}
176
178 if(m_nonce.size() != 15 - L()) {
179 throw Invalid_State("CCM mode must set nonce");
180 }
181 secure_vector<uint8_t> C(CCM_BS);
182
183 const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
184
185 C[0] = a_flags;
186 copy_mem(&C[1], m_nonce.data(), m_nonce.size());
187
188 return C;
189}
190
191size_t CCM_Encryption::output_length(size_t input_length) const {
192 return add_or_throw(input_length, tag_size(), "CCM input too large");
193}
194
195void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
196 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
197
198 buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
199
200 const size_t sz = buffer.size() - offset;
201 uint8_t* buf = buffer.data() + offset;
202
203 const secure_vector<uint8_t>& ad = ad_buf();
204 BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
205
206 const BlockCipher& E = cipher();
207
208 secure_vector<uint8_t> T(CCM_BS);
209 E.encrypt(format_b0(sz), T);
210
211 for(size_t i = 0; i != ad.size(); i += CCM_BS) {
212 xor_buf(T.data(), &ad[i], CCM_BS);
213 E.encrypt(T);
214 }
215
217 secure_vector<uint8_t> S0(CCM_BS);
218 E.encrypt(C, S0);
219 inc(C);
220
221 secure_vector<uint8_t> X(CCM_BS);
222
223 const uint8_t* buf_end = &buf[sz];
224
225 while(buf != buf_end) {
226 const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
227
228 xor_buf(T.data(), buf, to_proc);
229 E.encrypt(T);
230
231 E.encrypt(C, X);
232 xor_buf(buf, X.data(), to_proc);
233 inc(C);
234
235 buf += to_proc;
236 }
237
238 T ^= S0;
239
240 buffer += std::make_pair(T.data(), tag_size());
241
242 reset();
243}
244
245size_t CCM_Decryption::output_length(size_t input_length) const {
246 BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
247 return input_length - tag_size();
248}
249
250void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
251 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
252
253 buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
254
255 const size_t sz = buffer.size() - offset;
256 uint8_t* buf = buffer.data() + offset;
257
258 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
259
260 const secure_vector<uint8_t>& ad = ad_buf();
261 BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
262
263 const BlockCipher& E = cipher();
264
265 secure_vector<uint8_t> T(CCM_BS);
266 E.encrypt(format_b0(sz - tag_size()), T);
267
268 for(size_t i = 0; i != ad.size(); i += CCM_BS) {
269 xor_buf(T.data(), &ad[i], CCM_BS);
270 E.encrypt(T);
271 }
272
274
275 secure_vector<uint8_t> S0(CCM_BS);
276 E.encrypt(C, S0);
277 inc(C);
278
279 secure_vector<uint8_t> X(CCM_BS);
280
281 const uint8_t* buf_end = &buf[sz - tag_size()];
282
283 while(buf != buf_end) {
284 const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
285
286 E.encrypt(C, X);
287 xor_buf(buf, X.data(), to_proc);
288 inc(C);
289
290 xor_buf(T.data(), buf, to_proc);
291 E.encrypt(T);
292
293 buf += to_proc;
294 }
295
296 T ^= S0;
297
298 if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) {
299 clear_mem(std::span{buffer}.subspan(offset, sz - tag_size()));
300 // Reset on the failure path too, matching GCM/SIV/ChaCha20Poly1305, so a
301 // failed decryptor is reusable rather than stuck in a partial state.
302 reset();
303 throw Invalid_Authentication_Tag("CCM tag check failed");
304 }
305
306 buffer.resize(buffer.size() - tag_size());
307
308 reset();
309}
310
311} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
void encrypt(const uint8_t in[], uint8_t out[]) const
size_t output_length(size_t input_length) const override
Definition ccm.cpp:245
size_t output_length(size_t input_length) const override
Definition ccm.cpp:191
size_t ideal_granularity() const final
Definition ccm.cpp:68
bool requires_entire_message() const final
Definition ccm.cpp:73
void reset() final
Definition ccm.cpp:47
static void inc(secure_vector< uint8_t > &C)
Definition ccm.cpp:151
size_t update_granularity() const final
Definition ccm.cpp:64
void clear() final
Definition ccm.cpp:41
secure_vector< uint8_t > & msg_buf()
Definition ccm.h:64
const BlockCipher & cipher() const
Definition ccm.h:56
void encode_length(uint64_t len, uint8_t out[])
Definition ccm.cpp:137
size_t L() const
Definition ccm.h:54
size_t tag_size() const final
Definition ccm.h:47
Key_Length_Specification key_spec() const final
Definition ccm.cpp:77
bool valid_nonce_length(size_t length) const final
Definition ccm.cpp:56
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) final
Definition ccm.cpp:92
secure_vector< uint8_t > format_c0()
Definition ccm.cpp:177
bool has_keying_material() const final
Definition ccm.cpp:81
std::string name() const final
Definition ccm.cpp:52
const secure_vector< uint8_t > & ad_buf() const
Definition ccm.h:62
CCM_Mode(std::unique_ptr< BlockCipher > cipher, size_t tag_size, size_t L)
Definition ccm.cpp:26
secure_vector< uint8_t > format_b0(size_t msg_size)
Definition ccm.cpp:161
size_t default_nonce_length() const final
Definition ccm.cpp:60
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
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 uint8_t get_byte_var(size_t byte_num, T input)
Definition loadstor.h:69
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118