ccm.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2019 Mikhail Komarov <nemo@nil.foundation>
3 //
4 // MIT License
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in all
14 // copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 // SOFTWARE.
23 //---------------------------------------------------------------------------//
24 
25 #ifndef CRYPTO3_MODES_AEAD_CCM_HPP
26 #define CRYPTO3_MODES_AEAD_CCM_HPP
27 
28 #include <nil/crypto3/detail/make_uint_t.hpp>
29 
31 
32 namespace nil {
33  namespace crypto3 {
34  namespace block {
35  namespace modes {
36  namespace detail {
37  template<typename Cipher, typename Padding, std::size_t NonceBits,
38  std::size_t TagBits = 16 * CHAR_BIT, std::size_t LengthBits = 3 * CHAR_BIT,
39  template<typename> class Allocator = std::allocator>
40  struct ccm_policy {
41  typedef Cipher cipher_type;
42  typedef Padding padding_type;
43 
44  constexpr static const std::size_t tag_bits = TagBits;
45  constexpr static const std::size_t length_bits = LengthBits;
46 
48  BOOST_STATIC_ASSERT(tag_bits >= 4 * CHAR_BIT && tag_bits <= 16 * CHAR_BIT);
50 
51  constexpr static const std::size_t block_bits = cipher_type::block_bits;
52  constexpr static const std::size_t block_words = cipher_type::block_words;
53  typedef typename cipher_type::block_type block_type;
54 
56 
57  constexpr static const std::size_t min_nonce_bits = 0;
58  constexpr static const std::size_t max_nonce_bits = 15 * CHAR_BIT - length_bits;
59  constexpr static const std::size_t nonce_bits = NonceBits;
60  constexpr static const std::size_t nonce_size = nonce_bits / CHAR_BIT;
61  typedef std::array<std::uint8_t, nonce_size> nonce_type;
62 
64 
65  typedef std::vector<boost::uint_t<CHAR_BIT>, Allocator<boost::uint_t<CHAR_BIT>>>
67 
68  template<typename Container>
69  inline static void inc(Container &C) {
70  for (size_t i = 0; i != C.size(); ++i) {
71  if (++C[C.size() - i - 1]) {
72  break;
73  }
74  }
75  }
76 
77  inline static void encode_length(std::size_t len, uint8_t out[]) {
78  using namespace nil::crypto3::detail;
79 
80  const size_t len_bytes = length_bits / CHAR_BIT;
81 
82  BOOST_ASSERT_MSG(len_bytes < sizeof(size_t), "Length field fits");
83 
84  for (size_t i = 0; i != len_bytes; ++i) {
85  out[len_bytes - 1 - i] = extract_uint_t<CHAR_BIT>(len, sizeof(size_t) - 1 - i);
86  }
87 
88  BOOST_ASSERT_MSG((len >> (len_bytes * 8)) == 0, "Message length fits in field");
89  }
90 
91  inline static block_type format_b0(const associated_data_type &ad, const nonce_type &nonce,
92  size_t sz) {
93  block_type b0 = {static_cast<uint8_t>((ad.size() ? 64 : 0) +
94  ((tag_bits / 2 * CHAR_BIT - 1) << 3U) +
95  (length_bits / CHAR_BIT - 1))};
96 
97  copy_mem(&b0[1], nonce.data(), nonce.size());
98  encode_length(sz, &b0[nonce.size() + 1]);
99 
100  return b0;
101  }
102 
103  inline static block_type format_c0(const nonce_type &nonce) {
104  block_type c = {static_cast<uint8_t>(length_bits / CHAR_BIT - 1)};
105  copy_mem(&c[1], nonce.data(), nonce.size());
106 
107  return c;
108  }
109  };
110 
111  template<typename Cipher, typename Padding, std::size_t NonceBits,
112  std::size_t TagBits = 16 * CHAR_BIT, std::size_t LengthBits = 3 * CHAR_BIT,
113  template<typename> class Allocator = std::allocator>
115  : public ccm_policy<Cipher, Padding, NonceBits, TagBits, LengthBits, Allocator> {
117 
118  constexpr static const std::size_t length_bits = policy_type::length_bits;
119  constexpr static const std::size_t tag_bits = policy_type::tag_bits;
120 
123 
124  constexpr static const std::size_t nonce_bits = policy_type::nonce_bits;
126 
128 
129  constexpr static const std::size_t block_bits = policy_type::block_bits;
130  constexpr static const std::size_t block_words = policy_type::block_words;
132 
133  inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext) {
134  return plaintext;
135  }
136 
137  inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext) {
138  return plaintext;
139  }
140 
141  inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext) {
142  block_type result = {0};
143 
144  BOOST_ASSERT_MSG(buffer.size() >= offset, "Offset is sane");
145 
146  buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
147 
148  const size_t sz = buffer.size() - offset;
149  uint8_t *buf = buffer.data() + offset;
150 
151  const secure_vector<uint8_t> &ad = ad_buf();
152  BOOST_ASSERT_MSG(ad.size() % CCM_BS == 0, "AD is block size multiple");
153 
154  secure_vector<uint8_t> T(CCM_BS);
155  cipher.encrypt(policy_type::format_b0(sz), T);
156 
157  for (size_t i = 0; i != ad.size(); i += CCM_BS) {
158  xor_buf(T.data(), &ad[i], CCM_BS);
159  cipher.encrypt(T);
160  }
161 
162  secure_vector<uint8_t> C = policy_type::format_c0();
163  secure_vector<uint8_t> S0(CCM_BS);
164  cipher.encrypt(C, S0);
165  policy_type::inc(C);
166 
167  secure_vector<uint8_t> X(CCM_BS);
168 
169  const uint8_t *buf_end = &buf[sz];
170 
171  while (buf != buf_end) {
172  const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
173 
174  xor_buf(T.data(), buf, to_proc);
175  cipher.encrypt(T);
176 
177  cipher.encrypt(C, X);
178  xor_buf(buf, X.data(), to_proc);
179  policy_type::inc(C);
180 
181  buf += to_proc;
182  }
183 
184  T ^= S0;
185 
186  buffer += std::make_pair(T.data(), tag_bits / CHAR_BIT);
187 
188  return result;
189  }
190  };
191 
192  template<typename Cipher, typename Padding, std::size_t NonceBits,
193  std::size_t TagBits = 16 * CHAR_BIT, std::size_t LengthBits = 3 * CHAR_BIT,
194  template<typename> class Allocator = std::allocator>
196  : public ccm_policy<Cipher, Padding, NonceBits, TagBits, LengthBits, Allocator> {
197 
199 
200  constexpr static const std::size_t length_bits = policy_type::length_bits;
201  constexpr static const std::size_t tag_bits = policy_type::tag_bits;
202 
205 
206  constexpr static const std::size_t nonce_bits = policy_type::nonce_bits;
208 
210 
211  constexpr static const std::size_t block_bits = policy_type::block_bits;
212  constexpr static const std::size_t block_words = policy_type::block_words;
214 
215  inline static block_type begin_message(const cipher_type &cipher, const block_type &plaintext) {
216  return plaintext;
217  }
218 
219  inline static block_type process_block(const cipher_type &cipher, const block_type &plaintext) {
220  return plaintext;
221  }
222 
223  inline static block_type end_message(const cipher_type &cipher, const block_type &plaintext) {
224  block_type result = {0};
225 
226  BOOST_ASSERT_MSG(buffer.size() >= offset, "Offset is sane");
227 
228  buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
229 
230  const size_t sz = buffer.size() - offset;
231  uint8_t *buf = buffer.data() + offset;
232 
233  BOOST_ASSERT_MSG(sz >= tag_size(), "We have the tag");
234 
235  const secure_vector<uint8_t> &ad = ad_buf();
236  BOOST_ASSERT_MSG(ad.size() % CCM_BS == 0, "AD is block size multiple");
237 
238  secure_vector<uint8_t> T(CCM_BS);
239  cipher.encrypt(format_b0(sz - tag_size()), T);
240 
241  for (size_t i = 0; i != ad.size(); i += CCM_BS) {
242  xor_buf(T.data(), &ad[i], CCM_BS);
243  cipher.encrypt(T);
244  }
245 
246  secure_vector<uint8_t> C = format_c0();
247 
248  secure_vector<uint8_t> S0(CCM_BS);
249  cipher.encrypt(C, S0);
250  policy_type::inc(C);
251 
252  secure_vector<uint8_t> X(CCM_BS);
253 
254  const uint8_t *buf_end = &buf[sz - tag_size()];
255 
256  while (buf != buf_end) {
257  const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
258 
259  cipher.encrypt(C, X);
260  xor_buf(buf, X.data(), to_proc);
261  policy_type::inc(C);
262 
263  xor_buf(T.data(), buf, to_proc);
264  cipher.encrypt(T);
265 
266  buf += to_proc;
267  }
268 
269  T ^= S0;
270 
271  if (!constant_time_compare(T.data(), buf_end, tag_size())) {
272  throw integrity_failure("CCM tag check failed");
273  }
274 
275  buffer.resize(buffer.size() - tag_size());
276 
277  return result;
278  }
279  };
280 
281  template<typename Policy>
282  class ccm {
283  typedef Policy policy_type;
284 
285  public:
286  typedef typename policy_type::cipher_type cipher_type;
287  typedef typename policy_type::padding_type padding_type;
288 
289  constexpr static const std::size_t key_bits = cipher_type::key_bits;
290  typedef typename cipher_type::key_type key_type;
291 
292  constexpr static const std::size_t nonce_bits = policy_type::nonce_bits;
293  typedef typename policy_type::nonce_type nonce_type;
294 
295  typedef typename policy_type::associated_data_type associated_data_type;
296 
297  constexpr static const std::size_t block_bits = policy_type::block_bits;
298  constexpr static const std::size_t block_words = policy_type::block_words;
299  typedef typename cipher_type::block_type block_type;
300 
301  template<typename AssociatedDataContainer>
302  ccm(const cipher_type &cipher, const AssociatedDataContainer &associated_data,
303  const nonce_type &nonce) :
304  cipher(cipher) {
305  schedule_associated_data(associated_data);
306  }
307 
308  template<typename AssociatedDataContainer>
309  ccm(const key_type &key, const AssociatedDataContainer &associated_data,
310  const nonce_type &nonce) :
311  cipher(key) {
312  }
313 
314  inline block_type begin_message(const block_type &plaintext) {
315  return policy_type::begin_message(cipher, plaintext, ad);
316  }
317 
318  inline block_type process_block(const block_type &plaintext) {
319  return policy_type::process_block(cipher, plaintext, ad);
320  }
321 
322  inline block_type end_message(const block_type &plaintext) {
323  return policy_type::end_message(cipher, plaintext, ad);
324  }
325 
326  inline static std::size_t required_output_size(std::size_t inputlen) {
327  return padding_type::required_output_size(inputlen);
328  }
329 
330  protected:
331  template<typename AssociatedDataContainer>
332  inline void schedule_associated_data(const AssociatedDataContainer &input_ad) {
333  using namespace nil::crypto3::detail;
334 
335  if (!input_ad.empty()) {
336  // FIXME: support larger AD using length encoding rules
337  BOOST_ASSERT_MSG(length < (0xFFFF - 0xFF), "Supported CCM AD length");
338 
339  ad.push_back(extract_uint_t<CHAR_BIT>(static_cast<uint16_t>(length), 0));
340  ad.push_back(extract_uint_t<CHAR_BIT>(static_cast<uint16_t>(length), 1));
341  ad += std::make_pair(ad, length);
342  while (ad.size() % (block_bits / CHAR_BIT)) {
343  ad.push_back(0);
344  } // pad with zeros to full block size
345  }
346  }
347 
349 
351  };
352  } // namespace detail
353 
363  template<typename BlockCipher, template<typename> class Padding, std::size_t NonceBits,
364  std::size_t TagBits = 16 * CHAR_BIT, std::size_t LengthBits = 3 * CHAR_BIT,
365  template<typename> class Allocator = std::allocator>
366  struct ccm {
368  typedef Padding<BlockCipher> padding_type;
369 
370  typedef detail::ccm_encryption_policy<cipher_type, padding_type, NonceBits, TagBits, LengthBits,
371  Allocator>
373  typedef detail::ccm_decryption_policy<cipher_type, padding_type, NonceBits, TagBits, LengthBits,
374  Allocator>
376 
377  template<
378  template<typename, typename, std::size_t, std::size_t, std::size_t, template<typename> class>
379  class Policy>
380  struct bind {
381  typedef detail::ccm<
382  Policy<cipher_type, padding_type, NonceBits, TagBits, LengthBits, Allocator>>
384  };
385  };
386  } // namespace modes
387  } // namespace block
388  } // namespace crypto3
389 } // namespace nil
390 
391 #endif
ccm(const key_type &key, const AssociatedDataContainer &associated_data, const nonce_type &nonce)
Definition: ccm.hpp:309
cipher_type::key_type key_type
Definition: ccm.hpp:290
policy_type::padding_type padding_type
Definition: ccm.hpp:287
ccm(const cipher_type &cipher, const AssociatedDataContainer &associated_data, const nonce_type &nonce)
Definition: ccm.hpp:302
cipher_type cipher
Definition: ccm.hpp:350
static std::size_t required_output_size(std::size_t inputlen)
Definition: ccm.hpp:326
policy_type::nonce_type nonce_type
Definition: ccm.hpp:293
block_type end_message(const block_type &plaintext)
Definition: ccm.hpp:322
cipher_type::block_type block_type
Definition: ccm.hpp:299
block_type process_block(const block_type &plaintext)
Definition: ccm.hpp:318
associated_data_type ad
Definition: ccm.hpp:348
policy_type::associated_data_type associated_data_type
Definition: ccm.hpp:295
policy_type::cipher_type cipher_type
Definition: ccm.hpp:286
block_type begin_message(const block_type &plaintext)
Definition: ccm.hpp:314
void schedule_associated_data(const AssociatedDataContainer &input_ad)
Definition: ccm.hpp:332
boost::mpl::apply< AccumulatorSet, tag::block< Mode > >::type::result_type block(const AccumulatorSet &acc)
Definition: accumulators/block.hpp:259
Definition: algebra/include/nil/crypto3/detail/make_array.hpp:33
boost::accumulators::accumulator_set< mac::digest< MessageAuthenticationCode::input_block_bits >, boost::accumulators::features< accumulators::tag::mac< MessageAuthenticationCode > >> BlockCipher
Definition: cbc_mac_state.hpp:40
void xor_buf(uint8_t out[], const uint8_t in[], size_t length)
Definition: memory_operations.hpp:245
bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len)
Definition: memory_operations.hpp:143
void copy_mem(T *out, const T *in, size_t n)
Definition: memory_operations.hpp:186
Definition: pair.hpp:31
Definition: cipher.hpp:38
detail::ccm< Policy< cipher_type, padding_type, NonceBits, TagBits, LengthBits, Allocator > > type
Definition: ccm.hpp:383
CCM encryption and decryption.
Definition: ccm.hpp:366
BlockCipher cipher_type
Definition: ccm.hpp:367
detail::ccm_decryption_policy< cipher_type, padding_type, NonceBits, TagBits, LengthBits, Allocator > decryption_policy
Definition: ccm.hpp:375
Padding< BlockCipher > padding_type
Definition: ccm.hpp:368
detail::ccm_encryption_policy< cipher_type, padding_type, NonceBits, TagBits, LengthBits, Allocator > encryption_policy
Definition: ccm.hpp:372
policy_type::padding_type padding_type
Definition: ccm.hpp:204
policy_type::cipher_type cipher_type
Definition: ccm.hpp:203
static block_type process_block(const cipher_type &cipher, const block_type &plaintext)
Definition: ccm.hpp:219
policy_type::associated_data_type associated_data_type
Definition: ccm.hpp:209
static block_type begin_message(const cipher_type &cipher, const block_type &plaintext)
Definition: ccm.hpp:215
policy_type::block_type block_type
Definition: ccm.hpp:213
static block_type end_message(const cipher_type &cipher, const block_type &plaintext)
Definition: ccm.hpp:223
ccm_policy< Cipher, Padding, NonceBits, TagBits, LengthBits, Allocator > policy_type
Definition: ccm.hpp:198
policy_type::nonce_type nonce_type
Definition: ccm.hpp:207
static block_type begin_message(const cipher_type &cipher, const block_type &plaintext)
Definition: ccm.hpp:133
policy_type::block_type block_type
Definition: ccm.hpp:131
ccm_policy< Cipher, Padding, NonceBits, TagBits, LengthBits, Allocator > policy_type
Definition: ccm.hpp:116
policy_type::associated_data_type associated_data_type
Definition: ccm.hpp:127
static block_type end_message(const cipher_type &cipher, const block_type &plaintext)
Definition: ccm.hpp:141
policy_type::nonce_type nonce_type
Definition: ccm.hpp:125
static block_type process_block(const cipher_type &cipher, const block_type &plaintext)
Definition: ccm.hpp:137
policy_type::cipher_type cipher_type
Definition: ccm.hpp:121
policy_type::padding_type padding_type
Definition: ccm.hpp:122
BOOST_STATIC_ASSERT(length_bits >=2 &&length_bits<=8)
static void encode_length(std::size_t len, uint8_t out[])
Definition: ccm.hpp:77
constexpr static const std::size_t length_bits
Definition: ccm.hpp:45
cipher_type::block_type block_type
Definition: ccm.hpp:53
constexpr static const std::size_t block_bits
Definition: ccm.hpp:51
constexpr static const std::size_t nonce_bits
Definition: ccm.hpp:59
static block_type format_b0(const associated_data_type &ad, const nonce_type &nonce, size_t sz)
Definition: ccm.hpp:91
BOOST_STATIC_ASSERT(nonce_bits >=min_nonce_bits &&nonce_bits<=max_nonce_bits)
static block_type format_c0(const nonce_type &nonce)
Definition: ccm.hpp:103
constexpr static const std::size_t tag_bits
Definition: ccm.hpp:44
static void inc(Container &C)
Definition: ccm.hpp:69
std::array< std::uint8_t, nonce_size > nonce_type
Definition: ccm.hpp:61
BOOST_STATIC_ASSERT(tag_bits >=4 *CHAR_BIT &&tag_bits<=16 *CHAR_BIT)
constexpr static const std::size_t nonce_size
Definition: ccm.hpp:60
Cipher cipher_type
Definition: ccm.hpp:41
Padding padding_type
Definition: ccm.hpp:42
std::vector< boost::uint_t< CHAR_BIT >, Allocator< boost::uint_t< CHAR_BIT > > > associated_data_type
Definition: ccm.hpp:66
constexpr static const std::size_t block_words
Definition: ccm.hpp:52
constexpr static const std::size_t min_nonce_bits
Definition: ccm.hpp:57
constexpr static const std::size_t max_nonce_bits
Definition: ccm.hpp:58