git: 8fbc0cc23a60 - stable/13 - <crypto/chacha20_poly1305>: Fix operations with 8 byte nonce.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Tue, 15 Nov 2022 01:42:45 UTC
The branch stable/13 has been updated by jhb:

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

commit 8fbc0cc23a607bb1a5ef13e18d9e19b5ee54a90d
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-11-15 01:24:56 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-11-15 01:39:54 +0000

    <crypto/chacha20_poly1305>: Fix operations with 8 byte nonce.
    
    In head, the inline ChaCha20+Poly1305 API is implemented using the
    software implementation backing OCF, but that requires API changes
    that can't be MFC'd.  As a result, this API in stable/13 uses
    libsodium directly.
    
    However, libsodium's version of ChaCha20+Poly1305 with an 8 byte nonce
    uses a different construction for the Poly1305 hash than is used for
    the standard IETF AEAD cipher used for TLS and IPsec.  WireGuard's use
    of an 8 byte nonce also uses the more standard construction.
    
    Since the verison in stable/13 was using libsodium directly for the 8
    byte nonce case, it was generating incorrect MACs for if_wg(4).  As a
    workaround, change the direct API to always use the IETF API from
    libsodium which uses 12 byte nonces.  This can be done by
    zero-extending the provided 8 byte nonce to 12 bytes so long as the
    passed in buffers are sufficiently small to not overflow a 4 byte
    counter.
    
    This fixes key negotiation for if_wg(4) on stable/13.  This is also
    a direct commit to stable/13.
    
    Reported by:    Marek Zarychta <mzar@bpine64.dom.potoki.eu>
---
 sys/crypto/chacha20_poly1305.c | 46 +++++++++++++++++++++++++++++++++---------
 1 file changed, 36 insertions(+), 10 deletions(-)

diff --git a/sys/crypto/chacha20_poly1305.c b/sys/crypto/chacha20_poly1305.c
index 9aaa570ccca4..516fc333f949 100644
--- a/sys/crypto/chacha20_poly1305.c
+++ b/sys/crypto/chacha20_poly1305.c
@@ -34,17 +34,38 @@
 #include <sodium/crypto_aead_chacha20poly1305.h>
 #include <sodium/crypto_aead_xchacha20poly1305.h>
 
+/*
+ * libsodium's chacha20poly1305 AEAD cipher does not construct the
+ * Poly1305 digest in the same method as the IETF AEAD construct.
+ * Specifically, libsodium does not pad the AAD and cipher text with
+ * zeroes to a 16 byte boundary, and libsodium inserts the AAD and
+ * cipher text lengths as inputs into the digest after each data
+ * segment rather than appending both data lengths after the padded
+ * cipher text.
+ *
+ * Instead, always use libsodium's chacha20poly1305 IETF AEAD cipher.
+ * This cipher uses a 96-bit nonce with a 32-bit counter.  The data
+ * encrypted here should never be large enough to overflow the counter
+ * to the second word, so just pass zeros as the first word of the
+ * nonce to mimic a 64-bit nonce and 64-bit counter.
+ */
 void
 chacha20_poly1305_encrypt(uint8_t *dst, const uint8_t *src,
     const size_t src_len, const uint8_t *aad, const size_t aad_len,
     const uint8_t *nonce, const size_t nonce_len, const uint8_t *key)
 {
+	char local_nonce[12];
+
+	MPASS(aad_len + src_len <=
+	    crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX);
 	if (nonce_len == crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
-		crypto_aead_chacha20poly1305_ietf_encrypt(dst, NULL, src,
-		    src_len, aad, aad_len, NULL, nonce, key);
-	else
-		crypto_aead_chacha20poly1305_encrypt(dst, NULL, src,
-		    src_len, aad, aad_len, NULL, nonce, key);
+		memcpy(local_nonce, nonce, sizeof(local_nonce));
+	else {
+		memset(local_nonce, 0, 4);
+		memcpy(local_nonce + 4, nonce, 8);
+	}
+	crypto_aead_chacha20poly1305_ietf_encrypt(dst, NULL, src, src_len,
+	    aad, aad_len, NULL, local_nonce, key);
 }
 
 bool
@@ -52,14 +73,19 @@ chacha20_poly1305_decrypt(uint8_t *dst, const uint8_t *src,
     const size_t src_len, const uint8_t *aad, const size_t aad_len,
     const uint8_t *nonce, const size_t nonce_len, const uint8_t *key)
 {
+	char local_nonce[12];
 	int ret;
 
+	MPASS(aad_len + src_len <=
+	    crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX);
 	if (nonce_len == crypto_aead_chacha20poly1305_ietf_NPUBBYTES)
-		ret = crypto_aead_chacha20poly1305_ietf_decrypt(dst, NULL,
-		    NULL, src, src_len, aad, aad_len, nonce, key);
-	else
-		ret = crypto_aead_chacha20poly1305_decrypt(dst, NULL,
-		    NULL, src, src_len, aad, aad_len, nonce, key);
+		memcpy(local_nonce, nonce, sizeof(local_nonce));
+	else {
+		memset(local_nonce, 0, 4);
+		memcpy(local_nonce + 4, nonce, 8);
+	}
+	ret = crypto_aead_chacha20poly1305_ietf_decrypt(dst, NULL, NULL,
+	    src, src_len, aad, aad_len, local_nonce, key);
 	return (ret == 0);
 }