Coverage Report

Created: 2026-10-10 06:25

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