ccm.c
Go to the documentation of this file.
1 /**
2  * @file ccm.c
3  * @brief Cipher Block Chaining-Message Authentication Code (CCM)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCrypto Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * CCM mode (Cipher Block Chaining-Message Authentication Code) is a mode of
30  * operation for cryptographic block ciphers. It is an authenticated encryption
31  * algorithm designed to provide both authentication and confidentiality. CCM
32  * mode is only defined for block ciphers with a block length of 128 bits.
33  * Refer to SP 800-38D for more details
34  *
35  * @author Oryx Embedded SARL (www.oryx-embedded.com)
36  * @version 1.9.6
37  **/
38 
39 //Switch to the appropriate trace level
40 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
41 
42 //Dependencies
43 #include "core/crypto.h"
44 #include "aead/ccm.h"
45 #include "debug.h"
46 
47 //Check crypto library configuration
48 #if (CCM_SUPPORT == ENABLED)
49 
50 
51 /**
52  * @brief Authenticated encryption using CCM
53  * @param[in] cipher Cipher algorithm
54  * @param[in] context Cipher algorithm context
55  * @param[in] n Nonce
56  * @param[in] nLen Length of the nonce
57  * @param[in] a Additional authenticated data
58  * @param[in] aLen Length of the additional data
59  * @param[in] p Plaintext to be encrypted
60  * @param[out] c Ciphertext resulting from the encryption
61  * @param[in] length Total number of data bytes to be encrypted
62  * @param[out] t MAC resulting from the encryption process
63  * @param[in] tLen Length of the MAC
64  * @return Error code
65  **/
66 
67 error_t ccmEncrypt(const CipherAlgo *cipher, void *context, const uint8_t *n,
68  size_t nLen, const uint8_t *a, size_t aLen, const uint8_t *p, uint8_t *c,
69  size_t length, uint8_t *t, size_t tLen)
70 {
71  size_t m;
72  size_t q;
73  size_t qLen;
74  uint8_t b[16];
75  uint8_t y[16];
76  uint8_t s[16];
77 
78  //Check parameters
79  if(cipher == NULL || context == NULL)
81 
82  //CCM supports only symmetric block ciphers whose block size is 128 bits
83  if(cipher->type != CIPHER_ALGO_TYPE_BLOCK || cipher->blockSize != 16)
85 
86  //Check the length of the nonce
87  if(nLen < 7 || nLen > 13)
88  return ERROR_INVALID_LENGTH;
89 
90  //Check the length of the MAC
91  if(tLen < 4 || tLen > 16 || (tLen % 2) != 0)
92  return ERROR_INVALID_LENGTH;
93 
94  //Q is the bit string representation of the octet length of P
95  q = length;
96  //Compute the octet length of Q
97  qLen = 15 - nLen;
98 
99  //Format the leading octet of the first block
100  b[0] = (aLen > 0) ? 0x40 : 0x00;
101  //Encode the octet length of T
102  b[0] |= ((tLen - 2) / 2) << 3;
103  //Encode the octet length of Q
104  b[0] |= qLen - 1;
105 
106  //Copy the nonce
107  cryptoMemcpy(b + 1, n, nLen);
108 
109  //Encode the length field Q
110  for(m = 0; m < qLen; m++, q >>= 8)
111  {
112  b[15 - m] = q & 0xFF;
113  }
114 
115  //Invalid length?
116  if(q != 0)
117  return ERROR_INVALID_LENGTH;
118 
119  //Set Y(0) = CIPH(B(0))
120  cipher->encryptBlock(context, b, y);
121 
122  //Any additional data?
123  if(aLen > 0)
124  {
125  //Format the associated data
126  cryptoMemset(b, 0, 16);
127 
128  //Check the length of the associated data string
129  if(aLen < 0xFF00)
130  {
131  //The length is encoded as 2 octets
132  STORE16BE(aLen, b);
133  //Number of bytes to copy
134  m = MIN(aLen, 16 - 2);
135  //Concatenate the associated data A
136  cryptoMemcpy(b + 2, a, m);
137  }
138  else
139  {
140  //The length is encoded as 6 octets
141  b[0] = 0xFF;
142  b[1] = 0xFE;
143  //MSB is stored first
144  STORE32BE(aLen, b + 2);
145  //Number of bytes to copy
146  m = MIN(aLen, 16 - 6);
147  //Concatenate the associated data A
148  cryptoMemcpy(b + 6, a, m);
149  }
150 
151  //XOR B(1) with Y(0)
152  ccmXorBlock(y, b, y, 16);
153  //Compute Y(1) = CIPH(B(1) ^ Y(0))
154  cipher->encryptBlock(context, y, y);
155 
156  //Number of remaining data bytes
157  aLen -= m;
158  a += m;
159 
160  //Process the remaining data bytes
161  while(aLen > 0)
162  {
163  //Associated data are processed in a block-by-block fashion
164  m = MIN(aLen, 16);
165 
166  //XOR B(i) with Y(i-1)
167  ccmXorBlock(y, a, y, m);
168  //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
169  cipher->encryptBlock(context, y, y);
170 
171  //Next block
172  aLen -= m;
173  a += m;
174  }
175  }
176 
177  //Format CTR(0)
178  b[0] = (uint8_t) (qLen - 1);
179  //Copy the nonce
180  cryptoMemcpy(b + 1, n, nLen);
181  //Initialize counter value
182  cryptoMemset(b + 1 + nLen, 0, qLen);
183 
184  //Compute S(0) = CIPH(CTR(0))
185  cipher->encryptBlock(context, b, s);
186  //Save MSB(S(0))
187  cryptoMemcpy(t, s, tLen);
188 
189  //Encrypt plaintext
190  while(length > 0)
191  {
192  //The encryption operates in a block-by-block fashion
193  m = MIN(length, 16);
194 
195  //XOR B(i) with Y(i-1)
196  ccmXorBlock(y, p, y, m);
197  //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
198  cipher->encryptBlock(context, y, y);
199 
200  //Increment counter
201  ccmIncCounter(b, qLen);
202  //Compute S(i) = CIPH(CTR(i))
203  cipher->encryptBlock(context, b, s);
204  //Compute C(i) = B(i) XOR S(i)
205  ccmXorBlock(c, p, s, m);
206 
207  //Next block
208  length -= m;
209  p += m;
210  c += m;
211  }
212 
213  //Compute MAC
214  ccmXorBlock(t, t, y, tLen);
215 
216  //Successful encryption
217  return NO_ERROR;
218 }
219 
220 
221 /**
222  * @brief Authenticated decryption using CCM
223  * @param[in] cipher Cipher algorithm
224  * @param[in] context Cipher algorithm context
225  * @param[in] n Nonce
226  * @param[in] nLen Length of the nonce
227  * @param[in] a Additional authenticated data
228  * @param[in] aLen Length of the additional data
229  * @param[in] c Ciphertext to be decrypted
230  * @param[out] p Plaintext resulting from the decryption
231  * @param[in] length Total number of data bytes to be decrypted
232  * @param[in] t MAC to be verified
233  * @param[in] tLen Length of the MAC
234  * @return Error code
235  **/
236 
237 error_t ccmDecrypt(const CipherAlgo *cipher, void *context, const uint8_t *n,
238  size_t nLen, const uint8_t *a, size_t aLen, const uint8_t *c, uint8_t *p,
239  size_t length, const uint8_t *t, size_t tLen)
240 {
241  uint8_t mask;
242  size_t m;
243  size_t q;
244  size_t qLen;
245  uint8_t b[16];
246  uint8_t y[16];
247  uint8_t r[16];
248  uint8_t s[16];
249 
250  //Check parameters
251  if(cipher == NULL || context == NULL)
253 
254  //CCM supports only symmetric block ciphers whose block size is 128 bits
255  if(cipher->type != CIPHER_ALGO_TYPE_BLOCK || cipher->blockSize != 16)
257 
258  //Check the length of the nonce
259  if(nLen < 7 || nLen > 13)
260  return ERROR_INVALID_LENGTH;
261 
262  //Check the length of the MAC
263  if(tLen < 4 || tLen > 16 || (tLen % 2) != 0)
264  return ERROR_INVALID_LENGTH;
265 
266  //Q is the bit string representation of the octet length of C
267  q = length;
268  //Compute the octet length of Q
269  qLen = 15 - nLen;
270 
271  //Format the leading octet of the first block
272  b[0] = (aLen > 0) ? 0x40 : 0x00;
273  //Encode the octet length of T
274  b[0] |= ((tLen - 2) / 2) << 3;
275  //Encode the octet length of Q
276  b[0] |= qLen - 1;
277 
278  //Copy the nonce
279  cryptoMemcpy(b + 1, n, nLen);
280 
281  //Encode the length field Q
282  for(m = 0; m < qLen; m++, q >>= 8)
283  {
284  b[15 - m] = q & 0xFF;
285  }
286 
287  //Invalid length?
288  if(q != 0)
289  return ERROR_INVALID_LENGTH;
290 
291  //Set Y(0) = CIPH(B(0))
292  cipher->encryptBlock(context, b, y);
293 
294  //Any additional data?
295  if(aLen > 0)
296  {
297  //Format the associated data
298  cryptoMemset(b, 0, 16);
299 
300  //Check the length of the associated data string
301  if(aLen < 0xFF00)
302  {
303  //The length is encoded as 2 octets
304  STORE16BE(aLen, b);
305  //Number of bytes to copy
306  m = MIN(aLen, 16 - 2);
307  //Concatenate the associated data A
308  cryptoMemcpy(b + 2, a, m);
309  }
310  else
311  {
312  //The length is encoded as 6 octets
313  b[0] = 0xFF;
314  b[1] = 0xFE;
315  //MSB is stored first
316  STORE32BE(aLen, b + 2);
317  //Number of bytes to copy
318  m = MIN(aLen, 16 - 6);
319  //Concatenate the associated data A
320  cryptoMemcpy(b + 6, a, m);
321  }
322 
323  //XOR B(1) with Y(0)
324  ccmXorBlock(y, b, y, 16);
325  //Compute Y(1) = CIPH(B(1) ^ Y(0))
326  cipher->encryptBlock(context, y, y);
327 
328  //Number of remaining data bytes
329  aLen -= m;
330  a += m;
331 
332  //Process the remaining data bytes
333  while(aLen > 0)
334  {
335  //Associated data are processed in a block-by-block fashion
336  m = MIN(aLen, 16);
337 
338  //XOR B(i) with Y(i-1)
339  ccmXorBlock(y, a, y, m);
340  //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
341  cipher->encryptBlock(context, y, y);
342 
343  //Next block
344  aLen -= m;
345  a += m;
346  }
347  }
348 
349  //Format CTR(0)
350  b[0] = (uint8_t) (qLen - 1);
351  //Copy the nonce
352  cryptoMemcpy(b + 1, n, nLen);
353  //Initialize counter value
354  cryptoMemset(b + 1 + nLen, 0, qLen);
355 
356  //Compute S(0) = CIPH(CTR(0))
357  cipher->encryptBlock(context, b, s);
358  //Save MSB(S(0))
359  cryptoMemcpy(r, s, tLen);
360 
361  //Decrypt ciphertext
362  while(length > 0)
363  {
364  //The decryption operates in a block-by-block fashion
365  m = MIN(length, 16);
366 
367  //Increment counter
368  ccmIncCounter(b, qLen);
369  //Compute S(i) = CIPH(CTR(i))
370  cipher->encryptBlock(context, b, s);
371  //Compute B(i) = C(i) XOR S(i)
372  ccmXorBlock(p, c, s, m);
373 
374  //XOR B(i) with Y(i-1)
375  ccmXorBlock(y, p, y, m);
376  //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
377  cipher->encryptBlock(context, y, y);
378 
379  //Next block
380  length -= m;
381  c += m;
382  p += m;
383  }
384 
385  //Compute MAC
386  ccmXorBlock(r, r, y, tLen);
387 
388  //The calculated tag is bitwise compared to the received tag. The message
389  //is authenticated if and only if the tags match
390  for(mask = 0, m = 0; m < tLen; m++)
391  {
392  mask |= r[m] ^ t[m];
393  }
394 
395  //Return status code
396  return (mask == 0) ? NO_ERROR : ERROR_FAILURE;
397 }
398 
399 
400 /**
401  * @brief XOR operation
402  * @param[out] x Block resulting from the XOR operation
403  * @param[in] a First block
404  * @param[in] b Second block
405  * @param[in] n Size of the block
406  **/
407 
408 void ccmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
409 {
410  size_t i;
411 
412  //Perform XOR operation
413  for(i = 0; i < n; i++)
414  {
415  x[i] = a[i] ^ b[i];
416  }
417 }
418 
419 
420 /**
421  * @brief Increment counter block
422  * @param[in,out] x Pointer to the counter block
423  * @param[in] n Size in bytes of the specific part of the block to be incremented
424  **/
425 
426 void ccmIncCounter(uint8_t *x, size_t n)
427 {
428  size_t i;
429 
430  //The function increments the right-most bytes of the block. The remaining
431  //left-most bytes remain unchanged
432  for(i = 0; i < n; i++)
433  {
434  //Increment the current byte and propagate the carry if necessary
435  if(++(x[15 - i]) != 0)
436  {
437  break;
438  }
439  }
440 }
441 
442 #endif
uint8_t length
Definition: dtls_misc.h:149
void ccmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
XOR operation.
Definition: ccm.c:408
uint8_t a
Definition: ndp.h:410
uint8_t b[6]
Definition: dtls_misc.h:139
uint8_t p
Definition: ndp.h:298
@ CIPHER_ALGO_TYPE_BLOCK
Definition: crypto.h:1069
size_t blockSize
Definition: crypto.h:1155
CipherAlgoEncryptBlock encryptBlock
Definition: crypto.h:1159
uint32_t r
Definition: ndp.h:345
uint8_t t
Definition: llmnr_common.h:81
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
error_t
Error codes.
Definition: error.h:42
CipherAlgoType type
Definition: crypto.h:1154
void ccmIncCounter(uint8_t *x, size_t n)
Increment counter block.
Definition: ccm.c:426
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
#define STORE16BE(a, p)
Definition: cpu_endian.h:246
@ ERROR_INVALID_LENGTH
Definition: error.h:109
General definitions for cryptographic algorithms.
uint8_t mask
Definition: web_socket.h:317
#define MIN(a, b)
Definition: os_port.h:62
#define cryptoMemset(p, value, length)
Definition: crypto.h:636
error_t ccmDecrypt(const CipherAlgo *cipher, void *context, const uint8_t *n, size_t nLen, const uint8_t *a, size_t aLen, const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen)
Authenticated decryption using CCM.
Definition: ccm.c:237
uint8_t m
Definition: ndp.h:302
uint8_t n
#define cryptoMemcpy(dest, src, length)
Definition: crypto.h:642
uint8_t s
Common interface for encryption algorithms.
Definition: crypto.h:1150
error_t ccmEncrypt(const CipherAlgo *cipher, void *context, const uint8_t *n, size_t nLen, const uint8_t *a, size_t aLen, const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen)
Authenticated encryption using CCM.
Definition: ccm.c:67
Cipher Block Chaining-Message Authentication Code (CCM)
#define STORE32BE(a, p)
Definition: cpu_endian.h:270
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:513
Debugging facilities.