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