Coverage Report

Created: 2026-04-28 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/modes/aead/ccm/ccm.cpp
Line
Count
Source
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/loadstor.h>
16
17
namespace Botan {
18
19
// 128-bit cipher is intrinsic to CCM definition
20
static const size_t CCM_BS = 16;
21
22
/*
23
* CCM_Mode Constructor
24
*/
25
CCM_Mode::CCM_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size, size_t L) :
26
161
      m_tag_size(tag_size), m_L(L), m_cipher(std::move(cipher)) {
27
161
   if(m_cipher->block_size() != CCM_BS) {
28
0
      throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
29
0
   }
30
31
161
   if(L < 2 || L > 8) {
32
0
      throw Invalid_Argument(fmt("Invalid CCM L value {}", L));
33
0
   }
34
35
161
   if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) {
36
0
      throw Invalid_Argument(fmt("Invalid CCM tag length {}", tag_size));
37
0
   }
38
161
}
39
40
0
void CCM_Mode::clear() {
41
0
   m_cipher->clear();
42
0
   reset();
43
0
}
44
45
132
void CCM_Mode::reset() {
46
132
   m_nonce.clear();
47
132
   m_msg_buf.clear();
48
132
   m_ad_buf.clear();
49
132
}
50
51
0
std::string CCM_Mode::name() const {
52
0
   return fmt("{}/CCM({},{})", m_cipher->name(), tag_size(), L());
53
0
}
54
55
210
bool CCM_Mode::valid_nonce_length(size_t length) const {
56
210
   return (length == (15 - L()));
57
210
}
58
59
0
size_t CCM_Mode::default_nonce_length() const {
60
0
   return (15 - L());
61
0
}
62
63
0
size_t CCM_Mode::update_granularity() const {
64
0
   return 1;
65
0
}
66
67
0
size_t CCM_Mode::ideal_granularity() const {
68
   // Completely arbitrary
69
0
   return m_cipher->parallel_bytes();
70
0
}
71
72
0
bool CCM_Mode::requires_entire_message() const {
73
0
   return true;
74
0
}
75
76
161
Key_Length_Specification CCM_Mode::key_spec() const {
77
161
   return m_cipher->key_spec();
78
161
}
79
80
0
bool CCM_Mode::has_keying_material() const {
81
0
   return m_cipher->has_keying_material();
82
0
}
83
84
161
void CCM_Mode::key_schedule(std::span<const uint8_t> key) {
85
161
   m_cipher->set_key(key);
86
161
}
87
88
210
void CCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
89
210
   BOTAN_ARG_CHECK(idx == 0, "CCM: cannot handle non-zero index in set_associated_data_n");
90
91
210
   m_ad_buf.clear();
92
93
210
   if(!ad.empty()) {
94
      // FIXME: support larger AD using length encoding rules
95
210
      BOTAN_ARG_CHECK(ad.size() < (0xFFFF - 0xFF), "Supported CCM AD length");
96
97
210
      m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(ad.size())));
98
210
      m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(ad.size())));
99
210
      m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end());
100
420
      while(m_ad_buf.size() % CCM_BS != 0) {
101
210
         m_ad_buf.push_back(0);  // pad with zeros to full block size
102
210
      }
103
210
   }
104
210
}
105
106
210
void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
107
210
   if(!valid_nonce_length(nonce_len)) {
108
0
      throw Invalid_IV_Length(name(), nonce_len);
109
0
   }
110
111
210
   m_nonce.assign(nonce, nonce + nonce_len);
112
210
   m_msg_buf.clear();
