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