From: Ard Biesheuvel <ardb+git@google.com>
To: linux-crypto@vger.kernel.org
Cc: Ard Biesheuvel <ardb@kernel.org>,
Eric Biggers <ebiggers@kernel.org>,
"Jason A. Donenfeld" <Jason@zx2c4.com>
Subject: [RFC PATCH] libcrypto/chachapoly: Use strict typing for fixed size array arguments
Date: Fri, 14 Nov 2025 19:07:07 +0100 [thread overview]
Message-ID: <20251114180706.318152-2-ardb+git@google.com> (raw)
From: Ard Biesheuvel <ardb@kernel.org>
The C routines in libcrypto take in/outputs of arbitrary size on the one
hand (plaintext, ciphertext, etc) and in/outputs of fixed size on the
other (keys, nonces, digests).
In many cases, we pass these fixed size in/outputs as array of u8, with
the dimension in square brackets. However, this dimension only serves as
a human readable annotation, and is completely ignored by the compiler.
For example, mixing up any of the const u8[]/const u8* arguments in the
prototype below will not trigger a compiler diagnostic:
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE],
const u8 key[CHACHA20POLY1305_KEY_SIZE])
If we change this to the below, the codegen is identical, given that the
actual value passed as the argument is the address of the entire array,
which is equal to the address of its first element,
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE],
const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
However, this variant is checked more strictly by the compiler, and only
arrays of the correct size are accepted as plain arguments (using the &
operator), and so inadvertent mixing up of arguments or passing buffers
of an incorrect size will trigger an error at build time.
So let's switch to this for the ChaCha20Poly1305 library used by
WireGuard.
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
Sending this RFC as an example, as a starting point for a discussion
whether or not we want to go down this route for all library crypto.
drivers/net/wireguard/cookie.c | 8 ++--
drivers/net/wireguard/noise.c | 16 ++++----
drivers/net/wireguard/receive.c | 2 +-
drivers/net/wireguard/send.c | 2 +-
include/crypto/chacha20poly1305.h | 16 ++++----
lib/crypto/chacha20poly1305.c | 41 ++++++++++----------
6 files changed, 43 insertions(+), 42 deletions(-)
diff --git a/drivers/net/wireguard/cookie.c b/drivers/net/wireguard/cookie.c
index 08731b3fa32b..b0ebbd2f8af4 100644
--- a/drivers/net/wireguard/cookie.c
+++ b/drivers/net/wireguard/cookie.c
@@ -191,8 +191,8 @@ void wg_cookie_message_create(struct message_handshake_cookie *dst,
make_cookie(cookie, skb, checker);
xchacha20poly1305_encrypt(dst->encrypted_cookie, cookie, COOKIE_LEN,
- macs->mac1, COOKIE_LEN, dst->nonce,
- checker->cookie_encryption_key);
+ macs->mac1, COOKIE_LEN, &dst->nonce,
+ &checker->cookie_encryption_key);
}
void wg_cookie_message_consume(struct message_handshake_cookie *src,
@@ -215,8 +215,8 @@ void wg_cookie_message_consume(struct message_handshake_cookie *src,
}
ret = xchacha20poly1305_decrypt(
cookie, src->encrypted_cookie, sizeof(src->encrypted_cookie),
- peer->latest_cookie.last_mac1_sent, COOKIE_LEN, src->nonce,
- peer->latest_cookie.cookie_decryption_key);
+ peer->latest_cookie.last_mac1_sent, COOKIE_LEN, &src->nonce,
+ &peer->latest_cookie.cookie_decryption_key);
up_read(&peer->latest_cookie.lock);
if (ret) {
diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c
index 1fe8468f0bef..e4f560df5f03 100644
--- a/drivers/net/wireguard/noise.c
+++ b/drivers/net/wireguard/noise.c
@@ -461,7 +461,7 @@ static void handshake_init(u8 chaining_key[NOISE_HASH_LEN],
}
static void message_encrypt(u8 *dst_ciphertext, const u8 *src_plaintext,
- size_t src_len, u8 key[NOISE_SYMMETRIC_KEY_LEN],
+ size_t src_len, u8 (*key)[NOISE_SYMMETRIC_KEY_LEN],
u8 hash[NOISE_HASH_LEN])
{
chacha20poly1305_encrypt(dst_ciphertext, src_plaintext, src_len, hash,
@@ -471,7 +471,7 @@ static void message_encrypt(u8 *dst_ciphertext, const u8 *src_plaintext,
}
static bool message_decrypt(u8 *dst_plaintext, const u8 *src_ciphertext,
- size_t src_len, u8 key[NOISE_SYMMETRIC_KEY_LEN],
+ size_t src_len, u8 (*key)[NOISE_SYMMETRIC_KEY_LEN],
u8 hash[NOISE_HASH_LEN])
{
if (!chacha20poly1305_decrypt(dst_plaintext, src_ciphertext, src_len,
@@ -554,7 +554,7 @@ wg_noise_handshake_create_initiation(struct message_handshake_initiation *dst,
/* s */
message_encrypt(dst->encrypted_static,
handshake->static_identity->static_public,
- NOISE_PUBLIC_KEY_LEN, key, handshake->hash);
+ NOISE_PUBLIC_KEY_LEN, &key, handshake->hash);
/* ss */
if (!mix_precomputed_dh(handshake->chaining_key, key,
@@ -564,7 +564,7 @@ wg_noise_handshake_create_initiation(struct message_handshake_initiation *dst,
/* {t} */
tai64n_now(timestamp);
message_encrypt(dst->encrypted_timestamp, timestamp,
- NOISE_TIMESTAMP_LEN, key, handshake->hash);
+ NOISE_TIMESTAMP_LEN, &key, handshake->hash);
dst->sender_index = wg_index_hashtable_insert(
handshake->entry.peer->device->index_hashtable,
@@ -610,7 +610,7 @@ wg_noise_handshake_consume_initiation(struct message_handshake_initiation *src,
/* s */
if (!message_decrypt(s, src->encrypted_static,
- sizeof(src->encrypted_static), key, hash))
+ sizeof(src->encrypted_static), &key, hash))
goto out;
/* Lookup which peer we're actually talking to */
@@ -626,7 +626,7 @@ wg_noise_handshake_consume_initiation(struct message_handshake_initiation *src,
/* {t} */
if (!message_decrypt(t, src->encrypted_timestamp,
- sizeof(src->encrypted_timestamp), key, hash))
+ sizeof(src->encrypted_timestamp), &key, hash))
goto out;
down_read(&handshake->lock);
@@ -708,7 +708,7 @@ bool wg_noise_handshake_create_response(struct message_handshake_response *dst,
handshake->preshared_key);
/* {} */
- message_encrypt(dst->encrypted_nothing, NULL, 0, key, handshake->hash);
+ message_encrypt(dst->encrypted_nothing, NULL, 0, &key, handshake->hash);
dst->sender_index = wg_index_hashtable_insert(
handshake->entry.peer->device->index_hashtable,
@@ -779,7 +779,7 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src,
/* {} */
if (!message_decrypt(NULL, src->encrypted_nothing,
- sizeof(src->encrypted_nothing), key, hash))
+ sizeof(src->encrypted_nothing), &key, hash))
goto fail;
/* Success! Copy everything to peer */
diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c
index eb8851113654..21b4d825e9f7 100644
--- a/drivers/net/wireguard/receive.c
+++ b/drivers/net/wireguard/receive.c
@@ -277,7 +277,7 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
if (!chacha20poly1305_decrypt_sg_inplace(sg, skb->len, NULL, 0,
PACKET_CB(skb)->nonce,
- keypair->receiving.key))
+ &keypair->receiving.key))
return false;
/* Another ugly situation of pushing and pulling the header so as to
diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c
index 26e09c30d596..a0d8c541b72c 100644
--- a/drivers/net/wireguard/send.c
+++ b/drivers/net/wireguard/send.c
@@ -215,7 +215,7 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
return false;
return chacha20poly1305_encrypt_sg_inplace(sg, plaintext_len, NULL, 0,
PACKET_CB(skb)->nonce,
- keypair->sending.key);
+ &keypair->sending.key);
}
void wg_packet_send_keepalive(struct wg_peer *peer)
diff --git a/include/crypto/chacha20poly1305.h b/include/crypto/chacha20poly1305.h
index d2ac3ff7dc1e..4b49b229eb77 100644
--- a/include/crypto/chacha20poly1305.h
+++ b/include/crypto/chacha20poly1305.h
@@ -18,32 +18,32 @@ enum chacha20poly1305_lengths {
void chacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE]);
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
bool __must_check
chacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len, const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE]);
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
- const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE],
- const u8 key[CHACHA20POLY1305_KEY_SIZE]);
+ const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE],
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
bool __must_check xchacha20poly1305_decrypt(
u8 *dst, const u8 *src, const size_t src_len, const u8 *ad,
- const size_t ad_len, const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE],
- const u8 key[CHACHA20POLY1305_KEY_SIZE]);
+ const size_t ad_len, const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE],
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
bool chacha20poly1305_encrypt_sg_inplace(struct scatterlist *src, size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE]);
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
bool chacha20poly1305_decrypt_sg_inplace(struct scatterlist *src, size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE]);
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
bool chacha20poly1305_selftest(void);
diff --git a/lib/crypto/chacha20poly1305.c b/lib/crypto/chacha20poly1305.c
index 0b49d6aedefd..413e06eb1da0 100644
--- a/lib/crypto/chacha20poly1305.c
+++ b/lib/crypto/chacha20poly1305.c
@@ -18,20 +18,21 @@
#include <linux/module.h>
#include <linux/unaligned.h>
-static void chacha_load_key(u32 *k, const u8 *in)
+static void chacha_load_key(u32 *k, const u8 (*in)[CHACHA20POLY1305_KEY_SIZE])
{
- k[0] = get_unaligned_le32(in);
- k[1] = get_unaligned_le32(in + 4);
- k[2] = get_unaligned_le32(in + 8);
- k[3] = get_unaligned_le32(in + 12);
- k[4] = get_unaligned_le32(in + 16);
- k[5] = get_unaligned_le32(in + 20);
- k[6] = get_unaligned_le32(in + 24);
- k[7] = get_unaligned_le32(in + 28);
+ k[0] = get_unaligned_le32((u8 *)in);
+ k[1] = get_unaligned_le32((u8 *)in + 4);
+ k[2] = get_unaligned_le32((u8 *)in + 8);
+ k[3] = get_unaligned_le32((u8 *)in + 12);
+ k[4] = get_unaligned_le32((u8 *)in + 16);
+ k[5] = get_unaligned_le32((u8 *)in + 20);
+ k[6] = get_unaligned_le32((u8 *)in + 24);
+ k[7] = get_unaligned_le32((u8 *)in + 28);
}
static void xchacha_init(struct chacha_state *chacha_state,
- const u8 *key, const u8 *nonce)
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE],
+ const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE])
{
u32 k[CHACHA_KEY_WORDS];
u8 iv[CHACHA_IV_SIZE];
@@ -42,7 +43,7 @@ static void xchacha_init(struct chacha_state *chacha_state,
chacha_load_key(k, key);
/* Compute the subkey given the original key and first 128 nonce bits */
- chacha_init(chacha_state, k, nonce);
+ chacha_init(chacha_state, k, (u8 *)nonce);
hchacha_block(chacha_state, k, 20);
chacha_init(chacha_state, k, iv);
@@ -89,7 +90,7 @@ __chacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
void chacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE])
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
{
struct chacha_state chacha_state;
u32 k[CHACHA_KEY_WORDS];
@@ -111,8 +112,8 @@ EXPORT_SYMBOL(chacha20poly1305_encrypt);
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
- const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE],
- const u8 key[CHACHA20POLY1305_KEY_SIZE])
+ const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE],
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
{
struct chacha_state chacha_state;
@@ -170,7 +171,7 @@ __chacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
bool chacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE])
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
{
struct chacha_state chacha_state;
u32 k[CHACHA_KEY_WORDS];
@@ -195,8 +196,8 @@ EXPORT_SYMBOL(chacha20poly1305_decrypt);
bool xchacha20poly1305_decrypt(u8 *dst, const u8 *src, const size_t src_len,
const u8 *ad, const size_t ad_len,
- const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE],
- const u8 key[CHACHA20POLY1305_KEY_SIZE])
+ const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE],
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
{
struct chacha_state chacha_state;
@@ -211,7 +212,7 @@ bool chacha20poly1305_crypt_sg_inplace(struct scatterlist *src,
const size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE],
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE],
int encrypt)
{
const u8 *pad0 = page_address(ZERO_PAGE(0));
@@ -335,7 +336,7 @@ bool chacha20poly1305_crypt_sg_inplace(struct scatterlist *src,
bool chacha20poly1305_encrypt_sg_inplace(struct scatterlist *src, size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE])
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
{
return chacha20poly1305_crypt_sg_inplace(src, src_len, ad, ad_len,
nonce, key, 1);
@@ -345,7 +346,7 @@ EXPORT_SYMBOL(chacha20poly1305_encrypt_sg_inplace);
bool chacha20poly1305_decrypt_sg_inplace(struct scatterlist *src, size_t src_len,
const u8 *ad, const size_t ad_len,
const u64 nonce,
- const u8 key[CHACHA20POLY1305_KEY_SIZE])
+ const u8 (*key)[CHACHA20POLY1305_KEY_SIZE])
{
if (unlikely(src_len < POLY1305_DIGEST_SIZE))
return false;
--
2.52.0.rc1.455.g30608eb744-goog
next reply other threads:[~2025-11-14 18:07 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-14 18:07 Ard Biesheuvel [this message]
2025-11-14 20:23 ` [RFC PATCH] libcrypto/chachapoly: Use strict typing for fixed size array arguments Jason A. Donenfeld
2025-11-14 20:33 ` Ard Biesheuvel
2025-11-15 2:14 ` Eric Biggers
2025-11-15 17:11 ` Linus Torvalds
2025-11-15 17:32 ` Linus Torvalds
2025-11-15 17:39 ` Jason A. Donenfeld
2025-12-02 1:12 ` Alejandro Colomar
2025-12-02 1:57 ` Eric Biggers
2025-12-02 10:14 ` Alejandro Colomar
2025-12-02 13:42 ` Alejandro Colomar
2025-12-02 16:49 ` Eric Biggers
2025-12-02 21:27 ` Alejandro Colomar
2025-12-03 17:24 ` Daniel Thompson
2025-12-03 18:01 ` Alejandro Colomar
2025-12-05 13:59 ` Daniel Thompson
2025-12-13 20:04 ` Alejandro Colomar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251114180706.318152-2-ardb+git@google.com \
--to=ardb+git@google.com \
--cc=Jason@zx2c4.com \
--cc=ardb@kernel.org \
--cc=ebiggers@kernel.org \
--cc=linux-crypto@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).