Coverage Report

Created: 2023-02-13 06:21

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