netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3
@ 2024-11-14 15:50 Sabrina Dubroca
  2024-11-14 15:50 ` [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending Sabrina Dubroca
                   ` (6 more replies)
  0 siblings, 7 replies; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

This adds support for receiving KeyUpdate messages (RFC 8446, 4.6.3
[1]). A sender transmits a KeyUpdate message and then changes its TX
key. The receiver should react by updating its RX key before
processing the next message.

This patchset implements key updates by:
 1. pausing decryption when a KeyUpdate message is received, to avoid
    attempting to use the old key to decrypt a record encrypted with
    the new key
 2. returning -EKEYEXPIRED to syscalls that cannot receive the
    KeyUpdate message, until the rekey has been performed by userspace
 3. passing the KeyUpdate message to userspace as a control message
 4. allowing updates of the crypto_info via the TLS_TX/TLS_RX
    setsockopts

This API has been tested with gnutls to make sure that it allows
userspace libraries to implement key updates [2]. Thanks to Frantisek
Krenzelok <fkrenzel@redhat.com> for providing the implementation in
gnutls and testing the kernel patches.


=======================================================================
Discussions around v2 of this patchset focused on how HW offload would
interact with rekey.

RX
 - The existing SW path will handle all records between the KeyUpdate
   message signaling the change of key and the new key becoming known
   to the kernel -- those will be queued encrypted, and decrypted in
   SW as they are read by userspace (once the key is provided, ie same
   as this patchset)
 - Call ->tls_dev_del + ->tls_dev_add immediately during
   setsockopt(TLS_RX)

TX
 - After setsockopt(TLS_TX), switch to the existing SW path (not the
   current device_fallback) until we're able to re-enable HW offload
   - tls_device_sendmsg will call into tls_sw_sendmsg under lock_sock
     to avoid changing socket ops during the rekey while another
     thread might be waiting on the lock
 - We only re-enable HW offload (call ->tls_dev_add to install the new
   key in HW) once all records sent with the old key have been
   ACKed. At this point, all unacked records are SW-encrypted with the
   new key, and the old key is unused by both HW and retransmissions.
   - If there are no unacked records when userspace does
     setsockopt(TLS_TX), we can (try to) install the new key in HW
     immediately.
   - If yet another key has been provided via setsockopt(TLS_TX), we
     don't install intermediate keys, only the latest.
   - TCP notifies ktls of ACKs via the icsk_clean_acked callback. In
     case of a rekey, tls_icsk_clean_acked will record when all data
     sent with the most recent past key has been sent. The next call
     to sendmsg will install the new key in HW.
   - We close and push the current SW record before reenabling
     offload.

If ->tls_dev_add fails to install the new key in HW, we stay in SW
mode. We can add a counter to keep track of this.


In addition:

Because we can't change socket ops during a rekey, we'll also have to
modify do_tls_setsockopt_conf to check ctx->tx_conf and only call
either tls_set_device_offload or tls_set_sw_offload. RX already uses
the same ops for both TLS_HW and TLS_SW, so we could switch between HW
and SW mode on rekey.

An alternative would be to have a common sendmsg which locks
the socket and then calls the correct implementation. We'll need that
anyway for the offload under rekey case, so that would only add a test
to the SW path's ops (compared to the current code). That should allow
us to simplify build_protos a bit, but might have a performance
impact - we'll need to check it if we want to go that route.
=======================================================================

Changes since v3:
 - rebase on top of net-next
 - rework tls_check_pending_rekey according to Jakub's feedback
 - add statistics for rekey: {RX,TX}REKEY{OK,ERROR}
 - some coding style clean ups

Link: https://lore.kernel.org/netdev/cover.1691584074.git.sd@queasysnail.net/ [v3]
Link: https://lore.kernel.org/netdev/cover.1676052788.git.sd@queasysnail.net/ [v2]
Link: https://lore.kernel.org/netdev/cover.1673952268.git.sd@queasysnail.net/ [v1]

Link: https://www.rfc-editor.org/rfc/rfc8446#section-4.6.3 [1]
Link: https://gitlab.com/gnutls/gnutls/-/merge_requests/1625 [2]

Sabrina Dubroca (6):
  tls: block decryption when a rekey is pending
  tls: implement rekey for TLS1.3
  tls: add counters for rekey
  docs: tls: document TLS1.3 key updates
  selftests: tls: add key_generation argument to tls_crypto_info_init
  selftests: tls: add rekey tests

 Documentation/networking/tls.rst  |  31 ++
 include/net/tls.h                 |   3 +
 include/uapi/linux/snmp.h         |   4 +
 net/tls/tls.h                     |   3 +-
 net/tls/tls_device.c              |   2 +-
 net/tls/tls_main.c                |  71 ++++-
 net/tls/tls_proc.c                |   4 +
 net/tls/tls_sw.c                  | 138 +++++++--
 tools/testing/selftests/net/tls.c | 480 +++++++++++++++++++++++++++++-
 9 files changed, 676 insertions(+), 60 deletions(-)

-- 
2.47.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
@ 2024-11-14 15:50 ` Sabrina Dubroca
  2024-12-04  3:47   ` Jakub Kicinski
  2024-12-05 12:30   ` Parthiban.Veerasooran
  2024-11-14 15:50 ` [PATCH net-next v4 2/6] tls: implement rekey for TLS1.3 Sabrina Dubroca
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

When a TLS handshake record carrying a KeyUpdate message is received,
all subsequent records will be encrypted with a new key. We need to
stop decrypting incoming records with the old key, and wait until
userspace provides a new key.

Make a note of this in the RX context just after decrypting that
record, and stop recvmsg/splice calls with EKEYEXPIRED until the new
key is available.

key_update_pending can't be combined with the existing bitfield,
because we will read it locklessly in ->poll.

v3:
 - move key_update_pending check into tls_rx_rec_wait (Jakub)
 - TLS_RECORD_TYPE_HANDSHAKE was added to include/net/tls_prot.h by
   the tls handshake series, drop that from this patch
 - move key_update_pending into an existing hole

v4:
 - flip TLS_RECORD_TYPE_HANDSHAKE test and use likely() (Jakub)
 - pass ctx rather than sk to tls_check_pending_rekey (Jakub)
 - use WRITE_ONCE to set key_update_pending to pair with ->poll's
   lockless read

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 include/net/tls.h |  3 +++
 net/tls/tls_sw.c  | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/include/net/tls.h b/include/net/tls.h
index 3a33924db2bc..870e4421c599 100644
--- a/include/net/tls.h
+++ b/include/net/tls.h
@@ -59,6 +59,8 @@ struct tls_rec;
 
 #define TLS_CRYPTO_INFO_READY(info)	((info)->cipher_type)
 
+#define TLS_HANDSHAKE_KEYUPDATE		24	/* rfc8446 B.3: Key update */
+
 #define TLS_AAD_SPACE_SIZE		13
 
 #define TLS_MAX_IV_SIZE			16
@@ -130,6 +132,7 @@ struct tls_sw_context_rx {
 	u8 async_capable:1;
 	u8 zc_capable:1;
 	u8 reader_contended:1;
+	bool key_update_pending;
 
 	struct tls_strparser strp;
 
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index bbf26cc4f6ee..db98710c4810 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1314,6 +1314,10 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
 	int ret = 0;
 	long timeo;
 
+	/* a rekey is pending, let userspace deal with it */
+	if (unlikely(ctx->key_update_pending))
+		return -EKEYEXPIRED;
+
 	timeo = sock_rcvtimeo(sk, nonblock);
 
 	while (!tls_strp_msg_ready(ctx)) {
@@ -1720,6 +1724,32 @@ tls_decrypt_device(struct sock *sk, struct msghdr *msg,
 	return 1;
 }
 
+static int tls_check_pending_rekey(struct tls_context *ctx, struct sk_buff *skb)
+{
+	const struct tls_msg *tlm = tls_msg(skb);
+	const struct strp_msg *rxm = strp_msg(skb);
+	char hs_type;
+	int err;
+
+	if (likely(tlm->control != TLS_RECORD_TYPE_HANDSHAKE))
+		return 0;
+
+	if (rxm->full_len < 1)
+		return -EINVAL;
+
+	err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
+	if (err < 0)
+		return err;
+
+	if (hs_type == TLS_HANDSHAKE_KEYUPDATE) {
+		struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
+
+		WRITE_ONCE(rx_ctx->key_update_pending, true);
+	}
+
+	return 0;
+}
+
 static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
 			     struct tls_decrypt_arg *darg)
 {
@@ -1739,6 +1769,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
 	rxm->full_len -= prot->overhead_size;
 	tls_advance_record_sn(sk, prot, &tls_ctx->rx);
 
+	err = tls_check_pending_rekey(tls_ctx, darg->skb);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -2719,6 +2753,7 @@ int tls_set_sw_offload(struct sock *sk, int tx)
 		crypto_info = &ctx->crypto_recv.info;
 		cctx = &ctx->rx;
 		aead = &sw_ctx_rx->aead_recv;
+		sw_ctx_rx->key_update_pending = false;
 	}
 
 	cipher_desc = get_cipher_desc(crypto_info->cipher_type);
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v4 2/6] tls: implement rekey for TLS1.3
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
  2024-11-14 15:50 ` [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending Sabrina Dubroca
@ 2024-11-14 15:50 ` Sabrina Dubroca
  2024-12-04  3:58   ` Jakub Kicinski
  2024-11-14 15:50 ` [PATCH net-next v4 3/6] tls: add counters for rekey Sabrina Dubroca
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

