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