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

randombit / botan / 15445290297

04 Jun 2025 02:40PM UTC coverage: 90.571% (+0.02%) from 90.554%
15445290297

Pull #4880

github

web-flow
Merge 54efa82ec into 6a4f830b2
Pull Request #4880: Refactor: Cipher_Mode::finish_msg() using std::span

98959 of 109261 relevant lines covered (90.57%)

12373059.89 hits per line

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

94.74
/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/internal/ct_utils.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/loadstor.h>
14
#include <botan/internal/stl_util.h>
15

16
namespace Botan {
17

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

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

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

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

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

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

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

54
bool CCM_Mode::valid_nonce_length(size_t n) const {
655✔
55
   return (n == (15 - L()));
655✔
56
}
57

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

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

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

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

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

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

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

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

90
   m_ad_buf.clear();
684✔
91

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

96
      m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(ad.size())));
660✔
97
      m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(ad.size())));
660✔
98
      m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end());
660✔
99
      while(m_ad_buf.size() % CCM_BS) {
6,135✔
100
         m_ad_buf.push_back(0);  // pad with zeros to full block size
5,475✔
101
      }
102
   }
103
}
684✔
104

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

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

114
size_t CCM_Mode::process_msg(uint8_t buf[], size_t sz) {
1,894✔
115
   BOTAN_STATE_CHECK(!m_nonce.empty());
1,894✔
116
   m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
1,754✔
117
   return 0;  // no output until finished
1,754✔
118
}
119

120
void CCM_Mode::encode_length(uint64_t len, uint8_t out[]) {
550✔
121
   const size_t len_bytes = L();
550✔
122

123
   BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
550✔
124

125
   for(size_t i = 0; i != len_bytes; ++i) {
2,203✔
126
      out[len_bytes - 1 - i] = get_byte_var(sizeof(uint64_t) - 1 - i, len);
1,653✔
127
   }
128

129
   if(len_bytes < 8 && (len >> (len_bytes * 8)) > 0) {
550✔
130
      throw Encoding_Error("CCM message length too long to encode in L field");
×
131
   }
132
}
550✔
133

134
void CCM_Mode::inc(secure_vector<uint8_t>& C) {
6,442✔
135
   for(size_t i = 0; i != C.size(); ++i) {
6,442✔
136
      if(++C[C.size() - i - 1]) {
6,442✔
137
         break;
138
      }
139
   }
140
}
×
141

142
secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz) {
690✔
143
   if(m_nonce.size() != 15 - L()) {
690✔
144
      throw Invalid_State("CCM mode must set nonce");
140✔
145
   }
146
   secure_vector<uint8_t> B0(CCM_BS);
550✔
147

148
   const uint8_t b_flags =
550✔
149
      static_cast<uint8_t>((!m_ad_buf.empty() ? 64 : 0) + (((tag_size() / 2) - 1) << 3) + (L() - 1));
572✔
150

151
   B0[0] = b_flags;
550✔
152
   copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
550✔
153
   encode_length(sz, &B0[m_nonce.size() + 1]);
550✔
154

155
   return B0;
550✔
156
}
×
157

158
secure_vector<uint8_t> CCM_Mode::format_c0() {
550✔
159
   if(m_nonce.size() != 15 - L()) {
550✔
160
      throw Invalid_State("CCM mode must set nonce");
×
161
   }
162
   secure_vector<uint8_t> C(CCM_BS);
550✔
163

164
   const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
550✔
165

166
   C[0] = a_flags;
550✔
167
   copy_mem(&C[1], m_nonce.data(), m_nonce.size());
550✔
168

169
   return C;
550✔
170
}
×
171

