• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 26735862306

31 May 2026 11:43PM UTC coverage: 89.37%. Remained the same
26735862306

push

github

web-flow
Merge pull request #5631 from randombit/jack/overflow-checks

Systematically eliminate any possible integer overflows

110295 of 123414 relevant lines covered (89.37%)

11075877.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/src/lib/modes/aead/ccm/ccm.cpp
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

18
namespace Botan {
19

20
// 128-bit cipher is intrinsic to CCM definition
21
static const size_t CCM_BS = 16;
22

23
/*
24
* CCM_Mode Constructor
25
*/
26
CCM_Mode::CCM_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size, size_t L) :
246✔
27
      m_tag_size(tag_size), m_L(L), m_cipher(std::move(cipher)) {
246✔
28
   if(m_cipher->block_size() != CCM_BS) {
246✔
29
      throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
×
30
   }
31

32
   if(L < 2 || L > 8) {
246✔
33
      throw Invalid_Argument(fmt("Invalid CCM L value {}", L));
×
34
   }
35

36
   if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0) {
246✔
37
      throw Invalid_Argument(fmt("Invalid CCM tag length {}", tag_size));
×
38
   }
39
}
246✔
40

41
void CCM_Mode::clear() {
70✔
42
   m_cipher->clear();
70✔
43
   m_ad_buf.clear();
70✔
44
   reset();
140✔
45
}
70✔
46

47
void CCM_Mode::reset() {
1,637✔
48
   m_nonce.clear();
762✔
49
   m_msg_buf.clear();
1,637✔
50
}
692✔
51

52
std::string CCM_Mode::name() const {
37✔
53
   return fmt("{}/CCM({},{})", m_cipher->name(), tag_size(), L());
37✔
54
}
55

56
bool CCM_Mode::valid_nonce_length(size_t length) const {
1,051✔
57
   return (length == (15 - L()));
1,051✔
58
}
59

60
size_t CCM_Mode::default_nonce_length() const {
36✔
61
   return (15 - L());
36✔
62
}
63

64
size_t CCM_Mode::update_granularity() const {
456✔
65
   return 1;
456✔
66
}
67

68
size_t CCM_Mode::ideal_granularity() const {
216✔
69
   // Completely arbitrary
70
   return m_cipher->parallel_bytes();
216✔
71
}
72

73
bool CCM_Mode::requires_entire_message() const {
84✔
74
   return true;
84✔
75
}
76

77
Key_Length_Specification CCM_Mode::key_spec() const {
246✔
78
   return m_cipher->key_spec();
246✔
79
}
80

81
bool CCM_Mode::has_keying_material() const {
245✔
82
   return m_cipher->has_keying_material();
245✔
83
}
84

85
void CCM_Mode::key_schedule(std::span<const uint8_t> key) {
244✔
86
   m_cipher->set_key(key);
244✔
87
   // Clear any per-message state; AD is preserved per AEAD contract
88
   // (CCM advertises associated_data_requires_key() == false).
89
   reset();
244✔
90
}
244✔
91

92
void CCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
985✔
93
   BOTAN_ARG_CHECK(idx == 0, "CCM: cannot handle non-zero index in set_associated_data_n");
985✔
94
   BOTAN_STATE_CHECK(m_nonce.empty());
915✔
95

96
   m_ad_buf.clear();
845✔
97

98
   if(!ad.empty()) {
845✔
99
      // FIXME: support larger AD using length encoding rules
100
      BOTAN_ARG_CHECK(ad.size() < (0xFFFF - 0xFF), "Supported CCM AD length");
811✔
101

102
      m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(ad.size())));
811✔
103
      m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(ad.size())));
811✔
104
      m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end());
811✔
105
      while(m_ad_buf.size() % CCM_BS != 0) {
8,056✔
106
         m_ad_buf.push_back(0);  // pad with zeros to full block size
7,245✔
107
      }
108
   }
109
}
845✔
110

111
void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
1,086✔
112
   BOTAN_STATE_CHECK(m_nonce.empty());
1,086✔
113

114
   if(!valid_nonce_length(nonce_len)) {
1,016✔
115
      throw Invalid_IV_Length(name(), nonce_len);
×
116
   }
117

118
   m_nonce.assign(nonce, nonce + nonce_len);
1,016✔
119
   m_msg_buf.clear();
1,016✔
120
}
1,016✔
121

122
size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz) {
1,894✔
123
   BOTAN_STATE_CHECK(!m_nonce.empty());
1,894✔
124
   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
1,754✔
125

126
   // CCM message length is limited to 2^(8*L) - 1 bytes
127
   if(L() < 8) {
1,754✔
128
      const uint64_t max_msg_len = (static_cast<uint64_t>(1) << (8 * L())) - 1;
1,692✔
129
      if(m_msg_buf.size() > max_msg_len) {
1,692✔
130
         throw Invalid_State("CCM message length exceeds the limit for L");
×
131
      }
132
   }
133

134
   return 0;  // no output until finished
1,754✔
135
}
136

137
void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) {
736✔
138
   const size_t len_bytes = L();
736✔
139

140
   BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
736✔
141

142
   for(size_t i = 0; i != len_bytes; ++i) {
2,957✔
143
      out[len_bytes - 1 - i] = get_byte_var(sizeof(uint64_t) - 1 - i, len);
2,221✔
144
   }
145

146
   if(len_bytes < 8 && (len >> (len_bytes * 8)) > 0) {
736✔
147
      throw Encoding_Error("CCM message length too long to encode in L field");
×
148
   }
149
}
736✔
150

