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

randombit / botan / 24032704228

06 Apr 2026 12:58PM UTC coverage: 89.451% (-0.003%) from 89.454%
24032704228

Pull #5521

github

web-flow
Merge 17f437d7f into 417709dd7
Pull Request #5521: Rollup of small fixes

105882 of 118369 relevant lines covered (89.45%)

11473459.55 hits per line

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

93.83
/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/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) :
244✔
26
      m_tag_size(tag_size), m_L(L), m_cipher(std::move(cipher)) {
244✔
27
   if(m_cipher->block_size() != CCM_BS) {
244✔
28
      throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
×
29
   }
30

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

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

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

45
void CCM_Mode::reset() {
807✔
46
   m_nonce.clear();
382✔
47
   m_msg_buf.clear();
807✔
48
   m_ad_buf.clear();
807✔
49
}
312✔
50

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

55
bool CCM_Mode::valid_nonce_length(size_t length) const {
635✔
56
   return (length == (15 - L()));
635✔
57
}
58

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

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

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

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

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

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

84
void CCM_Mode::key_schedule(std::span<const uint8_t> key) {
174✔
85
   m_cipher->set_key(key);
174✔
86
}
174✔
87

88
void CCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
664✔
89
   BOTAN_ARG_CHECK(idx == 0, "CCM: cannot handle non-zero index in set_associated_data_n");
664✔
90

91
   m_ad_buf.clear();
664✔
92

93
   if(!ad.empty()) {
664✔
94
      // FIXME: support larger AD using length encoding rules
95
      BOTAN_ARG_CHECK(ad.size() < (0xFFFF - 0xFF), "Supported CCM AD length");
640✔
96

97
      m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(ad.size())));
640✔
98
      m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(ad.size())));
640✔
99
      m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end());
640✔
100
      while(m_ad_buf.size() % CCM_BS != 0) {
6,089✔
101
         m_ad_buf.push_back(0);  // pad with zeros to full block size
5,449✔
102
      }
103
   }
104
}
664✔
105

106
void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
600✔
107
   if(!valid_nonce_length(nonce_len)) {
600✔
108
      throw Invalid_IV_Length(name(), nonce_len);
×
109
   }
110

111
   m_nonce.assign(nonce, nonce + nonce_len);
600✔
112
   m_msg_buf.clear();
600✔
113
}
600✔
114

115
size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz) {
1,894✔
116
   BOTAN_STATE_CHECK(!m_nonce.empty());
1,894✔
117
   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
1,754✔
118

119
   // CCM message length is limited to 2^(8*L) - 1 bytes
120
   if(L() < 8) {
1,754✔
121
      const uint64_t max_msg_len = (static_cast<uint64_t>(1) << (8 * L())) - 1;
1,692✔
122
      if(m_msg_buf.size() > max_msg_len) {
1,692✔
123
         throw Invalid_State("CCM message length exceeds the limit for L");
×
124
      }
125
   }
126

127
   return 0;  // no output until finished
1,754✔
128
}
129

130
void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) {
530✔
131
   const size_t len_bytes = L();
530✔
132

133
   BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
530✔
134

135
   for(size_t i = 0; i != len_bytes; ++i) {
2,123✔
136
      out[len_bytes - 1 - i] = get_byte_var(sizeof(uint64_t) - 1 - i, len);
1,593✔
137
   }
138

139
   if(len_bytes < 8 && (len >> (len_bytes * 8)) > 0) {
530✔
140
      throw Encoding_Error("CCM message length too long to encode in L field");
×
141
   }
142
}
530✔
143

144
void CCM_Mode::inc(secure_vector<uint8_t>& C) {
6,154✔
145
   for(size_t i = 0; i != C.size(); ++i) {
6,154✔
146
      uint8_t& b = C[C.size() - i - 1];
6,154✔
147
      b += 1;
6,154✔
148
      if(b > 0) {
6,154✔
149
         break;
150
      }
151
   }
152
}
×
153

154
secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) {
740✔
155
   if(m_nonce.size() != 15 - L()) {
740✔
156
      throw Invalid_State("CCM mode must set nonce");
210✔
157
   }
158
   secure_vector<uint8_t> B0(CCM_BS);
530✔
159

160
   const uint8_t b_flags =
530✔
161
      static_cast<uint8_t>((!m_ad_buf.empty() ? 64 : 0) + (((tag_size() / 2) - 1) << 3) + (L() - 1));
552✔
162

163
   B0[0] = b_flags;
530✔
164
   copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
530✔
165
   encode_length(sz, &B0[m_nonce.size() + 1]);
530✔
166

167
   return B0;
530✔
168
}
×
169

