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. =======================================================================
Note: in a future series, I'll clean up tls_set_sw_offload and eliminate the per-cipher copy-paste using tls_cipher_size_desc.
[1] https://www.rfc-editor.org/rfc/rfc8446#section-4.6.3 [2] https://gitlab.com/gnutls/gnutls/-/merge_requests/1625
Sabrina Dubroca (6): tls: remove tls_context argument from tls_set_sw_offload tls: block decryption when a rekey is pending tls: implement rekey for TLS1.3 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 | 21 ++ include/net/tls.h | 3 + net/tls/tls.h | 3 +- net/tls/tls_device.c | 2 +- net/tls/tls_main.c | 47 ++- net/tls/tls_sw.c | 184 +++++++++--- tools/testing/selftests/net/tls.c | 466 +++++++++++++++++++++++++++++- 7 files changed, 661 insertions(+), 65 deletions(-)
It's not really needed since we end up refetching it as tls_ctx. We can also remove the NULL check, since we have already dereferenced ctx in do_tls_setsockopt_conf.
v2: reverse xmas tree
Signed-off-by: Sabrina Dubroca sd@queasysnail.net --- net/tls/tls.h | 2 +- net/tls/tls_device.c | 2 +- net/tls/tls_main.c | 4 ++-- net/tls/tls_sw.c | 17 +++++++---------- 4 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h index 164d6a955e26..6916ff4fbde6 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -89,7 +89,7 @@ void update_sk_prot(struct sock *sk, struct tls_context *ctx); int wait_on_pending_writer(struct sock *sk, long *timeo); void tls_err_abort(struct sock *sk, int err);
-int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx); +int tls_set_sw_offload(struct sock *sk, int tx); 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 5df18f696d7f..cc1918a279d4 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1275,7 +1275,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, ctx, 0); + rc = tls_set_sw_offload(sk, 0); if (rc) goto release_ctx;
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 7dbb8cd8f809..ffc50454758e 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -799,7 +799,7 @@ 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, ctx, 1); + rc = tls_set_sw_offload(sk, 1); if (rc) goto err_crypto_info; TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXSW); @@ -813,7 +813,7 @@ 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, ctx, 0); + rc = tls_set_sw_offload(sk, 0); if (rc) goto err_crypto_info; TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW); diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 5c122d7bb784..2ca0eb90a2a5 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2581,25 +2581,22 @@ void tls_update_rx_zc_capable(struct tls_context *tls_ctx) tls_ctx->prot_info.version != TLS_1_3_VERSION; }
-int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) +int tls_set_sw_offload(struct sock *sk, int tx) { - struct tls_context *tls_ctx = tls_get_ctx(sk); - struct tls_prot_info *prot = &tls_ctx->prot_info; - struct tls_crypto_info *crypto_info; + u16 nonce_size, tag_size, iv_size, rec_seq_size, salt_size; + char *iv, *rec_seq, *key, *salt, *cipher_name; struct tls_sw_context_tx *sw_ctx_tx = NULL; struct tls_sw_context_rx *sw_ctx_rx = NULL; + struct tls_context *ctx = tls_get_ctx(sk); + struct tls_crypto_info *crypto_info; struct cipher_context *cctx; + struct tls_prot_info *prot; struct crypto_aead **aead; - u16 nonce_size, tag_size, iv_size, rec_seq_size, salt_size; struct crypto_tfm *tfm; - char *iv, *rec_seq, *key, *salt, *cipher_name; size_t keysize; int rc = 0;
- if (!ctx) { - rc = -EINVAL; - goto out; - } + prot = &ctx->prot_info;
if (tx) { if (!ctx->priv_ctx_tx) {
On Wed, Aug 09, 2023 at 02:58:50PM +0200, Sabrina Dubroca wrote:
It's not really needed since we end up refetching it as tls_ctx. We can also remove the NULL check, since we have already dereferenced ctx in do_tls_setsockopt_conf.
v2: reverse xmas tree
Signed-off-by: Sabrina Dubroca sd@queasysnail.net
Reviewed-by: Simon Horman horms@kernel.org
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.
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
Signed-off-by: Sabrina Dubroca sd@queasysnail.net --- include/net/tls.h | 3 +++ net/tls/tls_sw.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/include/net/tls.h b/include/net/tls.h index 06fca9160346..219a4f38c0e4 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -69,6 +69,8 @@ extern const struct tls_cipher_size_desc tls_cipher_size_desc[];
#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 MAX_IV_SIZE 16 @@ -141,6 +143,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 2ca0eb90a2a5..497f56c5f169 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1293,6 +1293,10 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, DEFINE_WAIT_FUNC(wait, woken_wake_function); 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)) { @@ -1689,6 +1693,33 @@ tls_decrypt_device(struct sock *sk, struct msghdr *msg, return 1; }
+static int tls_check_pending_rekey(struct sock *sk, struct sk_buff *skb) +{ + const struct tls_msg *tlm = tls_msg(skb); + const struct strp_msg *rxm = strp_msg(skb); + + if (tlm->control == TLS_RECORD_TYPE_HANDSHAKE) { + char hs_type; + int err; + + 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_context *ctx = tls_get_ctx(sk); + struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx; + + 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) { @@ -1708,6 +1739,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(sk, darg->skb); + if (err < 0) + return err; + return 0; }
@@ -2642,6 +2677,7 @@ int tls_set_sw_offload(struct sock *sk, int tx) skb_queue_head_init(&sw_ctx_rx->rx_list); skb_queue_head_init(&sw_ctx_rx->async_hold); aead = &sw_ctx_rx->aead_recv; + sw_ctx_rx->key_update_pending = false; }
switch (crypto_info->cipher_type) {
On Wed, Aug 09, 2023 at 02:58:51PM +0200, Sabrina Dubroca wrote:
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.
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
Signed-off-by: Sabrina Dubroca sd@queasysnail.net
include/net/tls.h | 3 +++ net/tls/tls_sw.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/include/net/tls.h b/include/net/tls.h index 06fca9160346..219a4f38c0e4 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -69,6 +69,8 @@ extern const struct tls_cipher_size_desc tls_cipher_size_desc[]; #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 MAX_IV_SIZE 16 @@ -141,6 +143,7 @@ struct tls_sw_context_rx { u8 async_capable:1; u8 zc_capable:1; u8 reader_contended:1;
- bool key_update_pending;
Hi Sabrina,
Would it make sense for this to be
u8 key_update_pending:1;
struct tls_strparser strp; diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 2ca0eb90a2a5..497f56c5f169 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1293,6 +1293,10 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, DEFINE_WAIT_FUNC(wait, woken_wake_function); 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)) { @@ -1689,6 +1693,33 @@ tls_decrypt_device(struct sock *sk, struct msghdr *msg, return 1; } +static int tls_check_pending_rekey(struct sock *sk, struct sk_buff *skb) +{
- const struct tls_msg *tlm = tls_msg(skb);
- const struct strp_msg *rxm = strp_msg(skb);
nit: reverse xmas tree
- if (tlm->control == TLS_RECORD_TYPE_HANDSHAKE) {
char hs_type;
int err;
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_context *ctx = tls_get_ctx(sk);
struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
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) { @@ -1708,6 +1739,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(sk, darg->skb);
- if (err < 0)
return err;
- return 0;
} @@ -2642,6 +2677,7 @@ int tls_set_sw_offload(struct sock *sk, int tx) skb_queue_head_init(&sw_ctx_rx->rx_list); skb_queue_head_init(&sw_ctx_rx->async_hold); aead = &sw_ctx_rx->aead_recv;
}sw_ctx_rx->key_update_pending = false;
switch (crypto_info->cipher_type) { -- 2.40.1
On Wed, 9 Aug 2023 14:58:51 +0200 Sabrina Dubroca wrote:
+static int tls_check_pending_rekey(struct sock *sk, struct sk_buff *skb) +{
- const struct tls_msg *tlm = tls_msg(skb);
- const struct strp_msg *rxm = strp_msg(skb);
- if (tlm->control == TLS_RECORD_TYPE_HANDSHAKE) {
unlikely()
does the nachine code look worse if we flip the condition and return early instead of indenting the entire function?
char hs_type;
int err;
I'd probably err on the side of declaring those on the outside, but if we don't we should move rxm in here, it's not needed outside. Either, or.
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_context *ctx = tls_get_ctx(sk);
feels a bit like we should just pass ctx rather than sk?
struct tls_sw_context_rx *rx_ctx = ctx->priv_ctx_rx;
rx_ctx->key_update_pending = true;
}
- }
- return 0;
+}
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
Signed-off-by: Sabrina Dubroca sd@queasysnail.net --- net/tls/tls.h | 3 +- net/tls/tls_device.c | 2 +- net/tls/tls_main.c | 47 +++++++++++---- net/tls/tls_sw.c | 137 +++++++++++++++++++++++++++++++------------ 4 files changed, 138 insertions(+), 51 deletions(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h index 6916ff4fbde6..fd80cfc7604a 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -89,7 +89,8 @@ void update_sk_prot(struct sock *sk, struct tls_context *ctx); int wait_on_pending_writer(struct sock *sk, long *timeo); void tls_err_abort(struct sock *sk, int err);
-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 cc1918a279d4..af269875a772 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1275,7 +1275,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 ffc50454758e..a5dfb562d99b 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -380,9 +380,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) @@ -696,9 +697,11 @@ static int tls_getsockopt(struct sock *sk, int level, int optname, 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); + union tls_crypto_context tmp = {}; + bool update = false; size_t optsize; int rc = 0; int conf; @@ -714,9 +717,17 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, alt_crypto_info = &ctx->crypto_send.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; + }
rc = copy_from_sockptr(crypto_info, optval, sizeof(*crypto_info)); if (rc) { @@ -731,8 +742,15 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, goto err_crypto_info; }
- /* Ensure that TLS version and ciphers are same in both directions */ - if (TLS_CRYPTO_INFO_READY(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; + goto err_crypto_info; + } + } else if (TLS_CRYPTO_INFO_READY(alt_crypto_info)) { + /* Ensure that TLS version and ciphers are same in both directions */ if (alt_crypto_info->version != crypto_info->version || alt_crypto_info->cipher_type != crypto_info->cipher_type) { rc = -EINVAL; @@ -799,7 +817,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); @@ -813,14 +832,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 497f56c5f169..bb5560fc8bcf 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2616,23 +2616,52 @@ void tls_update_rx_zc_capable(struct tls_context *tls_ctx) tls_ctx->prot_info.version != TLS_1_3_VERSION; }
-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) { u16 nonce_size, tag_size, iv_size, rec_seq_size, salt_size; + struct tls_crypto_info *crypto_info, *src_crypto_info; char *iv, *rec_seq, *key, *salt, *cipher_name; struct tls_sw_context_tx *sw_ctx_tx = NULL; struct tls_sw_context_rx *sw_ctx_rx = NULL; struct tls_context *ctx = tls_get_ctx(sk); - struct tls_crypto_info *crypto_info; + size_t keysize, crypto_info_size; struct cipher_context *cctx; struct tls_prot_info *prot; struct crypto_aead **aead; struct crypto_tfm *tfm; - size_t keysize; int rc = 0;
prot = &ctx->prot_info;
+ if (new_crypto_info) { + /* non-NULL new_crypto_info means rekey */ + src_crypto_info = new_crypto_info; + if (tx) { + sw_ctx_tx = ctx->priv_ctx_tx; + crypto_info = &ctx->crypto_send.info; + cctx = &ctx->tx; + aead = &sw_ctx_tx->aead_send; + sw_ctx_tx = NULL; + } else { + 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 = NULL; + } + goto skip_init; + } + if (tx) { if (!ctx->priv_ctx_tx) { sw_ctx_tx = kzalloc(sizeof(*sw_ctx_tx), GFP_KERNEL); @@ -2679,12 +2708,15 @@ int tls_set_sw_offload(struct sock *sk, int tx) aead = &sw_ctx_rx->aead_recv; sw_ctx_rx->key_update_pending = false; } + src_crypto_info = crypto_info;
+skip_init: switch (crypto_info->cipher_type) { case TLS_CIPHER_AES_GCM_128: { struct tls12_crypto_info_aes_gcm_128 *gcm_128_info;
- gcm_128_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_aes_gcm_128); + gcm_128_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_AES_GCM_128_IV_SIZE; tag_size = TLS_CIPHER_AES_GCM_128_TAG_SIZE; iv_size = TLS_CIPHER_AES_GCM_128_IV_SIZE; @@ -2701,7 +2733,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_AES_GCM_256: { struct tls12_crypto_info_aes_gcm_256 *gcm_256_info;
- gcm_256_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_aes_gcm_256); + gcm_256_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_AES_GCM_256_IV_SIZE; tag_size = TLS_CIPHER_AES_GCM_256_TAG_SIZE; iv_size = TLS_CIPHER_AES_GCM_256_IV_SIZE; @@ -2718,7 +2751,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_AES_CCM_128: { struct tls12_crypto_info_aes_ccm_128 *ccm_128_info;
- ccm_128_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_aes_ccm_128); + ccm_128_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_AES_CCM_128_IV_SIZE; tag_size = TLS_CIPHER_AES_CCM_128_TAG_SIZE; iv_size = TLS_CIPHER_AES_CCM_128_IV_SIZE; @@ -2735,7 +2769,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_CHACHA20_POLY1305: { struct tls12_crypto_info_chacha20_poly1305 *chacha20_poly1305_info;
- chacha20_poly1305_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_chacha20_poly1305); + chacha20_poly1305_info = (void *)src_crypto_info; nonce_size = 0; tag_size = TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE; iv_size = TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE; @@ -2752,7 +2787,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_SM4_GCM: { struct tls12_crypto_info_sm4_gcm *sm4_gcm_info;
- sm4_gcm_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_sm4_gcm); + sm4_gcm_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_SM4_GCM_IV_SIZE; tag_size = TLS_CIPHER_SM4_GCM_TAG_SIZE; iv_size = TLS_CIPHER_SM4_GCM_IV_SIZE; @@ -2769,7 +2805,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_SM4_CCM: { struct tls12_crypto_info_sm4_ccm *sm4_ccm_info;
- sm4_ccm_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_sm4_ccm); + sm4_ccm_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_SM4_CCM_IV_SIZE; tag_size = TLS_CIPHER_SM4_CCM_TAG_SIZE; iv_size = TLS_CIPHER_SM4_CCM_IV_SIZE; @@ -2786,7 +2823,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_ARIA_GCM_128: { struct tls12_crypto_info_aria_gcm_128 *aria_gcm_128_info;
- aria_gcm_128_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_aria_gcm_128); + aria_gcm_128_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_ARIA_GCM_128_IV_SIZE; tag_size = TLS_CIPHER_ARIA_GCM_128_TAG_SIZE; iv_size = TLS_CIPHER_ARIA_GCM_128_IV_SIZE; @@ -2803,7 +2841,8 @@ int tls_set_sw_offload(struct sock *sk, int tx) case TLS_CIPHER_ARIA_GCM_256: { struct tls12_crypto_info_aria_gcm_256 *gcm_256_info;
- gcm_256_info = (void *)crypto_info; + crypto_info_size = sizeof(struct tls12_crypto_info_aria_gcm_256); + gcm_256_info = (void *)src_crypto_info; nonce_size = TLS_CIPHER_ARIA_GCM_256_IV_SIZE; tag_size = TLS_CIPHER_ARIA_GCM_256_TAG_SIZE; iv_size = TLS_CIPHER_ARIA_GCM_256_IV_SIZE; @@ -2847,19 +2886,18 @@ int tls_set_sw_offload(struct sock *sk, int tx) prot->tag_size + prot->tail_size; prot->iv_size = iv_size; prot->salt_size = salt_size; - cctx->iv = kmalloc(iv_size + salt_size, GFP_KERNEL); - if (!cctx->iv) { - rc = -ENOMEM; - goto free_priv; - } - /* Note: 128 & 256 bit salt are the same size */ - prot->rec_seq_size = rec_seq_size; - memcpy(cctx->iv, salt, salt_size); - memcpy(cctx->iv + salt_size, iv, iv_size); - cctx->rec_seq = kmemdup(rec_seq, rec_seq_size, GFP_KERNEL); - if (!cctx->rec_seq) { - rc = -ENOMEM; - goto free_iv; + if (!new_crypto_info) { + cctx->iv = kmalloc(iv_size + salt_size, GFP_KERNEL); + if (!cctx->iv) { + rc = -ENOMEM; + goto free_priv; + } + + cctx->rec_seq = kmemdup(rec_seq, rec_seq_size, GFP_KERNEL); + if (!cctx->rec_seq) { + rc = -ENOMEM; + goto free_iv; + } }
if (!*aead) { @@ -2873,14 +2911,24 @@ 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, keysize); + if (rc) { + if (new_crypto_info) + goto out; + else + goto free_aead; + }
- if (rc) - 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) { tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv); @@ -2895,6 +2943,21 @@ int tls_set_sw_offload(struct sock *sk, int tx) goto free_aead; }
+ /* Note: 128 & 256 bit salt are the same size */ + prot->rec_seq_size = rec_seq_size; + memcpy(cctx->iv, salt, salt_size); + memcpy(cctx->iv + salt_size, iv, iv_size); + + if (new_crypto_info) { + memcpy(cctx->rec_seq, rec_seq, rec_seq_size); + + unsafe_memcpy(crypto_info, new_crypto_info, crypto_info_size, + /* size was checked in do_tls_getsockopt_conf */); + memzero_explicit(new_crypto_info, crypto_info_size); + if (!tx) + tls_finish_key_update(sk, ctx); + } + goto out;
free_aead: @@ -2907,12 +2970,14 @@ int tls_set_sw_offload(struct sock *sk, int tx) kfree(cctx->iv); cctx->iv = 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;
On Wed, Aug 09, 2023 at 02:58:52PM +0200, 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.
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
Signed-off-by: Sabrina Dubroca sd@queasysnail.net
...
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
...
@@ -2873,14 +2911,24 @@ 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, keysize);*/
- if (rc) {
if (new_crypto_info)
goto out;
else
goto free_aead;
- }
- if (rc)
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;
}
nit: no need for {} here.
- }
if (sw_ctx_rx) { tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv);
...
On Wed, 9 Aug 2023 14:58:52 +0200 Sabrina Dubroca wrote:
TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW); conf = TLS_SW;
Should we add a statistic for rekeying?
+int tls_set_sw_offload(struct sock *sk, int tx,
struct tls_crypto_info *new_crypto_info)
{
This function is already 300 LoC and we're making longer with a not-so-pretty goto skip;
Any way we can refactor it first? I think someone had a plan to at least make the per-algo stuff less verbose?
2023-08-11, 18:43:47 -0700, Jakub Kicinski wrote:
On Wed, 9 Aug 2023 14:58:52 +0200 Sabrina Dubroca wrote:
TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW); conf = TLS_SW;
Should we add a statistic for rekeying?
Hmpf, at least I shouldn't be incrementing the existing stats on every update, especially not TLSCURR* :/
I don't see much benefit in tracking succesful rekeys. Failed rekeys seem more interesting to me. What would we get from counting succesful rekeys?
+int tls_set_sw_offload(struct sock *sk, int tx,
struct tls_crypto_info *new_crypto_info)
{
This function is already 300 LoC and we're making longer with a not-so-pretty goto skip;
Any way we can refactor it first?
I guess all the actual init (alloc software context and set it up) could go in a separate function. I'll see if I can split a few more things out from the end of the function without making it too messy.
I think someone had a plan to at least make the per-algo stuff less verbose?
Yep, that's me. Basically, expanding the existing tls_cipher_size_desc to completely remove the switch (crypto_info->cipher_type) (from both setsockopt and getsockopt). I'll clean up and submit those changes, and then rebase the next version of this series on top. Dunno why I decided to focus on the rekey first.
On Mon, 14 Aug 2023 17:06:10 +0200 Sabrina Dubroca wrote:
2023-08-11, 18:43:47 -0700, Jakub Kicinski wrote:
On Wed, 9 Aug 2023 14:58:52 +0200 Sabrina Dubroca wrote:
TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW); conf = TLS_SW;
Should we add a statistic for rekeying?
Hmpf, at least I shouldn't be incrementing the existing stats on every update, especially not TLSCURR* :/
I don't see much benefit in tracking succesful rekeys. Failed rekeys seem more interesting to me. What would we get from counting succesful rekeys?
No huge benefit from counting rekeys, the main (only?) one I see is that when user reports issues we can see whether rekeys were involved (given that they are fairly rare). It could help narrow down triage.
2023-08-14, 08:21:28 -0700, Jakub Kicinski wrote:
On Mon, 14 Aug 2023 17:06:10 +0200 Sabrina Dubroca wrote:
2023-08-11, 18:43:47 -0700, Jakub Kicinski wrote:
On Wed, 9 Aug 2023 14:58:52 +0200 Sabrina Dubroca wrote:
TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXSW); conf = TLS_SW;
Should we add a statistic for rekeying?
Hmpf, at least I shouldn't be incrementing the existing stats on every update, especially not TLSCURR* :/
I don't see much benefit in tracking succesful rekeys. Failed rekeys seem more interesting to me. What would we get from counting succesful rekeys?
No huge benefit from counting rekeys, the main (only?) one I see is that when user reports issues we can see whether rekeys were involved (given that they are fairly rare). It could help narrow down triage.
Ok. So unless you objcet I'll add 4 more counters: {RX,TX}REKEY{OK,ERROR}.
And it probably shouldn't be "rekey" in case we decide to implement full 1.2 renegotiation (with cipher change) and use the same counter. Or 1.2 renegotiation without cipher change gets to use the rekey counters, and cipher change would get a new set of counters.
I could also just call them *UPDATE* but that might be a bit too vague.
v3: added following Jakub's comment
Signed-off-by: Sabrina Dubroca sd@queasysnail.net --- Documentation/networking/tls.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/Documentation/networking/tls.rst b/Documentation/networking/tls.rst index 658ed3a71e1b..ea6a22eafe2b 100644 --- a/Documentation/networking/tls.rst +++ b/Documentation/networking/tls.rst @@ -200,6 +200,27 @@ 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. + Integrating in to userspace TLS library ---------------------------------------
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
Signed-off-by: Sabrina Dubroca sd@queasysnail.net --- tools/testing/selftests/net/tls.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 4b63708c6a81..8d05748a0f57 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -41,9 +41,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: @@ -322,7 +324,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);
@@ -1092,7 +1094,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); @@ -1500,7 +1502,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); @@ -1922,7 +1924,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; @@ -1981,7 +1983,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);
v2: add rekey_fail test (reject changing the version/cipher) v3: add rekey_peek_splice (suggested by Jakub) add rekey+poll tests
Signed-off-by: Sabrina Dubroca sd@queasysnail.net --- tools/testing/selftests/net/tls.c | 450 ++++++++++++++++++++++++++++++ 1 file changed, 450 insertions(+)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 8d05748a0f57..d1a50995a662 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -1474,6 +1474,456 @@ TEST_F(tls, shutdown_reuse) EXPECT_EQ(errno, EISCONN); }
+#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]; + + 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); + + /* 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: 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(!_metadata->passed); + } +} + +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(!_metadata->passed); + } +} + FIXTURE(tls_err) { int fd, cfd;
On Wed, Aug 09, 2023 at 02:58:55PM +0200, Sabrina Dubroca wrote:
nit: Ideally a patch description would go here.
v2: add rekey_fail test (reject changing the version/cipher) v3: add rekey_peek_splice (suggested by Jakub) add rekey+poll tests
Signed-off-by: Sabrina Dubroca sd@queasysnail.net
...
2023-08-10, 19:58:48 +0200, Simon Horman wrote:
On Wed, Aug 09, 2023 at 02:58:55PM +0200, Sabrina Dubroca wrote:
nit: Ideally a patch description would go here.
I'm not opposed to adding one, but I couldn't come up with anything that the title doesn't already say :(
v2: add rekey_fail test (reject changing the version/cipher) v3: add rekey_peek_splice (suggested by Jakub) add rekey+poll tests
Signed-off-by: Sabrina Dubroca sd@queasysnail.net
...
linux-kselftest-mirror@lists.linaro.org