151
void CCM_Mode::inc(secure_vector<uint8_t>& C) {
6,907✔
152
   for(size_t i = 0; i != C.size(); ++i) {
6,907✔
153
      uint8_t& b = C[C.size() - i - 1];
6,907✔
154
      b += 1;
6,907✔
155
      if(b > 0) {
6,907✔
156
         break;
157
      }
158
   }
159
}
×
160

161
secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) {
1,016✔
162
   if(m_nonce.size() != 15 - L()) {
1,016✔
163
      throw Invalid_State("CCM mode must set nonce");
280✔
164
   }
165
   secure_vector<uint8_t> B0(CCM_BS);
736✔
166

167
   const uint8_t b_flags =
736✔
168
      static_cast<uint8_t>((!m_ad_buf.empty() ? 64 : 0) + (((tag_size() / 2) - 1) << 3) + (L() - 1));
762✔
169

170
   B0[0] = b_flags;
736✔
171
   copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
736✔
172
   encode_length(sz, &B0[m_nonce.size() + 1]);
736✔
173

174
   return B0;
736✔
175
}
×
176

177
secure_vector<uint8_t> CCM_Mode::format_c0() {
736✔
178
   if(m_nonce.size() != 15 - L()) {
736✔
179
      throw Invalid_State("CCM mode must set nonce");
×
180
   }
181
   secure_vector<uint8_t> C(CCM_BS);
736✔
182

183
   const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
736✔
184

185
   C[0] = a_flags;
736✔
186
   copy_mem(&C[1], m_nonce.data(), m_nonce.size());
736✔
187

188
   return C;
736✔
189
}
×
190

191
size_t CCM_Encryption::output_length(size_t input_length) const {
145✔
192
   return add_or_throw(input_length, tag_size(), "CCM input too large");
145✔
193
}
194

195
void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
489✔
196
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
489✔
197

198
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
454✔
199

200
   const size_t sz = buffer.size() - offset;
454✔
201
   uint8_t* buf = buffer.data() + offset;
454✔
202

203
   const secure_vector<uint8_t>& ad = ad_buf();
454✔
204
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
454✔
205

206
   const BlockCipher& E = cipher();
454✔
207

208
   secure_vector<uint8_t> T(CCM_BS);
454✔
209
   E.encrypt(format_b0(sz), T);
768✔
210

211
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
861✔
212
      xor_buf(T.data(), &ad[i], CCM_BS);
547✔
213
      E.encrypt(T);
1,094✔
214
   }
215

216
   secure_vector<uint8_t> C = format_c0();
314✔
217
   secure_vector<uint8_t> S0(CCM_BS);
314✔
218
   E.encrypt(C, S0);
314✔
219
   inc(C);
314✔
220

221
   secure_vector<uint8_t> X(CCM_BS);
314✔
222

223
   const uint8_t* buf_end = &buf[sz];
314✔
224

225
   while(buf != buf_end) {
3,297✔
226
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
2,983✔
227

228
      xor_buf(T.data(), buf, to_proc);
2,983✔
229
      E.encrypt(T);
2,983✔
230

231
      E.encrypt(C, X);
2,983✔
232
      xor_buf(buf, X.data(), to_proc);
2,983✔
233
      inc(C);
2,983✔
234

235
      buf += to_proc;
2,983✔
236
   }
237

238
   T ^= S0;
314✔
239

240
   buffer += std::make_pair(T.data(), tag_size());
314✔
241

242
   reset();
628✔
243
}
1,256✔
244

245
size_t CCM_Decryption::output_length(size_t input_length) const {
146✔
246
   BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
146✔
247
   return input_length - tag_size();
146✔
248
}
249

250
void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
597✔
251
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
597✔
252

253
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
562✔
254

255
   const size_t sz = buffer.size() - offset;
562✔
256
   uint8_t* buf = buffer.data() + offset;
562✔
257

258
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
562✔
259

260
   const secure_vector<uint8_t>& ad = ad_buf();
562✔
261
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
562✔
262

263
   const BlockCipher& E = cipher();
562✔
264

265
   secure_vector<uint8_t> T(CCM_BS);
562✔
266
   E.encrypt(format_b0(sz - tag_size()), T);
1,089✔
267

268
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
1,188✔
269
      xor_buf(T.data(), &ad[i], CCM_BS);
766✔
270
      E.encrypt(T);
1,532✔
271
   }
272

273
   secure_vector<uint8_t> C = format_c0();
422✔
274

275
   secure_vector<uint8_t> S0(CCM_BS);
527✔
276
   E.encrypt(C, S0);
422✔
277
   inc(C);
422✔
278

279
   secure_vector<uint8_t> X(CCM_BS);
527✔
280

281
   const uint8_t* buf_end = &buf[sz - tag_size()];
422✔
282

283
   while(buf != buf_end) {
3,610✔
284
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
3,188✔
285

286
      E.encrypt(C, X);
3,188✔
287
      xor_buf(buf, X.data(), to_proc);
3,188✔
288
      inc(C);
3,188✔
289

290
      xor_buf(T.data(), buf, to_proc);
3,188✔
291
      E.encrypt(T);
3,188✔
292

293
      buf += to_proc;
3,188✔
294
   }
295

296
   T ^= S0;
422✔
297

298
   if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) {
422✔
299
      clear_mem(std::span{buffer}.subspan(offset, sz - tag_size()));
105✔
300
      throw Invalid_Authentication_Tag("CCM tag check failed");
105✔
301
   }
302

303
   buffer.resize(buffer.size() - tag_size());
317✔
304

305
   reset();
634✔
306
}
1,583✔
307

308
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc