From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (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.subspace.kernel.org (Postfix) with ESMTPS id 26457606A326 for ; Mon, 26 Oct 2026 00:37:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=nCPCR5pC; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=S265UhYz DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:Reply-To:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To: References:Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version: Subject:Date:From:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Unsubscribe-Post: List-Owner; bh=s9evbENNBbayZ1V/2KrB5pee4kIx2DAxcemY4LGKEMw=; b=nCPCR5pCOIUsPd ZNqI2OB62JT0zTU5Oyp+gO0lnN9t09VWZ/G151dXtLl37pNoh1lYxaq1KLQzldcy7DYc9DvLVScZA 1l0TWEQ03z/WJs/NBIzSQH20awsBb4lrVM4XyJPV9lgP2nFIOWz7aYXJgnqCt6PRjd/Cp0CJ5e1Zf ILJ10gFjqm6G7iUUgKXA1DEGtdaruPwAOAiqcgpY/JWUvqA9NvwRQOwp+sIu65/ZJ2thzcSRdkXBt QsH52Gu65SCQ37wItmyGoK86tIUDv+sTmPoHmZOLRCHWrDmCH+Vu949i3SxSN0Aaf5Wq1fgUVkOEa cuVT9A9Q0deRKNr+zWrw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.100.1 #2 (Red Hat Linux)) id 1xL8hs-00000001X4r-3XjA; Mon, 26 Oct 2026 00:36:56 +0000 Received: from sea.source.kernel.org ([2600:3c0a:e001:78e:0:1991:8:25]) by bombadil.infradead.org with esmtps (Exim 4.100.1 #2 (Red Hat Linux)) id 1xL8hq-00000001X3b-12BC for linux-nvme@lists.infradead.org; Mon, 26 Oct 2026 00:36:54 +0000 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) by sea.source.kernel.org (Postfix) with ESMTP id 930D8416C0; Mon, 26 Oct 2026 00:36:53 +0000 (UTC) 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-Archived-By: 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 X-BeenThere: linux-nvme@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: martin.nvme.dev@gmail.com Sender: "Linux-nvme" X-Spam-Status: No, score=-19.2 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 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