Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 21 Oct 2021 17:06:51 GMT
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: d5cbcae312e7 - stable/13 - Add an implementation of CHACHA20_POLY1305 to cryptosoft.
Message-ID:  <202110211706.19LH6pms080073@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=d5cbcae312e705fb89e2ce79c823e9d8697c6b0c

commit d5cbcae312e705fb89e2ce79c823e9d8697c6b0c
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2021-02-18 17:22:18 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2021-10-21 15:51:22 +0000

    Add an implementation of CHACHA20_POLY1305 to cryptosoft.
    
    This uses the chacha20 IETF and poly1305 implementations from
    libsodium.  A seperate auth_hash is created for the auth side whose
    Setkey method derives the poly1305 key from the AEAD key and nonce as
    described in RFC 8439.
    
    Sponsored by:   Netflix
    Differential Revision:  https://reviews.freebsd.org/D27837
    
    (cherry picked from commit dd2e1352b68aa33f7f6f8c19aaf88cf287013ae8)
---
 sys/conf/files                           |   3 +-
 sys/modules/crypto/Makefile              |   9 +-
 sys/opencrypto/cryptosoft.c              | 199 +++++++++++++++++++++++++++++++
 sys/opencrypto/xform_auth.h              |   1 +
 sys/opencrypto/xform_chacha20_poly1305.c | 128 +++++++++++++++++++-
 5 files changed, 335 insertions(+), 5 deletions(-)

diff --git a/sys/conf/files b/sys/conf/files
index a352a0759c99..f21c216288b1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4948,7 +4948,8 @@ opencrypto/gfmult.c		optional crypto | ipsec | ipsec_support
 opencrypto/rmd160.c		optional crypto | ipsec | ipsec_support
 opencrypto/xform.c		optional crypto | ipsec | ipsec_support
 opencrypto/xform_cbc_mac.c	optional crypto
-opencrypto/xform_chacha20_poly1305.c	optional crypto
+opencrypto/xform_chacha20_poly1305.c	optional crypto \
+	compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium"
 opencrypto/xform_poly1305.c	optional crypto \
 	compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium"
 contrib/libsodium/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \
diff --git a/sys/modules/crypto/Makefile b/sys/modules/crypto/Makefile
index 89af6ab8fc61..f64ea1f6fc8b 100644
--- a/sys/modules/crypto/Makefile
+++ b/sys/modules/crypto/Makefile
@@ -15,6 +15,8 @@ LIBSODIUM=${SRCTOP}/sys/contrib/libsodium/src/libsodium
 .PATH:	${SRCTOP}/sys/contrib/libb2
 .PATH:	${LIBSODIUM}/crypto_onetimeauth/poly1305
 .PATH:	${LIBSODIUM}/crypto_onetimeauth/poly1305/donna
+.PATH:	${LIBSODIUM}/crypto_stream/chacha20
+.PATH:	${LIBSODIUM}/crypto_stream/chacha20/ref
 .PATH:	${LIBSODIUM}/crypto_verify/sodium
 .PATH:	${SRCTOP}/sys/crypto/libsodium
 
@@ -55,8 +57,14 @@ SRCS	+= chacha-sw.c
 
 LIBSODIUM_INC=${LIBSODIUM}/include
 LIBSODIUM_COMPAT=${SRCTOP}/sys/crypto/libsodium
+SRCS	+= xform_chacha20_poly1305.c
+CFLAGS.xform_chacha20_poly1305.c+= -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
 SRCS	+= xform_poly1305.c
 CFLAGS.xform_poly1305.c		+= -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
+SRCS	+= stream_chacha20.c
+CFLAGS.stream_chacha20.c	+= -I${LIBSODIUM_INC}/sodium -I${LIBSODIUM_COMPAT}
+SRCS	+= chacha20_ref.c
+CFLAGS.chacha20_ref.c		+= -I${LIBSODIUM_INC}/sodium -I${LIBSODIUM_COMPAT}
 SRCS	+= onetimeauth_poly1305.c
 CFLAGS.onetimeauth_poly1305.c	+= -I${LIBSODIUM_INC}/sodium -I${LIBSODIUM_COMPAT}
 SRCS	+= poly1305_donna.c
