Merge branch 'tls1.3-key-updates'

Sabrina Dubroca says:

====================
tls: implement key updates for TLS1.3

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 v4:
 - add counter for received KeyUpdate messages
 - improve wording in the documentation
 - improve handling of bogus messages when looking for KeyUpdate's
 - some coding style clean ups

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
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2024-12-16 12:47:30 +00:00
commit da3e3186ef
9 changed files with 682 additions and 61 deletions

View File

@ -200,6 +200,32 @@ 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() will
not report any read events from the socket 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. The ``TlsRxRekeyReceived`` statistic
counts KeyUpdate handshake messages that have been received.
Integrating in to userspace TLS library
---------------------------------------
@ -286,3 +312,13 @@ 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
- ``TlsRxRekeyReceived`` -
number of received KeyUpdate handshake messages, requiring userspace
to provide a new RX key

View File

@ -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;

View File

@ -358,6 +358,11 @@ 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_TLSRXREKEYRECEIVED, /* TlsRxRekeyReceived */
__LINUX_MIB_TLSMAX
};

View File

@ -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);

View File

@ -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;

View File

@ -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,21 @@ 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) {
TLS_INC_STATS(sock_net(sk), tx ? LINUX_MIB_TLSTXREKEYERROR
: LINUX_MIB_TLSRXREKEYERROR);
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 +658,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,11 +695,17 @@ 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);
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 {
@ -687,14 +715,21 @@ 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);
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;
}
tls_sw_strparser_arm(sk, ctx);
if (!update)
tls_sw_strparser_arm(sk, ctx);
}
if (tx)
@ -713,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;
}

View File

@ -22,6 +22,11 @@ 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_ITEM("TlsRxRekeyReceived", LINUX_MIB_TLSRXREKEYRECEIVED),
SNMP_MIB_SENTINEL
};

View File

@ -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,36 @@ tls_decrypt_device(struct sock *sk, struct msghdr *msg,
return 1;
}
static int tls_check_pending_rekey(struct sock *sk, struct tls_context *ctx,
struct sk_buff *skb)
{
const struct strp_msg *rxm = strp_msg(skb);
const struct tls_msg *tlm = tls_msg(skb);
char hs_type;
int err;
if (likely(tlm->control != TLS_RECORD_TYPE_HANDSHAKE))
return 0;
if (rxm->full_len < 1)
return 0;
err = skb_copy_bits(skb, rxm->offset, &hs_type, 1);
if (err < 0) {
DEBUG_NET_WARN_ON_ONCE(1);
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);
TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXREKEYRECEIVED);
}
return 0;
}
static int tls_rx_one_record(struct sock *sk, struct msghdr *msg,
struct tls_decrypt_arg *darg)
{
@ -1739,7 +1773,7 @@ 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);
return 0;
return tls_check_pending_rekey(sk, tls_ctx, darg->skb);
}
int decrypt_skb(struct sock *sk, struct scatterlist *sgout)
@ -2684,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;
@ -2701,44 +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;
}
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);
@ -2751,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);
@ -2772,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;

View File

@ -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);
@ -1668,6 +1670,464 @@ 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;
@ -1696,7 +2156,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 +2578,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 +2637,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);