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