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