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