This adds the possibility to change the key and IV when using
TLS1.3. Changing the cipher or TLS version is not supported.

Once we have updated the RX key, we can unblock the receive side. If
the rekey fails, the context is unmodified and userspace is free to
retry the update or close the socket.

This change only affects tls_sw, since 1.3 offload isn't supported.

v2:
 - reverse xmas tree
 - turn the alt_crypto_info into an else if
 - don't modify the context when rekey fails

v3:
 - only call tls_sw_strparser_arm when setting the initial RX key, not
   on rekeys
 - update tls_sk_poll to not say the socket is readable when we're
   waiting for a rekey, and wake up poll() when the new key is installed
 - use unsafe_memcpy to make FORTIFY_SOURCE happy

v4:
 - rebase on top of current net-next
 - no {} needed around single line (Simon)

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 net/tls/tls.h        |   3 +-
 net/tls/tls_device.c |   2 +-
 net/tls/tls_main.c   |  46 ++++++++++++++-----
 net/tls/tls_sw.c     | 105 +++++++++++++++++++++++++++++--------------
 4 files changed, 108 insertions(+), 48 deletions(-)

diff --git a/net/tls/tls.h b/net/tls/tls.h
index e5e47452308a..774859b63f0d 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -145,7 +145,8 @@ void tls_err_abort(struct sock *sk, int err);
 int init_prot_info(struct tls_prot_info *prot,
 		   const struct tls_crypto_info *crypto_info,
 		   const struct tls_cipher_desc *cipher_desc);
-int tls_set_sw_offload(struct sock *sk, int tx);
+int tls_set_sw_offload(struct sock *sk, int tx,
+		       struct tls_crypto_info *new_crypto_info);
 void tls_update_rx_zc_capable(struct tls_context *tls_ctx);
 void tls_sw_strparser_arm(struct sock *sk, struct tls_context *ctx);
 void tls_sw_strparser_done(struct tls_context *tls_ctx);
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
index dc063c2c7950..e50b6e71df13 100644
--- a/net/tls/tls_device.c
+++ b/net/tls/tls_device.c
@@ -1227,7 +1227,7 @@ int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx)
 	context->resync_nh_reset = 1;
 
 	ctx->priv_ctx_rx = context;
-	rc = tls_set_sw_offload(sk, 0);
+	rc = tls_set_sw_offload(sk, 0, NULL);
 	if (rc)
 		goto release_ctx;
 
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 6b4b9f2749a6..68b5735dafc1 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -423,9 +423,10 @@ static __poll_t tls_sk_poll(struct file *file, struct socket *sock,
 	ctx = tls_sw_ctx_rx(tls_ctx);
 	psock = sk_psock_get(sk);
 
-	if (skb_queue_empty_lockless(&ctx->rx_list) &&
-	    !tls_strp_msg_ready(ctx) &&
-	    sk_psock_queue_empty(psock))
+	if ((skb_queue_empty_lockless(&ctx->rx_list) &&
+	     !tls_strp_msg_ready(ctx) &&
+	     sk_psock_queue_empty(psock)) ||
+	    READ_ONCE(ctx->key_update_pending))
 		mask &= ~(EPOLLIN | EPOLLRDNORM);
 
 	if (psock)
@@ -612,11 +613,13 @@ static int validate_crypto_info(const struct tls_crypto_info *crypto_info,
 static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 				  unsigned int optlen, int tx)
 {
-	struct tls_crypto_info *crypto_info;
-	struct tls_crypto_info *alt_crypto_info;
+	struct tls_crypto_info *crypto_info, *alt_crypto_info;
+	struct tls_crypto_info *old_crypto_info = NULL;
 	struct tls_context *ctx = tls_get_ctx(sk);
 	const struct tls_cipher_desc *cipher_desc;
 	union tls_crypto_context *crypto_ctx;
+	union tls_crypto_context tmp = {};
+	bool update = false;
 	int rc = 0;
 	int conf;
 
@@ -633,9 +636,18 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 
 	crypto_info = &crypto_ctx->info;
 
-	/* Currently we don't support set crypto info more than one time */
-	if (TLS_CRYPTO_INFO_READY(crypto_info))
-		return -EBUSY;
+	if (TLS_CRYPTO_INFO_READY(crypto_info)) {
+		/* Currently we only support setting crypto info more
+		 * than one time for TLS 1.3
+		 */
+		if (crypto_info->version != TLS_1_3_VERSION)
+			return -EBUSY;
+
+		update = true;
+		old_crypto_info = crypto_info;
+		crypto_info = &tmp.info;
+		crypto_ctx = &tmp;
+	}
 
 	rc = copy_from_sockptr(crypto_info, optval, sizeof(*crypto_info));
 	if (rc) {
@@ -643,7 +655,14 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 		goto err_crypto_info;
 	}
 
-	rc = validate_crypto_info(crypto_info, alt_crypto_info);
+	if (update) {
+		/* Ensure that TLS version and ciphers are not modified */
+		if (crypto_info->version != old_crypto_info->version ||
+		    crypto_info->cipher_type != old_crypto_info->cipher_type)
+			rc = -EINVAL;
+	} else {
+		rc = validate_crypto_info(crypto_info, alt_crypto_info);
+	}
 	if (rc)
 		goto err_crypto_info;
 
@@ -673,7 +692,8 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXDEVICE);
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXDEVICE);
 		} else {
-			rc = tls_set_sw_offload(sk, 1);
+			rc = tls_set_sw_offload(sk, 1,
+						update ? crypto_info : NULL);
 			if (rc)
 				goto err_crypto_info;
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXSW);
@@ -687,14 +707,16 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXDEVICE);
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXDEVICE);
 		} else {
-			rc = tls_set_sw_offload(sk, 0);
+			rc = tls_set_sw_offload(sk, 0,
+						update ? crypto_info : NULL);
 			if (rc)
 				goto err_crypto_info;
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW);
 			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW);
 			conf = TLS_SW;
 		}
-		tls_sw_strparser_arm(sk, ctx);
+		if (!update)
+			tls_sw_strparser_arm(sk, ctx);
 	}
 
 	if (tx)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index db98710c4810..78b9b89f495e 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2718,12 +2718,22 @@ int init_prot_info(struct tls_prot_info *prot,
 	return 0;
 }
 
