git: 8fbc0cc23a60 - stable/13 - <crypto/chacha20_poly1305>: Fix operations with 8 byte nonce.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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);
}