git: 64c043d2d2cf - stable/13 - crypto: Support Chacha20-Poly1305 with a nonce size of 8 bytes.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Thu, 21 Oct 2021 22:04:29 UTC
The branch stable/13 has been updated by jhb:

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

commit 64c043d2d2cfa44128361c9354d1fad023087be8
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2021-10-06 21:08:49 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2021-10-21 21:19:30 +0000

    crypto: Support Chacha20-Poly1305 with a nonce size of 8 bytes.
    
    This is useful for WireGuard which uses a nonce of 8 bytes rather
    than the 12 bytes used for IPsec and TLS.
    
    Note that this also fixes a (should be) harmless bug in ossl(4) where
    the counter was incorrectly treated as a 64-bit counter instead of a
    32-bit counter in terms of wrapping when using a 12 byte nonce.
    However, this required a single message (TLS record) longer than 64 *
    (2^32 - 1) bytes (about 256 GB) to trigger.
    
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D32122
    
    (cherry picked from commit 42dcd39528c6188a259951e28bbad309234324e4)
---
 share/man/man7/crypto.7                  |  2 +-
 sys/crypto/openssl/ossl_chacha20.c       | 14 ++++++++------
 sys/opencrypto/crypto.c                  |  7 ++++++-
 sys/opencrypto/cryptosoft.c              |  3 ---
 sys/opencrypto/xform_chacha20_poly1305.c | 31 +++++++++++++++++++++++++------
 tools/tools/crypto/cryptocheck.c         |  4 ++--
 6 files changed, 42 insertions(+), 19 deletions(-)

diff --git a/share/man/man7/crypto.7 b/share/man/man7/crypto.7
index d75daa62adcb..c7af22af4deb 100644
--- a/share/man/man7/crypto.7
+++ b/share/man/man7/crypto.7
@@ -167,7 +167,7 @@ The following AEAD algorithms are supported:
 AES Galois/Counter Mode
 .It Dv CRYPTO_AES_CCM_16 Ta 12, 7-13 Ta 16, 24, 32 Ta 16 Ta
 AES Counter with CBC-MAC
-.It Dv CRYPTO_CHACHA20_POLY1305 Ta 12 Ta 32 Ta 16 Ta
+.It Dv CRYPTO_CHACHA20_POLY1305 Ta 12, 8 Ta 32 Ta 16 Ta
 ChaCha20-Poly1305
 .El
 .Sh SEE ALSO
