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