git: 8f35841f1f35 - main - crypto: Add support for the XChaCha20-Poly1305 AEAD cipher.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Tue, 11 Jan 2022 22:38:08 UTC
The branch main has been updated by jhb:

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

commit 8f35841f1f35cce332e94f0a3a69f51e2eb5e762
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-01-11 22:16:05 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-01-11 22:16:41 +0000

    crypto: Add support for the XChaCha20-Poly1305 AEAD cipher.
    
    This cipher is a wrapper around the ChaCha20-Poly1305 AEAD cipher
    which accepts a larger nonce.  Part of the nonce is used along with
    the key as an input to HChaCha20 to generate a derived key used for
    ChaCha20-Poly1305.
    
    This cipher is used by WireGuard.
    
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D33523
---
 share/man/man7/crypto.7                  |  5 ++-
 sys/opencrypto/crypto.c                  |  9 +++++
 sys/opencrypto/cryptodev.h               |  5 ++-
 sys/opencrypto/cryptosoft.c              |  6 ++++
 sys/opencrypto/xform_chacha20_poly1305.c | 62 ++++++++++++++++++++++++++++++++
 sys/opencrypto/xform_enc.h               |  1 +
 6 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/share/man/man7/crypto.7 b/share/man/man7/crypto.7
index c7af22af4deb..01ff339fa31f 100644
--- a/share/man/man7/crypto.7
+++ b/share/man/man7/crypto.7
@@ -31,7 +31,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 6, 2021
+.Dd January 11, 2022
 .Dt CRYPTO 7
 .Os
 .Sh NAME
@@ -170,6 +170,9 @@ AES Counter with CBC-MAC
 .It Dv CRYPTO_CHACHA20_POLY1305 Ta 12, 8 Ta 32 Ta 16 Ta
 ChaCha20-Poly1305
 .El
+.It Dv CRYPTO_XCHACHA20_POLY1305 Ta 24 Ta 32 Ta 16 Ta
+XChaCha20-Poly1305
+.El
 .Sh SEE ALSO
 .Xr crypto 4 ,
 .Xr crypto 9
diff --git a/sys/opencrypto/crypto.c b/sys/opencrypto/crypto.c
index acc7b8d2ecb8..0b2c250a7af2 100644
--- a/sys/opencrypto/crypto.c
+++ b/sys/opencrypto/crypto.c
@@ -579,6 +579,8 @@ crypto_cipher(const struct crypto_session_params *csp)
 		return (&enc_xform_ccm);
 	case CRYPTO_CHACHA20_POLY1305:
 		return (&enc_xform_chacha20_poly1305);
+	case CRYPTO_XCHACHA20_POLY1305:
+		return (&enc_xform_xchacha20_poly1305);
 	default:
 		return (NULL);
 	}
@@ -671,6 +673,7 @@ static enum alg_type {
 	[CRYPTO_AES_CCM_CBC_MAC] = ALG_KEYED_DIGEST,
 	[CRYPTO_AES_CCM_16] = ALG_AEAD,
 	[CRYPTO_CHACHA20_POLY1305] = ALG_AEAD,
+	[CRYPTO_XCHACHA20_POLY1305] = ALG_AEAD,
 };
 
 static enum alg_type
@@ -865,6 +868,12 @@ check_csp(const struct crypto_session_params *csp)
 			if (csp->csp_auth_mlen > POLY1305_HASH_LEN)
 				return (false);
 			break;
+		case CRYPTO_XCHACHA20_POLY1305:
+			if (csp->csp_ivlen != XCHACHA20_POLY1305_IV_LEN)
+				return (false);
+			if (csp->csp_auth_mlen > POLY1305_HASH_LEN)
+				return (false);
+			break;
 		}
 		break;
 	case CSP_MODE_ETA:
diff --git a/sys/opencrypto/cryptodev.h b/sys/opencrypto/cryptodev.h
index ed396ed86912..b2eb01566f4f 100644
--- a/sys/opencrypto/cryptodev.h
+++ b/sys/opencrypto/cryptodev.h
@@ -129,6 +129,7 @@
 #define	AES_XTS_IV_LEN		8
 #define	AES_XTS_ALPHA		0x87	/* GF(2^128) generator polynomial */
 #define	CHACHA20_POLY1305_IV_LEN	12