@@ -67,7 +75,6 @@ SRCS	+= randombytes.c
 CFLAGS.randombytes.c		+= -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
 SRCS	+= utils.c
 CFLAGS.utils.c			+= -I${LIBSODIUM_INC} -I${LIBSODIUM_COMPAT}
-SRCS	+= xform_chacha20_poly1305.c
 
 SRCS	+= opt_param.h cryptodev_if.h bus_if.h device_if.h
 SRCS	+= opt_compat.h
diff --git a/sys/opencrypto/cryptosoft.c b/sys/opencrypto/cryptosoft.c
index 60d1f60d6cc1..6ac628b9c21f 100644
--- a/sys/opencrypto/cryptosoft.c
+++ b/sys/opencrypto/cryptosoft.c
@@ -867,6 +867,168 @@ out:
 	return (error);
 }
 
+static int
+swcr_chacha20_poly1305(struct swcr_session *ses, struct cryptop *crp)
+{
+	const struct crypto_session_params *csp;
+	uint64_t blkbuf[howmany(CHACHA20_NATIVE_BLOCK_LEN, sizeof(uint64_t))];
+	u_char *blk = (u_char *)blkbuf;
+	u_char tag[POLY1305_HASH_LEN];
+	struct crypto_buffer_cursor cc_in, cc_out;
+	const u_char *inblk;
+	u_char *outblk;
+	uint64_t *blkp;
+	union authctx ctx;
+	struct swcr_auth *swa;
+	struct swcr_encdec *swe;
+	struct auth_hash *axf;
+	struct enc_xform *exf;
+	int blksz, error, r, resid;
+
+	swa = &ses->swcr_auth;
+	axf = swa->sw_axf;
+
+	swe = &ses->swcr_encdec;
+	exf = swe->sw_exf;
+	blksz = exf->native_blocksize;
+	KASSERT(blksz <= sizeof(blkbuf), ("%s: blocksize mismatch", __func__));
+
+	if ((crp->crp_flags & CRYPTO_F_IV_SEPARATE) == 0)
+		return (EINVAL);
+
+	csp = crypto_get_params(crp->crp_session);
+
+	/* Generate Poly1305 key. */
+	if (crp->crp_cipher_key != NULL)
+		axf->Setkey(&ctx, crp->crp_cipher_key, csp->csp_cipher_klen);
+	else
+		axf->Setkey(&ctx, csp->csp_cipher_key, csp->csp_cipher_klen);
+	axf->Reinit(&ctx, crp->crp_iv, csp->csp_ivlen);
+
+	/* Supply MAC with AAD */
+	if (crp->crp_aad != NULL)
+		axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length);
+	else
+		crypto_apply(crp, crp->crp_aad_start,
+		    crp->crp_aad_length, axf->Update, &ctx);
+	if (crp->crp_aad_length % 16 != 0) {
+		/* padding1 */
+		memset(blk, 0, 16);
+		axf->Update(&ctx, blk, 16 - crp->crp_aad_length % 16);
+	}
+
+	if (crp->crp_cipher_key != NULL)
+		exf->setkey(swe->sw_kschedule, crp->crp_cipher_key,
+		    csp->csp_cipher_klen);
+	exf->reinit(swe->sw_kschedule, crp->crp_iv);
+
+	/* Do encryption with MAC */
+	crypto_cursor_init(&cc_in, &crp->crp_buf);
+	crypto_cursor_advance(&cc_in, crp->crp_payload_start);
+	if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
+		crypto_cursor_init(&cc_out, &crp->crp_obuf);
+		crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
+	} else
+		cc_out = cc_in;
+	for (resid = crp->crp_payload_length; resid >= blksz; resid -= blksz) {
+		if (crypto_cursor_seglen(&cc_in) < blksz) {
+			crypto_cursor_copydata(&cc_in, blksz, blk);
+			inblk = blk;
+		} else {
+			inblk = crypto_cursor_segbase(&cc_in);
+			crypto_cursor_advance(&cc_in, blksz);
+		}
+		if (CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
+			if (crypto_cursor_seglen(&cc_out) < blksz)
+				outblk = blk;
+			else
+				outblk = crypto_cursor_segbase(&cc_out);
+			exf->encrypt(swe->sw_kschedule, inblk, outblk);
+			axf->Update(&ctx, outblk, blksz);
+			if (outblk == blk)
+				crypto_cursor_copyback(&cc_out, blksz, blk);
+			else
+				crypto_cursor_advance(&cc_out, blksz);
+		} else {
+			axf->Update(&ctx, inblk, blksz);
+		}
+	}
+	if (resid > 0) {
+		crypto_cursor_copydata(&cc_in, resid, blk);
+		if (CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
+			exf->encrypt_last(swe->sw_kschedule, blk, blk, resid);
+			crypto_cursor_copyback(&cc_out, resid, blk);
+		}
+		axf->Update(&ctx, blk, resid);
+		if (resid % 16 != 0) {
+			/* padding2 */
+			memset(blk, 0, 16);
+			axf->Update(&ctx, blk, 16 - resid % 16);
+		}
+	}
+
+	/* lengths */
+	blkp = (uint64_t *)blk;
+	blkp[0] = htole64(crp->crp_aad_length);
+	blkp[1] = htole64(crp->crp_payload_length);
+	axf->Update(&ctx, blk, sizeof(uint64_t) * 2);
+
+	/* Finalize MAC */
+	axf->Final(tag, &ctx);
+
+	/* Validate tag */
+	error = 0;
+	if (!CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
+		u_char tag2[POLY1305_HASH_LEN];
+
+		crypto_copydata(crp, crp->crp_digest_start, swa->sw_mlen, tag2);
+
+		r = timingsafe_bcmp(tag, tag2, swa->sw_mlen);
+		explicit_bzero(tag2, sizeof(tag2));
+		if (r != 0) {
+			error = EBADMSG;
+			goto out;
+		}
+
+		/* tag matches, decrypt data */
+		crypto_cursor_init(&cc_in, &crp->crp_buf);
+		crypto_cursor_advance(&cc_in, crp->crp_payload_start);
+		for (resid = crp->crp_payload_length; resid > blksz;
+		     resid -= blksz) {
+			if (crypto_cursor_seglen(&cc_in) < blksz) {
+				crypto_cursor_copydata(&cc_in, blksz, blk);
+				inblk = blk;
+			} else {
+				inblk = crypto_cursor_segbase(&cc_in);
+				crypto_cursor_advance(&cc_in, blksz);
+			}
+			if (crypto_cursor_seglen(&cc_out) < blksz)
+				outblk = blk;
+			else
+				outblk = crypto_cursor_segbase(&cc_out);
+			exf->decrypt(swe->sw_kschedule, inblk, outblk);
+			if (outblk == blk)
+				crypto_cursor_copyback(&cc_out, blksz, blk);
+			else
+				crypto_cursor_advance(&cc_out, blksz);
+		}
+		if (resid > 0) {
+			crypto_cursor_copydata(&cc_in, resid, blk);
+			exf->decrypt_last(swe->sw_kschedule, blk, blk, resid);
+			crypto_cursor_copyback(&cc_out, resid, blk);
+		}
+	} else {
+		/* Inject the authentication data */
+		crypto_copyback(crp, crp->crp_digest_start, swa->sw_mlen, tag);
+	}
+
+out:
+	explicit_bzero(blkbuf, sizeof(blkbuf));
+	explicit_bzero(tag, sizeof(tag));
+	explicit_bzero(&ctx, sizeof(ctx));
+	return (error);
+}
+
 /*
  * Apply a cipher and a digest to perform EtA.
  */
