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
_chacha20poly1305_init(struct chacha20poly1305_ctx * ctx,bool enc_mode,const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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
_chacha20poly1305_update_ad(struct chacha20poly1305_ctx * ctx,const uint8_t * ad,size_t ad_len)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
_chacha20poly1305_update(struct chacha20poly1305_ctx * ctx,uint8_t * out,const uint8_t * in,size_t in_len)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
_chacha20poly1305_final(struct chacha20poly1305_ctx * ctx,uint8_t * out,const uint8_t * in,size_t in_len)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
chacha20poly1305_encrypt(uint8_t * dst,const uint8_t * src,size_t src_len,const uint8_t * ad,size_t ad_len,const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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
chacha20poly1305_decrypt(uint8_t * dst,const uint8_t * src,size_t src_len,const uint8_t * ad,size_t ad_len,const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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
xchacha20poly1305_encrypt(uint8_t * dst,const uint8_t * src,size_t src_len,const uint8_t * ad,size_t ad_len,const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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
xchacha20poly1305_decrypt(uint8_t * dst,const uint8_t * src,size_t src_len,const uint8_t * ad,size_t ad_len,const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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
_chacha20poly1305_mbuf(struct chacha20poly1305_ctx * ctx,struct mbuf * m,int len)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
chacha20poly1305_encrypt_mbuf(struct mbuf * m,const uint8_t * ad,size_t ad_len,const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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
chacha20poly1305_decrypt_mbuf(struct mbuf * m,const uint8_t * ad,size_t ad_len,const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],const uint8_t key[CHACHA20POLY1305_KEY_SIZE])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