-int tls_set_sw_offload(struct sock *sk, int tx)
+static void tls_finish_key_update(struct sock *sk, struct tls_context *tls_ctx)
 {
+	struct tls_sw_context_rx *ctx = tls_ctx->priv_ctx_rx;
+
+	WRITE_ONCE(ctx->key_update_pending, false);
+	/* wake-up pre-existing poll() */
+	ctx->saved_data_ready(sk);
+}
+
+int tls_set_sw_offload(struct sock *sk, int tx,
+		       struct tls_crypto_info *new_crypto_info)
+{
+	struct tls_crypto_info *crypto_info, *src_crypto_info;
 	struct tls_sw_context_tx *sw_ctx_tx = NULL;
 	struct tls_sw_context_rx *sw_ctx_rx = NULL;
 	const struct tls_cipher_desc *cipher_desc;
-	struct tls_crypto_info *crypto_info;
 	char *iv, *rec_seq, *key, *salt;
 	struct cipher_context *cctx;
 	struct tls_prot_info *prot;
@@ -2735,45 +2745,47 @@ int tls_set_sw_offload(struct sock *sk, int tx)
 	ctx = tls_get_ctx(sk);
 	prot = &ctx->prot_info;
 
-	if (tx) {
-		ctx->priv_ctx_tx = init_ctx_tx(ctx, sk);
-		if (!ctx->priv_ctx_tx)
-			return -ENOMEM;
+	/* new_crypto_info != NULL means rekey */
+	if (!new_crypto_info) {
+		if (tx) {
+			ctx->priv_ctx_tx = init_ctx_tx(ctx, sk);
+			if (!ctx->priv_ctx_tx)
+				return -ENOMEM;
+		} else {
+			ctx->priv_ctx_rx = init_ctx_rx(ctx);
+			if (!ctx->priv_ctx_rx)
+				return -ENOMEM;
+		}
+	}
 
+	if (tx) {
 		sw_ctx_tx = ctx->priv_ctx_tx;
 		crypto_info = &ctx->crypto_send.info;
 		cctx = &ctx->tx;
 		aead = &sw_ctx_tx->aead_send;
 	} else {
-		ctx->priv_ctx_rx = init_ctx_rx(ctx);
-		if (!ctx->priv_ctx_rx)
-			return -ENOMEM;
-
 		sw_ctx_rx = ctx->priv_ctx_rx;
 		crypto_info = &ctx->crypto_recv.info;
 		cctx = &ctx->rx;
 		aead = &sw_ctx_rx->aead_recv;
-		sw_ctx_rx->key_update_pending = false;
 	}
 
-	cipher_desc = get_cipher_desc(crypto_info->cipher_type);
+	src_crypto_info = new_crypto_info ?: crypto_info;
+
+	cipher_desc = get_cipher_desc(src_crypto_info->cipher_type);
 	if (!cipher_desc) {
 		rc = -EINVAL;
 		goto free_priv;
 	}
 
-	rc = init_prot_info(prot, crypto_info, cipher_desc);
+	rc = init_prot_info(prot, src_crypto_info, cipher_desc);
 	if (rc)
 		goto free_priv;
 
-	iv = crypto_info_iv(crypto_info, cipher_desc);
-	key = crypto_info_key(crypto_info, cipher_desc);
-	salt = crypto_info_salt(crypto_info, cipher_desc);
-	rec_seq = crypto_info_rec_seq(crypto_info, cipher_desc);
-
-	memcpy(cctx->iv, salt, cipher_desc->salt);
-	memcpy(cctx->iv + cipher_desc->salt, iv, cipher_desc->iv);
-	memcpy(cctx->rec_seq, rec_seq, cipher_desc->rec_seq);
+	iv = crypto_info_iv(src_crypto_info, cipher_desc);
+	key = crypto_info_key(src_crypto_info, cipher_desc);
+	salt = crypto_info_salt(src_crypto_info, cipher_desc);
+	rec_seq = crypto_info_rec_seq(src_crypto_info, cipher_desc);
 
 	if (!*aead) {
 		*aead = crypto_alloc_aead(cipher_desc->cipher_name, 0, 0);
@@ -2786,20 +2798,30 @@ int tls_set_sw_offload(struct sock *sk, int tx)
 
 	ctx->push_pending_record = tls_sw_push_pending_record;
 
+	/* setkey is the last operation that could fail during a
+	 * rekey. if it succeeds, we can start modifying the
+	 * context.
+	 */
 	rc = crypto_aead_setkey(*aead, key, cipher_desc->key);
-	if (rc)
-		goto free_aead;
+	if (rc) {
+		if (new_crypto_info)
+			goto out;
+		else
+			goto free_aead;
+	}
 
-	rc = crypto_aead_setauthsize(*aead, prot->tag_size);
-	if (rc)
-		goto free_aead;
+	if (!new_crypto_info) {
+		rc = crypto_aead_setauthsize(*aead, prot->tag_size);
+		if (rc)
+			goto free_aead;
+	}
 
-	if (sw_ctx_rx) {
+	if (!tx && !new_crypto_info) {
 		tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv);
 
 		tls_update_rx_zc_capable(ctx);
 		sw_ctx_rx->async_capable =
-			crypto_info->version != TLS_1_3_VERSION &&
+			src_crypto_info->version != TLS_1_3_VERSION &&
 			!!(tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC);
 
 		rc = tls_strp_init(&sw_ctx_rx->strp, sk);
@@ -2807,18 +2829,33 @@ int tls_set_sw_offload(struct sock *sk, int tx)
 			goto free_aead;
 	}
 
+	memcpy(cctx->iv, salt, cipher_desc->salt);
+	memcpy(cctx->iv + cipher_desc->salt, iv, cipher_desc->iv);
+	memcpy(cctx->rec_seq, rec_seq, cipher_desc->rec_seq);
+
+	if (new_crypto_info) {
+		unsafe_memcpy(crypto_info, new_crypto_info,
+			      cipher_desc->crypto_info,
+			      /* size was checked in do_tls_setsockopt_conf */);
+		memzero_explicit(new_crypto_info, cipher_desc->crypto_info);
+		if (!tx)
+			tls_finish_key_update(sk, ctx);
+	}
+
 	goto out;
 
 free_aead:
 	crypto_free_aead(*aead);
 	*aead = NULL;
 free_priv:
-	if (tx) {
-		kfree(ctx->priv_ctx_tx);
-		ctx->priv_ctx_tx = NULL;
-	} else {
-		kfree(ctx->priv_ctx_rx);
-		ctx->priv_ctx_rx = NULL;
+	if (!new_crypto_info) {
+		if (tx) {
+			kfree(ctx->priv_ctx_tx);
+			ctx->priv_ctx_tx = NULL;
+		} else {
+			kfree(ctx->priv_ctx_rx);
+			ctx->priv_ctx_rx = NULL;
+		}
 	}
 out:
 	return rc;
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v4 3/6] tls: add counters for rekey
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
  2024-11-14 15:50 ` [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending Sabrina Dubroca
  2024-11-14 15:50 ` [PATCH net-next v4 2/6] tls: implement rekey for TLS1.3 Sabrina Dubroca
@ 2024-11-14 15:50 ` Sabrina Dubroca
  2024-12-04  3:54   ` Jakub Kicinski
  2024-11-14 15:50 ` [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates Sabrina Dubroca
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

This introduces 4 counters to keep track of key updates:
Tls{Rx,Tx}Rekey{Ok,Error}.

v4: new patch

Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 include/uapi/linux/snmp.h |  4 ++++
 net/tls/tls_main.c        | 27 ++++++++++++++++++++++-----
 net/tls/tls_proc.c        |  4 ++++
 3 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index adf5fd78dd50..b0e6f922e72f 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -358,6 +358,10 @@ enum
 	LINUX_MIB_TLSRXDEVICERESYNC,		/* TlsRxDeviceResync */
 	LINUX_MIB_TLSDECRYPTRETRY,		/* TlsDecryptRetry */
 	LINUX_MIB_TLSRXNOPADVIOL,		/* TlsRxNoPadViolation */
+	LINUX_MIB_TLSRXREKEYOK,			/* TlsRxRekeyOk */
+	LINUX_MIB_TLSRXREKEYERROR,		/* TlsRxRekeyError */
+	LINUX_MIB_TLSTXREKEYOK,			/* TlsTxRekeyOk */
+	LINUX_MIB_TLSTXREKEYERROR,		/* TlsTxRekeyError */
 	__LINUX_MIB_TLSMAX
 };
 
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 68b5735dafc1..9ee5a83c5b40 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -640,8 +640,11 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 		/* Currently we only support setting crypto info more
 		 * than one time for TLS 1.3
 		 */
-		if (crypto_info->version != TLS_1_3_VERSION)
+		if (crypto_info->version != TLS_1_3_VERSION) {
+			TLS_INC_STATS(sock_net(sk), tx ? LINUX_MIB_TLSTXREKEYERROR
+						       : LINUX_MIB_TLSRXREKEYERROR);
 			return -EBUSY;
+		}
 
 		update = true;
 		old_crypto_info = crypto_info;
@@ -696,8 +699,13 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 						update ? crypto_info : NULL);
 			if (rc)
 				goto err_crypto_info;
-			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXSW);
-			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXSW);
+
+			if (update) {
+				TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXREKEYOK);
+			} else {
+				TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXSW);
+				TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXSW);
+			}
 			conf = TLS_SW;
 		}
 	} else {
@@ -711,8 +719,13 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 						update ? crypto_info : NULL);
 			if (rc)
 				goto err_crypto_info;
-			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW);
-			TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW);
+
+			if (update) {
+				TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXREKEYOK);
+			} else {
+				TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW);
+				TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW);
+			}
 			conf = TLS_SW;
 		}
 		if (!update)
@@ -735,6 +748,10 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
 	return 0;
 
 err_crypto_info:
+	if (update) {
+		TLS_INC_STATS(sock_net(sk), tx ? LINUX_MIB_TLSTXREKEYERROR
+					       : LINUX_MIB_TLSRXREKEYERROR);
+	}
 	memzero_explicit(crypto_ctx, sizeof(*crypto_ctx));
 	return rc;
 }
diff --git a/net/tls/tls_proc.c b/net/tls/tls_proc.c
index 68982728f620..44ccdc90095c 100644
--- a/net/tls/tls_proc.c
+++ b/net/tls/tls_proc.c
@@ -22,6 +22,10 @@ static const struct snmp_mib tls_mib_list[] = {
 	SNMP_MIB_ITEM("TlsRxDeviceResync", LINUX_MIB_TLSRXDEVICERESYNC),
 	SNMP_MIB_ITEM("TlsDecryptRetry", LINUX_MIB_TLSDECRYPTRETRY),
 	SNMP_MIB_ITEM("TlsRxNoPadViolation", LINUX_MIB_TLSRXNOPADVIOL),
+	SNMP_MIB_ITEM("TlsRxRekeyOk", LINUX_MIB_TLSRXREKEYOK),
+	SNMP_MIB_ITEM("TlsRxRekeyError", LINUX_MIB_TLSRXREKEYERROR),
+	SNMP_MIB_ITEM("TlsTxRekeyOk", LINUX_MIB_TLSTXREKEYOK),
+	SNMP_MIB_ITEM("TlsTxRekeyError", LINUX_MIB_TLSTXREKEYERROR),
 	SNMP_MIB_SENTINEL
 };
 
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
                   ` (2 preceding siblings ...)
  2024-11-14 15:50 ` [PATCH net-next v4 3/6] tls: add counters for rekey Sabrina Dubroca
@ 2024-11-14 15:50 ` Sabrina Dubroca
  2024-12-04  3:51   ` Jakub Kicinski
  2024-11-14 15:50 ` [PATCH net-next v4 5/6] selftests: tls: add key_generation argument to tls_crypto_info_init Sabrina Dubroca
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

v3: added following Jakub's comment on the cover letter
v4: add the new counters

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 Documentation/networking/tls.rst | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/Documentation/networking/tls.rst b/Documentation/networking/tls.rst
index 658ed3a71e1b..dfce109fe2ca 100644
--- a/Documentation/networking/tls.rst
+++ b/Documentation/networking/tls.rst
@@ -200,6 +200,31 @@ received without a cmsg buffer set.
 
 recv will never return data from mixed types of TLS records.
 
+TLS 1.3 Key Updates
+-------------------
+
+In TLS 1.3, KeyUpdate handshake messages signal that the sender is
+updating its TX key. Any message sent after a KeyUpdate will be
+encrypted using the new key. The userspace library can pass the new
+key to the kernel using the TLS_TX and TLS_RX socket options, as for
+the initial keys. TLS version and cipher cannot be changed.
+
+To prevent attempting to decrypt incoming records using the wrong key,
+decryption will be paused when a KeyUpdate message is received by the
+kernel, until the new key has been provided using the TLS_RX socket
+option. Any read occurring after the KeyUpdate has been read and
+before the new key is provided will fail with EKEYEXPIRED. Poll()'ing
+the socket will also sleep until the new key is provided. There is no
+pausing on the transmit side.
+
+Userspace should make sure that the crypto_info provided has been set
+properly. In particular, the kernel will not check for key/nonce
+reuse.
+
+The number of successful and failed key updates is tracked in the
+``TlsTxRekeyOk``, ``TlsRxRekeyOk``, ``TlsTxRekeyError``,
+``TlsRxRekeyError`` statistics.
+
 Integrating in to userspace TLS library
 ---------------------------------------
 
@@ -286,3 +311,9 @@ TLS implementation exposes the following per-namespace statistics
 - ``TlsRxNoPadViolation`` -
   number of data RX records which had to be re-decrypted due to
   ``TLS_RX_EXPECT_NO_PAD`` mis-prediction.
+
+- ``TlsTxRekeyOk``, ``TlsRxRekeyOk`` -
+  number of successful rekeys on existing sessions for TX and RX
+
+- ``TlsTxRekeyError``, ``TlsRxRekeyError`` -
+  number of failed rekeys on existing sessions for TX and RX
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v4 5/6] selftests: tls: add key_generation argument to tls_crypto_info_init
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
                   ` (3 preceding siblings ...)
  2024-11-14 15:50 ` [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates Sabrina Dubroca
@ 2024-11-14 15:50 ` Sabrina Dubroca
  2024-11-14 15:50 ` [PATCH net-next v4 6/6] selftests: tls: add rekey tests Sabrina Dubroca
  2024-11-19  3:41 ` [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Jakub Kicinski
  6 siblings, 0 replies; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

This allows us to generate different keys, so that we can test that
rekey is using the correct one.

v3: update for newly added tests
v4: update for newly added tests

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 tools/testing/selftests/net/tls.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 1a706d03bb6b..b1f52d2bb096 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -44,9 +44,11 @@ struct tls_crypto_info_keys {
 };
 
 static void tls_crypto_info_init(uint16_t tls_version, uint16_t cipher_type,
-				 struct tls_crypto_info_keys *tls12)
+				 struct tls_crypto_info_keys *tls12,
+				 char key_generation)
 {
-	memset(tls12, 0, sizeof(*tls12));
+	memset(tls12, key_generation, sizeof(*tls12));
+	memset(tls12, 0, sizeof(struct tls_crypto_info));
 
 	switch (cipher_type) {
 	case TLS_CIPHER_CHACHA20_POLY1305:
@@ -275,7 +277,7 @@ TEST_F(tls_basic, recseq_wrap)
 	if (self->notls)
 		SKIP(return, "no TLS support");
 
-	tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls12);
+	tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls12, 0);
 	memset(&tls12.aes128.rec_seq, 0xff, sizeof(tls12.aes128.rec_seq));
 
 	ASSERT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
@@ -391,7 +393,7 @@ FIXTURE_SETUP(tls)
 		SKIP(return, "Unsupported cipher in FIPS mode");
 
 	tls_crypto_info_init(variant->tls_version, variant->cipher_type,
-			     &tls12);
+			     &tls12, 0);
 
 	ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls);
 
@@ -1175,7 +1177,7 @@ TEST_F(tls, bidir)
 		struct tls_crypto_info_keys tls12;
 
 		tls_crypto_info_init(variant->tls_version, variant->cipher_type,
-				     &tls12);
+				     &tls12, 0);
 
 		ret = setsockopt(self->fd, SOL_TLS, TLS_RX, &tls12,
 				 tls12.len);
@@ -1614,7 +1616,7 @@ TEST_F(tls, getsockopt)
 	EXPECT_EQ(get.crypto_info.cipher_type, variant->cipher_type);
 
 	/* get the full crypto_info */
-	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &expect);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &expect, 0);
 	len = expect.len;
 	memrnd(&get, sizeof(get));
 	EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &get, &len), 0);
@@ -1696,7 +1698,7 @@ FIXTURE_SETUP(tls_err)
 	int ret;
 
 	tls_crypto_info_init(variant->tls_version, TLS_CIPHER_AES_GCM_128,
-			     &tls12);
+			     &tls12, 0);
 
 	ulp_sock_pair(_metadata, &self->fd, &self->cfd, &self->notls);
 	ulp_sock_pair(_metadata, &self->fd2, &self->cfd2, &self->notls);
@@ -2118,7 +2120,7 @@ TEST(tls_v6ops) {
 	int sfd, ret, fd;
 	socklen_t len, len2;
 
-	tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls12);
+	tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_128, &tls12, 0);
 
 	addr.sin6_family = AF_INET6;
 	addr.sin6_addr = in6addr_any;
@@ -2177,7 +2179,7 @@ TEST(prequeue) {
 	len = sizeof(addr);
 	memrnd(buf, sizeof(buf));
 
-	tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_256, &tls12);
+	tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_GCM_256, &tls12, 0);
 
 	addr.sin_family = AF_INET;
 	addr.sin_addr.s_addr = htonl(INADDR_ANY);
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v4 6/6] selftests: tls: add rekey tests
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
                   ` (4 preceding siblings ...)
  2024-11-14 15:50 ` [PATCH net-next v4 5/6] selftests: tls: add key_generation argument to tls_crypto_info_init Sabrina Dubroca
@ 2024-11-14 15:50 ` Sabrina Dubroca
  2024-11-19  3:41 ` [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Jakub Kicinski
  6 siblings, 0 replies; 20+ messages in thread
From: Sabrina Dubroca @ 2024-11-14 15:50 UTC (permalink / raw)
  To: netdev
  Cc: Sabrina Dubroca, Vadim Fedorenko, Frantisek Krenzelok,
	Jakub Kicinski, Kuniyuki Iwashima, Apoorv Kothari, Boris Pismenny,
	John Fastabend, Shuah Khan, linux-kselftest, Gal Pressman,
	Marcel Holtmann, Simon Horman

v2: add rekey_fail test (reject changing the version/cipher)
v3: add rekey_peek_splice following Jakub's comment
    add rekey+poll tests
v4: rebase, new selftests were added
    check that rekey isn't supported on TLS1.2

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 tools/testing/selftests/net/tls.c | 460 ++++++++++++++++++++++++++++++
 1 file changed, 460 insertions(+)

diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index b1f52d2bb096..4fc07da10b9e 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -1670,6 +1670,466 @@ TEST_F(tls, recv_efault)
 		EXPECT_EQ(memcmp(rec2, recv_mem + 9, ret - 9), 0);
 }
 
+#define TLS_RECORD_TYPE_HANDSHAKE      0x16
+/* key_update, length 1, update_not_requested */
+static const char key_update_msg[] = "\x18\x00\x00\x01\x00";
+static void tls_send_keyupdate(struct __test_metadata *_metadata, int fd)
+{
+	size_t len = sizeof(key_update_msg);
+
+	EXPECT_EQ(tls_send_cmsg(fd, TLS_RECORD_TYPE_HANDSHAKE,
+				(char *)key_update_msg, len, 0),
+		  len);
+}
+
+static void tls_recv_keyupdate(struct __test_metadata *_metadata, int fd, int flags)
+{
+	char buf[100];
+
+	EXPECT_EQ(tls_recv_cmsg(_metadata, fd, TLS_RECORD_TYPE_HANDSHAKE, buf, sizeof(buf), flags),
+		  sizeof(key_update_msg));
+	EXPECT_EQ(memcmp(buf, key_update_msg, sizeof(key_update_msg)), 0);
+}
+
+/* set the key to 0 then 1 for RX, immediately to 1 for TX */
+TEST_F(tls_basic, rekey_rx)
+{
+	struct tls_crypto_info_keys tls12_0, tls12_1;
+	char const *test_str = "test_message";
+	int send_len = strlen(test_str) + 1;
+	char buf[20];
+	int ret;
+
+	if (self->notls)
+		return;
+
+	tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
+			     &tls12_0, 0);
+	tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
+			     &tls12_1, 1);
+
+
+	ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_1, tls12_1.len);
+	ASSERT_EQ(ret, 0);
+
+	ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_0, tls12_0.len);
+	ASSERT_EQ(ret, 0);
+
+	ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_1, tls12_1.len);
+	EXPECT_EQ(ret, 0);
+
+	EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
+	EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+}
+
+/* set the key to 0 then 1 for TX, immediately to 1 for RX */
+TEST_F(tls_basic, rekey_tx)
+{
+	struct tls_crypto_info_keys tls12_0, tls12_1;
+	char const *test_str = "test_message";
+	int send_len = strlen(test_str) + 1;
+	char buf[20];
+	int ret;
+
+	if (self->notls)
+		return;
+
+	tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
+			     &tls12_0, 0);
+	tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
+			     &tls12_1, 1);
+
+
+	ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_0, tls12_0.len);
+	ASSERT_EQ(ret, 0);
+
+	ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_1, tls12_1.len);
+	ASSERT_EQ(ret, 0);
+
+	ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_1, tls12_1.len);
+	EXPECT_EQ(ret, 0);
+
+	EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
+	EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+}
+
+TEST_F(tls, rekey)
+{
+	char const *test_str_1 = "test_message_before_rekey";
+	char const *test_str_2 = "test_message_after_rekey";
+	struct tls_crypto_info_keys tls12;
+	int send_len;
+	char buf[100];
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	/* initial send/recv */
+	send_len = strlen(test_str_1) + 1;
+	EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
+	EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	/* send after rekey */
+	send_len = strlen(test_str_2) + 1;
+	EXPECT_EQ(send(self->fd, test_str_2, send_len, 0), send_len);
+
+	/* can't receive the KeyUpdate without a control message */
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
+
+	/* get KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+
+	/* recv blocking -> -EKEYEXPIRED */
+	EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
+	EXPECT_EQ(errno, EKEYEXPIRED);
+
+	/* recv non-blocking -> -EKEYEXPIRED */
+	EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1);
+	EXPECT_EQ(errno, EKEYEXPIRED);
+
+	/* update RX key */
+	EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+
+	/* recv after rekey */
+	EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
+	EXPECT_EQ(memcmp(buf, test_str_2, send_len), 0);
+}
+
+TEST_F(tls, rekey_fail)
+{
+	char const *test_str_1 = "test_message_before_rekey";
+	char const *test_str_2 = "test_message_after_rekey";
+	struct tls_crypto_info_keys tls12;
+	int send_len;
+	char buf[100];
+
+	/* initial send/recv */
+	send_len = strlen(test_str_1) + 1;
+	EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
+	EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+
+	if (variant->tls_version != TLS_1_3_VERSION) {
+		/* just check that rekey is not supported and return */
+		tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+		EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), -1);
+		EXPECT_EQ(errno, EBUSY);
+		return;
+	}
+
+	/* successful update */
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	/* invalid update: change of version */
+	tls_crypto_info_init(TLS_1_2_VERSION, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	/* invalid update (RX socket): change of version */
+	tls_crypto_info_init(TLS_1_2_VERSION, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	/* invalid update: change of cipher */
+	if (variant->cipher_type == TLS_CIPHER_AES_GCM_256)
+		tls_crypto_info_init(variant->tls_version, TLS_CIPHER_CHACHA20_POLY1305, &tls12, 1);
+	else
+		tls_crypto_info_init(variant->tls_version, TLS_CIPHER_AES_GCM_256, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	/* send after rekey, the invalid updates shouldn't have an effect */
+	send_len = strlen(test_str_2) + 1;
+	EXPECT_EQ(send(self->fd, test_str_2, send_len, 0), send_len);
+
+	/* can't receive the KeyUpdate without a control message */
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), -1);
+
+	/* get KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+
+	/* recv blocking -> -EKEYEXPIRED */
+	EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
+	EXPECT_EQ(errno, EKEYEXPIRED);
+
+	/* recv non-blocking -> -EKEYEXPIRED */
+	EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1);
+	EXPECT_EQ(errno, EKEYEXPIRED);
+
+	/* update RX key */
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+
+	/* recv after rekey */
+	EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
+	EXPECT_EQ(memcmp(buf, test_str_2, send_len), 0);
+}
+
+TEST_F(tls, rekey_peek)
+{
+	char const *test_str_1 = "test_message_before_rekey";
+	struct tls_crypto_info_keys tls12;
+	int send_len;
+	char buf[100];
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	send_len = strlen(test_str_1) + 1;
+	EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
+	EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
+
+	EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len);
+	EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
+
+	/* can't receive the KeyUpdate without a control message */
+	EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_PEEK), -1);
+
+	/* peek KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, MSG_PEEK);
+
+	/* get KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+
+	/* update RX key */
+	EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+}
+
+TEST_F(tls, splice_rekey)
+{
+	int send_len = TLS_PAYLOAD_MAX_LEN / 2;
+	char mem_send[TLS_PAYLOAD_MAX_LEN];
+	char mem_recv[TLS_PAYLOAD_MAX_LEN];
+	struct tls_crypto_info_keys tls12;
+	int p[2];
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	memrnd(mem_send, sizeof(mem_send));
+
+	ASSERT_GE(pipe(p), 0);
+	EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+
+	EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), send_len);
+	EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+	EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+
+	/* can't splice the KeyUpdate */
+	EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	/* peek KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, MSG_PEEK);
+
+	/* get KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+
+	/* can't splice before updating the key */
+	EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), -1);
+	EXPECT_EQ(errno, EKEYEXPIRED);
+
+	/* update RX key */
+	EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+
+	EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), send_len);
+	EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+	EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+}
+
+TEST_F(tls, rekey_peek_splice)
+{
+	char const *test_str_1 = "test_message_before_rekey";
+	struct tls_crypto_info_keys tls12;
+	int send_len;
+	char buf[100];
+	char mem_recv[TLS_PAYLOAD_MAX_LEN];
+	int p[2];
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	ASSERT_GE(pipe(p), 0);
+
+	send_len = strlen(test_str_1) + 1;
+	EXPECT_EQ(send(self->fd, test_str_1, send_len, 0), send_len);
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
+	EXPECT_EQ(memcmp(buf, test_str_1, send_len), 0);
+
+	EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, TLS_PAYLOAD_MAX_LEN, 0), send_len);
+	EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+	EXPECT_EQ(memcmp(mem_recv, test_str_1, send_len), 0);
+}
+
+TEST_F(tls, rekey_getsockopt)
+{
+	struct tls_crypto_info_keys tls12;
+	struct tls_crypto_info_keys tls12_get;
+	socklen_t len;
+
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 0);
+
+	len = tls12.len;
+	EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_get, &len), 0);
+	EXPECT_EQ(len, tls12.len);
+	EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
+
+	len = tls12.len;
+	EXPECT_EQ(getsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_get, &len), 0);
+	EXPECT_EQ(len, tls12.len);
+	EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+	EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+
+	len = tls12.len;
+	EXPECT_EQ(getsockopt(self->fd, SOL_TLS, TLS_TX, &tls12_get, &len), 0);
+	EXPECT_EQ(len, tls12.len);
+	EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
+
+	len = tls12.len;
+	EXPECT_EQ(getsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12_get, &len), 0);
+	EXPECT_EQ(len, tls12.len);
+	EXPECT_EQ(memcmp(&tls12_get, &tls12, tls12.len), 0);
+}
+
+TEST_F(tls, rekey_poll_pending)
+{
+	char const *test_str = "test_message_after_rekey";
+	struct tls_crypto_info_keys tls12;
+	struct pollfd pfd = { };
+	int send_len;
+	int ret;
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	/* get KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+
+	/* send immediately after rekey */
+	send_len = strlen(test_str) + 1;
+	EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+
+	/* key hasn't been updated, expect cfd to be non-readable */
+	pfd.fd = self->cfd;
+	pfd.events = POLLIN;
+	EXPECT_EQ(poll(&pfd, 1, 0), 0);
+
+	ret = fork();
+	ASSERT_GE(ret, 0);
+
+	if (ret) {
+		int pid2, status;
+
+		/* wait before installing the new key */
+		sleep(1);
+
+		/* update RX key while poll() is sleeping */
+		EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+
+		pid2 = wait(&status);
+		EXPECT_EQ(pid2, ret);
+		EXPECT_EQ(status, 0);
+	} else {
+		pfd.fd = self->cfd;
+		pfd.events = POLLIN;
+		EXPECT_EQ(poll(&pfd, 1, 5000), 1);
+
+		exit(!__test_passed(_metadata));
+	}
+}
+
+TEST_F(tls, rekey_poll_delay)
+{
+	char const *test_str = "test_message_after_rekey";
+	struct tls_crypto_info_keys tls12;
+	struct pollfd pfd = { };
+	int send_len;
+	int ret;
+
+	if (variant->tls_version != TLS_1_3_VERSION)
+		return;
+
+	/* update TX key */
+	tls_send_keyupdate(_metadata, self->fd);
+	tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12, 1);
+	EXPECT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &tls12, tls12.len), 0);
+
+	/* get KeyUpdate */
+	tls_recv_keyupdate(_metadata, self->cfd, 0);
+
+	ret = fork();
+	ASSERT_GE(ret, 0);
+
+	if (ret) {
+		int pid2, status;
+
+		/* wait before installing the new key */
+		sleep(1);
+
+		/* update RX key while poll() is sleeping */
+		EXPECT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls12, tls12.len), 0);
+
+		sleep(1);
+		send_len = strlen(test_str) + 1;
+		EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+
+		pid2 = wait(&status);
+		EXPECT_EQ(pid2, ret);
+		EXPECT_EQ(status, 0);
+	} else {
+		pfd.fd = self->cfd;
+		pfd.events = POLLIN;
+		EXPECT_EQ(poll(&pfd, 1, 5000), 1);
+		exit(!__test_passed(_metadata));
+	}
+}
+
 FIXTURE(tls_err)
 {
 	int fd, cfd;
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3
  2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
                   ` (5 preceding siblings ...)
  2024-11-14 15:50 ` [PATCH net-next v4 6/6] selftests: tls: add rekey tests Sabrina Dubroca
@ 2024-11-19  3:41 ` Jakub Kicinski
  2024-12-03 16:16   ` Sabrina Dubroca
  6 siblings, 1 reply; 20+ messages in thread
From: Jakub Kicinski @ 2024-11-19  3:41 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Thu, 14 Nov 2024 16:50:47 +0100 Sabrina Dubroca wrote:
> This adds support for receiving KeyUpdate messages (RFC 8446, 4.6.3
> [1]). A sender transmits a KeyUpdate message and then changes its TX
> key. The receiver should react by updating its RX key before
> processing the next message.

Will review tomorrow/Wednesday but I haven't gotten to this in time 
for 6.13, sorry :(
-- 
pw-bot: defer

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3
  2024-11-19  3:41 ` [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Jakub Kicinski
@ 2024-12-03 16:16   ` Sabrina Dubroca
  2024-12-04  4:02     ` Jakub Kicinski
  0 siblings, 1 reply; 20+ messages in thread
From: Sabrina Dubroca @ 2024-12-03 16:16 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

Hey Jakub,

2024-11-18, 19:41:58 -0800, Jakub Kicinski wrote:
> On Thu, 14 Nov 2024 16:50:47 +0100 Sabrina Dubroca wrote:
> > This adds support for receiving KeyUpdate messages (RFC 8446, 4.6.3
> > [1]). A sender transmits a KeyUpdate message and then changes its TX
> > key. The receiver should react by updating its RX key before
> > processing the next message.
> 
> Will review tomorrow/Wednesday but I haven't gotten to this in time 
> for 6.13, sorry :(

Is this still on your todo list, or do you want me to resend? No
problem either way.

-- 
Sabrina

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending
  2024-11-14 15:50 ` [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending Sabrina Dubroca
@ 2024-12-04  3:47   ` Jakub Kicinski
  2024-12-10 16:16     ` Sabrina Dubroca
  2024-12-05 12:30   ` Parthiban.Veerasooran
  1 sibling, 1 reply; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-04  3:47 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Thu, 14 Nov 2024 16:50:48 +0100 Sabrina Dubroca wrote:
> +static int tls_check_pending_rekey(struct tls_context *ctx, struct sk_buff *skb)
> +{
> +	const struct tls_msg *tlm = tls_msg(skb);
> +	const struct strp_msg *rxm = strp_msg(skb);
> +	char hs_type;
> +	int err;
> +
> +	if (likely(tlm->control != TLS_RECORD_TYPE_HANDSHAKE))
> +		return 0;
> +
> +	if (rxm->full_len < 1)
> +		return -EINVAL;
> +
> +	err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
> +	if (err < 0)
> +		return err;
> +
> +	if (hs_type == TLS_HANDSHAKE_KEYUPDATE) {
> +		struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
> +
> +		WRITE_ONCE(rx_ctx->key_update_pending, true);
> +	}
> +
> +	return 0;
> +}
> +
>  static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
>  			     struct tls_decrypt_arg *darg)
>  {
> @@ -1739,6 +1769,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
>  	rxm->full_len -= prot->overhead_size;
>  	tls_advance_record_sn(sk, prot, &tls_ctx->rx);
>  
> +	err = tls_check_pending_rekey(tls_ctx, darg->skb);
> +	if (err < 0)
> +		return err;

Sorry if I already asked this, is this 100% safe to error out from here
after we decrypted the record? Normally once we successfully decrypted
and pulled the message header / trailer we always call tls_rx_rec_done()

The only reason the check_pending_rekey() can fail is if the message is
mis-formatted, I wonder if we are better off ignoring mis-formatted
rekeys? User space will see them and break the connection, anyway.
Alternatively - we could add a selftest for this.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates
  2024-11-14 15:50 ` [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates Sabrina Dubroca
@ 2024-12-04  3:51   ` Jakub Kicinski
  2024-12-05 11:06     ` Sabrina Dubroca
  0 siblings, 1 reply; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-04  3:51 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Thu, 14 Nov 2024 16:50:51 +0100 Sabrina Dubroca wrote:
> +To prevent attempting to decrypt incoming records using the wrong key,
> +decryption will be paused when a KeyUpdate message is received by the
> +kernel, until the new key has been provided using the TLS_RX socket
> +option. Any read occurring after the KeyUpdate has been read and
> +before the new key is provided will fail with EKEYEXPIRED. Poll()'ing
> +the socket will also sleep until the new key is provided. There is no
> +pausing on the transmit side.

Thanks for the doc update, very useful. I'm not a socket expert so dunno
if suppressing POLLIN is the right thing to do. But a nit on the
phrasing - I'd say "poll() will not report any events from the socket
until.." ? Could be just me but sleep is a second order effect.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 3/6] tls: add counters for rekey
  2024-11-14 15:50 ` [PATCH net-next v4 3/6] tls: add counters for rekey Sabrina Dubroca
@ 2024-12-04  3:54   ` Jakub Kicinski
  2024-12-05 11:29     ` Sabrina Dubroca
  0 siblings, 1 reply; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-04  3:54 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Thu, 14 Nov 2024 16:50:50 +0100 Sabrina Dubroca wrote:
> This introduces 4 counters to keep track of key updates:
> Tls{Rx,Tx}Rekey{Ok,Error}.

Possibly track detected rekey messages, too? Could help us identify
when kernel blocks the socket but user space doesn't know how to rekey.
Either way:

Reviewed-by: Jakub Kicinski <kuba@kernel.org>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 2/6] tls: implement rekey for TLS1.3
  2024-11-14 15:50 ` [PATCH net-next v4 2/6] tls: implement rekey for TLS1.3 Sabrina Dubroca
@ 2024-12-04  3:58   ` Jakub Kicinski
  0 siblings, 0 replies; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-04  3:58 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Thu, 14 Nov 2024 16:50:49 +0100 Sabrina Dubroca wrote:
> This adds the possibility to change the key and IV when using
> TLS1.3. Changing the cipher or TLS version is not supported.
> 
> Once we have updated the RX key, we can unblock the receive side. If
> the rekey fails, the context is unmodified and userspace is free to
> retry the update or close the socket.
> 
> This change only affects tls_sw, since 1.3 offload isn't supported.

Acked-by: Jakub Kicinski <kuba@kernel.org>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3
  2024-12-03 16:16   ` Sabrina Dubroca
@ 2024-12-04  4:02     ` Jakub Kicinski
  0 siblings, 0 replies; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-04  4:02 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Tue, 3 Dec 2024 17:16:52 +0100 Sabrina Dubroca wrote:
> 2024-11-18, 19:41:58 -0800, Jakub Kicinski wrote:
> > On Thu, 14 Nov 2024 16:50:47 +0100 Sabrina Dubroca wrote:  
> > > This adds support for receiving KeyUpdate messages (RFC 8446, 4.6.3
> > > [1]). A sender transmits a KeyUpdate message and then changes its TX
> > > key. The receiver should react by updating its RX key before
> > > processing the next message.  
> > 
> > Will review tomorrow/Wednesday but I haven't gotten to this in time 
> > for 6.13, sorry :(  
> 
> Is this still on your todo list, or do you want me to resend?
> No problem either way.

Sorry for the delay :( I had a nice plan to get this reviewed and
then corporate life did its thing.

I left a few comments, hopefully some of them make sense, if not
feel free to repost as non-RFC to avoid further delays.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates
  2024-12-04  3:51   ` Jakub Kicinski
@ 2024-12-05 11:06     ` Sabrina Dubroca
  2024-12-06  0:34       ` Jakub Kicinski
  0 siblings, 1 reply; 20+ messages in thread
From: Sabrina Dubroca @ 2024-12-05 11:06 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

2024-12-03, 19:51:29 -0800, Jakub Kicinski wrote:
> On Thu, 14 Nov 2024 16:50:51 +0100 Sabrina Dubroca wrote:
> > +To prevent attempting to decrypt incoming records using the wrong key,
> > +decryption will be paused when a KeyUpdate message is received by the
> > +kernel, until the new key has been provided using the TLS_RX socket
> > +option. Any read occurring after the KeyUpdate has been read and
> > +before the new key is provided will fail with EKEYEXPIRED. Poll()'ing
> > +the socket will also sleep until the new key is provided. There is no
> > +pausing on the transmit side.
> 
> Thanks for the doc update, very useful. I'm not a socket expert so dunno
> if suppressing POLLIN is the right thing to do.

Not an expert either. I picked that because there's no data to be
read, which is what POLLIN should mean.

man 2 poll:
       POLLIN There is data to read.

man 3 poll:
       POLLIN      Data other than high-priority data may be read
                   without blocking.

Based on this, reporting POLLIN seems wrong to me.

> But a nit on the
> phrasing - I'd say "poll() will not report any events from the socket
> until.." ? Could be just me but sleep is a second order effect.

Agree, thanks for the suggestion. Maybe actually "will not report read
events"? Other events are unaffected by a pending rekey.

-- 
Sabrina

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 3/6] tls: add counters for rekey
  2024-12-04  3:54   ` Jakub Kicinski
@ 2024-12-05 11:29     ` Sabrina Dubroca
  0 siblings, 0 replies; 20+ messages in thread
From: Sabrina Dubroca @ 2024-12-05 11:29 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

2024-12-03, 19:54:14 -0800, Jakub Kicinski wrote:
> On Thu, 14 Nov 2024 16:50:50 +0100 Sabrina Dubroca wrote:
> > This introduces 4 counters to keep track of key updates:
> > Tls{Rx,Tx}Rekey{Ok,Error}.
> 
> Possibly track detected rekey messages, too? Could help us identify
> when kernel blocks the socket but user space doesn't know how to rekey.

Right, that makes sense. I'll add TlsRxRekeyReceived unless you have a
better name to suggest.

-- 
Sabrina

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending
  2024-11-14 15:50 ` [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending Sabrina Dubroca
  2024-12-04  3:47   ` Jakub Kicinski
@ 2024-12-05 12:30   ` Parthiban.Veerasooran
  1 sibling, 0 replies; 20+ messages in thread
From: Parthiban.Veerasooran @ 2024-12-05 12:30 UTC (permalink / raw)
  To: sd
  Cc: vfedorenko, fkrenzel, kuba, kuniyu, apoorvko, borisp,
	john.fastabend, shuah, linux-kselftest, gal, marcel, horms,
	netdev

Hi,

On 14/11/24 9:20 pm, Sabrina Dubroca wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> When a TLS handshake record carrying a KeyUpdate message is received,
> all subsequent records will be encrypted with a new key. We need to
> stop decrypting incoming records with the old key, and wait until
> userspace provides a new key.
> 
> Make a note of this in the RX context just after decrypting that
> record, and stop recvmsg/splice calls with EKEYEXPIRED until the new
> key is available.
> 
> key_update_pending can't be combined with the existing bitfield,
> because we will read it locklessly in ->poll.
> 
> v3:
>   - move key_update_pending check into tls_rx_rec_wait (Jakub)
>   - TLS_RECORD_TYPE_HANDSHAKE was added to include/net/tls_prot.h by
>     the tls handshake series, drop that from this patch
>   - move key_update_pending into an existing hole
> 
> v4:
>   - flip TLS_RECORD_TYPE_HANDSHAKE test and use likely() (Jakub)
>   - pass ctx rather than sk to tls_check_pending_rekey (Jakub)
>   - use WRITE_ONCE to set key_update_pending to pair with ->poll's
>     lockless read
> 
> Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
> ---
>   include/net/tls.h |  3 +++
>   net/tls/tls_sw.c  | 35 +++++++++++++++++++++++++++++++++++
>   2 files changed, 38 insertions(+)
> 
> diff --git a/include/net/tls.h b/include/net/tls.h
> index 3a33924db2bc..870e4421c599 100644
> --- a/include/net/tls.h
> +++ b/include/net/tls.h
> @@ -59,6 +59,8 @@ struct tls_rec;
> 
>   #define TLS_CRYPTO_INFO_READY(info)    ((info)->cipher_type)
> 
> +#define TLS_HANDSHAKE_KEYUPDATE                24      /* rfc8446 B.3: Key update */
> +
>   #define TLS_AAD_SPACE_SIZE             13
> 
>   #define TLS_MAX_IV_SIZE                        16
> @@ -130,6 +132,7 @@ struct tls_sw_context_rx {
>          u8 async_capable:1;
>          u8 zc_capable:1;
>          u8 reader_contended:1;
> +       bool key_update_pending;
> 
>          struct tls_strparser strp;
> 
> diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
> index bbf26cc4f6ee..db98710c4810 100644
> --- a/net/tls/tls_sw.c
> +++ b/net/tls/tls_sw.c
> @@ -1314,6 +1314,10 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock,
>          int ret = 0;
>          long timeo;
> 
> +       /* a rekey is pending, let userspace deal with it */
> +       if (unlikely(ctx->key_update_pending))
> +               return -EKEYEXPIRED;
> +
>          timeo = sock_rcvtimeo(sk, nonblock);
> 
>          while (!tls_strp_msg_ready(ctx)) {
> @@ -1720,6 +1724,32 @@ tls_decrypt_device(struct sock *sk, struct msghdr *msg,
>          return 1;
>   }
> 
> +static int tls_check_pending_rekey(struct tls_context *ctx, struct sk_buff *skb)
> +{
> +       const struct tls_msg *tlm = tls_msg(skb);
> +       const struct strp_msg *rxm = strp_msg(skb);
Missing reverse xmas tree format.
> +       char hs_type;
> +       int err;
> +
> +       if (likely(tlm->control != TLS_RECORD_TYPE_HANDSHAKE))
> +               return 0;
> +
> +       if (rxm->full_len < 1)
> +               return -EINVAL;
> +
> +       err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
> +       if (err < 0)
> +               return err;
> +
> +       if (hs_type == TLS_HANDSHAKE_KEYUPDATE) {
> +               struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
> +
> +               WRITE_ONCE(rx_ctx->key_update_pending, true);
> +       }
> +
> +       return 0;
> +}
> +
>   static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
>                               struct tls_decrypt_arg *darg)
>   {
> @@ -1739,6 +1769,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
>          rxm->full_len -= prot->overhead_size;
>          tls_advance_record_sn(sk, prot, &tls_ctx->rx);
> 
> +       err = tls_check_pending_rekey(tls_ctx, darg->skb);
I think you can directly return from here.

Best regards,
Parthiban V
> +       if (err < 0)
> +               return err;
> +
>          return 0;
>   }
> 
> @@ -2719,6 +2753,7 @@ int tls_set_sw_offload(struct sock *sk, int tx)
>                  crypto_info = &ctx->crypto_recv.info;
>                  cctx = &ctx->rx;
>                  aead = &sw_ctx_rx->aead_recv;
> +               sw_ctx_rx->key_update_pending = false;
>          }
> 
>          cipher_desc = get_cipher_desc(crypto_info->cipher_type);
> --
> 2.47.0
> 
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates
  2024-12-05 11:06     ` Sabrina Dubroca
@ 2024-12-06  0:34       ` Jakub Kicinski
  0 siblings, 0 replies; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-06  0:34 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Thu, 5 Dec 2024 12:06:37 +0100 Sabrina Dubroca wrote:
> > But a nit on the
> > phrasing - I'd say "poll() will not report any events from the socket
> > until.." ? Could be just me but sleep is a second order effect.  
> 
> Agree, thanks for the suggestion. Maybe actually "will not report read
> events"? Other events are unaffected by a pending rekey.

Good point, SG!

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending
  2024-12-04  3:47   ` Jakub Kicinski
@ 2024-12-10 16:16     ` Sabrina Dubroca
  2024-12-10 23:33       ` Jakub Kicinski
  0 siblings, 1 reply; 20+ messages in thread
From: Sabrina Dubroca @ 2024-12-10 16:16 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

2024-12-03, 19:47:01 -0800, Jakub Kicinski wrote:
> On Thu, 14 Nov 2024 16:50:48 +0100 Sabrina Dubroca wrote:
> > +static int tls_check_pending_rekey(struct tls_context *ctx, struct sk_buff *skb)
> > +{
> > +	const struct tls_msg *tlm = tls_msg(skb);
> > +	const struct strp_msg *rxm = strp_msg(skb);
> > +	char hs_type;
> > +	int err;
> > +
> > +	if (likely(tlm->control != TLS_RECORD_TYPE_HANDSHAKE))
> > +		return 0;
> > +
> > +	if (rxm->full_len < 1)
> > +		return -EINVAL;
> > +
> > +	err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	if (hs_type == TLS_HANDSHAKE_KEYUPDATE) {
> > +		struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
> > +
> > +		WRITE_ONCE(rx_ctx->key_update_pending, true);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
> >  			     struct tls_decrypt_arg *darg)
> >  {
> > @@ -1739,6 +1769,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
> >  	rxm->full_len -= prot->overhead_size;
> >  	tls_advance_record_sn(sk, prot, &tls_ctx->rx);
> >  
> > +	err = tls_check_pending_rekey(tls_ctx, darg->skb);
> > +	if (err < 0)
> > +		return err;
> 
> Sorry if I already asked this, is this 100% safe to error out from here
> after we decrypted the record? Normally once we successfully decrypted
> and pulled the message header / trailer we always call tls_rx_rec_done()

This is the same thing tls_rx_one_record does when tls_decrypt_sw
fails. Return <0 immediately, let the caller deal with the fallout. In
the case where tls_padding_length fails, tls_decrypt_sw has an extra
consume_skb though.

Returning an error here will make tls_rx_one_record() also return an
error, and when that happens we always call tls_err_abort(). It's a
big hammer, but it should be safe.

> The only reason the check_pending_rekey() can fail is if the message is
> mis-formatted, I wonder if we are better off ignoring mis-formatted
> rekeys? User space will see them and break the connection, anyway.
> Alternatively - we could add a selftest for this.


Going back to tls_check_pending_rekey():

> > +	if (rxm->full_len < 1)
> > +		return -EINVAL;

There's no real reason to fail here, we should probably just ignore
it. It's not a rekey, and it's not a valid handshake message, but one
could say that's not the kernel's problem. I'll make that return 0
unless you want to keep -EINVAL.

Hard to write a selftest for because we'd have to do a sendmsg with
len=0, or do the crypto in the selftest.

> > +	err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
> > +	if (err < 0)
> > +		return err;

This probably means that the skb we got from the parser was broken. If
we can't read 1B with full_len >= 1, something's wrong. Maybe worth a
DEBUG_NET_WARN_ON_ONCE?

> > +	if (hs_type == TLS_HANDSHAKE_KEYUPDATE) {

Here I don't actually check if it's a correct KeyUpdate message [1],
we pause decryption and let userspace decide what to do (probably
break the connection as you said).

[1] https://datatracker.ietf.org/doc/html/rfc8446#page-25
    https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.3

> > +		struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
> > +
> > +		WRITE_ONCE(rx_ctx->key_update_pending, true);
> > +	}
> > +
> > +	return 0;
> > +}

-- 
Sabrina

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending
  2024-12-10 16:16     ` Sabrina Dubroca
@ 2024-12-10 23:33       ` Jakub Kicinski
  0 siblings, 0 replies; 20+ messages in thread
From: Jakub Kicinski @ 2024-12-10 23:33 UTC (permalink / raw)
  To: Sabrina Dubroca
  Cc: netdev, Vadim Fedorenko, Frantisek Krenzelok, Kuniyuki Iwashima,
	Apoorv Kothari, Boris Pismenny, John Fastabend, Shuah Khan,
	linux-kselftest, Gal Pressman, Marcel Holtmann, Simon Horman

On Tue, 10 Dec 2024 17:16:09 +0100 Sabrina Dubroca wrote:
> > The only reason the check_pending_rekey() can fail is if the message is
> > mis-formatted, I wonder if we are better off ignoring mis-formatted
> > rekeys? User space will see them and break the connection, anyway.
> > Alternatively - we could add a selftest for this.  
> 
> Going back to tls_check_pending_rekey():
> 
> > > +	if (rxm->full_len < 1)
> > > +		return -EINVAL;  
> 
> There's no real reason to fail here, we should probably just ignore
> it. It's not a rekey, and it's not a valid handshake message, but one
> could say that's not the kernel's problem. I'll make that return 0
> unless you want to keep -EINVAL.

returning 0 SGTM

> Hard to write a selftest for because we'd have to do a sendmsg with
> len=0, or do the crypto in the selftest.
> 
> > > +	err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
> > > +	if (err < 0)
> > > +		return err;  
> 
> This probably means that the skb we got from the parser was broken. If
> we can't read 1B with full_len >= 1, something's wrong. Maybe worth a
> DEBUG_NET_WARN_ON_ONCE?

Also SG!

> > > +	if (hs_type == TLS_HANDSHAKE_KEYUPDATE) {  
> 
> Here I don't actually check if it's a correct KeyUpdate message [1],
> we pause decryption and let userspace decide what to do (probably
> break the connection as you said).

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2024-12-10 23:33 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-14 15:50 [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Sabrina Dubroca
2024-11-14 15:50 ` [PATCH net-next v4 1/6] tls: block decryption when a rekey is pending Sabrina Dubroca
2024-12-04  3:47   ` Jakub Kicinski
2024-12-10 16:16     ` Sabrina Dubroca
2024-12-10 23:33       ` Jakub Kicinski
2024-12-05 12:30   ` Parthiban.Veerasooran
2024-11-14 15:50 ` [PATCH net-next v4 2/6] tls: implement rekey for TLS1.3 Sabrina Dubroca
2024-12-04  3:58   ` Jakub Kicinski
2024-11-14 15:50 ` [PATCH net-next v4 3/6] tls: add counters for rekey Sabrina Dubroca
2024-12-04  3:54   ` Jakub Kicinski
2024-12-05 11:29     ` Sabrina Dubroca
2024-11-14 15:50 ` [PATCH net-next v4 4/6] docs: tls: document TLS1.3 key updates Sabrina Dubroca
2024-12-04  3:51   ` Jakub Kicinski
2024-12-05 11:06     ` Sabrina Dubroca
2024-12-06  0:34       ` Jakub Kicinski
2024-11-14 15:50 ` [PATCH net-next v4 5/6] selftests: tls: add key_generation argument to tls_crypto_info_init Sabrina Dubroca
2024-11-14 15:50 ` [PATCH net-next v4 6/6] selftests: tls: add rekey tests Sabrina Dubroca
2024-11-19  3:41 ` [PATCH net-next v4 0/6] tls: implement key updates for TLS1.3 Jakub Kicinski
2024-12-03 16:16   ` Sabrina Dubroca
2024-12-04  4:02     ` Jakub Kicinski

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).