From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=pass header.oldest-pass=0 smtp.remote-ip=100.103.45.18 ARC-Seal: i=2; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=pass; t=1792975014; b=O7DksCsLO5bwX5pA1SjbgQPtCs/oTwfchAfD6qUykuAo89JLbOwix9QV8+yHsB144haH +8xNYt05ru7CIbQeamLcgt9u0Hazc3TFNOGExqfsoui/d3V21czBFjfehDB1BH747mG/u MUy5C/bMmRYcM+cF1jf695wIs/kY5H4HFpzB8fIezhDgGnlTsz3sPxZQsuuXqUFNXPC2C 3m6By9l9J7PgSyHXUb79TnGewApmnt2D8j+YnEsJkbQMkve0X7yrWWl3dh94NvXH0FaSv UvhJcjQi5ixtXrDzfn0Aet/tEiVQvewmuAGyYyBgJOwgHnGIetpp10elZIHYKXN/64Q== ARC-Message-Signature: i=2; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792975014; h=Received:DKIM-Signature:Received:From:Date:Subject:MIME-Version: Content-Type:Content-Transfer-Encoding:Message-Id:References: In-Reply-To:To:Cc:X-Mailer:X-Developer-Signature:X-Developer-Key: X-Endpoint-Received:X-Original-From:Reply-To; bh=s9evbENNBbayZ1V/2KrB5pee4kIx2DAxcemY4LGKEMw=; b=yhbX7TVcNtnrdW5u64XqG3z2JqgfqIsiBSAIh8HBq4NGn1+sPqXYJE32GqPzYIqldXOo IxDjoS/oacB2rHWY+4oB+8TDP5eA3bxyiZFlO7deytw6sELF4CwGlk6TjrWLjunzAvS8q uacvN2gII28eqGMfENCVX5iaTEFq3mCOQX5A9OrCM/uJk5UV+vdfI6TqMw+rEiU4JU4NX wjqvJr6SYGRq+r3PgIaYeh/PFNLhKJlvZHeyrr4t0xUgSHIWQuVbnbsSC+rHFUFtRR4I4 r2ovDEOIGrRDIMRe53njUI3X55E5yLOi5fykn6MjeMEjHk/m5Gzj+ebI3x8a1rvJDBA== ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=S265UhYz; arc=pass header.oldest-pass=0 smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B1DCC606A328; Mon, 26 Oct 2026 00:36:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=S265UhYz Authentication-Results: smtp.kernel.org; arc=none smtp.remote-ip=100.80.115.77 ARC-Seal: i=1; d=kernel.org; s=arc20260519; a=rsa-sha256; cv=none; t=1792975013; b=jpehDSEhrMm0x1M96JQZUFWSo3ZAEI44QxU3+xBg3ZaxV23D0bgrr+CvYwGChBSHV6If N3bdtwhDvmpJDr+phGktRW1TlccuhgcJcrNt7MEpAYkyGeX1h4b5ydG5ZbFZqoK0uptjS /sqUFCsykDod5uZrBCe8Am4TGMIEqbFSgGhzLgIS7tqoQJhrFFetEO+H/oF0DXfy+YkGk QE5FqSkWMgne0Fw+4q23HOjHGR/UPysa8p1ERoqaYulVfWBGIFWZta7jGaOuWyTDuAkqc Lk8z3ETA63XynUQoUzDeQDg/ACh0Gpt1Yzof54/7B0JoT55LfojtmK6pnGbAyXpMmTw== ARC-Message-Signature: i=1; d=kernel.org; s=arc20260519; a=rsa-sha256; c=relaxed/relaxed; t=1792975013; h=DKIM-Signature:Received:From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:References:In-Reply-To:To:Cc: X-Mailer:X-Developer-Signature:X-Developer-Key:X-Endpoint-Received: X-Original-From:Reply-To; bh=s9evbENNBbayZ1V/2KrB5pee4kIx2DAxcemY4LGKEMw=; b=mvb58XmiI35JdO9J9DIrnO2dYPIUWIPW5i91fyDP/XN8KZe/FYBF6iLQtvCuX5HOLXrg cimqirS7wyrE6dadkUYBB5YYvODVD72pF5vuX+ZUSGKLeOBzowA4F7FjGAoQZsrBJqFlT LORvG9KTYA7KVHRcxsjfSaKXcPr15Ugw5LD2YIw4CKz4WHIafOdWhCpr0aodnC5Y0m09l Pie6JK8Uh7BZ+RE+IxKv9N+jnqXoGC9g32T+vmc4i7MdX83SDL398hFWI9ZrMRgLbw5Zd H0IsZWN4QtBiAaNPew6BLScI1VPjZO79mNCqeuSuxBhDehPrTloMwS8pT6Pt6xi3n6g== ARC-Authentication-Results: i=1; smtp.kernel.org; arc=none smtp.remote-ip=100.80.115.77 Received: from lkml.kernel.org (nexus.subspace.kernel.org [100.80.115.77]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.kernel.org (Postfix) with ESMTPS id C96C61F031E1; Mon, 26 Oct 2026 00:36:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1792975013; bh=s9evbENNBbayZ1V/2KrB5pee4kIx2DAxcemY4LGKEMw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To; b=S265UhYzYVqO/EgrFZupwOEcJhrl0H8lDQPPEK5+u7wWienKDqgAGtLJ7/GoA/qV0 byQj328iSB/SQGDjPDzdEUxP7Z3hI6d98bmpTJhbYw/LBMj62smX3Gxf3+iAVNEiTe 0Yp0pr9j5qEFqNXRrnROArU7K7cKzE8oSSJwNQ4sLns4mrztIThFTU8uiT+N8objJr qU5qNQqVJ2YQo9eWA/ooSkx9zQm4+YmMYiHmo821hWqN44kvc1l7HWuS7JbqLFYftm 9uQv5+kAAvINKCYI8Qa1POTLdkNkC2VpsS8mfv+sw3c7TGh+ibD6wDaEqeGB19ed6Y 8EQg6Nr2shNNw== Received: from nexus.subspace.kernel.org (localhost [IPv6:::1]) by lkml.kernel.org (Postfix) with ESMTP id 3CE3844B62; Mon, 26 Oct 2026 00:36:51 +0000 (UTC) From: Martin Belanger via B4 Relay Date: Sun, 25 Oct 2026 20:36:49 -0400 Subject: [PATCH RFC net-next 2/5] tls: add socket options to register the traffic secrets X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261025-keyupdate-rfc-v1-2-60b7756eccbd@gmail.com> References: <20261025-keyupdate-rfc-v1-0-60b7756eccbd@gmail.com> In-Reply-To: <20261025-keyupdate-rfc-v1-0-60b7756eccbd@gmail.com> To: John Fastabend , Jakub Kicinski , Sabrina Dubroca , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Jonathan Corbet , Shuah Khan , Randy Dunlap , Shuah Khan Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, Hannes Reinecke , Chuck Lever , kernel-tls-handshake@lists.linux.dev, linux-nvme@lists.infradead.org, Alistair Francis , Martin Belanger X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1792975010; l=8253; i=martin.nvme.dev@gmail.com; s=20261014; h=from:subject:message-id; bh=gcy54DSUt2k+GtduIJp1DynyxrDG/MV0qQVKikAqXdk=; b=8WLPxKxaMyoek49DEacg2ddRcZdyR4bn7DdG2u662TM/HP02CMvcjEhofQk/PshHZzUIiMAjW KU072aSBqjhAMQhWOzogmyHq81f5i+jMePlaHtQaMM5DsSOLPWhe9vB X-Developer-Key: i=martin.nvme.dev@gmail.com; a=ed25519; pk=4K+XXetGK35w2LzRRddd/bbHawZU222BN+YZRuSpN+k= X-Endpoint-Received: by B4 Relay for martin.nvme.dev@gmail.com/20261014 with auth_id=1176 X-Original-From: Martin Belanger Reply-To: martin.nvme.dev@gmail.com From: Martin Belanger kTLS holds the key and IV of each direction, but not the traffic secret they were derived from. The derivation is one-way, so the kernel cannot compute the next generation for a KeyUpdate on its own. Add TLS_TX_TRAFFIC_SECRET and TLS_RX_TRAFFIC_SECRET. The owner of the socket passes the TLS 1.3 application traffic secret of a direction after installing its key. The cipher of the installed key selects the hash, so the option takes only the secret. A socket without a registered secret behaves as before. The TX secret can be registered alone. The RX secret needs the TX secret first: a received KeyUpdate can ask for one in reply, and since the kernel consumes the record, only the kernel can send that reply. Installing a new key with TLS_TX or TLS_RX removes the secrets that no longer apply. The secrets are cleared when the context is freed and cannot be read back. Assisted-by: LLM Signed-off-by: Martin Belanger --- Documentation/networking/tls.rst | 26 ++++++++++++ include/net/tls.h | 13 ++++++ include/uapi/linux/tls.h | 2 + net/tls/tls_main.c | 86 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/tls.rst b/Documentation/networking/tls.rst index 980c442d7161..2e011bd1ce4a 100644 --- a/Documentation/networking/tls.rst +++ b/Documentation/networking/tls.rst @@ -300,6 +300,32 @@ extra byte used by the ContentType field. [1] https://datatracker.ietf.org/doc/html/rfc8449 +TLS_TX_TRAFFIC_SECRET and TLS_RX_TRAFFIC_SECRET +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``TLS_TX_TRAFFIC_SECRET`` registers the TLS 1.3 application traffic secret +of the transmit direction, ``TLS_RX_TRAFFIC_SECRET`` that of the receive +direction. With a registered secret, the kernel performs KeyUpdate for that +direction itself (RFC 9846, section 4.7.3). The value is the secret itself. +TLS 1.3 only, software path only. + +Install the key of the direction first, with the ``TLS_TX`` or ``TLS_RX`` +socket option described above. The cipher of that key selects the hash, and +the secret must have the length of that hash: 32 bytes for SHA-256 +(AES-128-GCM, AES-128-CCM, ChaCha20-Poly1305) or 48 bytes for SHA-384 +(AES-256-GCM). Other ciphers return ``EOPNOTSUPP``. + +Each secret can be registered once. ``TLS_RX_TRAFFIC_SECRET`` needs +``TLS_TX_TRAFFIC_SECRET`` first: a received KeyUpdate can ask for one in +reply, and only the kernel sees it. ``TLS_TX_TRAFFIC_SECRET`` alone is +allowed. + +If the application installs a new key itself with ``TLS_TX``, both secrets +are removed. With ``TLS_RX``, the RX secret is removed. KeyUpdate for that +direction is then left to the application again. + +The secrets cannot be read back. + Statistics ========== diff --git a/include/net/tls.h b/include/net/tls.h index e57bef58851e..aacc6d12226e 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -65,6 +65,9 @@ struct tls_rec; #define TLS_AAD_SPACE_SIZE 13 +/* SHA-384, the longest hash of a TLS 1.3 cipher suite kTLS supports */ +#define TLS_MAX_TRAFFIC_SECRET_SIZE 48 + #define TLS_MAX_IV_SIZE 16 #define TLS_MAX_SALT_SIZE 4 #define TLS_TAG_SIZE 16 @@ -224,6 +227,12 @@ struct tls_prot_info { u16 tail_size; }; +/* A TLS 1.3 application traffic secret. len is 0 if none is registered. */ +struct tls_traffic_secret { + u8 data[TLS_MAX_TRAFFIC_SECRET_SIZE]; + u8 len; +}; + struct tls_context { /* read-only cache line */ struct tls_prot_info prot_info; @@ -266,6 +275,10 @@ struct tls_context { union tls_crypto_context crypto_send; union tls_crypto_context crypto_recv; + /* Registered for KeyUpdate, protected by the socket lock */ + struct tls_traffic_secret tx_secret; + struct tls_traffic_secret rx_secret; + struct list_head list; refcount_t refcount; struct rcu_head rcu; diff --git a/include/uapi/linux/tls.h b/include/uapi/linux/tls.h index 1245ab38afc1..d1b136ecea96 100644 --- a/include/uapi/linux/tls.h +++ b/include/uapi/linux/tls.h @@ -42,6 +42,8 @@ #define TLS_TX_ZEROCOPY_RO 3 /* TX zerocopy (only sendfile now) */ #define TLS_RX_EXPECT_NO_PAD 4 /* Attempt opportunistic zero-copy */ #define TLS_TX_MAX_PAYLOAD_LEN 5 /* Maximum plaintext size */ +#define TLS_TX_TRAFFIC_SECRET 6 /* TLS 1.3 TX traffic secret */ +#define TLS_RX_TRAFFIC_SECRET 7 /* TLS 1.3 RX traffic secret */ /* Supported versions */ #define TLS_VERSION_MINOR(ver) ((ver) & 0xFF) diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 8bdb78718a8d..1c6e993ec012 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #include #include @@ -325,6 +326,8 @@ void tls_ctx_free(struct sock *sk, struct tls_context *ctx) memzero_explicit(&ctx->crypto_send, sizeof(ctx->crypto_send)); memzero_explicit(&ctx->crypto_recv, sizeof(ctx->crypto_recv)); + memzero_explicit(&ctx->tx_secret, sizeof(ctx->tx_secret)); + memzero_explicit(&ctx->rx_secret, sizeof(ctx->rx_secret)); mutex_destroy(&ctx->tx_lock); if (sk) @@ -757,8 +760,16 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, ctx->rx_conf = conf; update_sk_prot(sk, ctx); - if (update) + if (update) { + /* The registered secret no longer matches the key. The RX + * secret also needs the TX one, see do_tls_setsockopt_secret(). + */ + if (tx) + memzero_explicit(&ctx->tx_secret, + sizeof(ctx->tx_secret)); + memzero_explicit(&ctx->rx_secret, sizeof(ctx->rx_secret)); return 0; + } if (tx) { ctx->sk_write_space = sk->sk_write_space; @@ -857,6 +868,72 @@ static int do_tls_setsockopt_tx_payload_len(struct sock *sk, sockptr_t optval, return 0; } +/* Length of the TLS 1.3 traffic secret for @cipher_type, the size of + * its hash. 0 if KeyUpdate is not supported for the cipher. + */ +static int tls_traffic_secret_len(u16 cipher_type) +{ + switch (cipher_type) { + case TLS_CIPHER_AES_GCM_128: + case TLS_CIPHER_AES_CCM_128: + case TLS_CIPHER_CHACHA20_POLY1305: + return SHA256_DIGEST_SIZE; + case TLS_CIPHER_AES_GCM_256: + return SHA384_DIGEST_SIZE; + default: + return 0; + } +} + +static int do_tls_setsockopt_secret(struct sock *sk, sockptr_t optval, + unsigned int optlen, int tx) +{ + struct tls_context *ctx = tls_get_ctx(sk); + struct tls_crypto_info *crypto_info; + struct tls_traffic_secret *secret; + int len; + + if (tx) { + crypto_info = &ctx->crypto_send.info; + secret = &ctx->tx_secret; + } else { + crypto_info = &ctx->crypto_recv.info; + secret = &ctx->rx_secret; + } + + /* The key must be installed first: its cipher selects the hash */ + if (!TLS_CRYPTO_INFO_READY(crypto_info) || + crypto_info->version != TLS_1_3_VERSION) + return -EINVAL; + + if ((tx ? ctx->tx_conf : ctx->rx_conf) != TLS_SW) + return -EOPNOTSUPP; + + len = tls_traffic_secret_len(crypto_info->cipher_type); + if (!len) + return -EOPNOTSUPP; + + if (sockptr_is_null(optval) || optlen != len) + return -EINVAL; + + /* A received KeyUpdate may ask for one in reply. The kernel + * consumes the record, so only the kernel can send the reply. + */ + if (!tx && !ctx->tx_secret.len) + return -EINVAL; + + if (secret->len) + return -EBUSY; + + if (copy_from_sockptr(secret->data, optval, len)) { + memzero_explicit(secret->data, sizeof(secret->data)); + return -EFAULT; + } + secret->len = len; + + return 0; +} + static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, unsigned int optlen) { @@ -883,6 +960,13 @@ static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, rc = do_tls_setsockopt_tx_payload_len(sk, optval, optlen); release_sock(sk); break; + case TLS_TX_TRAFFIC_SECRET: + case TLS_RX_TRAFFIC_SECRET: + lock_sock(sk); + rc = do_tls_setsockopt_secret(sk, optval, optlen, + optname == TLS_TX_TRAFFIC_SECRET); + release_sock(sk); + break; default: rc = -ENOPROTOOPT; break; -- 2.53.0