172
size_t CCM_Encryption::finish_msg(std::span<uint8_t> buffer, size_t input_bytes) {
327✔
173
   const auto tag_length = tag_size();
327✔
174
   const auto& buffered = msg_buf();
327✔
175

176
   BOTAN_ASSERT_NOMSG(buffered.size() + input_bytes + tag_length == buffer.size());
327✔
177

178
   const auto entire_payload = buffer.first(buffered.size() + input_bytes);
327✔
179
   const auto tag = buffer.last(tag_length);
327✔
180

181
   const secure_vector<uint8_t>& ad = ad_buf();
327✔
182
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
327✔
183

184
   const BlockCipher& E = cipher();
327✔
185

186
   // TODO: consider using std::array<> for all those block-size'ed buffers
187
   //       (this requires adapting more helper functions like `format_b0`, ...)
188
   secure_vector<uint8_t> T(CCM_BS);
327✔
189
   E.encrypt(format_b0(entire_payload.size()), T);
549✔
190

191
   BufferSlicer ad_bs(ad);
222✔
192
   while(!ad_bs.empty()) {
222✔
193
      xor_buf(T, ad_bs.take(CCM_BS));
458✔
194
      E.encrypt(T);
928✔
195
   }
196

197
   secure_vector<uint8_t> C = format_c0();
222✔
198
   secure_vector<uint8_t> S0(CCM_BS);
222✔
199
   E.encrypt(C, S0);
222✔
200
   inc(C);
222✔
201

202
   secure_vector<uint8_t> X(CCM_BS);
222✔
203

204
   // copy all buffered input into the in/out buffer if needed
205
   if(!buffered.empty()) {
222✔
206
      copy_mem(entire_payload.last(input_bytes), entire_payload.first(input_bytes));
69✔
207
      copy_mem(entire_payload.first(buffered.size()), buffered);
69✔
208
   }
209

210
   // TODO: Use BufferTransformer, once it is available
211
   //       See https://github.com/randombit/botan/pull/4151
212
   BufferSlicer payload_slicer(entire_payload);
222✔
213
   BufferStuffer payload_stuffer(entire_payload);
222✔
214

215
   while(!payload_slicer.empty()) {
3,065✔
216
      const size_t to_proc = std::min<size_t>(CCM_BS, payload_slicer.remaining());
2,843✔
217
      const auto in_chunk = payload_slicer.take(to_proc);
2,843✔
218
      const auto out_chunk = payload_stuffer.next(to_proc);
2,843✔
219

220
      xor_buf(std::span{T}.first(in_chunk.size()), in_chunk);
2,843✔
221
      E.encrypt(T);
2,843✔
222

223
      E.encrypt(C, X);
2,843✔
224
      xor_buf(out_chunk, std::span{X}.first(out_chunk.size()));
2,843✔
225
      inc(C);
5,686✔
226
   }
227

228
   T ^= S0;
222✔
229

230
   BOTAN_DEBUG_ASSERT(tag.size() <= T.size());
222✔
231
   copy_mem(tag, std::span{T}.first(tag.size()));
222✔
232

233
   reset();
222✔
234

235
   return buffer.size();
222✔
236
}
6,796✔
237

238
size_t CCM_Decryption::finish_msg(std::span<uint8_t> buffer, size_t input_bytes) {
363✔
239
   const auto tag_length = tag_size();
363✔
240
   const auto& buffered = msg_buf();
363✔
241

242
   BOTAN_ASSERT_NOMSG(buffer.size() >= tag_length);
363✔
243
   BOTAN_ASSERT_NOMSG(buffered.size() + input_bytes == buffer.size());
363✔
244

245
   const auto entire_payload = buffer.first(buffer.size() - tag_length);
363✔
246
   const auto tag = buffer.last(tag_length);
363✔
247

248
   const secure_vector<uint8_t>& ad = ad_buf();
363✔
249
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
363✔
250

251
   const BlockCipher& E = cipher();
363✔
252

253
   secure_vector<uint8_t> T(CCM_BS);
363✔
254
   E.encrypt(format_b0(entire_payload.size()), T);
691✔
255

256
   BufferSlicer ad_bs(ad);
328✔
257
   while(!ad_bs.empty()) {
328✔
258
      xor_buf(T, ad_bs.take<CCM_BS>());
705✔
259
      E.encrypt(T);
1,458✔
260
   }
261

262
   secure_vector<uint8_t> C = format_c0();
328✔
263

264
   secure_vector<uint8_t> S0(CCM_BS);
433✔
265
   E.encrypt(C, S0);
328✔
266
   inc(C);
328✔
267

268
   secure_vector<uint8_t> X(CCM_BS);
433✔
269

270
   // copy all buffered input into the in/out buffer if needed
271
   if(!buffered.empty()) {
328✔
272
      copy_mem(buffer.last(input_bytes), buffer.first(input_bytes));
69✔
273
      copy_mem(buffer.first(buffered.size()), buffered);
69✔
274
   }
275

276
   // TODO: Use BufferTransformer, once it is available
277
   //       See https://github.com/randombit/botan/pull/4151
278
   BufferSlicer payload_slicer(entire_payload);
328✔
279
   BufferStuffer payload_stuffer(entire_payload);
328✔
280

281
   while(!payload_slicer.empty()) {
3,377✔
282
      const size_t to_proc = std::min<size_t>(CCM_BS, payload_slicer.remaining());
3,049✔
283
      const auto in_chunk = payload_slicer.take(to_proc);
3,049✔
284
      const auto out_chunk = payload_stuffer.next(to_proc);
3,049✔
285

286
      E.encrypt(C, X);
3,049✔
287
      xor_buf(out_chunk, std::span{X}.first(out_chunk.size()));
3,049✔
288
      inc(C);
3,049✔
289

290
      xor_buf(std::span{T}.first(in_chunk.size()), in_chunk);
3,154✔
291
      E.encrypt(T);
6,098✔
292
   }
293

294
   T ^= S0;
328✔
295

296
   if(!CT::is_equal(T.data(), tag.data(), tag.size()).as_bool()) {
656✔
297
      throw Invalid_Authentication_Tag("CCM tag check failed");
105✔
298
   }
299

300
   reset();
223✔
301

302
   return entire_payload.size();
223✔
303
}
1,207✔
304

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