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 if (ctx->ad_len > 0 && (ctx->flags & F_AD_DONE) == 0) { 162 KKASSERT(ctx->data_len == 0); 163 poly1305_update(&ctx->poly, pad0, PADDING_LEN(ctx->ad_len)); 164 ctx->flags |= F_AD_DONE; 165 } 166 167 if (in_len > 0) { 168 if (ctx->flags & F_MODE_ENCRYPTION) { 169 chacha_encrypt_bytes(&ctx->chacha, in, out, in_len); 170 poly1305_update(&ctx->poly, out, in_len); 171 } else { 172 poly1305_update(&ctx->poly, in, in_len); 173 chacha_encrypt_bytes(&ctx->chacha, in, out, in_len); 174 } 175 poly1305_update(&ctx->poly, pad0, PADDING_LEN(in_len)); 176 ctx->data_len += in_len; 177 } 178 179 /* Calculate the tag. */ 180 lens[0] = htole64(ctx->ad_len); 181 lens[1] = htole64(ctx->data_len); 182 poly1305_update(&ctx->poly, (uint8_t *)lens, sizeof(lens)); 183 poly1305_finish(&ctx->poly, tag); 184 185 if (ctx->flags & F_MODE_ENCRYPTION) { 186 ret = true; 187 explicit_bzero(ctx, sizeof(*ctx)); 188 memcpy(ctx->tag, tag, sizeof(tag)); 189 } else { 190 ret = (timingsafe_bcmp(ctx->tag, tag, sizeof(tag)) == 0); 191 explicit_bzero(ctx, sizeof(*ctx)); 192 } 193 194 return (ret); 195 } 196 197 /*-------------------------------------------------------------------*/ 198 199 void 200 chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 201 const uint8_t *ad, size_t ad_len, 202 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 203 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 204 { 205 struct chacha20poly1305_ctx ctx; 206 size_t len; 207 208 _chacha20poly1305_init(&ctx, true, nonce, key); 209 210 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 211 212 len = rounddown2(src_len, CHACHA_BLOCKLEN); 213 _chacha20poly1305_update(&ctx, dst, src, len); 214 215 _chacha20poly1305_final(&ctx, dst + len, src + len, src_len - len); 216 memcpy(dst + src_len, ctx.tag, sizeof(ctx.tag)); 217 } 218 219 bool 220 chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 221 const uint8_t *ad, size_t ad_len, 222 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 223 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 224 { 225 struct chacha20poly1305_ctx ctx; 226 size_t data_len, len; 227 228 if (src_len < sizeof(ctx.tag)) 229 return (false); 230 231 _chacha20poly1305_init(&ctx, false, nonce, key); 232 233 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 234 235 data_len = src_len - sizeof(ctx.tag); 236 len = rounddown2(data_len, CHACHA_BLOCKLEN); 237 _chacha20poly1305_update(&ctx, dst, src, len); 238 239 memcpy(ctx.tag, src + data_len, sizeof(ctx.tag)); 240 return _chacha20poly1305_final(&ctx, dst + len, src + len, 241 data_len - len); 242 } 243 244 /*-------------------------------------------------------------------*/ 245 246 /* 247 * XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305 248 * RFC draft: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha 249 */ 250 void 251 xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 252 const uint8_t *ad, size_t ad_len, 253 const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], 254 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 255 { 256 uint8_t derived_key[CHACHA20POLY1305_KEY_SIZE]; 257 uint8_t derived_nonce[CHACHA20POLY1305_NONCE_SIZE]; 258 259 /* Derive a subkey using the first 16 bytes of the nonce. */ 260 hchacha20(derived_key, nonce, key); 261 262 /* Prefix the remaining 8 bytes of the nonce with 4 NUL bytes. */ 263 bzero(derived_nonce, sizeof(derived_nonce)); 264 memcpy(derived_nonce + 4, nonce + 16, 8); 265 266 chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len, 267 derived_nonce, derived_key); 268 269 explicit_bzero(derived_key, sizeof(derived_key)); 270 } 271 272 bool 273 xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, 274 const uint8_t *ad, size_t ad_len, 275 const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE], 276 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 277 { 278 uint8_t derived_key[CHACHA20POLY1305_KEY_SIZE]; 279 uint8_t derived_nonce[CHACHA20POLY1305_NONCE_SIZE]; 280 bool ret; 281 282 hchacha20(derived_key, nonce, key); 283 284 bzero(derived_nonce, sizeof(derived_nonce)); 285 memcpy(derived_nonce + 4, nonce + 16, 8); 286 287 ret = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len, 288 derived_nonce, derived_key); 289 290 explicit_bzero(derived_key, sizeof(derived_key)); 291 return (ret); 292 } 293 294 /*-------------------------------------------------------------------*/ 295 296 static int 297 _chacha20poly1305_mbuf(struct chacha20poly1305_ctx *ctx, 298 struct mbuf *m, int len) 299 { 300 uint8_t block[CHACHA_BLOCKLEN], *p; 301 int off, blen, ret; 302 303 off = 0; 304 while (len >= (int)sizeof(block)) { 305 if (m->m_len == off) { 306 off = 0; 307 m = m->m_next; 308 /* Skip possibly empty mbufs. */ 309 while (m != NULL && m->m_len == 0) 310 m = m->m_next; 311 if (m == NULL) 312 return (EINVAL); /* because len > 0 */ 313 } 314 315 if (m->m_len >= off + sizeof(block)) { 316 p = mtod(m, uint8_t *) + off; 317 blen = rounddown2(MIN(len, m->m_len - off), 318 sizeof(block)); 319 _chacha20poly1305_update(ctx, p, p, blen); 320 321 off += blen; 322 len -= blen; 323 } else { 324 /* Insufficient data at the end; do some copying. */ 325 m_copydata(m, off, sizeof(block), block); 326 _chacha20poly1305_update(ctx, block, block, 327 sizeof(block)); 328 m_copyback(m, off, sizeof(block), block); 329 330 /* Advance pointer. */ 331 m = m_getptr(m, off + sizeof(block), &off); 332 if (m == NULL) 333 return (EINVAL); 334 335 len -= (int)sizeof(block); 336 } 337 } 338 339 m_copydata(m, off, len, block); /* len may be 0 */ 340 if (ctx->flags & F_MODE_ENCRYPTION) { 341 ret = _chacha20poly1305_final(ctx, block, block, len) ? 342 0 : EBADMSG; 343 m_copyback(m, off + len, sizeof(ctx->tag), ctx->tag); 344 } else { 345 m_copydata(m, off + len, sizeof(ctx->tag), ctx->tag); 346 ret = _chacha20poly1305_final(ctx, block, block, len) ? 347 0 : EBADMSG; 348 } 349 m_copyback(m, off, len, block); 350 351 return (ret); 352 } 353 354 int 355 chacha20poly1305_encrypt_mbuf(struct mbuf *m, const uint8_t *ad, size_t ad_len, 356 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 357 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 358 { 359 static const uint8_t blank_tag[CHACHA20POLY1305_AUTHTAG_SIZE] = { 0 }; 360 struct chacha20poly1305_ctx ctx; 361 int len; 362 363 M_ASSERTPKTHDR(m); 364 len = m->m_pkthdr.len; 365 366 if (!m_append(m, sizeof(blank_tag), blank_tag)) 367 return (ENOMEM); 368 369 _chacha20poly1305_init(&ctx, true, nonce, key); 370 371 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 372 373 return _chacha20poly1305_mbuf(&ctx, m, len); 374 } 375 376 int 377 chacha20poly1305_decrypt_mbuf(struct mbuf *m, const uint8_t *ad, size_t ad_len, 378 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE], 379 const uint8_t key[CHACHA20POLY1305_KEY_SIZE]) 380 { 381 struct chacha20poly1305_ctx ctx; 382 int len, ret; 383 384 M_ASSERTPKTHDR(m); 385 len = m->m_pkthdr.len - CHACHA20POLY1305_AUTHTAG_SIZE; 386 if (len < 0) 387 return (EBADMSG); 388 389 _chacha20poly1305_init(&ctx, false, nonce, key); 390 391 _chacha20poly1305_update_ad(&ctx, ad, ad_len); 392 393 ret = _chacha20poly1305_mbuf(&ctx, m, len); 394 if (ret == 0) 395 m_adj(m, -CHACHA20POLY1305_AUTHTAG_SIZE); 396 397 return (ret); 398 } 399