1 /**
2 * \file chachapoly.c
3 *
4 * \brief ChaCha20-Poly1305 AEAD construction based on RFC 7539.
5 *
6 * Copyright The Mbed TLS Contributors
7 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
8 */
9 #include "common.h"
10
11 #if defined(MBEDTLS_CHACHAPOLY_C)
12
13 #include "mbedtls/chachapoly.h"
14 #include "mbedtls/platform_util.h"
15 #include "mbedtls/error.h"
16 #include "mbedtls/constant_time.h"
17
18 #include <string.h>
19
20 #include "mbedtls/platform.h"
21
22 #if !defined(MBEDTLS_CHACHAPOLY_ALT)
23
24 #define CHACHAPOLY_STATE_INIT (0)
25 #define CHACHAPOLY_STATE_AAD (1)
26 #define CHACHAPOLY_STATE_CIPHERTEXT (2) /* Encrypting or decrypting */
27 #define CHACHAPOLY_STATE_FINISHED (3)
28
29 /**
30 * \brief Adds nul bytes to pad the AAD for Poly1305.
31 *
32 * \param ctx The ChaCha20-Poly1305 context.
33 */
chachapoly_pad_aad(mbedtls_chachapoly_context * ctx)34 static int chachapoly_pad_aad(mbedtls_chachapoly_context *ctx)
35 {
36 uint32_t partial_block_len = (uint32_t) (ctx->aad_len % 16U);
37 unsigned char zeroes[15];
38
39 if (partial_block_len == 0U) {
40 return 0;
41 }
42
43 memset(zeroes, 0, sizeof(zeroes));
44
45 return mbedtls_poly1305_update(&ctx->poly1305_ctx,
46 zeroes,
47 16U - partial_block_len);
48 }
49
50 /**
51 * \brief Adds nul bytes to pad the ciphertext for Poly1305.
52 *
53 * \param ctx The ChaCha20-Poly1305 context.
54 */
chachapoly_pad_ciphertext(mbedtls_chachapoly_context * ctx)55 static int chachapoly_pad_ciphertext(mbedtls_chachapoly_context *ctx)
56 {
57 uint32_t partial_block_len = (uint32_t) (ctx->ciphertext_len % 16U);
58 unsigned char zeroes[15];
59
60 if (partial_block_len == 0U) {
61 return 0;
62 }
63
64 memset(zeroes, 0, sizeof(zeroes));
65 return mbedtls_poly1305_update(&ctx->poly1305_ctx,
66 zeroes,
67 16U - partial_block_len);
68 }
69
mbedtls_chachapoly_init(mbedtls_chachapoly_context * ctx)70 void mbedtls_chachapoly_init(mbedtls_chachapoly_context *ctx)
71 {
72 mbedtls_chacha20_init(&ctx->chacha20_ctx);
73 mbedtls_poly1305_init(&ctx->poly1305_ctx);
74 ctx->aad_len = 0U;
75 ctx->ciphertext_len = 0U;
76 ctx->state = CHACHAPOLY_STATE_INIT;
77 ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT;
78 }
79
mbedtls_chachapoly_free(mbedtls_chachapoly_context * ctx)80 void mbedtls_chachapoly_free(mbedtls_chachapoly_context *ctx)
81 {
82 if (ctx == NULL) {
83 return;
84 }
85
86 mbedtls_chacha20_free(&ctx->chacha20_ctx);
87 mbedtls_poly1305_free(&ctx->poly1305_ctx);
88 ctx->aad_len = 0U;
89 ctx->ciphertext_len = 0U;
90 ctx->state = CHACHAPOLY_STATE_INIT;
91 ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT;
92 }
93
mbedtls_chachapoly_setkey(mbedtls_chachapoly_context * ctx,const unsigned char key[32])94 int mbedtls_chachapoly_setkey(mbedtls_chachapoly_context *ctx,
95 const unsigned char key[32])
96 {
97 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
98
99 ret = mbedtls_chacha20_setkey(&ctx->chacha20_ctx, key);
100
101 return ret;
102 }
103
mbedtls_chachapoly_starts(mbedtls_chachapoly_context * ctx,const unsigned char nonce[12],mbedtls_chachapoly_mode_t mode)104 int mbedtls_chachapoly_starts(mbedtls_chachapoly_context *ctx,
105 const unsigned char nonce[12],
106 mbedtls_chachapoly_mode_t mode)
107 {
108 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
109 unsigned char poly1305_key[64];
110
111 /* Set counter = 0, will be update to 1 when generating Poly1305 key */
112 ret = mbedtls_chacha20_starts(&ctx->chacha20_ctx, nonce, 0U);
113 if (ret != 0) {
114 goto cleanup;
115 }
116
117 /* Generate the Poly1305 key by getting the ChaCha20 keystream output with
118 * counter = 0. This is the same as encrypting a buffer of zeroes.
119 * Only the first 256-bits (32 bytes) of the key is used for Poly1305.
120 * The other 256 bits are discarded.
121 */
122 memset(poly1305_key, 0, sizeof(poly1305_key));
123 ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, sizeof(poly1305_key),
124 poly1305_key, poly1305_key);
125 if (ret != 0) {
126 goto cleanup;
127 }
128
129 ret = mbedtls_poly1305_starts(&ctx->poly1305_ctx, poly1305_key);
130
131 if (ret == 0) {
132 ctx->aad_len = 0U;
133 ctx->ciphertext_len = 0U;
134 ctx->state = CHACHAPOLY_STATE_AAD;
135 ctx->mode = mode;
136 }
137
138 cleanup:
139 mbedtls_platform_zeroize(poly1305_key, 64U);
140 return ret;
141 }
142
mbedtls_chachapoly_update_aad(mbedtls_chachapoly_context * ctx,const unsigned char * aad,size_t aad_len)143 int mbedtls_chachapoly_update_aad(mbedtls_chachapoly_context *ctx,
144 const unsigned char *aad,
145 size_t aad_len)
146 {
147 if (ctx->state != CHACHAPOLY_STATE_AAD) {
148 return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE;
149 }
150
151 ctx->aad_len += aad_len;
152
153 return mbedtls_poly1305_update(&ctx->poly1305_ctx, aad, aad_len);
154 }
155
mbedtls_chachapoly_update(mbedtls_chachapoly_context * ctx,size_t len,const unsigned char * input,unsigned char * output)156 int mbedtls_chachapoly_update(mbedtls_chachapoly_context *ctx,
157 size_t len,
158 const unsigned char *input,
159 unsigned char *output)
160 {
161 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
162
163 if ((ctx->state != CHACHAPOLY_STATE_AAD) &&
164 (ctx->state != CHACHAPOLY_STATE_CIPHERTEXT)) {
165 return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE;
166 }
167
168 if (ctx->state == CHACHAPOLY_STATE_AAD) {
169 ctx->state = CHACHAPOLY_STATE_CIPHERTEXT;
170
171 ret = chachapoly_pad_aad(ctx);
172 if (ret != 0) {
173 return ret;
174 }
175 }
176
177 ctx->ciphertext_len += len;
178
179 if (ctx->mode == MBEDTLS_CHACHAPOLY_ENCRYPT) {
180 ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, len, input, output);
181 if (ret != 0) {
182 return ret;
183 }
184
185 ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, output, len);
186 if (ret != 0) {
187 return ret;
188 }
189 } else { /* DECRYPT */
190 ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, input, len);
191 if (ret != 0) {
192 return ret;
193 }
194
195 ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, len, input, output);
196 if (ret != 0) {
197 return ret;
198 }
199 }
200
201 return 0;
202 }
203
mbedtls_chachapoly_finish(mbedtls_chachapoly_context * ctx,unsigned char mac[16])204 int mbedtls_chachapoly_finish(mbedtls_chachapoly_context *ctx,
205 unsigned char mac[16])
206 {
207 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
208 unsigned char len_block[16];
209
210 if (ctx->state == CHACHAPOLY_STATE_INIT) {
211 return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE;
212 }
213
214 if (ctx->state == CHACHAPOLY_STATE_AAD) {
215 ret = chachapoly_pad_aad(ctx);
216 if (ret != 0) {
217 return ret;
218 }
219 } else if (ctx->state == CHACHAPOLY_STATE_CIPHERTEXT) {
220 ret = chachapoly_pad_ciphertext(ctx);
221 if (ret != 0) {
222 return ret;
223 }
224 }
225
226 ctx->state = CHACHAPOLY_STATE_FINISHED;
227
228 /* The lengths of the AAD and ciphertext are processed by
229 * Poly1305 as the final 128-bit block, encoded as little-endian integers.
230 */
231 MBEDTLS_PUT_UINT64_LE(ctx->aad_len, len_block, 0);
232 MBEDTLS_PUT_UINT64_LE(ctx->ciphertext_len, len_block, 8);
233
234 ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, len_block, 16U);
235 if (ret != 0) {
236 return ret;
237 }
238
239 ret = mbedtls_poly1305_finish(&ctx->poly1305_ctx, mac);
240
241 return ret;
242 }
243
chachapoly_crypt_and_tag(mbedtls_chachapoly_context * ctx,mbedtls_chachapoly_mode_t mode,size_t length,const unsigned char nonce[12],const unsigned char * aad,size_t aad_len,const unsigned char * input,unsigned char * output,unsigned char tag[16])244 static int chachapoly_crypt_and_tag(mbedtls_chachapoly_context *ctx,
245 mbedtls_chachapoly_mode_t mode,
246 size_t length,
247 const unsigned char nonce[12],
248 const unsigned char *aad,
249 size_t aad_len,
250 const unsigned char *input,
251 unsigned char *output,
252 unsigned char tag[16])
253 {
254 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
255
256 ret = mbedtls_chachapoly_starts(ctx, nonce, mode);
257 if (ret != 0) {
258 goto cleanup;
259 }
260
261 ret = mbedtls_chachapoly_update_aad(ctx, aad, aad_len);
262 if (ret != 0) {
263 goto cleanup;
264 }
265
266 ret = mbedtls_chachapoly_update(ctx, length, input, output);
267 if (ret != 0) {
268 goto cleanup;
269 }
270
271 ret = mbedtls_chachapoly_finish(ctx, tag);
272
273 cleanup:
274 return ret;
275 }
276
mbedtls_chachapoly_encrypt_and_tag(mbedtls_chachapoly_context * ctx,size_t length,const unsigned char nonce[12],const unsigned char * aad,size_t aad_len,const unsigned char * input,unsigned char * output,unsigned char tag[16])277 int mbedtls_chachapoly_encrypt_and_tag(mbedtls_chachapoly_context *ctx,
278 size_t length,
279 const unsigned char nonce[12],
280 const unsigned char *aad,
281 size_t aad_len,
282 const unsigned char *input,
283 unsigned char *output,
284 unsigned char tag[16])
285 {
286 return chachapoly_crypt_and_tag(ctx, MBEDTLS_CHACHAPOLY_ENCRYPT,
287 length, nonce, aad, aad_len,
288 input, output, tag);
289 }
290
mbedtls_chachapoly_auth_decrypt(mbedtls_chachapoly_context * ctx,size_t length,const unsigned char nonce[12],const unsigned char * aad,size_t aad_len,const unsigned char tag[16],const unsigned char * input,unsigned char * output)291 int mbedtls_chachapoly_auth_decrypt(mbedtls_chachapoly_context *ctx,
292 size_t length,
293 const unsigned char nonce[12],
294 const unsigned char *aad,
295 size_t aad_len,
296 const unsigned char tag[16],
297 const unsigned char *input,
298 unsigned char *output)
299 {
300 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
301 unsigned char check_tag[16];
302 int diff;
303
304 if ((ret = chachapoly_crypt_and_tag(ctx,
305 MBEDTLS_CHACHAPOLY_DECRYPT, length, nonce,
306 aad, aad_len, input, output, check_tag)) != 0) {
307 return ret;
308 }
309
310 /* Check tag in "constant-time" */
311 diff = mbedtls_ct_memcmp(tag, check_tag, sizeof(check_tag));
312
313 if (diff != 0) {
314 mbedtls_platform_zeroize(output, length);
315 return MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED;
316 }
317
318 return 0;
319 }
320
321 #endif /* MBEDTLS_CHACHAPOLY_ALT */
322
323 #if defined(MBEDTLS_SELF_TEST)
324
325 static const unsigned char test_key[1][32] =
326 {
327 {
328 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
329 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
330 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
331 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
332 }
333 };
334
335 static const unsigned char test_nonce[1][12] =
336 {
337 {
338 0x07, 0x00, 0x00, 0x00, /* 32-bit common part */
339 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 /* 64-bit IV */
340 }
341 };
342
343 static const unsigned char test_aad[1][12] =
344 {
345 {
346 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
347 0xc4, 0xc5, 0xc6, 0xc7
348 }
349 };
350
351 static const size_t test_aad_len[1] =
352 {
353 12U
354 };
355
356 static const unsigned char test_input[1][114] =
357 {
358 {
359 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
360 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
361 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
362 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
363 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
364 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
365 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
366 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
367 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
368 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
369 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
370 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
371 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
372 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
373 0x74, 0x2e
374 }
375 };
376
377 static const unsigned char test_output[1][114] =
378 {
379 {
380 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
381 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
382 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
383 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
384 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
385 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
386 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
387 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
388 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
389 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
390 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
391 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
392 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
393 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
394 0x61, 0x16
395 }
396 };
397
398 static const size_t test_input_len[1] =
399 {
400 114U
401 };
402
403 static const unsigned char test_mac[1][16] =
404 {
405 {
406 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
407 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91
408 }
409 };
410
411 /* Make sure no other definition is already present. */
412 #undef ASSERT
413
414 #define ASSERT(cond, args) \
415 do \
416 { \
417 if (!(cond)) \
418 { \
419 if (verbose != 0) \
420 mbedtls_printf args; \
421 \
422 return -1; \
423 } \
424 } \
425 while (0)
426
mbedtls_chachapoly_self_test(int verbose)427 int mbedtls_chachapoly_self_test(int verbose)
428 {
429 mbedtls_chachapoly_context ctx;
430 unsigned i;
431 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
432 unsigned char output[200];
433 unsigned char mac[16];
434
435 for (i = 0U; i < 1U; i++) {
436 if (verbose != 0) {
437 mbedtls_printf(" ChaCha20-Poly1305 test %u ", i);
438 }
439
440 mbedtls_chachapoly_init(&ctx);
441
442 ret = mbedtls_chachapoly_setkey(&ctx, test_key[i]);
443 ASSERT(0 == ret, ("setkey() error code: %i\n", ret));
444
445 ret = mbedtls_chachapoly_encrypt_and_tag(&ctx,
446 test_input_len[i],
447 test_nonce[i],
448 test_aad[i],
449 test_aad_len[i],
450 test_input[i],
451 output,
452 mac);
453
454 ASSERT(0 == ret, ("crypt_and_tag() error code: %i\n", ret));
455
456 ASSERT(0 == memcmp(output, test_output[i], test_input_len[i]),
457 ("failure (wrong output)\n"));
458
459 ASSERT(0 == memcmp(mac, test_mac[i], 16U),
460 ("failure (wrong MAC)\n"));
461
462 mbedtls_chachapoly_free(&ctx);
463
464 if (verbose != 0) {
465 mbedtls_printf("passed\n");
466 }
467 }
468
469 if (verbose != 0) {
470 mbedtls_printf("\n");
471 }
472
473 return 0;
474 }
475
476 #endif /* MBEDTLS_SELF_TEST */
477
478 #endif /* MBEDTLS_CHACHAPOLY_C */
479