@@ -1171,6 +1333,33 @@ swcr_setup_ccm(struct swcr_session *ses,
 	return (swcr_setup_cipher(ses, csp));
 }
 
+static int
+swcr_setup_chacha20_poly1305(struct swcr_session *ses,
+    const struct crypto_session_params *csp)
+{
+	struct swcr_auth *swa;
+	struct auth_hash *axf;
+
+	if (csp->csp_ivlen != CHACHA20_POLY1305_IV_LEN)
+		return (EINVAL);
+
+	/* First, setup the auth side. */
+	swa = &ses->swcr_auth;
+	axf = &auth_hash_chacha20_poly1305;
+	swa->sw_axf = axf;
+	if (csp->csp_auth_mlen < 0 || csp->csp_auth_mlen > axf->hashsize)
+		return (EINVAL);
+	if (csp->csp_auth_mlen == 0)
+		swa->sw_mlen = axf->hashsize;
+	else
+		swa->sw_mlen = csp->csp_auth_mlen;
+
+	/* The auth state is regenerated for each nonce. */
+
+	/* Second, setup the cipher side. */
+	return (swcr_setup_cipher(ses, csp));
+}
+
 static bool
 swcr_auth_supported(const struct crypto_session_params *csp)
 {
@@ -1258,6 +1447,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
 		switch (csp->csp_cipher_alg) {
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
+		case CRYPTO_CHACHA20_POLY1305:
 			return (EINVAL);
 		default:
 			if (!swcr_cipher_supported(csp))
@@ -1273,6 +1463,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
 		switch (csp->csp_cipher_alg) {
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
+		case CRYPTO_CHACHA20_POLY1305:
 			break;
 		default:
 			return (EINVAL);
@@ -1283,6 +1474,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
 		switch (csp->csp_cipher_alg) {
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
+		case CRYPTO_CHACHA20_POLY1305:
 			return (EINVAL);
 		}
 		switch (csp->csp_auth_alg) {
@@ -1343,6 +1535,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
 #ifdef INVARIANTS
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
+		case CRYPTO_CHACHA20_POLY1305:
 			panic("bad cipher algo");
 #endif
 		default:
@@ -1366,6 +1559,11 @@ swcr_newsession(device_t dev, crypto_session_t cses,
 			if (error == 0)
 				ses->swcr_process = swcr_ccm;
 			break;
+		case CRYPTO_CHACHA20_POLY1305:
+			error = swcr_setup_chacha20_poly1305(ses, csp);
+			if (error == 0)
+				ses->swcr_process = swcr_chacha20_poly1305;
+			break;
 #ifdef INVARIANTS
 		default:
 			panic("bad aead algo");
@@ -1377,6 +1575,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
 		switch (csp->csp_cipher_alg) {
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
+		case CRYPTO_CHACHA20_POLY1305:
 			panic("bad eta cipher algo");
 		}
 		switch (csp->csp_auth_alg) {
diff --git a/sys/opencrypto/xform_auth.h b/sys/opencrypto/xform_auth.h
index dbdb278209b5..6427965671d3 100644
--- a/sys/opencrypto/xform_auth.h
+++ b/sys/opencrypto/xform_auth.h
@@ -84,6 +84,7 @@ extern struct auth_hash auth_hash_poly1305;
 extern struct auth_hash auth_hash_ccm_cbc_mac_128;
 extern struct auth_hash auth_hash_ccm_cbc_mac_192;
 extern struct auth_hash auth_hash_ccm_cbc_mac_256;
+extern struct auth_hash auth_hash_chacha20_poly1305;
 
 union authctx {
 	SHA1_CTX sha1ctx;
diff --git a/sys/opencrypto/xform_chacha20_poly1305.c b/sys/opencrypto/xform_chacha20_poly1305.c
index e568e76cad51..3a72c06f931b 100644
--- a/sys/opencrypto/xform_chacha20_poly1305.c
+++ b/sys/opencrypto/xform_chacha20_poly1305.c
@@ -25,17 +25,139 @@
  * SUCH DAMAGE.
  */
 
-#include <crypto/chacha20/chacha.h>
+#include <opencrypto/xform_auth.h>
 #include <opencrypto/xform_enc.h>
 
+#include <sodium/crypto_onetimeauth_poly1305.h>
+#include <sodium/crypto_stream_chacha20.h>
+
+struct chacha20_poly1305_cipher_ctx {
+	const void *key;
+	uint32_t ic;
+	char nonce[CHACHA20_POLY1305_IV_LEN];
+};
+
+static int
+chacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
+{
+	struct chacha20_poly1305_cipher_ctx *ctx = vctx;
+
+	if (len != CHACHA20_POLY1305_KEY)
+		return (EINVAL);
+
+	ctx->key = key;
+	return (0);
+}
+
+static void
+chacha20_poly1305_reinit(void *vctx, const uint8_t *iv)
+{
+	struct chacha20_poly1305_cipher_ctx *ctx = vctx;
+
+	/* Block 0 is used for the poly1305 key. */
+	memcpy(ctx->nonce, iv, sizeof(ctx->nonce));
+	ctx->ic = 1;
+}
+
+static void
+chacha20_poly1305_crypt(void *vctx, const uint8_t *in, uint8_t *out)
+{
+	struct chacha20_poly1305_cipher_ctx *ctx = vctx;
+	int error;
+
+	error = crypto_stream_chacha20_ietf_xor_ic(out, in,
+	    CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key);
+	KASSERT(error == 0, ("%s failed: %d", __func__, error));
+	ctx->ic++;
+}
+
+static void
+chacha20_poly1305_crypt_last(void *vctx, const uint8_t *in, uint8_t *out,
+    size_t len)
+{
+	struct chacha20_poly1305_cipher_ctx *ctx = vctx;
+
+	int error;
+
+	error = crypto_stream_chacha20_ietf_xor_ic(out, in, len, ctx->nonce,
+	    ctx->ic, ctx->key);
+	KASSERT(error == 0, ("%s failed: %d", __func__, error));
+}
+
 struct enc_xform enc_xform_chacha20_poly1305 = {
 	.type = CRYPTO_CHACHA20_POLY1305,
 	.name = "ChaCha20-Poly1305",
-	.ctxsize = sizeof(struct chacha_ctx),
+	.ctxsize = sizeof(struct chacha20_poly1305_cipher_ctx),
 	.blocksize = 1,
-	.native_blocksize = CHACHA_BLOCKLEN,
+	.native_blocksize = CHACHA20_NATIVE_BLOCK_LEN,
 	.ivsize = CHACHA20_POLY1305_IV_LEN,
 	.minkey = CHACHA20_POLY1305_KEY,
 	.maxkey = CHACHA20_POLY1305_KEY,
+	.encrypt = chacha20_poly1305_crypt,
+	.decrypt = chacha20_poly1305_crypt,
+	.setkey = chacha20_poly1305_setkey,
+	.reinit = chacha20_poly1305_reinit,
+	.encrypt_last = chacha20_poly1305_crypt_last,
+	.decrypt_last = chacha20_poly1305_crypt_last,
 };
 
+struct chacha20_poly1305_auth_ctx {
+	struct crypto_onetimeauth_poly1305_state state;
+	const void *key;
+};
+CTASSERT(sizeof(union authctx) >= sizeof(struct chacha20_poly1305_auth_ctx));
+
+static void
+chacha20_poly1305_Init(void *vctx)
+{
+}
+
+static void
+chacha20_poly1305_Setkey(void *vctx, const uint8_t *key, u_int klen)
+{
+	struct chacha20_poly1305_auth_ctx *ctx = vctx;
+
+	ctx->key = key;
+}
+
+static void
+chacha20_poly1305_Reinit(void *vctx, const uint8_t *nonce, u_int noncelen)
+{
+	struct chacha20_poly1305_auth_ctx *ctx = vctx;
+	char block[CHACHA20_NATIVE_BLOCK_LEN];
+
+	crypto_stream_chacha20_ietf(block, sizeof(block), nonce, ctx->key);
+	crypto_onetimeauth_poly1305_init(&ctx->state, block);
+	explicit_bzero(block, sizeof(block));
+}
+
+static int
+chacha20_poly1305_Update(void *vctx, const void *data, u_int len)
+{
+	struct chacha20_poly1305_auth_ctx *ctx = vctx;
+
+	crypto_onetimeauth_poly1305_update(&ctx->state, data, len);
+	return (0);
+}
+
+static void
+chacha20_poly1305_Final(uint8_t *digest, void *vctx)
+{
+	struct chacha20_poly1305_auth_ctx *ctx = vctx;
+
+	crypto_onetimeauth_poly1305_final(&ctx->state, digest);
+}
+
+struct auth_hash auth_hash_chacha20_poly1305 = {
+	.type = CRYPTO_POLY1305,
+	.name = "ChaCha20-Poly1305",
+	.keysize = POLY1305_KEY_LEN,
+	.hashsize = POLY1305_HASH_LEN,
+	.ctxsize = sizeof(struct chacha20_poly1305_auth_ctx),
+	.blocksize = crypto_onetimeauth_poly1305_BYTES,
+	.Init = chacha20_poly1305_Init,
+	.Setkey = chacha20_poly1305_Setkey,
+	.Reinit = chacha20_poly1305_Reinit,
+	.Update = chacha20_poly1305_Update,
+	.Final = chacha20_poly1305_Final,
+};



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202110211706.19LH6pms080073>