+#define	XCHACHA20_POLY1305_IV_LEN	24
 
 /* Min and Max Encryption Key Sizes */
 #define	NULL_MIN_KEY		0
@@ -142,6 +143,7 @@
 #define	CAMELLIA_MIN_KEY	16
 #define	CAMELLIA_MAX_KEY	32
 #define	CHACHA20_POLY1305_KEY	32
+#define	XCHACHA20_POLY1305_KEY	32
 
 /* Maximum hash algorithm result length */
 #define	AALG_MAX_RESULT_LEN	64 /* Keep this updated */
@@ -191,7 +193,8 @@
 #define	CRYPTO_AES_CCM_CBC_MAC	39	/* auth side */
 #define	CRYPTO_AES_CCM_16	40	/* cipher side */
 #define	CRYPTO_CHACHA20_POLY1305 41	/* combined AEAD cipher per RFC 8439 */
-#define	CRYPTO_ALGORITHM_MAX	41	/* Keep updated - see below */
+#define	CRYPTO_XCHACHA20_POLY1305 42
+#define	CRYPTO_ALGORITHM_MAX	42	/* Keep updated - see below */
 
 #define	CRYPTO_ALGO_VALID(x)	((x) >= CRYPTO_ALGORITHM_MIN && \
 				 (x) <= CRYPTO_ALGORITHM_MAX)
diff --git a/sys/opencrypto/cryptosoft.c b/sys/opencrypto/cryptosoft.c
index 43fefae99c40..65eb29bdfd91 100644
--- a/sys/opencrypto/cryptosoft.c
+++ b/sys/opencrypto/cryptosoft.c
@@ -1330,6 +1330,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
 		case CRYPTO_CHACHA20_POLY1305:
+		case CRYPTO_XCHACHA20_POLY1305:
 			return (EINVAL);
 		default:
 			if (!swcr_cipher_supported(csp))
@@ -1355,6 +1356,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
 			}
 			break;
 		case CRYPTO_CHACHA20_POLY1305:
+		case CRYPTO_XCHACHA20_POLY1305:
 			break;
 		default:
 			return (EINVAL);
@@ -1366,6 +1368,7 @@ swcr_probesession(device_t dev, const struct crypto_session_params *csp)
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
 		case CRYPTO_CHACHA20_POLY1305:
