xref: /dragonfly/sys/crypto/chachapoly.c (revision aebcea9c)
16f63b8faSAaron LI /*
26f63b8faSAaron LI  * Copyright (c) 2015 Mike Belopuhov
3117b0b40SAaron LI  * Copyright (c) 2023-2024 Aaron LI <aly@aaronly.me>
46f63b8faSAaron LI  *
56f63b8faSAaron LI  * Permission to use, copy, modify, and distribute this software for any
66f63b8faSAaron LI  * purpose with or without fee is hereby granted, provided that the above
76f63b8faSAaron LI  * copyright notice and this permission notice appear in all copies.
86f63b8faSAaron LI  *
96f63b8faSAaron LI  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106f63b8faSAaron LI  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116f63b8faSAaron LI  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126f63b8faSAaron LI  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136f63b8faSAaron LI  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146f63b8faSAaron LI  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156f63b8faSAaron LI  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166f63b8faSAaron LI  */
176f63b8faSAaron LI 
186f63b8faSAaron LI #include <sys/param.h>
196f63b8faSAaron LI #include <sys/systm.h>
206f63b8faSAaron LI #include <sys/endian.h>
2137273911SAaron LI #include <sys/mbuf.h>
226f63b8faSAaron LI 
236f63b8faSAaron LI #include <crypto/chachapoly.h>
246f63b8faSAaron LI #include <crypto/chacha20/chacha.h>
256f63b8faSAaron LI #include <crypto/poly1305/poly1305.h>
266f63b8faSAaron LI 
276f63b8faSAaron LI 
2859e2d684SAaron LI struct chacha20poly1305_ctx {
2959e2d684SAaron LI 	struct chacha_ctx	chacha;
3059e2d684SAaron LI 	poly1305_state		poly;
3159e2d684SAaron LI 	uint8_t			tag[CHACHA20POLY1305_AUTHTAG_SIZE];
3259e2d684SAaron LI 	size_t			ad_len;
3359e2d684SAaron LI 	size_t			data_len;
3459e2d684SAaron LI 	int			flags;
3559e2d684SAaron LI #define F_MODE_ENCRYPTION	0x001	/* encryption operation */
3659e2d684SAaron LI #define F_INITIALIZED		0x002	/* context initialized */
3759e2d684SAaron LI #define F_AD_DONE		0x004	/* no more additional data */
3859e2d684SAaron LI };
3959e2d684SAaron LI 
406f63b8faSAaron LI #define PADDING_SIZE		16
416f63b8faSAaron LI #define PADDING_LEN(len) \
426f63b8faSAaron LI 	((PADDING_SIZE - ((len) & (PADDING_SIZE - 1))) & (PADDING_SIZE - 1))
436f63b8faSAaron LI 
446f63b8faSAaron LI static const uint8_t pad0[PADDING_SIZE] = { 0 };
456f63b8faSAaron LI 
466f63b8faSAaron LI 
4759e2d684SAaron LI 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])4859e2d684SAaron LI _chacha20poly1305_init(struct chacha20poly1305_ctx *ctx, bool enc_mode,
496f63b8faSAaron LI 		       const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],
506f63b8faSAaron LI 		       const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
516f63b8faSAaron LI {
526f63b8faSAaron LI 	uint8_t poly_key[CHACHA20POLY1305_KEY_SIZE];
536f63b8faSAaron LI 	uint8_t nonce64[CHACHA_NONCELEN], counter64[CHACHA_CTRLEN];
546f63b8faSAaron LI 
5559e2d684SAaron LI 	bzero(ctx, sizeof(*ctx));
566f63b8faSAaron LI 	bzero(poly_key, sizeof(poly_key));
576f63b8faSAaron LI 	bzero(nonce64, sizeof(nonce64));
586f63b8faSAaron LI 	bzero(counter64, sizeof(counter64));
596f63b8faSAaron LI 
606f63b8faSAaron LI 	/*
616f63b8faSAaron LI 	 * The original ChaCha uses a 64-bit nonce and a 64-bit counter,
6259e2d684SAaron LI 	 * but the IETF version is modified to use a 96-bit nonce and
636f63b8faSAaron LI 	 * a 32-bit counter. (RFC 8439)
646f63b8faSAaron LI 	 */
656f63b8faSAaron LI 	memcpy(counter64 + 4, nonce, 4);
666f63b8faSAaron LI 	memcpy(nonce64, nonce + 4, 8);
676f63b8faSAaron LI 
686f63b8faSAaron LI 	/* Generate the Poly1305 one-time key. */
6959e2d684SAaron LI 	chacha_keysetup(&ctx->chacha, key, CHACHA20POLY1305_KEY_SIZE * 8);
7059e2d684SAaron LI 	chacha_ivsetup(&ctx->chacha, nonce64, counter64);
7159e2d684SAaron LI 	chacha_encrypt_bytes(&ctx->chacha, poly_key, poly_key,
7259e2d684SAaron LI 			     sizeof(poly_key));
736f63b8faSAaron LI 
7459e2d684SAaron LI 	/* Initialize the authenticator. */
7559e2d684SAaron LI 	poly1305_init(&ctx->poly, poly_key);
7659e2d684SAaron LI 
7759e2d684SAaron LI 	ctx->flags |= (enc_mode ? F_MODE_ENCRYPTION : 0);
7859e2d684SAaron LI 	ctx->flags |= F_INITIALIZED;
7959e2d684SAaron LI }
8059e2d684SAaron LI 
8159e2d684SAaron LI /*
82117b0b40SAaron LI  * Authenticate the additional data (AD).
8359e2d684SAaron LI  *
84117b0b40SAaron LI  * This function may be called zero, one or more times to pass successive
85117b0b40SAaron LI  * fragments of the AD, but must be done before processing any cipher data,
86117b0b40SAaron LI  * i.e., calling _chacha20poly1305_update().
87117b0b40SAaron LI  */
88117b0b40SAaron LI static void
_chacha20poly1305_update_ad(struct chacha20poly1305_ctx * ctx,const uint8_t * ad,size_t ad_len)89117b0b40SAaron LI _chacha20poly1305_update_ad(struct chacha20poly1305_ctx *ctx,
90117b0b40SAaron LI 			    const uint8_t *ad, size_t ad_len)
91117b0b40SAaron LI {
92117b0b40SAaron LI 	KKASSERT((ctx->flags & F_INITIALIZED) != 0);
93117b0b40SAaron LI 	KKASSERT((ctx->flags & F_AD_DONE) == 0);
94117b0b40SAaron LI 
95117b0b40SAaron LI 	if (ad_len == 0 || ad == NULL)
96117b0b40SAaron LI 		return;
97117b0b40SAaron LI 
98117b0b40SAaron LI 	poly1305_update(&ctx->poly, ad, ad_len);
99117b0b40SAaron LI 	ctx->ad_len += ad_len;
100117b0b40SAaron LI }
101117b0b40SAaron LI 
102117b0b40SAaron LI /*
103117b0b40SAaron LI  * Encrypt/decrypt the cipher data (i.e., plaintext or ciphertext).
10459e2d684SAaron LI  *
10559e2d684SAaron LI  * NOTE: The cipher data must be complete blocks.  This requirement is
10659e2d684SAaron LI  *       placed to help easily implement the in-place encryption/
10759e2d684SAaron LI  *       decryption for data in non-contiguous buffers (e.g., mbuf chain).
10859e2d684SAaron LI  */
10959e2d684SAaron LI static void
_chacha20poly1305_update(struct chacha20poly1305_ctx * ctx,uint8_t * out,const uint8_t * in,size_t in_len)11059e2d684SAaron LI _chacha20poly1305_update(struct chacha20poly1305_ctx *ctx,
11159e2d684SAaron LI 			 uint8_t *out, const uint8_t *in, size_t in_len)
11259e2d684SAaron LI {
11359e2d684SAaron LI 	KKASSERT((ctx->flags & F_INITIALIZED) != 0);
114117b0b40SAaron LI 	KKASSERT(in_len == 0 || (in_len > 0 && out != NULL));
115117b0b40SAaron LI 	KKASSERT(in_len % CHACHA_BLOCKLEN == 0); /* must be complete blocks */
11659e2d684SAaron LI 
11759e2d684SAaron LI 	if ((ctx->flags & F_AD_DONE) == 0) {
11859e2d684SAaron LI 		poly1305_update(&ctx->poly, pad0, PADDING_LEN(ctx->ad_len));
11959e2d684SAaron LI 		ctx->flags |= F_AD_DONE;
12059e2d684SAaron LI 	}
12159e2d684SAaron LI 
122117b0b40SAaron LI 	if (in_len == 0 || in == NULL)
123117b0b40SAaron LI 		return;
124117b0b40SAaron LI 
12559e2d684SAaron LI 	/* Swap the Poly1305/ChaCha order to support in-place operation. */
12659e2d684SAaron LI 	if (ctx->flags & F_MODE_ENCRYPTION) {
12759e2d684SAaron LI 		chacha_encrypt_bytes(&ctx->chacha, in, out, in_len);
12859e2d684SAaron LI 		poly1305_update(&ctx->poly, out, in_len);
12959e2d684SAaron LI 	} else {
13059e2d684SAaron LI 		poly1305_update(&ctx->poly, in, in_len);
13159e2d684SAaron LI 		chacha_encrypt_bytes(&ctx->chacha, in, out, in_len);
13259e2d684SAaron LI 	}
13359e2d684SAaron LI 
13459e2d684SAaron LI 	ctx->data_len += in_len;
13559e2d684SAaron LI }
13659e2d684SAaron LI 
13759e2d684SAaron LI /*
13859e2d684SAaron LI  * Process the last cipher block, which can be empty, complete or incomplete.
13959e2d684SAaron LI  *
14059e2d684SAaron LI  * In the encryption mode, the tag is calculated and is available via
14159e2d684SAaron LI  * 'ctx->tag'.  Let the caller get the tag and append to the output, because
14259e2d684SAaron LI  * the allocated tag space may not directly follow the output buffer; e.g.,
14359e2d684SAaron LI  * the tag is allocated on a separate mbuf.
14459e2d684SAaron LI  *
14559e2d684SAaron LI  * In the decryption mode, the caller must set the expected tag in
14659e2d684SAaron LI  * 'ctx->tag' before calling this function.  The tag will be verified and
14759e2d684SAaron LI  * a boolean is returned to indicate whether the tag matches.
14859e2d684SAaron LI  */
14959e2d684SAaron LI static bool
_chacha20poly1305_final(struct chacha20poly1305_ctx * ctx,uint8_t * out,const uint8_t * in,size_t in_len)15059e2d684SAaron LI _chacha20poly1305_final(struct chacha20poly1305_ctx *ctx,
15159e2d684SAaron LI 			uint8_t *out, const uint8_t *in, size_t in_len)
15259e2d684SAaron LI {
15359e2d684SAaron LI 	uint64_t lens[2];
15459e2d684SAaron LI 	uint8_t tag[CHACHA20POLY1305_AUTHTAG_SIZE];
15559e2d684SAaron LI 	bool ret;
15659e2d684SAaron LI 
15759e2d684SAaron LI 	KKASSERT((ctx->flags & F_INITIALIZED) != 0);
158117b0b40SAaron LI 	KKASSERT(in_len == 0 || (in_len > 0 && out != NULL));
15959e2d684SAaron LI 	KKASSERT(in_len <= CHACHA_BLOCKLEN);
16059e2d684SAaron LI 
161*aebcea9cSAaron LI 	/* Handle the case that no _update() was called. */
16259e2d684SAaron LI 	if (ctx->ad_len > 0 && (ctx->flags & F_AD_DONE) == 0) {
16359e2d684SAaron LI 		KKASSERT(ctx->data_len == 0);
16459e2d684SAaron LI 		poly1305_update(&ctx->poly, pad0, PADDING_LEN(ctx->ad_len));
16559e2d684SAaron LI 		ctx->flags |= F_AD_DONE;
16659e2d684SAaron LI 	}
16759e2d684SAaron LI 
168117b0b40SAaron LI 	if (in_len > 0) {
16959e2d684SAaron LI 		if (ctx->flags & F_MODE_ENCRYPTION) {
17059e2d684SAaron LI 			chacha_encrypt_bytes(&ctx->chacha, in, out, in_len);
17159e2d684SAaron LI 			poly1305_update(&ctx->poly, out, in_len);
17259e2d684SAaron LI 		} else {
17359e2d684SAaron LI 			poly1305_update(&ctx->poly, in, in_len);
17459e2d684SAaron LI 			chacha_encrypt_bytes(&ctx->chacha, in, out, in_len);
17559e2d684SAaron LI 		}
176*aebcea9cSAaron LI 		/*
177*aebcea9cSAaron LI 		 * Only need to pad the last chunk, because the data chunks
178*aebcea9cSAaron LI 		 * processed by _update() are complete blocks.
179*aebcea9cSAaron LI 		 */
18059e2d684SAaron LI 		poly1305_update(&ctx->poly, pad0, PADDING_LEN(in_len));
18159e2d684SAaron LI 		ctx->data_len += in_len;
182117b0b40SAaron LI 	}
1836f63b8faSAaron LI 
1846f63b8faSAaron LI 	/* Calculate the tag. */
18559e2d684SAaron LI 	lens[0] = htole64(ctx->ad_len);
18659e2d684SAaron LI 	lens[1] = htole64(ctx->data_len);
18759e2d684SAaron LI 	poly1305_update(&ctx->poly, (uint8_t *)lens, sizeof(lens));
18859e2d684SAaron LI 	poly1305_finish(&ctx->poly, tag);
1896f63b8faSAaron LI 
19059e2d684SAaron LI 	if (ctx->flags & F_MODE_ENCRYPTION) {
19159e2d684SAaron LI 		ret = true;
19259e2d684SAaron LI 		explicit_bzero(ctx, sizeof(*ctx));
19359e2d684SAaron LI 		memcpy(ctx->tag, tag, sizeof(tag));
19459e2d684SAaron LI 	} else {
19559e2d684SAaron LI 		ret = (timingsafe_bcmp(ctx->tag, tag, sizeof(tag)) == 0);
19659e2d684SAaron LI 		explicit_bzero(ctx, sizeof(*ctx));
19759e2d684SAaron LI 	}
19859e2d684SAaron LI 
19959e2d684SAaron LI 	return (ret);
20059e2d684SAaron LI }
20159e2d684SAaron LI 
20259e2d684SAaron LI /*-------------------------------------------------------------------*/
20359e2d684SAaron LI 
20459e2d684SAaron LI 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])20559e2d684SAaron LI chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len,
20659e2d684SAaron LI 			 const uint8_t *ad, size_t ad_len,
20759e2d684SAaron LI 			 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],
20859e2d684SAaron LI 			 const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
20959e2d684SAaron LI {
21059e2d684SAaron LI 	struct chacha20poly1305_ctx ctx;
21159e2d684SAaron LI 	size_t len;
21259e2d684SAaron LI 
21359e2d684SAaron LI 	_chacha20poly1305_init(&ctx, true, nonce, key);
21459e2d684SAaron LI 
215117b0b40SAaron LI 	_chacha20poly1305_update_ad(&ctx, ad, ad_len);
21659e2d684SAaron LI 
21759e2d684SAaron LI 	len = rounddown2(src_len, CHACHA_BLOCKLEN);
21859e2d684SAaron LI 	_chacha20poly1305_update(&ctx, dst, src, len);
21959e2d684SAaron LI 
22059e2d684SAaron LI 	_chacha20poly1305_final(&ctx, dst + len, src + len, src_len - len);
22159e2d684SAaron LI 	memcpy(dst + src_len, ctx.tag, sizeof(ctx.tag));
2226f63b8faSAaron LI }
2236f63b8faSAaron LI 
2246f63b8faSAaron LI 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])2256f63b8faSAaron LI chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len,
2266f63b8faSAaron LI 			 const uint8_t *ad, size_t ad_len,
2276f63b8faSAaron LI 			 const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],
2286f63b8faSAaron LI 			 const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
2296f63b8faSAaron LI {
23059e2d684SAaron LI 	struct chacha20poly1305_ctx ctx;
23159e2d684SAaron LI 	size_t data_len, len;
2326f63b8faSAaron LI 
23359e2d684SAaron LI 	if (src_len < sizeof(ctx.tag))
2346f63b8faSAaron LI 		return (false);
2356f63b8faSAaron LI 
23659e2d684SAaron LI 	_chacha20poly1305_init(&ctx, false, nonce, key);
2376f63b8faSAaron LI 
238117b0b40SAaron LI 	_chacha20poly1305_update_ad(&ctx, ad, ad_len);
2396f63b8faSAaron LI 
24059e2d684SAaron LI 	data_len = src_len - sizeof(ctx.tag);
24159e2d684SAaron LI 	len = rounddown2(data_len, CHACHA_BLOCKLEN);
24259e2d684SAaron LI 	_chacha20poly1305_update(&ctx, dst, src, len);
2436f63b8faSAaron LI 
24459e2d684SAaron LI 	memcpy(ctx.tag, src + data_len, sizeof(ctx.tag));
24559e2d684SAaron LI 	return _chacha20poly1305_final(&ctx, dst + len, src + len,
24659e2d684SAaron LI 				       data_len - len);
2476f63b8faSAaron LI }
2486f63b8faSAaron LI 
24959e2d684SAaron LI /*-------------------------------------------------------------------*/
2506f63b8faSAaron LI 
2516f63b8faSAaron LI /*
2526f63b8faSAaron LI  * XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305
2536f63b8faSAaron LI  * RFC draft: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
2546f63b8faSAaron LI  */
2556f63b8faSAaron LI 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])2566f63b8faSAaron LI xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len,
2576f63b8faSAaron LI 			  const uint8_t *ad, size_t ad_len,
2586f63b8faSAaron LI 			  const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
2596f63b8faSAaron LI 			  const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
2606f63b8faSAaron LI {
2616f63b8faSAaron LI 	uint8_t derived_key[CHACHA20POLY1305_KEY_SIZE];
2626f63b8faSAaron LI 	uint8_t derived_nonce[CHACHA20POLY1305_NONCE_SIZE];
2636f63b8faSAaron LI 
2646f63b8faSAaron LI 	/* Derive a subkey using the first 16 bytes of the nonce. */
2656f63b8faSAaron LI 	hchacha20(derived_key, nonce, key);
2666f63b8faSAaron LI 
2676f63b8faSAaron LI 	/* Prefix the remaining 8 bytes of the nonce with 4 NUL bytes. */
2686f63b8faSAaron LI 	bzero(derived_nonce, sizeof(derived_nonce));
2696f63b8faSAaron LI 	memcpy(derived_nonce + 4, nonce + 16, 8);
2706f63b8faSAaron LI 
2716f63b8faSAaron LI 	chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len,
2726f63b8faSAaron LI 				 derived_nonce, derived_key);
2736f63b8faSAaron LI 
2746f63b8faSAaron LI 	explicit_bzero(derived_key, sizeof(derived_key));
2756f63b8faSAaron LI }
2766f63b8faSAaron LI 
2776f63b8faSAaron LI 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])2786f63b8faSAaron LI xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len,
2796f63b8faSAaron LI 			  const uint8_t *ad, size_t ad_len,
2806f63b8faSAaron LI 			  const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
2816f63b8faSAaron LI 			  const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
2826f63b8faSAaron LI {
2836f63b8faSAaron LI 	uint8_t derived_key[CHACHA20POLY1305_KEY_SIZE];
2846f63b8faSAaron LI 	uint8_t derived_nonce[CHACHA20POLY1305_NONCE_SIZE];
2856f63b8faSAaron LI 	bool ret;
2866f63b8faSAaron LI 
2876f63b8faSAaron LI 	hchacha20(derived_key, nonce, key);
2886f63b8faSAaron LI 
2896f63b8faSAaron LI 	bzero(derived_nonce, sizeof(derived_nonce));
2906f63b8faSAaron LI 	memcpy(derived_nonce + 4, nonce + 16, 8);
2916f63b8faSAaron LI 
2926f63b8faSAaron LI 	ret = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len,
2936f63b8faSAaron LI 				       derived_nonce, derived_key);
2946f63b8faSAaron LI 
2956f63b8faSAaron LI 	explicit_bzero(derived_key, sizeof(derived_key));
2966f63b8faSAaron LI 	return (ret);
2976f63b8faSAaron LI }
29837273911SAaron LI 
29937273911SAaron LI /*-------------------------------------------------------------------*/
30037273911SAaron LI 
30137273911SAaron LI static int
_chacha20poly1305_mbuf(struct chacha20poly1305_ctx * ctx,struct mbuf * m,int len)30237273911SAaron LI _chacha20poly1305_mbuf(struct chacha20poly1305_ctx *ctx,
30337273911SAaron LI 		       struct mbuf *m, int len)
30437273911SAaron LI {
30537273911SAaron LI 	uint8_t block[CHACHA_BLOCKLEN], *p;
30637273911SAaron LI 	int off, blen, ret;
30737273911SAaron LI 
30837273911SAaron LI 	off = 0;
30937273911SAaron LI 	while (len >= (int)sizeof(block)) {
31037273911SAaron LI 		if (m->m_len == off) {
31137273911SAaron LI 			off = 0;
31237273911SAaron LI 			m = m->m_next;
31337273911SAaron LI 			/* Skip possibly empty mbufs. */
31437273911SAaron LI 			while (m != NULL && m->m_len == 0)
31537273911SAaron LI 				m = m->m_next;
31637273911SAaron LI 			if (m == NULL)
31737273911SAaron LI 				return (EINVAL); /* because len > 0 */
31837273911SAaron LI 		}
31937273911SAaron LI 
32037273911SAaron LI 		if (m->m_len >= off + sizeof(block)) {
32137273911SAaron LI 			p = mtod(m, uint8_t *) + off;
32237273911SAaron LI 			blen = rounddown2(MIN(len, m->m_len - off),
32337273911SAaron LI 					  sizeof(block));
32437273911SAaron LI 			_chacha20poly1305_update(ctx, p, p, blen);
32537273911SAaron LI 
32637273911SAaron LI 			off += blen;
32737273911SAaron LI 			len -= blen;
32837273911SAaron LI 		} else {
32937273911SAaron LI 			/* Insufficient data at the end; do some copying. */
33037273911SAaron LI 			m_copydata(m, off, sizeof(block), block);
33137273911SAaron LI 			_chacha20poly1305_update(ctx, block, block,
33237273911SAaron LI 						 sizeof(block));
33337273911SAaron LI 			m_copyback(m, off, sizeof(block), block);
33437273911SAaron LI 
33537273911SAaron LI 			/* Advance pointer. */
33637273911SAaron LI 			m = m_getptr(m, off + sizeof(block), &off);
33737273911SAaron LI 			if (m == NULL)
33837273911SAaron LI 				return (EINVAL);
33937273911SAaron LI 
34037273911SAaron LI 			len -= (int)sizeof(block);
34137273911SAaron LI 		}
34237273911SAaron LI 	}
34337273911SAaron LI 
34437273911SAaron LI 	m_copydata(m, off, len, block); /* len may be 0 */
34537273911SAaron LI 	if (ctx->flags & F_MODE_ENCRYPTION) {
34637273911SAaron LI 		ret = _chacha20poly1305_final(ctx, block, block, len) ?
34737273911SAaron LI 		      0 : EBADMSG;
34837273911SAaron LI 		m_copyback(m, off + len, sizeof(ctx->tag), ctx->tag);
34937273911SAaron LI 	} else {
35037273911SAaron LI 		m_copydata(m, off + len, sizeof(ctx->tag), ctx->tag);
35137273911SAaron LI 		ret = _chacha20poly1305_final(ctx, block, block, len) ?
35237273911SAaron LI 		      0 : EBADMSG;
35337273911SAaron LI 	}
35437273911SAaron LI 	m_copyback(m, off, len, block);
35537273911SAaron LI 
35637273911SAaron LI 	return (ret);
35737273911SAaron LI }
35837273911SAaron LI 
35937273911SAaron LI 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])36037273911SAaron LI chacha20poly1305_encrypt_mbuf(struct mbuf *m, const uint8_t *ad, size_t ad_len,
36137273911SAaron LI 			      const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],
36237273911SAaron LI 			      const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
36337273911SAaron LI {
36437273911SAaron LI 	static const uint8_t blank_tag[CHACHA20POLY1305_AUTHTAG_SIZE] = { 0 };
36537273911SAaron LI 	struct chacha20poly1305_ctx ctx;
36637273911SAaron LI 	int len;
36737273911SAaron LI 
36837273911SAaron LI 	M_ASSERTPKTHDR(m);
36937273911SAaron LI 	len = m->m_pkthdr.len;
37037273911SAaron LI 
37137273911SAaron LI 	if (!m_append(m, sizeof(blank_tag), blank_tag))
37237273911SAaron LI 		return (ENOMEM);
37337273911SAaron LI 
37437273911SAaron LI 	_chacha20poly1305_init(&ctx, true, nonce, key);
37537273911SAaron LI 
376117b0b40SAaron LI 	_chacha20poly1305_update_ad(&ctx, ad, ad_len);
37737273911SAaron LI 
37837273911SAaron LI 	return _chacha20poly1305_mbuf(&ctx, m, len);
37937273911SAaron LI }
38037273911SAaron LI 
38137273911SAaron LI 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])38237273911SAaron LI chacha20poly1305_decrypt_mbuf(struct mbuf *m, const uint8_t *ad, size_t ad_len,
38337273911SAaron LI 			      const uint8_t nonce[CHACHA20POLY1305_NONCE_SIZE],
38437273911SAaron LI 			      const uint8_t key[CHACHA20POLY1305_KEY_SIZE])
38537273911SAaron LI {
38637273911SAaron LI 	struct chacha20poly1305_ctx ctx;
38737273911SAaron LI 	int len, ret;
38837273911SAaron LI 
38937273911SAaron LI 	M_ASSERTPKTHDR(m);
39037273911SAaron LI 	len = m->m_pkthdr.len - CHACHA20POLY1305_AUTHTAG_SIZE;
39137273911SAaron LI 	if (len < 0)
39237273911SAaron LI 		return (EBADMSG);
39337273911SAaron LI 
39437273911SAaron LI 	_chacha20poly1305_init(&ctx, false, nonce, key);
39537273911SAaron LI 
396117b0b40SAaron LI 	_chacha20poly1305_update_ad(&ctx, ad, ad_len);
39737273911SAaron LI 
39837273911SAaron LI 	ret = _chacha20poly1305_mbuf(&ctx, m, len);
39937273911SAaron LI 	if (ret == 0)
40037273911SAaron LI 		m_adj(m, -CHACHA20POLY1305_AUTHTAG_SIZE);
40137273911SAaron LI 
40237273911SAaron LI 	return (ret);
40337273911SAaron LI }
404