xref: /dragonfly/sys/crypto/chachapoly.c (revision 35e996c9)
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