Line data Source code
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 : */
34 0 : static int chachapoly_pad_aad(mbedtls_chachapoly_context *ctx)
35 : {
36 0 : uint32_t partial_block_len = (uint32_t) (ctx->aad_len % 16U);
37 : unsigned char zeroes[15];
38 :
39 0 : if (partial_block_len == 0U) {
40 0 : return 0;
41 : }
42 :
43 0 : memset(zeroes, 0, sizeof(zeroes));
44 :
45 0 : return mbedtls_poly1305_update(&ctx->poly1305_ctx,
46 : zeroes,
47 0 : 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 : */
55 0 : static int chachapoly_pad_ciphertext(mbedtls_chachapoly_context *ctx)
56 : {
57 0 : uint32_t partial_block_len = (uint32_t) (ctx->ciphertext_len % 16U);
58 : unsigned char zeroes[15];
59 :
60 0 : if (partial_block_len == 0U) {
61 0 : return 0;
62 : }
63 :
64 0 : memset(zeroes, 0, sizeof(zeroes));
65 0 : return mbedtls_poly1305_update(&ctx->poly1305_ctx,
66 : zeroes,
67 0 : 16U - partial_block_len);
68 : }
69 :
70 0 : void mbedtls_chachapoly_init(mbedtls_chachapoly_context *ctx)
71 : {
72 0 : mbedtls_chacha20_init(&ctx->chacha20_ctx);
73 0 : mbedtls_poly1305_init(&ctx->poly1305_ctx);
74 0 : ctx->aad_len = 0U;
75 0 : ctx->ciphertext_len = 0U;
76 0 : ctx->state = CHACHAPOLY_STATE_INIT;
77 0 : ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT;
78 0 : }
79 :
80 0 : void mbedtls_chachapoly_free(mbedtls_chachapoly_context *ctx)
81 : {
82 0 : if (ctx == NULL) {
83 0 : return;
84 : }
85 :
86 0 : mbedtls_chacha20_free(&ctx->chacha20_ctx);
87 0 : mbedtls_poly1305_free(&ctx->poly1305_ctx);
88 0 : ctx->aad_len = 0U;
89 0 : ctx->ciphertext_len = 0U;
90 0 : ctx->state = CHACHAPOLY_STATE_INIT;
91 0 : ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT;
92 : }
93 :
94 0 : int mbedtls_chachapoly_setkey(mbedtls_chachapoly_context *ctx,
95 : const unsigned char key[32])
96 : {
97 0 : int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
98 :
99 0 : ret = mbedtls_chacha20_setkey(&ctx->chacha20_ctx, key);
100 :
101 0 : return ret;
102 : }
103 :
104 0 : int mbedtls_chachapoly_starts(mbedtls_chachapoly_context *ctx,
105 : const unsigned char nonce[12],
106 : mbedtls_chachapoly_mode_t mode)
107 : {
108 0 : 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 0 : ret = mbedtls_chacha20_starts(&ctx->chacha20_ctx, nonce, 0U);
113 0 : if (ret != 0) {
114 0 : 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 0 : memset(poly1305_key, 0, sizeof(poly1305_key));
123 0 : ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, sizeof(poly1305_key),
124 : poly1305_key, poly1305_key);
125 0 : if (ret != 0) {
126 0 : goto cleanup;
127 : }
128 :
129 0 : ret = mbedtls_poly1305_starts(&ctx->poly1305_ctx, poly1305_key);
130 :
131 0 : if (ret == 0) {
132 0 : ctx->aad_len = 0U;
133 0 : ctx->ciphertext_len = 0U;
134 0 : ctx->state = CHACHAPOLY_STATE_AAD;
135 0 : ctx->mode = mode;
136 : }
137 :
138 0 : cleanup:
139 0 : mbedtls_platform_zeroize(poly1305_key, 64U);
140 0 : return ret;
141 : }
142 :
143 0 : int mbedtls_chachapoly_update_aad(mbedtls_chachapoly_context *ctx,
144 : const unsigned char *aad,
145 : size_t aad_len)
146 : {
147 0 : if (ctx->state != CHACHAPOLY_STATE_AAD) {
148 0 : return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE;
149 : }
150 :
151 0 : ctx->aad_len += aad_len;
152 :
153 0 : return mbedtls_poly1305_update(&ctx->poly1305_ctx, aad, aad_len);
154 : }
155 :
156 0 : int mbedtls_chachapoly_update(mbedtls_chachapoly_context *ctx,
157 : size_t len,
158 : const unsigned char *input,
159 : unsigned char *output)
160 : {
161 0 : int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
162 :
163 0 : if ((ctx->state != CHACHAPOLY_STATE_AAD) &&
164 0 : (ctx->state != CHACHAPOLY_STATE_CIPHERTEXT)) {
165 0 : return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE;
166 : }
167 :
168 0 : if (ctx->state == CHACHAPOLY_STATE_AAD) {
169 0 : ctx->state = CHACHAPOLY_STATE_CIPHERTEXT;
170 :
171 0 : ret = chachapoly_pad_aad(ctx);
172 0 : if (ret != 0) {
173 0 : return ret;
174 : }
175 : }
176 :
177 0 : ctx->ciphertext_len += len;
178 :
179 0 : if (ctx->mode == MBEDTLS_CHACHAPOLY_ENCRYPT) {
180 0 : ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, len, input, output);
181 0 : if (ret != 0) {
182 0 : return ret;
183 : }
184 :
185 0 : ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, output, len);
186 0 : if (ret != 0) {
187 0 : return ret;
188 : }
189 : } else { /* DECRYPT */
190 0 : ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, input, len);
191 0 : if (ret != 0) {
192 0 : return ret;
193 : }
194 :
195 0 : ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, len, input, output);
196 0 : if (ret != 0) {
197 0 : return ret;
198 : }
199 : }
200 :
201 0 : return 0;
202 : }
203 :
204 0 : int mbedtls_chachapoly_finish(mbedtls_chachapoly_context *ctx,
205 : unsigned char mac[16])
206 : {
207 0 : int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
208 : unsigned char len_block[16];
209 :
210 0 : if (ctx->state == CHACHAPOLY_STATE_INIT) {
211 0 : return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE;
212 : }
213 :
214 0 : if (ctx->state == CHACHAPOLY_STATE_AAD) {
215 0 : ret = chachapoly_pad_aad(ctx);
216 0 : if (ret != 0) {
217 0 : return ret;
218 : }
219 0 : } else if (ctx->state == CHACHAPOLY_STATE_CIPHERTEXT) {
220 0 : ret = chachapoly_pad_ciphertext(ctx);
221 0 : if (ret != 0) {
222 0 : return ret;
223 : }
224 : }
225 :
226 0 : 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 0 : MBEDTLS_PUT_UINT64_LE(ctx->aad_len, len_block, 0);
232 0 : MBEDTLS_PUT_UINT64_LE(ctx->ciphertext_len, len_block, 8);
233 :
234 0 : ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, len_block, 16U);
235 0 : if (ret != 0) {
236 0 : return ret;
237 : }
238 :
239 0 : ret = mbedtls_poly1305_finish(&ctx->poly1305_ctx, mac);
240 :
241 0 : return ret;
242 : }
243 :
244 0 : 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 0 : int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
255 :
256 0 : ret = mbedtls_chachapoly_starts(ctx, nonce, mode);
257 0 : if (ret != 0) {
258 0 : goto cleanup;
259 : }
260 :
261 0 : ret = mbedtls_chachapoly_update_aad(ctx, aad, aad_len);
262 0 : if (ret != 0) {
263 0 : goto cleanup;
264 : }
265 :
266 0 : ret = mbedtls_chachapoly_update(ctx, length, input, output);
267 0 : if (ret != 0) {
268 0 : goto cleanup;
269 : }
270 :
271 0 : ret = mbedtls_chachapoly_finish(ctx, tag);
272 :
273 0 : cleanup:
274 0 : return ret;
275 : }
276 :
277 0 : 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 0 : return chachapoly_crypt_and_tag(ctx, MBEDTLS_CHACHAPOLY_ENCRYPT,
287 : length, nonce, aad, aad_len,
288 : input, output, tag);
289 : }
290 :
291 0 : 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 0 : int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
301 : unsigned char check_tag[16];
302 : int diff;
303 :
304 0 : if ((ret = chachapoly_crypt_and_tag(ctx,
305 : MBEDTLS_CHACHAPOLY_DECRYPT, length, nonce,
306 : aad, aad_len, input, output, check_tag)) != 0) {
307 0 : return ret;
308 : }
309 :
310 : /* Check tag in "constant-time" */
311 0 : diff = mbedtls_ct_memcmp(tag, check_tag, sizeof(check_tag));
312 :
313 0 : if (diff != 0) {
314 0 : mbedtls_platform_zeroize(output, length);
315 0 : return MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED;
316 : }
317 :
318 0 : 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 :
427 0 : int mbedtls_chachapoly_self_test(int verbose)
428 : {
429 : mbedtls_chachapoly_context ctx;
430 : unsigned i;
431 0 : int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
432 : unsigned char output[200];
433 : unsigned char mac[16];
434 :
435 0 : for (i = 0U; i < 1U; i++) {
436 0 : if (verbose != 0) {
437 0 : mbedtls_printf(" ChaCha20-Poly1305 test %u ", i);
438 : }
439 :
440 0 : mbedtls_chachapoly_init(&ctx);
441 :
442 0 : ret = mbedtls_chachapoly_setkey(&ctx, test_key[i]);
443 0 : ASSERT(0 == ret, ("setkey() error code: %i\n", ret));
444 :
445 0 : ret = mbedtls_chachapoly_encrypt_and_tag(&ctx,
446 0 : test_input_len[i],
447 0 : test_nonce[i],
448 0 : test_aad[i],
449 0 : test_aad_len[i],
450 0 : test_input[i],
451 : output,
452 : mac);
453 :
454 0 : ASSERT(0 == ret, ("crypt_and_tag() error code: %i\n", ret));
455 :
456 0 : ASSERT(0 == memcmp(output, test_output[i], test_input_len[i]),
457 : ("failure (wrong output)\n"));
458 :
459 0 : ASSERT(0 == memcmp(mac, test_mac[i], 16U),
460 : ("failure (wrong MAC)\n"));
461 :
462 0 : mbedtls_chachapoly_free(&ctx);
463 :
464 0 : if (verbose != 0) {
465 0 : mbedtls_printf("passed\n");
466 : }
467 : }
468 :
469 0 : if (verbose != 0) {
470 0 : mbedtls_printf("\n");
471 : }
472 :
473 0 : return 0;
474 : }
475 :
476 : #endif /* MBEDTLS_SELF_TEST */
477 :
478 : #endif /* MBEDTLS_CHACHAPOLY_C */
|