+		case CRYPTO_XCHACHA20_POLY1305:
 			return (EINVAL);
 		}
 		switch (csp->csp_auth_alg) {
@@ -1422,6 +1425,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
 		case CRYPTO_CHACHA20_POLY1305:
+		case CRYPTO_XCHACHA20_POLY1305:
 			panic("bad cipher algo");
 #endif
 		default:
@@ -1446,6 +1450,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
 				ses->swcr_process = swcr_ccm;
 			break;
 		case CRYPTO_CHACHA20_POLY1305:
+		case CRYPTO_XCHACHA20_POLY1305:
 			error = swcr_setup_aead(ses, csp);
 			if (error == 0)
 				ses->swcr_process = swcr_chacha20_poly1305;
@@ -1462,6 +1467,7 @@ swcr_newsession(device_t dev, crypto_session_t cses,
 		case CRYPTO_AES_NIST_GCM_16:
 		case CRYPTO_AES_CCM_16:
 		case CRYPTO_CHACHA20_POLY1305:
+		case CRYPTO_XCHACHA20_POLY1305:
 			panic("bad eta cipher algo");
 		}
 		switch (csp->csp_auth_alg) {
diff --git a/sys/opencrypto/xform_chacha20_poly1305.c b/sys/opencrypto/xform_chacha20_poly1305.c
index cb1fc4cea88d..eec82681bd2c 100644
--- a/sys/opencrypto/xform_chacha20_poly1305.c
+++ b/sys/opencrypto/xform_chacha20_poly1305.c
@@ -28,6 +28,7 @@
 #include <opencrypto/xform_auth.h>
 #include <opencrypto/xform_enc.h>
 
+#include <sodium/crypto_core_hchacha20.h>
 #include <sodium/crypto_onetimeauth_poly1305.h>
 #include <sodium/crypto_stream_chacha20.h>
 
@@ -39,6 +40,12 @@ struct chacha20_poly1305_ctx {
 	char nonce[CHACHA20_POLY1305_IV_LEN];
 };
 
+struct xchacha20_poly1305_ctx {
+	struct chacha20_poly1305_ctx base_ctx;	/* must be first */
+	const void *key;
+	char derived_key[CHACHA20_POLY1305_KEY];
+};
+
 static int
 chacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
 {
@@ -144,3 +151,58 @@ const struct enc_xform enc_xform_chacha20_poly1305 = {
 	.update = chacha20_poly1305_update,
 	.final = chacha20_poly1305_final,
 };
+
+static int
+xchacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
+{
+	struct xchacha20_poly1305_ctx *ctx = vctx;
+
+	if (len != XCHACHA20_POLY1305_KEY)
+		return (EINVAL);
+
+	ctx->key = key;
+	ctx->base_ctx.key = ctx->derived_key;
+	return (0);
+}
+
+static void
+xchacha20_poly1305_reinit(void *vctx, const uint8_t *iv, size_t ivlen)
+{
+	struct xchacha20_poly1305_ctx *ctx = vctx;
+	char nonce[CHACHA20_POLY1305_IV_LEN];
+
+	KASSERT(ivlen == XCHACHA20_POLY1305_IV_LEN,
+	    ("%s: invalid nonce length", __func__));
+
+	/*
+	 * Use HChaCha20 to derive the internal key used for
+	 * ChaCha20-Poly1305.
+	 */
+	crypto_core_hchacha20(ctx->derived_key, iv, ctx->key, NULL);
+
+	memset(nonce, 0, 4);
+	memcpy(nonce + 4, iv + crypto_core_hchacha20_INPUTBYTES,
+	    sizeof(nonce) - 4);
+	chacha20_poly1305_reinit(&ctx->base_ctx, nonce, sizeof(nonce));
+	explicit_bzero(nonce, sizeof(nonce));
+}
+
+const struct enc_xform enc_xform_xchacha20_poly1305 = {
+	.type = CRYPTO_XCHACHA20_POLY1305,
+	.name = "XChaCha20-Poly1305",
+	.ctxsize = sizeof(struct xchacha20_poly1305_ctx),
+	.blocksize = 1,
+	.native_blocksize = CHACHA20_NATIVE_BLOCK_LEN,
+	.ivsize = XCHACHA20_POLY1305_IV_LEN,
+	.minkey = XCHACHA20_POLY1305_KEY,
+	.maxkey = XCHACHA20_POLY1305_KEY,
+	.macsize = POLY1305_HASH_LEN,
+	.encrypt = chacha20_poly1305_crypt,
+	.decrypt = chacha20_poly1305_crypt,
+	.setkey = xchacha20_poly1305_setkey,
+	.reinit = xchacha20_poly1305_reinit,
+	.encrypt_last = chacha20_poly1305_crypt_last,
+	.decrypt_last = chacha20_poly1305_crypt_last,
+	.update = chacha20_poly1305_update,
+	.final = chacha20_poly1305_final,
+};
diff --git a/sys/opencrypto/xform_enc.h b/sys/opencrypto/xform_enc.h
index c998e06d4944..9364ff36431c 100644
--- a/sys/opencrypto/xform_enc.h
+++ b/sys/opencrypto/xform_enc.h
@@ -89,6 +89,7 @@ extern const struct enc_xform enc_xform_aes_xts;
 extern const struct enc_xform enc_xform_camellia;
 extern const struct enc_xform enc_xform_chacha20;
 extern const struct enc_xform enc_xform_chacha20_poly1305;
+extern const struct enc_xform enc_xform_xchacha20_poly1305;
 extern const struct enc_xform enc_xform_ccm;
 
 struct aes_icm_ctx {