170
secure_vector<uint8_t> CCM_Mode::format_c0() {
530✔
171
   if(m_nonce.size() != 15 - L()) {
530✔
172
      throw Invalid_State("CCM mode must set nonce");
×
173
   }
174
   secure_vector<uint8_t> C(CCM_BS);
530✔
175

176
   const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
530✔
177

178
   C[0] = a_flags;
530✔
179
   copy_mem(&C[1], m_nonce.data(), m_nonce.size());
530✔
180

181
   return C;
530✔
182
}
×
183

184
void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
317✔
185
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
317✔
186

187
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
317✔
188

189
   const size_t sz = buffer.size() - offset;
317✔
190
   uint8_t* buf = buffer.data() + offset;
317✔
191

192
   const secure_vector<uint8_t>& ad = ad_buf();
317✔
193
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
317✔
194

195
   const BlockCipher& E = cipher();
317✔
196

197
   secure_vector<uint8_t> T(CCM_BS);
317✔
198
   E.encrypt(format_b0(sz), T);
529✔
199

200
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
555✔
201
      xor_buf(T.data(), &ad[i], CCM_BS);
343✔
202
      E.encrypt(T);
686✔
203
   }
204

205
   secure_vector<uint8_t> C = format_c0();
212✔
206
   secure_vector<uint8_t> S0(CCM_BS);
212✔
207
   E.encrypt(C, S0);
212✔
208
   inc(C);
212✔
209

210
   secure_vector<uint8_t> X(CCM_BS);
212✔
211

212
   const uint8_t* buf_end = &buf[sz];
212✔
213

214
   while(buf != buf_end) {
2,922✔
215
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
2,710✔
216

217
      xor_buf(T.data(), buf, to_proc);
2,710✔
218
      E.encrypt(T);
2,710✔
219

220
      E.encrypt(C, X);
2,710✔
221
      xor_buf(buf, X.data(), to_proc);
2,710✔
222
      inc(C);
2,710✔
223

224
      buf += to_proc;
2,710✔
225
   }
226

227
   T ^= S0;
212✔
228

229
   buffer += std::make_pair(T.data(), tag_size());
212✔
230

231
   reset();
424✔
232
}
848✔
233

234
void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
423✔
235
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
423✔
236

237
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
423✔
238

239
   const size_t sz = buffer.size() - offset;
423✔
240
   uint8_t* buf = buffer.data() + offset;
423✔
241

242
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
423✔
243

244
   const secure_vector<uint8_t>& ad = ad_buf();
423✔
245
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
423✔
246

247
   const BlockCipher& E = cipher();
423✔
248

249
   secure_vector<uint8_t> T(CCM_BS);
423✔
250
   E.encrypt(format_b0(sz - tag_size()), T);
846✔
251

252
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
874✔
253
      xor_buf(T.data(), &ad[i], CCM_BS);
556✔
254
      E.encrypt(T);
1,112✔
255
   }
256

257
   secure_vector<uint8_t> C = format_c0();
318✔
258

259
   secure_vector<uint8_t> S0(CCM_BS);
423✔
260
   E.encrypt(C, S0);
318✔
261
   inc(C);
318✔
262

263
   secure_vector<uint8_t> X(CCM_BS);
423✔
264

265
   const uint8_t* buf_end = &buf[sz - tag_size()];
318✔
266

267
   while(buf != buf_end) {
3,232✔
268
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
2,914✔
269

270
      E.encrypt(C, X);
2,914✔
271
      xor_buf(buf, X.data(), to_proc);
2,914✔
272
      inc(C);
2,914✔
273

274
      xor_buf(T.data(), buf, to_proc);
2,914✔
275
      E.encrypt(T);
2,914✔
276

277
      buf += to_proc;
2,914✔
278
   }
279

280
   T ^= S0;
318✔
281

282
   if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) {
318✔
283
      clear_mem(std::span{buffer}.subspan(offset, sz - tag_size()));
105✔
284
      throw Invalid_Authentication_Tag("CCM tag check failed");
105✔
285
   }
286

287
   buffer.resize(buffer.size() - tag_size());
213✔
288

289
   reset();
426✔
290
}
1,167✔
291

292
}  // 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