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

randombit / botan / 6546858227

17 Oct 2023 12:02PM UTC coverage: 91.71% (+0.002%) from 91.708%
6546858227

push

github

randombit
Merge GH #3760 Move constant time memory comparisons to ct_utils.h

80095 of 87335 relevant lines covered (91.71%)

8508512.25 hits per line

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

94.19
/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

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

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

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

38
void CCM_Mode::clear() {
68✔
39
   m_cipher->clear();
68✔
40
   reset();
136✔
41
}
68✔
42

43
void CCM_Mode::reset() {
794✔
44
   m_nonce.clear();
371✔
45
   m_msg_buf.clear();
794✔
46
   m_ad_buf.clear();
794✔
47
}
303✔
48

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

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

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

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

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

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

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

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

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

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

89
   m_ad_buf.clear();
657✔
90

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

95
      m_ad_buf.push_back(get_byte<0>(static_cast<uint16_t>(ad.size())));
633✔
96
      m_ad_buf.push_back(get_byte<1>(static_cast<uint16_t>(ad.size())));
633✔
97
      m_ad_buf.insert(m_ad_buf.end(), ad.begin(), ad.end());
633✔
98
      while(m_ad_buf.size() % CCM_BS) {
5,956✔
99
         m_ad_buf.push_back(0);  // pad with zeros to full block size
5,323✔
100
      }
101
   }
102
}
657✔
103

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

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

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

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

122
   BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
525✔
123

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

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

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

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

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

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

154
   return B0;
525✔
155
}
×
156

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

163
   const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
525✔
164

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

168
   return C;
525✔
169
}
×
170

171
void CCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
313✔
172
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
313✔
173

174
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
313✔
175

176
   const size_t sz = buffer.size() - offset;
313✔
177
   uint8_t* buf = buffer.data() + offset;
313✔
178

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

182
   const BlockCipher& E = cipher();
313✔
183

184
   secure_vector<uint8_t> T(CCM_BS);
313✔
185
   E.encrypt(format_b0(sz), T);
524✔
186

187
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
550✔
188
      xor_buf(T.data(), &ad[i], CCM_BS);
339✔
189
      E.encrypt(T);
678✔
190
   }
191

192
   secure_vector<uint8_t> C = format_c0();
211✔
193
   secure_vector<uint8_t> S0(CCM_BS);
211✔
194
   E.encrypt(C, S0);
211✔
195
   inc(C);
211✔
196

197
   secure_vector<uint8_t> X(CCM_BS);
211✔
198

199
   const uint8_t* buf_end = &buf[sz];
211✔
200

201
   while(buf != buf_end) {
2,698✔
202
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
2,487✔
203

204
      xor_buf(T.data(), buf, to_proc);
2,487✔
205
      E.encrypt(T);
2,487✔
206

207
      E.encrypt(C, X);
2,487✔
208
      xor_buf(buf, X.data(), to_proc);
2,487✔
209
      inc(C);
2,487✔
210

211
      buf += to_proc;
2,487✔
212
   }
213

214
   T ^= S0;
211✔
215

216
   buffer += std::make_pair(T.data(), tag_size());
211✔
217

218
   reset();
422✔
219
}
844✔
220

221
void CCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
416✔
222
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
416✔
223

224
   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
416✔
225

226
   const size_t sz = buffer.size() - offset;
416✔
227
   uint8_t* buf = buffer.data() + offset;
416✔
228

229
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
416✔
230

231
   const secure_vector<uint8_t>& ad = ad_buf();
348✔
232
   BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
348✔
233

234
   const BlockCipher& E = cipher();
348✔
235

236
   secure_vector<uint8_t> T(CCM_BS);
348✔
237
   E.encrypt(format_b0(sz - tag_size()), T);
764✔
238

239
   for(size_t i = 0; i != ad.size(); i += CCM_BS) {
859✔
240
      xor_buf(T.data(), &ad[i], CCM_BS);
545✔
241
      E.encrypt(T);
1,090✔
242
   }
243

244
   secure_vector<uint8_t> C = format_c0();
314✔
245

246
   secure_vector<uint8_t> S0(CCM_BS);
416✔
247
   E.encrypt(C, S0);
314✔
248
   inc(C);
314✔
249

250
   secure_vector<uint8_t> X(CCM_BS);
416✔
251

252
   const uint8_t* buf_end = &buf[sz - tag_size()];
314✔
253

254
   while(buf != buf_end) {
2,994✔
255
      const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
2,680✔
256

257
      E.encrypt(C, X);
2,680✔
258
      xor_buf(buf, X.data(), to_proc);
2,680✔
259
      inc(C);
2,680✔
260

261
      xor_buf(T.data(), buf, to_proc);
2,680✔
262
      E.encrypt(T);
2,680✔
263

264
      buf += to_proc;
2,680✔
265
   }
266

267
   T ^= S0;
314✔
268

269
   if(!CT::is_equal(T.data(), buf_end, tag_size()).as_bool()) {
628✔
270
      throw Invalid_Authentication_Tag("CCM tag check failed");
102✔
271
   }
272

273
   buffer.resize(buffer.size() - tag_size());
212✔
274

275
   reset();
424✔
276
}
1,154✔
277

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