xref: /third_party/mbedtls/library/chachapoly.c (revision a8e1175b)
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 */
34static 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 */
55static 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
70void 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
80void 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
94int 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
104int 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
138cleanup:
139    mbedtls_platform_zeroize(poly1305_key, 64U);
140    return ret;
141}
142
143int 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
156int 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
204int 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
244static 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
273cleanup:
274    return ret;
275}
276
277int 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
291int 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
325static 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
335static 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
343static 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
351static const size_t test_aad_len[1] =
352{
353    12U
354};
355
356static 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
377static 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
398static const size_t test_input_len[1] =
399{
400    114U
401};
402
403static 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
427int 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