1 | /* |
2 | * NIST SP800-38C compliant CCM implementation |
3 | * |
4 | * Copyright The Mbed TLS Contributors |
5 | * SPDX-License-Identifier: Apache-2.0 |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
8 | * not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
18 | */ |
19 | |
20 | /* |
21 | * Definition of CCM: |
22 | * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf |
23 | * RFC 3610 "Counter with CBC-MAC (CCM)" |
24 | * |
25 | * Related: |
26 | * RFC 5116 "An Interface and Algorithms for Authenticated Encryption" |
27 | */ |
28 | |
29 | #include "common.h" |
30 | |
31 | #if defined(MBEDTLS_CCM_C) |
32 | |
33 | #include "mbedtls/ccm.h" |
34 | #include "mbedtls/platform_util.h" |
35 | #include "mbedtls/error.h" |
36 | |
37 | #include <string.h> |
38 | |
39 | #include "mbedtls/platform.h" |
40 | |
41 | #if !defined(MBEDTLS_CCM_ALT) |
42 | |
43 | #define CCM_VALIDATE_RET(cond) \ |
44 | MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_CCM_BAD_INPUT) |
45 | #define CCM_VALIDATE(cond) \ |
46 | MBEDTLS_INTERNAL_VALIDATE(cond) |
47 | |
48 | #define CCM_ENCRYPT 0 |
49 | #define CCM_DECRYPT 1 |
50 | |
51 | /* |
52 | * Initialize context |
53 | */ |
54 | void mbedtls_ccm_init(mbedtls_ccm_context *ctx) |
55 | { |
56 | CCM_VALIDATE(ctx != NULL); |
57 | memset(ctx, 0, sizeof(mbedtls_ccm_context)); |
58 | } |
59 | |
60 | int mbedtls_ccm_setkey(mbedtls_ccm_context *ctx, |
61 | mbedtls_cipher_id_t cipher, |
62 | const unsigned char *key, |
63 | unsigned int keybits) |
64 | { |
65 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
66 | const mbedtls_cipher_info_t *cipher_info; |
67 | |
68 | CCM_VALIDATE_RET(ctx != NULL); |
69 | CCM_VALIDATE_RET(key != NULL); |
70 | |
71 | cipher_info = mbedtls_cipher_info_from_values(cipher, keybits, |
72 | MBEDTLS_MODE_ECB); |
73 | if (cipher_info == NULL) { |
74 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
75 | } |
76 | |
77 | if (cipher_info->block_size != 16) { |
78 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
79 | } |
80 | |
81 | mbedtls_cipher_free(&ctx->cipher_ctx); |
82 | |
83 | if ((ret = mbedtls_cipher_setup(&ctx->cipher_ctx, cipher_info)) != 0) { |
84 | return ret; |
85 | } |
86 | |
87 | if ((ret = mbedtls_cipher_setkey(&ctx->cipher_ctx, key, keybits, |
88 | MBEDTLS_ENCRYPT)) != 0) { |
89 | return ret; |
90 | } |
91 | |
92 | return 0; |
93 | } |
94 | |
95 | /* |
96 | * Free context |
97 | */ |
98 | void mbedtls_ccm_free(mbedtls_ccm_context *ctx) |
99 | { |
100 | if (ctx == NULL) { |
101 | return; |
102 | } |
103 | mbedtls_cipher_free(&ctx->cipher_ctx); |
104 | mbedtls_platform_zeroize(ctx, sizeof(mbedtls_ccm_context)); |
105 | } |
106 | |
107 | /* |
108 | * Macros for common operations. |
109 | * Results in smaller compiled code than static inline functions. |
110 | */ |
111 | |
112 | /* |
113 | * Update the CBC-MAC state in y using a block in b |
114 | * (Always using b as the source helps the compiler optimise a bit better.) |
115 | */ |
116 | #define UPDATE_CBC_MAC \ |
117 | for (i = 0; i < 16; i++) \ |
118 | y[i] ^= b[i]; \ |
119 | \ |
120 | if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, y, 16, y, &olen)) != 0) \ |
121 | return ret; |
122 | |
123 | /* |
124 | * Encrypt or decrypt a partial block with CTR |
125 | * Warning: using b for temporary storage! src and dst must not be b! |
126 | * This avoids allocating one more 16 bytes buffer while allowing src == dst. |
127 | */ |
128 | #define CTR_CRYPT(dst, src, len) \ |
129 | do \ |
130 | { \ |
131 | if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, ctr, \ |
132 | 16, b, &olen)) != 0) \ |
133 | { \ |
134 | return ret; \ |
135 | } \ |
136 | \ |
137 | for (i = 0; i < (len); i++) \ |
138 | (dst)[i] = (src)[i] ^ b[i]; \ |
139 | } while (0) |
140 | |
141 | /* |
142 | * Authenticated encryption or decryption |
143 | */ |
144 | static int ccm_auth_crypt(mbedtls_ccm_context *ctx, int mode, size_t length, |
145 | const unsigned char *iv, size_t iv_len, |
146 | const unsigned char *add, size_t add_len, |
147 | const unsigned char *input, unsigned char *output, |
148 | unsigned char *tag, size_t tag_len) |
149 | { |
150 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
151 | unsigned char i; |
152 | unsigned char q; |
153 | size_t len_left, olen; |
154 | unsigned char b[16]; |
155 | unsigned char y[16]; |
156 | unsigned char ctr[16]; |
157 | const unsigned char *src; |
158 | unsigned char *dst; |
159 | |
160 | /* |
161 | * Check length requirements: SP800-38C A.1 |
162 | * Additional requirement: a < 2^16 - 2^8 to simplify the code. |
163 | * 'length' checked later (when writing it to the first block) |
164 | * |
165 | * Also, loosen the requirements to enable support for CCM* (IEEE 802.15.4). |
166 | */ |
167 | if (tag_len == 2 || tag_len > 16 || tag_len % 2 != 0) { |
168 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
169 | } |
170 | |
171 | /* Also implies q is within bounds */ |
172 | if (iv_len < 7 || iv_len > 13) { |
173 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
174 | } |
175 | |
176 | if (add_len >= 0xFF00) { |
177 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
178 | } |
179 | |
180 | q = 16 - 1 - (unsigned char) iv_len; |
181 | |
182 | /* |
183 | * First block B_0: |
184 | * 0 .. 0 flags |
185 | * 1 .. iv_len nonce (aka iv) |
186 | * iv_len+1 .. 15 length |
187 | * |
188 | * With flags as (bits): |
189 | * 7 0 |
190 | * 6 add present? |
191 | * 5 .. 3 (t - 2) / 2 |
192 | * 2 .. 0 q - 1 |
193 | */ |
194 | b[0] = 0; |
195 | b[0] |= (add_len > 0) << 6; |
196 | b[0] |= ((tag_len - 2) / 2) << 3; |
197 | b[0] |= q - 1; |
198 | |
199 | memcpy(b + 1, iv, iv_len); |
200 | |
201 | for (i = 0, len_left = length; i < q; i++, len_left >>= 8) { |
202 | b[15-i] = MBEDTLS_BYTE_0(len_left); |
203 | } |
204 | |
205 | if (len_left > 0) { |
206 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
207 | } |
208 | |
209 | |
210 | /* Start CBC-MAC with first block */ |
211 | memset(y, 0, 16); |
212 | UPDATE_CBC_MAC; |
213 | |
214 | /* |
215 | * If there is additional data, update CBC-MAC with |
216 | * add_len, add, 0 (padding to a block boundary) |
217 | */ |
218 | if (add_len > 0) { |
219 | size_t use_len; |
220 | len_left = add_len; |
221 | src = add; |
222 | |
223 | memset(b, 0, 16); |
224 | MBEDTLS_PUT_UINT16_BE(add_len, b, 0); |
225 | |
226 | use_len = len_left < 16 - 2 ? len_left : 16 - 2; |
227 | memcpy(b + 2, src, use_len); |
228 | len_left -= use_len; |
229 | src += use_len; |
230 | |
231 | UPDATE_CBC_MAC; |
232 | |
233 | while (len_left > 0) { |
234 | use_len = len_left > 16 ? 16 : len_left; |
235 | |
236 | memset(b, 0, 16); |
237 | memcpy(b, src, use_len); |
238 | UPDATE_CBC_MAC; |
239 | |
240 | len_left -= use_len; |
241 | src += use_len; |
242 | } |
243 | } |
244 | |
245 | /* |
246 | * Prepare counter block for encryption: |
247 | * 0 .. 0 flags |
248 | * 1 .. iv_len nonce (aka iv) |
249 | * iv_len+1 .. 15 counter (initially 1) |
250 | * |
251 | * With flags as (bits): |
252 | * 7 .. 3 0 |
253 | * 2 .. 0 q - 1 |
254 | */ |
255 | ctr[0] = q - 1; |
256 | memcpy(ctr + 1, iv, iv_len); |
257 | memset(ctr + 1 + iv_len, 0, q); |
258 | ctr[15] = 1; |
259 | |
260 | /* |
261 | * Authenticate and {en,de}crypt the message. |
262 | * |
263 | * The only difference between encryption and decryption is |
264 | * the respective order of authentication and {en,de}cryption. |
265 | */ |
266 | len_left = length; |
267 | src = input; |
268 | dst = output; |
269 | |
270 | while (len_left > 0) { |
271 | size_t use_len = len_left > 16 ? 16 : len_left; |
272 | |
273 | if (mode == CCM_ENCRYPT) { |
274 | memset(b, 0, 16); |
275 | memcpy(b, src, use_len); |
276 | UPDATE_CBC_MAC; |
277 | } |
278 | |
279 | CTR_CRYPT(dst, src, use_len); |
280 | |
281 | if (mode == CCM_DECRYPT) { |
282 | memset(b, 0, 16); |
283 | memcpy(b, dst, use_len); |
284 | UPDATE_CBC_MAC; |
285 | } |
286 | |
287 | dst += use_len; |
288 | src += use_len; |
289 | len_left -= use_len; |
290 | |
291 | /* |
292 | * Increment counter. |
293 | * No need to check for overflow thanks to the length check above. |
294 | */ |
295 | for (i = 0; i < q; i++) { |
296 | if (++ctr[15-i] != 0) { |
297 | break; |
298 | } |
299 | } |
300 | } |
301 | |
302 | /* |
303 | * Authentication: reset counter and crypt/mask internal tag |
304 | */ |
305 | for (i = 0; i < q; i++) { |
306 | ctr[15-i] = 0; |
307 | } |
308 | |
309 | CTR_CRYPT(y, y, 16); |
310 | memcpy(tag, y, tag_len); |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | /* |
316 | * Authenticated encryption |
317 | */ |
318 | int mbedtls_ccm_star_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length, |
319 | const unsigned char *iv, size_t iv_len, |
320 | const unsigned char *add, size_t add_len, |
321 | const unsigned char *input, unsigned char *output, |
322 | unsigned char *tag, size_t tag_len) |
323 | { |
324 | CCM_VALIDATE_RET(ctx != NULL); |
325 | CCM_VALIDATE_RET(iv != NULL); |
326 | CCM_VALIDATE_RET(add_len == 0 || add != NULL); |
327 | CCM_VALIDATE_RET(length == 0 || input != NULL); |
328 | CCM_VALIDATE_RET(length == 0 || output != NULL); |
329 | CCM_VALIDATE_RET(tag_len == 0 || tag != NULL); |
330 | return ccm_auth_crypt(ctx, CCM_ENCRYPT, length, iv, iv_len, |
331 | add, add_len, input, output, tag, tag_len); |
332 | } |
333 | |
334 | int mbedtls_ccm_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length, |
335 | const unsigned char *iv, size_t iv_len, |
336 | const unsigned char *add, size_t add_len, |
337 | const unsigned char *input, unsigned char *output, |
338 | unsigned char *tag, size_t tag_len) |
339 | { |
340 | CCM_VALIDATE_RET(ctx != NULL); |
341 | CCM_VALIDATE_RET(iv != NULL); |
342 | CCM_VALIDATE_RET(add_len == 0 || add != NULL); |
343 | CCM_VALIDATE_RET(length == 0 || input != NULL); |
344 | CCM_VALIDATE_RET(length == 0 || output != NULL); |
345 | CCM_VALIDATE_RET(tag_len == 0 || tag != NULL); |
346 | if (tag_len == 0) { |
347 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
348 | } |
349 | |
350 | return mbedtls_ccm_star_encrypt_and_tag(ctx, length, iv, iv_len, add, |
351 | add_len, input, output, tag, tag_len); |
352 | } |
353 | |
354 | /* |
355 | * Authenticated decryption |
356 | */ |
357 | int mbedtls_ccm_star_auth_decrypt(mbedtls_ccm_context *ctx, size_t length, |
358 | const unsigned char *iv, size_t iv_len, |
359 | const unsigned char *add, size_t add_len, |
360 | const unsigned char *input, unsigned char *output, |
361 | const unsigned char *tag, size_t tag_len) |
362 | { |
363 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
364 | unsigned char check_tag[16]; |
365 | unsigned char i; |
366 | int diff; |
367 | |
368 | CCM_VALIDATE_RET(ctx != NULL); |
369 | CCM_VALIDATE_RET(iv != NULL); |
370 | CCM_VALIDATE_RET(add_len == 0 || add != NULL); |
371 | CCM_VALIDATE_RET(length == 0 || input != NULL); |
372 | CCM_VALIDATE_RET(length == 0 || output != NULL); |
373 | CCM_VALIDATE_RET(tag_len == 0 || tag != NULL); |
374 | |
375 | if ((ret = ccm_auth_crypt(ctx, CCM_DECRYPT, length, |
376 | iv, iv_len, add, add_len, |
377 | input, output, check_tag, tag_len)) != 0) { |
378 | return ret; |
379 | } |
380 | |
381 | /* Check tag in "constant-time" */ |
382 | for (diff = 0, i = 0; i < tag_len; i++) { |
383 | diff |= tag[i] ^ check_tag[i]; |
384 | } |
385 | |
386 | if (diff != 0) { |
387 | mbedtls_platform_zeroize(output, length); |
388 | return MBEDTLS_ERR_CCM_AUTH_FAILED; |
389 | } |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | int mbedtls_ccm_auth_decrypt(mbedtls_ccm_context *ctx, size_t length, |
395 | const unsigned char *iv, size_t iv_len, |
396 | const unsigned char *add, size_t add_len, |
397 | const unsigned char *input, unsigned char *output, |
398 | const unsigned char *tag, size_t tag_len) |
399 | { |
400 | CCM_VALIDATE_RET(ctx != NULL); |
401 | CCM_VALIDATE_RET(iv != NULL); |
402 | CCM_VALIDATE_RET(add_len == 0 || add != NULL); |
403 | CCM_VALIDATE_RET(length == 0 || input != NULL); |
404 | CCM_VALIDATE_RET(length == 0 || output != NULL); |
405 | CCM_VALIDATE_RET(tag_len == 0 || tag != NULL); |
406 | |
407 | if (tag_len == 0) { |
408 | return MBEDTLS_ERR_CCM_BAD_INPUT; |
409 | } |
410 | |
411 | return mbedtls_ccm_star_auth_decrypt(ctx, length, iv, iv_len, add, |
412 | add_len, input, output, tag, tag_len); |
413 | } |
414 | #endif /* !MBEDTLS_CCM_ALT */ |
415 | |
416 | #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) |
417 | /* |
418 | * Examples 1 to 3 from SP800-38C Appendix C |
419 | */ |
420 | |
421 | #define NB_TESTS 3 |
422 | #define CCM_SELFTEST_PT_MAX_LEN 24 |
423 | #define CCM_SELFTEST_CT_MAX_LEN 32 |
424 | /* |
425 | * The data is the same for all tests, only the used length changes |
426 | */ |
427 | static const unsigned char key_test_data[] = { |
428 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, |
429 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f |
430 | }; |
431 | |
432 | static const unsigned char iv_test_data[] = { |
433 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
434 | 0x18, 0x19, 0x1a, 0x1b |
435 | }; |
436 | |
437 | static const unsigned char ad_test_data[] = { |
438 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
439 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
440 | 0x10, 0x11, 0x12, 0x13 |
441 | }; |
442 | |
443 | static const unsigned char msg_test_data[CCM_SELFTEST_PT_MAX_LEN] = { |
444 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, |
445 | 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
446 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, |
447 | }; |
448 | |
449 | static const size_t iv_len_test_data[NB_TESTS] = { 7, 8, 12 }; |
450 | static const size_t add_len_test_data[NB_TESTS] = { 8, 16, 20 }; |
451 | static const size_t msg_len_test_data[NB_TESTS] = { 4, 16, 24 }; |
452 | static const size_t tag_len_test_data[NB_TESTS] = { 4, 6, 8 }; |
453 | |
454 | static const unsigned char res_test_data[NB_TESTS][CCM_SELFTEST_CT_MAX_LEN] = { |
455 | { 0x71, 0x62, 0x01, 0x5b, 0x4d, 0xac, 0x25, 0x5d }, |
456 | { 0xd2, 0xa1, 0xf0, 0xe0, 0x51, 0xea, 0x5f, 0x62, |
457 | 0x08, 0x1a, 0x77, 0x92, 0x07, 0x3d, 0x59, 0x3d, |
458 | 0x1f, 0xc6, 0x4f, 0xbf, 0xac, 0xcd }, |
459 | { 0xe3, 0xb2, 0x01, 0xa9, 0xf5, 0xb7, 0x1a, 0x7a, |
460 | 0x9b, 0x1c, 0xea, 0xec, 0xcd, 0x97, 0xe7, 0x0b, |
461 | 0x61, 0x76, 0xaa, 0xd9, 0xa4, 0x42, 0x8a, 0xa5, |
462 | 0x48, 0x43, 0x92, 0xfb, 0xc1, 0xb0, 0x99, 0x51 } |
463 | }; |
464 | |
465 | int mbedtls_ccm_self_test(int verbose) |
466 | { |
467 | mbedtls_ccm_context ctx; |
468 | /* |
469 | * Some hardware accelerators require the input and output buffers |
470 | * would be in RAM, because the flash is not accessible. |
471 | * Use buffers on the stack to hold the test vectors data. |
472 | */ |
473 | unsigned char plaintext[CCM_SELFTEST_PT_MAX_LEN]; |
474 | unsigned char ciphertext[CCM_SELFTEST_CT_MAX_LEN]; |
475 | size_t i; |
476 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
477 | |
478 | mbedtls_ccm_init(&ctx); |
479 | |
480 | if (mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key_test_data, |
481 | 8 * sizeof(key_test_data)) != 0) { |
482 | if (verbose != 0) { |
483 | mbedtls_printf(" CCM: setup failed" ); |
484 | } |
485 | |
486 | return 1; |
487 | } |
488 | |
489 | for (i = 0; i < NB_TESTS; i++) { |
490 | if (verbose != 0) { |
491 | mbedtls_printf(" CCM-AES #%u: " , (unsigned int) i + 1); |
492 | } |
493 | |
494 | memset(plaintext, 0, CCM_SELFTEST_PT_MAX_LEN); |
495 | memset(ciphertext, 0, CCM_SELFTEST_CT_MAX_LEN); |
496 | memcpy(plaintext, msg_test_data, msg_len_test_data[i]); |
497 | |
498 | ret = mbedtls_ccm_encrypt_and_tag(&ctx, msg_len_test_data[i], |
499 | iv_test_data, iv_len_test_data[i], |
500 | ad_test_data, add_len_test_data[i], |
501 | plaintext, ciphertext, |
502 | ciphertext + msg_len_test_data[i], |
503 | tag_len_test_data[i]); |
504 | |
505 | if (ret != 0 || |
506 | memcmp(ciphertext, res_test_data[i], |
507 | msg_len_test_data[i] + tag_len_test_data[i]) != 0) { |
508 | if (verbose != 0) { |
509 | mbedtls_printf("failed\n" ); |
510 | } |
511 | |
512 | return 1; |
513 | } |
514 | memset(plaintext, 0, CCM_SELFTEST_PT_MAX_LEN); |
515 | |
516 | ret = mbedtls_ccm_auth_decrypt(&ctx, msg_len_test_data[i], |
517 | iv_test_data, iv_len_test_data[i], |
518 | ad_test_data, add_len_test_data[i], |
519 | ciphertext, plaintext, |
520 | ciphertext + msg_len_test_data[i], |
521 | tag_len_test_data[i]); |
522 | |
523 | if (ret != 0 || |
524 | memcmp(plaintext, msg_test_data, msg_len_test_data[i]) != 0) { |
525 | if (verbose != 0) { |
526 | mbedtls_printf("failed\n" ); |
527 | } |
528 | |
529 | return 1; |
530 | } |
531 | |
532 | if (verbose != 0) { |
533 | mbedtls_printf("passed\n" ); |
534 | } |
535 | } |
536 | |
537 | mbedtls_ccm_free(&ctx); |
538 | |
539 | if (verbose != 0) { |
540 | mbedtls_printf("\n" ); |
541 | } |
542 | |
543 | return 0; |
544 | } |
545 | |
546 | #endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ |
547 | |
548 | #endif /* MBEDTLS_CCM_C */ |
549 | |