1 /* 2 * Copyright (c) 2015 Mike Belopuhov 3 * Copyright (c) 2023-2024 Aaron LI <aly@aaronly.me> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/endian.h> 21 #include <sys/mbuf.h> 22 23 #include <crypto/chachapoly.h> 24 #include <crypto/chacha20/chacha.h> 25 #include <crypto/poly1305/poly1305.h> 26 27 28 struct chacha20poly1305_ctx { 29 struct chacha_ctx chacha; 30 poly1305_state poly; 31 uint8_t tag[CHACHA20POLY1305_AUTHTAG_SIZE]; 32 size_t ad_len; 33 size_t data_len; 34 int flags; 35 #define F_MODE_ENCRYPTION 0x001 /* encryption operation */ 36 #define F_INITIALIZED 0x002 /* context initialized */ 37 #define F_AD_DONE 0x004 /* no more additional data */ 38 }; 39 40 #define PADDING_SIZE 16 41 #define PADDING_LEN(len) \ 42 ((PADDING_SIZE - ((len) & (PADDING_SIZE - 1))) & (PADDING_SIZE - 1)) 43 44 static const uint8_t pad0[PADDING_SIZE] = { 0 }; 45 46 47 static void 48 _chacha20poly1305_init(struct chacha20poly1305_ctx *ctx, bool enc_mode, 49 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 50 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 51 { 52 uint8_t poly_key[CHACHA20POLY1305_KEY_SIZE]; 53 uint8_t nonce64[CHACHA_NONCELEN], counter64[CHACHA_CTRLEN]; 54 55 bzero(ctx, sizeof(*ctx)); 56 bzero(poly_key, sizeof(poly_key)); 57 bzero(nonce64, sizeof(nonce64)); 58 bzero(counter64, sizeof(counter64)); 59 60 /* 61 * The original ChaCha uses a 64-bit nonce and a 64-bit counter, 62 * but the IETF version is modified to use a 96-bit nonce and 63 * a 32-bit counter. (RFC 8439) 64 */ 65 memcpy(counter64 + 4, nonce, 4); 66 memcpy(nonce64, nonce + 4, 8); 67 68 /* Generate the Poly1305 one-time key. */ 69 chacha_keysetup(&ctx->chacha, key, CHACHA20POLY1305_KEY_SIZE * 8); 70 chacha_ivsetup(&ctx->chacha, nonce64, counter64); 71 chacha_encrypt_bytes(&ctx->chacha, poly_key, poly_key, 72 sizeof(poly_key)); 73 74 /* Initialize the authenticator. */ 75 poly1305_init(&ctx->poly, poly_key); 76 77 ctx->flags |= (enc_mode ? F_MODE_ENCRYPTION : 0); 78 ctx->flags |= F_INITIALIZED; 79 } 80 81 /* 82 * Authenticate the additional data (AD). 83 * 84 * This function may be called zero, one or more times to pass successive 85 * fragments of the AD, but must be done before processing any cipher data, 86 * i.e., calling _chacha20poly1305_update(). 87 */ 88 static void 89 _chacha20poly1305_update_ad(struct chacha20poly1305_ctx *ctx, 90 const uint8_t *ad, size_t ad_len) 91 { 92 KKASSERT((ctx->flags & F_INITIALIZED) != 0); 93 KKASSERT((ctx->flags & F_AD_DONE) == 0); 94 95 if (ad_len == 0 || ad == NULL) 96 return; 97 98 poly1305_update(&ctx->poly, ad, ad_len); 99 ctx->ad_len += ad_len; 100 } 101 102 /* 103 * Encrypt/decrypt the cipher data (i.e., plaintext or ciphertext). 104 * 105 * NOTE: The cipher data must be complete blocks. This requirement is 106 * placed to help easily implement the in-place encryption/ 107 * decryption for data in non-contiguous buffers (e.g., mbuf chain). 108 */ 109 static void 110 _chacha20poly1305_update(struct chacha20poly1305_ctx *ctx, 111 uint8_t *out, const uint8_t *in, size_t in_len) 112 { 113 KKASSERT((ctx->flags & F_INITIALIZED) != 0); 114 KKASSERT(in_len == 0 || (in_len > 0 && out != NULL)); 115 KKASSERT(in_len % CHACHA_BLOCKLEN == 0); /* must be complete blocks */ 116 117 if ((ctx->flags & F_AD_DONE) == 0) { 118 poly1305_update(&ctx->poly, pad0, PADDING_LEN(ctx->ad_len)); 119 ctx->flags |= F_AD_DONE; 120 } 121 122 if (in_len == 0 || in == NULL) 123 return; 124 125 /* Swap the Poly1305/ChaCha order to support in-place operation. */ 126 if (ctx->flags & F_MODE_ENCRYPTION) { 127 chacha_encrypt_bytes(&ctx->chacha, in, out, in_len); 128 poly1305_update(&ctx->poly, out, in_len); 129 } else { 130 poly1305_update(&ctx->poly, in, in_len); 131 chacha_encrypt_bytes(&ctx->chacha, in, out, in_len); 132 } 133 134 ctx->data_len += in_len; 135 } 136 137 /* 138 * Process the last cipher block, which can be empty, complete or incomplete. 139 * 140 * In the encryption mode, the tag is calculated and is available via 141 * 'ctx->tag'. Let the caller get the tag and append to the output, because 142 * the allocated tag space may not directly follow the output buffer; e.g., 143 * the tag is allocated on a separate mbuf. 144 * 145 * In the decryption mode, the caller must set the expected tag in 146 * 'ctx->tag' before calling this function. The tag will be verified and 147 * a boolean is returned to indicate whether the tag matches. 148 */ 149 static bool 150 _chacha20poly1305_final(struct chacha20poly1305_ctx *ctx, 151 uint8_t *out, const uint8_t *in, size_t in_len) 152 { 153 uint64_t lens[2]; 154 uint8_t tag[CHACHA20POLY1305_AUTHTAG_SIZE]; 155 bool ret; 156 157 KKASSERT((ctx->flags & F_INITIALIZED) != 0); 158 KKASSERT(in_len == 0 || (in_len > 0 && out != NULL)); 159 KKASSERT(in_len <= CHACHA_BLOCKLEN); 160 161 /* Handle the case that no _update() was called. */ 162 if (ctx->ad_len > 0 && (ctx->flags & F_AD_DONE) == 0) { 163 KKASSERT(ctx->data_len == 0); 164 poly1305_update(&ctx->poly, pad0, PADDING_LEN(ctx->ad_len)); 165 ctx->flags |= F_AD_DONE; 166 } 167 168 if (in_len > 0) { 169 if (ctx->flags & F_MODE_ENCRYPTION) { 170 chacha_encrypt_bytes(&ctx->chacha, in, out, in_len); 171 poly1305_update(&ctx->poly, out, in_len); 172 } else { 173 poly1305_update(&ctx->poly, in, in_len); 174 chacha_encrypt_bytes(&ctx->chacha, in, out, in_len); 175 } 176 /* 177 * Only need to pad the last chunk, because the data chunks 178 * processed by _update() are complete blocks. 179 */ 180 poly1305_update(&ctx->poly, pad0, PADDING_LEN(in_len)); 181 ctx->data_len += in_len; 182 } 183 184 /* Calculate the tag. */ 185 lens[0] = htole64(ctx->ad_len); 186 lens[1] = htole64(ctx->data_len); 187 poly1305_update(&ctx->poly, (uint8_t *)lens, sizeof(lens)); 188 poly1305_finish(&ctx->poly, tag); 189 190 if (ctx->flags & F_MODE_ENCRYPTION) { 191 ret = true; 192 explicit_bzero(ctx, sizeof(*ctx)); 193 memcpy(ctx->tag, tag, sizeof(tag)); 194 } else { 195 ret = (timingsafe_bcmp(ctx->tag, tag, sizeof(tag)) == 0); 196 explicit_bzero(ctx, sizeof(*ctx)); 197 } 198 199 return (ret); 200 } 201 202 /*-------------------------------------------------------------------*/ 203 204 void 205 chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 206 const uint8_t *ad, size_t ad_len, 207 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 208 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 209 { 210 struct chacha20poly1305_ctx ctx; 211 size_t len; 212 213 _chacha20poly1305_init(&ctx, true, nonce, key); 214 215 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 216 217 len = rounddown2(src_len, CHACHA_BLOCKLEN); 218 _chacha20poly1305_update(&ctx, dst, src, len); 219 220 _chacha20poly1305_final(&ctx, dst + len, src + len, src_len - len); 221 memcpy(dst + src_len, ctx.tag, sizeof(ctx.tag)); 222 } 223 224 bool 225 chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 226 const uint8_t *ad, size_t ad_len, 227 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 228 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 229 { 230 struct chacha20poly1305_ctx ctx; 231 size_t data_len, len; 232 233 if (src_len < sizeof(ctx.tag)) 234 return (false); 235 236 _chacha20poly1305_init(&ctx, false, nonce, key); 237 238 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 239 240 data_len = src_len - sizeof(ctx.tag); 241 len = rounddown2(data_len, CHACHA_BLOCKLEN); 242 _chacha20poly1305_update(&ctx, dst, src, len); 243 244 memcpy(ctx.tag, src + data_len, sizeof(ctx.tag)); 245 return _chacha20poly1305_final(&ctx, dst + len, src + len, 246 data_len - len); 247 } 248 249 /*-------------------------------------------------------------------*/ 250 251 /* 252 * XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 253 * RFC draft: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha 254 */ 255 void 256 xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 257 const uint8_t *ad, size_t ad_len, 258 const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], 259 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 260 { 261 uint8_t derived_key[CHACHA20POLY1305_KEY_SIZE]; 262 uint8_t derived_nonce[CHACHA20POLY1305_NONCE_SIZE]; 263 264 /* Derive a subkey using the first 16 bytes of the nonce. */ 265 hchacha20(derived_key, nonce, key); 266 267 /* Prefix the remaining 8 bytes of the nonce with 4 NUL bytes. */ 268 bzero(derived_nonce, sizeof(derived_nonce)); 269 memcpy(derived_nonce + 4, nonce + 16, 8); 270 271 chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len, 272 derived_nonce, derived_key); 273 274 explicit_bzero(derived_key, sizeof(derived_key)); 275 } 276 277 bool 278 xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 279 const uint8_t *ad, size_t ad_len, 280 const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], 281 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 282 { 283 uint8_t derived_key[CHACHA20POLY1305_KEY_SIZE]; 284 uint8_t derived_nonce[CHACHA20POLY1305_NONCE_SIZE]; 285 bool ret; 286 287 hchacha20(derived_key, nonce, key); 288 289 bzero(derived_nonce, sizeof(derived_nonce)); 290 memcpy(derived_nonce + 4, nonce + 16, 8); 291 292 ret = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len, 293 derived_nonce, derived_key); 294 295 explicit_bzero(derived_key, sizeof(derived_key)); 296 return (ret); 297 } 298 299 /*-------------------------------------------------------------------*/ 300 301 static int 302 _chacha20poly1305_mbuf(struct chacha20poly1305_ctx *ctx, 303 struct mbuf *m, int len) 304 { 305 uint8_t block[CHACHA_BLOCKLEN], *p; 306 int off, blen, ret; 307 308 off = 0; 309 while (len >= (int)sizeof(block)) { 310 if (m->m_len == off) { 311 off = 0; 312 m = m->m_next; 313 /* Skip possibly empty mbufs. */ 314 while (m != NULL && m->m_len == 0) 315 m = m->m_next; 316 if (m == NULL) 317 return (EINVAL); /* because len > 0 */ 318 } 319 320 if (m->m_len >= off + sizeof(block)) { 321 p = mtod(m, uint8_t *) + off; 322 blen = rounddown2(MIN(len, m->m_len - off), 323 sizeof(block)); 324 _chacha20poly1305_update(ctx, p, p, blen); 325 326 off += blen; 327 len -= blen; 328 } else { 329 /* Insufficient data at the end; do some copying. */ 330 m_copydata(m, off, sizeof(block), block); 331 _chacha20poly1305_update(ctx, block, block, 332 sizeof(block)); 333 m_copyback(m, off, sizeof(block), block); 334 335 /* Advance pointer. */ 336 m = m_getptr(m, off + sizeof(block), &off); 337 if (m == NULL) 338 return (EINVAL); 339 340 len -= (int)sizeof(block); 341 } 342 } 343 344 m_copydata(m, off, len, block); /* len may be 0 */ 345 if (ctx->flags & F_MODE_ENCRYPTION) { 346 ret = _chacha20poly1305_final(ctx, block, block, len) ? 347 0 : EBADMSG; 348 m_copyback(m, off + len, sizeof(ctx->tag), ctx->tag); 349 } else { 350 m_copydata(m, off + len, sizeof(ctx->tag), ctx->tag); 351 ret = _chacha20poly1305_final(ctx, block, block, len) ? 352 0 : EBADMSG; 353 } 354 m_copyback(m, off, len, block); 355 356 return (ret); 357 } 358 359 int 360 chacha20poly1305_encrypt_mbuf(struct mbuf *m, const uint8_t *ad, size_t ad_len, 361 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 362 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 363 { 364 static const uint8_t blank_tag[CHACHA20POLY1305_AUTHTAG_SIZE] = { 0 }; 365 struct chacha20poly1305_ctx ctx; 366 int len; 367 368 M_ASSERTPKTHDR(m); 369 len = m->m_pkthdr.len; 370 371 if (!m_append(m, sizeof(blank_tag), blank_tag)) 372 return (ENOMEM); 373 374 _chacha20poly1305_init(&ctx, true, nonce, key); 375 376 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 377 378 return _chacha20poly1305_mbuf(&ctx, m, len); 379 } 380 381 int 382 chacha20poly1305_decrypt_mbuf(struct mbuf *m, const uint8_t *ad, size_t ad_len, 383 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 384 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 385 { 386 struct chacha20poly1305_ctx ctx; 387 int len, ret; 388 389 M_ASSERTPKTHDR(m); 390 len = m->m_pkthdr.len - CHACHA20POLY1305_AUTHTAG_SIZE; 391 if (len < 0) 392 return (EBADMSG); 393 394 _chacha20poly1305_init(&ctx, false, nonce, key); 395 396 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 397 398 ret = _chacha20poly1305_mbuf(&ctx, m, len); 399 if (ret == 0) 400 m_adj(m, -CHACHA20POLY1305_AUTHTAG_SIZE); 401 402 return (ret); 403 } 404