113
210
}
114
115
0
size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz) {
116
0
   BOTAN_STATE_CHECK(!m_nonce.empty());
117
0
   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
118
119
   // CCM message length is limited to 2^(8*L) - 1 bytes
120
0
   if(L() < 8) {
121
0
      const uint64_t max_msg_len = (static_cast<uint64_t>(1) << (8 * L())) - 1;
122
0
      if(m_msg_buf.size() > max_msg_len) {
123
0
         throw Invalid_State("CCM message length exceeds the limit for L");
124
0
      }
125
0
   }
126
127
0
   return 0;  // no output until finished
128
0
}
129
130
210
void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) {
131
210
   const size_t len_bytes = L();
132
133
210
   BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
134
135
840
   for(size_t i = 0; i != len_bytes; ++i) {
136
630
      out[len_bytes - 1 - i] = get_byte_var(sizeof(uint64_t) - 1 - i, len);
137
630
   }
138
139
210
   if(len_bytes < 8 && (len >> (len_bytes * 8)) > 0) {
140
0
      throw Encoding_Error("CCM message length too long to encode in L field");
141
0
   }
142
210
}
143
144
3.60k
void CCM_Mode::inc(secure_vector<uint8_t>& C) {
145
3.61k
   for(size_t i = 0; i != C.size(); ++i) {
146
3.61k
      uint8_t& b = C[C.size() - i - 1];
147
3.61k
      b += 1;
148
3.61k
      if(b > 0) {
149
3.60k
         break;
150
3.60k
      }
151
3.61k
   }
152
3.60k
}
153
154
210
secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) {
155
210
   if(m_nonce.size() != 15 - L()) {
156
0
      throw Invalid_State("CCM mode must set nonce");
157
0
   }
158
210
   secure_vector<uint8_t> B0(CCM_BS);
159
160
210
   const uint8_t b_flags =
161
210
      static_cast<uint8_t>((!m_ad_buf.empty() ? 64 : 0) + (((tag_size() / 2) - 1) << 3) + (L() - 1));
162
163
210
   B0[0] = b_flags;
164
210
   copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
165
210
   encode_length(sz, &B0[m_nonce.size() + 1]);
166
167
210
   return B0;
168
210
}
169
170
210
secure_vector<uint8_t> CCM_Mode::format_c0() {
171
210
   if(m_nonce.size() != 15 - L()) {
172
0
      throw Invalid_State("CCM mode must set nonce");
173
0
   }
174
210
   secure_vector<uint8_t> C(CCM_BS);
175
176
210
   const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
177
178
210
   C[0] = a_flags;
179
210
   copy_mem(&C[1], m_nonce.data(), m_nonce.size());
180
181
210
   return C;
182
210
}
183
184
132
void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
185
132
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
186
187
132
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
188
189
132
   const size_t sz = buffer.size() - offset;
190
132
   uint8_t* buf = buffer.data() + offset;
191
192
132
   const secure_vector<uint8_t>& ad = ad_buf();
193
132
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
194
195
132
   const BlockCipher& E = cipher();
196
197
132
   secure_vector<uint8_t> T(CCM_BS);
198
132
   E.encrypt(format_b0(sz), T);
199
200
264
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
201
132
      xor_buf(T.data(), &ad[i], CCM_BS);
202
132
      E.encrypt(T);
203
132
   }
204
205
132
   secure_vector<uint8_t> C = format_c0();
206
132
   secure_vector<uint8_t> S0(CCM_BS);
207
132
   E.encrypt(C, S0);
208
132
   inc(C);
209
210
132
   secure_vector<uint8_t> X(CCM_BS);
211
212
132
   const uint8_t* buf_end = &buf[sz];
213
214
264
   while(buf != buf_end) {
215
132
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
216
217
132
      xor_buf(T.data(), buf, to_proc);
218
132
      E.encrypt(T);
219
220
132
      E.encrypt(C, X);
221
132
      xor_buf(buf, X.data(), to_proc);
222
132
      inc(C);
223
224
132
      buf += to_proc;
225
132
   }
226
227
132
   T ^= S0;
228
229
132
   buffer += std::make_pair(T.data(), tag_size());
230
231
132
   reset();
232
132
}
233
234
78
void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
235
78
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
236
237
78
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
238
239
78
   const size_t sz = buffer.size() - offset;
240
78
   uint8_t* buf = buffer.data() + offset;
241
242
78
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
243
244
78
   const secure_vector<uint8_t>& ad = ad_buf();
245
78
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
246
247
78
   const BlockCipher& E = cipher();
248
249
78
   secure_vector<uint8_t> T(CCM_BS);
250
78
   E.encrypt(format_b0(sz - tag_size()), T);
251
252
156
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
253
78
      xor_buf(T.data(), &ad[i], CCM_BS);
254
78
      E.encrypt(T);
255
78
   }
256
257
78
   secure_vector<uint8_t> C = format_c0();
258
259
78
   secure_vector<uint8_t> S0(CCM_BS);
260
78
   E.encrypt(C, S0);
261
78
   inc(C);
262
263
78
   secure_vector<uint8_t> X(CCM_BS);
264
265
78
   const uint8_t* buf_end = &buf[sz - tag_size()];
266
267
3.34k
   while(buf != buf_end) {
268
3.26k
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
269
270
3.26k
      E.encrypt(C, X);
271
3.26k
      xor_buf(buf, X.data(), to_proc);
272
3.26k
      inc(C);
273
274
3.26k
      xor_buf(T.data(), buf, to_proc);
275
3.26k
      E.encrypt(T);
276
277
3.26k
      buf += to_proc;
278
3.26k
   }
279
280
78
   T ^= S0;
281
282
78
   if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) {
283
78
      clear_mem(std::span{buffer}.subspan(offset, sz - tag_size()));
284
78
      throw Invalid_Authentication_Tag("CCM tag check failed");
285
78
   }
286
287
0
   buffer.resize(buffer.size() - tag_size());
288
289
0
   reset();
290
0
}
291
292
}  // namespace Botan