diff --git a/sys/crypto/openssl/ossl_chacha20.c b/sys/crypto/openssl/ossl_chacha20.c
index a6f56aca1f8f..aa125121e8b4 100644
--- a/sys/crypto/openssl/ossl_chacha20.c
+++ b/sys/crypto/openssl/ossl_chacha20.c
@@ -161,7 +161,8 @@ ossl_chacha20_poly1305_encrypt(struct cryptop *crp,
 	for (i = 0; i < nitems(key); i++)
 		key[i] = CHACHA_U8TOU32(cipher_key + i * 4);
 
-	crypto_read_iv(crp, counter + 1);
+	memset(counter, 0, sizeof(counter));
+	crypto_read_iv(crp, counter + (CHACHA_CTR_SIZE - csp->csp_ivlen) / 4);
 	for (i = 1; i < nitems(counter); i++)
 		counter[i] = le32toh(counter[i]);
 
@@ -223,7 +224,7 @@ ossl_chacha20_poly1305_encrypt(struct cryptop *crp,
 
 		/* Truncate if the 32-bit counter would roll over. */
 		next_counter = counter[0] + todo / CHACHA_BLK_SIZE;
-		if (next_counter < counter[0]) {
+		if (csp->csp_ivlen == 8 && next_counter < counter[0]) {
 			todo -= next_counter * CHACHA_BLK_SIZE;
 			next_counter = 0;
 		}
@@ -232,7 +233,7 @@ ossl_chacha20_poly1305_encrypt(struct cryptop *crp,
 		Poly1305_Update(&auth_ctx, out, todo);
 
 		counter[0] = next_counter;
-		if (counter[0] == 0)
+		if (csp->csp_ivlen == 8 && counter[0] == 0)
 			counter[1]++;
 
 		if (out == block) {
@@ -307,7 +308,8 @@ ossl_chacha20_poly1305_decrypt(struct cryptop *crp,
 	for (i = 0; i < nitems(key); i++)
 		key[i] = CHACHA_U8TOU32(cipher_key + i * 4);
 
-	crypto_read_iv(crp, counter + 1);
+	memset(counter, 0, sizeof(counter));
+	crypto_read_iv(crp, counter + (CHACHA_CTR_SIZE - csp->csp_ivlen) / 4);
 	for (i = 1; i < nitems(counter); i++)
 		counter[i] = le32toh(counter[i]);
 
@@ -391,7 +393,7 @@ ossl_chacha20_poly1305_decrypt(struct cryptop *crp,
 
 		/* Truncate if the 32-bit counter would roll over. */
 		next_counter = counter[0] + todo / CHACHA_BLK_SIZE;
-		if (next_counter < counter[0]) {
+		if (csp->csp_ivlen == 8 && next_counter < counter[0]) {
 			todo -= next_counter * CHACHA_BLK_SIZE;
 			next_counter = 0;
 		}
@@ -399,7 +401,7 @@ ossl_chacha20_poly1305_decrypt(struct cryptop *crp,
 		ChaCha20_ctr32(out, in, todo, key, counter);
 
 		counter[0] = next_counter;
-		if (counter[0] == 0)
+		if (csp->csp_ivlen == 8 && counter[0] == 0)
 			counter[1]++;
 
 		if (out == block) {
diff --git a/sys/opencrypto/crypto.c b/sys/opencrypto/crypto.c
index a3a42827d51b..f0fd3fe662a9 100644
--- a/sys/opencrypto/crypto.c
+++ b/sys/opencrypto/crypto.c
@@ -882,10 +882,15 @@ check_csp(const struct crypto_session_params *csp)
 				return (false);
 			break;
 		case CRYPTO_AES_NIST_GCM_16:
-		case CRYPTO_CHACHA20_POLY1305:
 			if (csp->csp_auth_mlen > 16)
 				return (false);
 			break;
+		case CRYPTO_CHACHA20_POLY1305:
+			if (csp->csp_ivlen != 8 && csp->csp_ivlen != 12)
+				return (false);
+			if (csp->csp_auth_mlen > POLY1305_HASH_LEN)
+				return (false);
+			break;
 		}
 		break;
 	case CSP_MODE_ETA:
diff --git a/sys/opencrypto/cryptosoft.c b/sys/opencrypto/cryptosoft.c
index fb43a08970c7..567a0f4748d5 100644
--- a/sys/opencrypto/cryptosoft.c
+++ b/sys/opencrypto/cryptosoft.c
@@ -1393,9 +1393,6 @@ swcr_setup_chacha20_poly1305(struct swcr_session *ses,
 	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;
diff --git a/sys/opencrypto/xform_chacha20_poly1305.c b/sys/opencrypto/xform_chacha20_poly1305.c
index e893287145f2..f593faa9b5ef 100644
--- a/sys/opencrypto/xform_chacha20_poly1305.c
+++ b/sys/opencrypto/xform_chacha20_poly1305.c
@@ -34,6 +34,7 @@
 struct chacha20_poly1305_cipher_ctx {
 	const void *key;
 	uint32_t ic;
+	bool ietf;
 	char nonce[CHACHA20_POLY1305_IV_LEN];
 };
 
@@ -58,7 +59,8 @@ chacha20_poly1305_reinit(void *vctx, const uint8_t *iv, size_t ivlen)
 	    ("%s: invalid nonce length", __func__));
 
 	/* Block 0 is used for the poly1305 key. */
-	memcpy(ctx->nonce, iv, sizeof(ctx->nonce));
+	memcpy(ctx->nonce, iv, ivlen);
+	ctx->ietf = (ivlen == CHACHA20_POLY1305_IV_LEN);
 	ctx->ic = 1;
 }
 
@@ -68,8 +70,12 @@ 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);
+	if (ctx->ietf)
+		error = crypto_stream_chacha20_ietf_xor_ic(out, in,
+		    CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key);
+	else
+		error = crypto_stream_chacha20_xor_ic(out, in,
+		    CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key);
 	KASSERT(error == 0, ("%s failed: %d", __func__, error));
 	ctx->ic++;
 }
@@ -82,8 +88,12 @@ chacha20_poly1305_crypt_last(void *vctx, const uint8_t *in, uint8_t *out,
 
 	int error;
 
-	error = crypto_stream_chacha20_ietf_xor_ic(out, in, len, ctx->nonce,
-	    ctx->ic, ctx->key);
+	if (ctx->ietf)
+		error = crypto_stream_chacha20_ietf_xor_ic(out, in, len,
+		    ctx->nonce, ctx->ic, ctx->key);
+	else
+		error = crypto_stream_chacha20_xor_ic(out, in, len, ctx->nonce,
+		    ctx->ic, ctx->key);
 	KASSERT(error == 0, ("%s failed: %d", __func__, error));
 }
 
@@ -129,7 +139,16 @@ 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);
+	switch (noncelen) {
+	case 8:
+		crypto_stream_chacha20(block, sizeof(block), nonce, ctx->key);
+		break;
+	case CHACHA20_POLY1305_IV_LEN:
+		crypto_stream_chacha20_ietf(block, sizeof(block), nonce, ctx->key);
+		break;
+	default:
+		__assert_unreachable();
+	}
 	crypto_onetimeauth_poly1305_init(&ctx->state, block);
 	explicit_bzero(block, sizeof(block));
 }
diff --git a/tools/tools/crypto/cryptocheck.c b/tools/tools/crypto/cryptocheck.c
index 63c6ce1f9d28..05b761a0c87a 100644
--- a/tools/tools/crypto/cryptocheck.c
+++ b/tools/tools/crypto/cryptocheck.c
@@ -126,7 +126,7 @@
  *	aes-ccm		128-bit AES-CCM
  *	aes-ccm192	192-bit AES-CCM
  *	aes-ccm256	256-bit AES-CCM
- *	chacha20-poly1305 Chacha20 (96 bit nonce) with Poly1305 per RFC 8439
+ *	chacha20-poly1305 Chacha20 with Poly1305 per RFC 8439
  */
 
 #include <sys/param.h>
@@ -233,7 +233,7 @@ static const struct alg {
 	  .evp_cipher = EVP_aes_256_ccm },
 	{ .name = "chacha20-poly1305", .cipher = CRYPTO_CHACHA20_POLY1305,
 	  .type = T_AEAD, .tag_len = POLY1305_HASH_LEN,
-	  .iv_sizes = { CHACHA20_POLY1305_IV_LEN },
+	  .iv_sizes = { CHACHA20_POLY1305_IV_LEN, 8 },
 	  .evp_cipher = EVP_chacha20_poly1305 },
 };