diff --git a/ChangeLog.d/ktls.txt b/ChangeLog.d/ktls.txt new file mode 100644 index 000000000000..1f7311f5bf91 --- /dev/null +++ b/ChangeLog.d/ktls.txt @@ -0,0 +1,20 @@ +Features + * Added `mbedtls_ssl_export_traffic_keys()` to derive and export traffic + keys and IVs for both TLS 1.2 and TLS 1.3 sessions. This enables + integration with Linux Kernel TLS (KTLS) by exposing the keying + material required for kernel-level record encryption and decryption. + * Added `mbedtls_ssl_get_sequence_numbers()` to retrieve the current + inbound and outbound record-layer sequence numbers, enabling + user-space KTLS implementations to construct per-record nonces. + +Changes + * Moved `struct mbedtls_ssl_key_set` to `ssl.h` to make it publicly + accessible for applications needing traffic key and IV information. + * Declared `ssl_tls13_get_cipher_key_info()` in `ssl_tls13_keys.h` + for use by external modules requiring cipher key and IV length + information. + * Added dedicated tests for `mbedtls_ssl_export_traffic_keys()`, + covering both TLS 1.2 and TLS 1.3 ciphersuites. + * Added a new `ssl/ktls.c` example demonstrating KTLS integration + with Mbed TLS, including traffic key export, sequence number + retrieval, and socket-level key installation. diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 02e527cdf5c7..706e68cd667b 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -690,6 +690,26 @@ union mbedtls_ssl_premaster_secret { #define MBEDTLS_SSL_KEEP_RANDBYTES #endif +/* cipher.h exports the maximum IV, key and block length from + * all ciphers enabled in the config, regardless of whether those + * ciphers are actually usable in SSL/TLS. Notably, XTS is enabled + * in the default configuration and uses 64 Byte keys, but it is + * not used for record protection in SSL/TLS. + * + * In order to prevent unnecessary inflation of key structures, + * we introduce SSL-specific variants of the max-{key,block,IV} + * macros here which are meant to only take those ciphers into + * account which can be negotiated in SSL/TLS. + * + * Since the current definitions of MBEDTLS_MAX_{KEY|BLOCK|IV}_LENGTH + * in cipher.h are rough overapproximations of the real maxima, here + * we content ourselves with replicating those overapproximations + * for the maximum block and IV length, and excluding XTS from the + * computation of the maximum key length. */ +#define MBEDTLS_SSL_MAX_BLOCK_LENGTH 16 +#define MBEDTLS_SSL_MAX_IV_LENGTH 16 +#define MBEDTLS_SSL_MAX_KEY_LENGTH 32 + #ifdef __cplusplus extern "C" { #endif @@ -860,6 +880,7 @@ typedef int mbedtls_ssl_get_timer_t(void *ctx); typedef struct mbedtls_ssl_session mbedtls_ssl_session; typedef struct mbedtls_ssl_context mbedtls_ssl_context; typedef struct mbedtls_ssl_config mbedtls_ssl_config; +typedef struct mbedtls_ssl_key_set mbedtls_ssl_key_set; /* Defined in library/ssl_misc.h */ typedef struct mbedtls_ssl_transform mbedtls_ssl_transform; @@ -1850,6 +1871,31 @@ struct mbedtls_ssl_context { mbedtls_ssl_user_data_t MBEDTLS_PRIVATE(user_data); }; +/** + * \brief The data structure holding the cryptographic material + * (key and IV) used for record protection in TLS. + */ +struct mbedtls_ssl_key_set +{ + /*! The key for client-to-server records. */ + unsigned char client_write_key[MBEDTLS_SSL_MAX_KEY_LENGTH]; + + /*! The key for server-to-client records. */ + unsigned char server_write_key[MBEDTLS_SSL_MAX_KEY_LENGTH]; + + /*! The IV for client-to-server records. */ + unsigned char client_write_iv[MBEDTLS_SSL_MAX_IV_LENGTH]; + + /*! The IV for server-to-client records. */ + unsigned char server_write_iv[MBEDTLS_SSL_MAX_IV_LENGTH]; + + /*! The key length, in bytes. */ + size_t key_len; + + /*! The IV length, in bytes. */ + size_t iv_len; +}; + /** * \brief Return the name of the ciphersuite associated with the * given ID @@ -5359,6 +5405,110 @@ int mbedtls_ssl_export_keying_material(mbedtls_ssl_context *ssl, const unsigned char *context, const size_t context_len, const int use_context); #endif + +/** + * \brief Derive and export traffic keys and IVs for custom record-layer + * handling. + * + * This function derives the symmetric traffic keys and initialization + * vectors (IVs) for both client and server sides, based on the TLS + * protocol version, negotiated ciphersuite, and provided secrets. + * Its primary purpose is to expose the keying material required to + * configure the Linux Kernel TLS (KTLS) interface, enabling zero-copy + * send/receive operations and kernel-level encryption. + * + * While it supports both TLS 1.2 and TLS 1.3, it is intended primarily + * for environments where TLS session state must be shared with the + * operating system, offloaded to a network stack, or used by a custom + * transport layer. + * + * \param ssl [in] The SSL context. + * \param keys [out] The key set structure to be filled with + * the derived traffic keys and IVs. + * \param cipher_type [out] The selected cipher type. + * \param secret [in] The input secret buffer. + * \param secret_len [in] The length of the secret buffer. + * \param randbytes [in] The concatenated client/server randoms + * (64 bytes total for TLS 1.2). + * \param tls_prf_type [in] The TLS PRF algorithm type to use for key + * derivation. + * + * \note **TLS 1.2 Random Ordering:** + * The PRF derivation in TLS 1.2 requires both + * random values. In this implementation, they are + * concatenated as \c server_random || + * client_random when passed to the exporter. The + * endpoint’s role (client or server) does not + * affect this order. + * + * \note **TLS 1.3 Traffic Secret Ordering:** + * TLS 1.3 does not use client/server randoms for + * key derivation. Instead, the exporter receives + * two distinct secrets — the client and server + * application traffic secrets — which are + * concatenated as \c client_secret || + * server_secret to derive the traffic keys. This + * concatenation ensures consistent key derivation + * regardless of endpoint role, matching the + * behavior expected by KTLS. The total secret + * length is the sum of both client and server + * traffic secret lengths. + * + * \note For AEAD ciphers in TLS 1.2, only the static IV + * is derived here. The per-record nonce must later + * be computed by combining the static IV with the + * record sequence number, as performed internally + * by AEAD modes. + * + * \warning The derived traffic keys and IVs are highly + * sensitive material. It is the caller’s + * responsibility to securely erase (\c zeroize) + * the contents of the \p keys structure once the + * keys are no longer needed. + * + * \return \c 0 if successful. + * \return An \c MBEDTLS_ERR_SSL_XXX error code on failure. + */ + +int mbedtls_ssl_export_traffic_keys(const mbedtls_ssl_context *ssl, + struct mbedtls_ssl_key_set *keys, + mbedtls_cipher_type_t *cipher_type, + const unsigned char *secret, + size_t secret_len, + const unsigned char *randbytes, + mbedtls_tls_prf_types tls_prf_type); + +/** + * \brief Retrieve the current inbound and outbound sequence numbers. + * + * This function provides direct access to the current record-layer + * sequence numbers for both inbound and outbound traffic. These + * sequence numbers are used in AEAD ciphers to compute the per-record + * nonce or implicit IV during encryption and decryption. + * + * \note This function does **not** copy or modify any + * state; it only exposes internal pointers. The + * returned pointers are owned by the SSL context + * and remain valid as long as the context itself + * remains valid. + * + * \warning The sequence numbers are internal to the TLS + * record layer. Modifying their contents directly + * will corrupt the session state and may result + * in data loss or connection failure. They are + * intended for read-only use (for example, to + * compute nonces when implementing custom KTLS + * send/recv paths). + * + * \param ssl [in] The SSL context. + * \param in_seq [out] The address of a pointer to receive the inbound + * sequence number. + * \param out_seq [out] The address of a pointer to receive the outbound + * sequence number. + */ +void mbedtls_ssl_get_sequence_numbers(const mbedtls_ssl_context *ssl, + const unsigned char **in_seq, + const unsigned char **out_seq); #ifdef __cplusplus } #endif diff --git a/library/ssl_misc.h b/library/ssl_misc.h index 0df7f96360c4..0cb8f28f2ca1 100644 --- a/library/ssl_misc.h +++ b/library/ssl_misc.h @@ -599,47 +599,6 @@ typedef int mbedtls_ssl_tls_prf_cb(const unsigned char *secret, size_t slen, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen); -/* cipher.h exports the maximum IV, key and block length from - * all ciphers enabled in the config, regardless of whether those - * ciphers are actually usable in SSL/TLS. Notably, XTS is enabled - * in the default configuration and uses 64 Byte keys, but it is - * not used for record protection in SSL/TLS. - * - * In order to prevent unnecessary inflation of key structures, - * we introduce SSL-specific variants of the max-{key,block,IV} - * macros here which are meant to only take those ciphers into - * account which can be negotiated in SSL/TLS. - * - * Since the current definitions of MBEDTLS_MAX_{KEY|BLOCK|IV}_LENGTH - * in cipher.h are rough overapproximations of the real maxima, here - * we content ourselves with replicating those overapproximations - * for the maximum block and IV length, and excluding XTS from the - * computation of the maximum key length. */ -#define MBEDTLS_SSL_MAX_BLOCK_LENGTH 16 -#define MBEDTLS_SSL_MAX_IV_LENGTH 16 -#define MBEDTLS_SSL_MAX_KEY_LENGTH 32 - -/** - * \brief The data structure holding the cryptographic material (key and IV) - * used for record protection in TLS 1.3. - */ -struct mbedtls_ssl_key_set { - /*! The key for client->server records. */ - unsigned char client_write_key[MBEDTLS_SSL_MAX_KEY_LENGTH]; - /*! The key for server->client records. */ - unsigned char server_write_key[MBEDTLS_SSL_MAX_KEY_LENGTH]; - /*! The IV for client->server records. */ - unsigned char client_write_iv[MBEDTLS_SSL_MAX_IV_LENGTH]; - /*! The IV for server->client records. */ - unsigned char server_write_iv[MBEDTLS_SSL_MAX_IV_LENGTH]; - - size_t key_len; /*!< The length of client_write_key and - * server_write_key, in Bytes. */ - size_t iv_len; /*!< The length of client_write_iv and - * server_write_iv, in Bytes. */ -}; -typedef struct mbedtls_ssl_key_set mbedtls_ssl_key_set; - typedef struct { unsigned char binder_key[MBEDTLS_TLS1_3_MD_MAX_SIZE]; unsigned char client_early_traffic_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 833af9f9735a..4bf39fc7064d 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -8998,6 +8998,163 @@ int mbedtls_ssl_export_keying_material(mbedtls_ssl_context *ssl, } } +/** + * \brief Derive and export traffic keys and IVs for custom record-layer handling. + */ +int mbedtls_ssl_export_traffic_keys(const mbedtls_ssl_context *ssl, + struct mbedtls_ssl_key_set *keys, + mbedtls_cipher_type_t *cipher_type, + const unsigned char *secret, + size_t secret_len, + const unsigned char *randbytes, + mbedtls_tls_prf_types tls_prf_type) +{ + /* During handshake callback only 'session_negotiate' is valid. + After mbedtls_ssl_handshake() completes, 'session' becomes active. */ + const mbedtls_ssl_session *session = ssl->session_negotiate ? ssl->session_negotiate : ssl->session; + + if (session == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + const int ciphersuite_id = mbedtls_ssl_session_get_ciphersuite_id(session); + + if (ciphersuite_id == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + const mbedtls_ssl_ciphersuite_t *ciphersuite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id); + + /* Map MbedTLS MAC type to PSA algorithm */ + const psa_algorithm_t mac_alg = mbedtls_md_psa_alg_from_type((mbedtls_md_type_t)ciphersuite->mac); + + if (mac_alg == PSA_ALG_NONE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("mbedtls_md_psa_alg_from_type for %u not found", (unsigned)ciphersuite->mac)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + const mbedtls_ssl_protocol_version version_number = mbedtls_ssl_get_version_number(ssl); + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *cipher_type = (mbedtls_cipher_type_t)ciphersuite->cipher; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (version_number == MBEDTLS_SSL_VERSION_TLS1_3) { + /* Determine key and IV lengths based on the ciphersuite */ + status = ssl_tls13_get_cipher_key_info(ciphersuite, &keys->key_len, &keys->iv_len); + + if (status == PSA_SUCCESS) { + const size_t hash_len = secret_len / 2; + const unsigned char *client_secret = secret; + const unsigned char *server_secret = secret + hash_len; + + /* Derive TLS 1.3 traffic keys */ + status = mbedtls_ssl_tls13_make_traffic_keys(mac_alg, client_secret, server_secret, hash_len, keys->key_len, + keys->iv_len, keys); + } + + return (status == PSA_SUCCESS) ? 0 : MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (version_number == MBEDTLS_SSL_VERSION_TLS1_2) { + unsigned char keyblk[256]; + psa_key_type_t key_type; + psa_algorithm_t alg; + + /* Resolve cipher properties to PSA equivalents */ + status = mbedtls_ssl_cipher_to_psa((mbedtls_cipher_type_t)ciphersuite->cipher, + ciphersuite->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16, &alg, &key_type, + &keys->key_len); + + if (status == PSA_SUCCESS) { + /* Get cipher mode */ + const mbedtls_ssl_mode_t ssl_mode = mbedtls_ssl_get_mode_from_ciphersuite( +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + session->encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + ciphersuite); + +#if defined(MBEDTLS_SSL_HAVE_AEAD) + if (ssl_mode == MBEDTLS_SSL_MODE_AEAD) { + /* + * AEAD ciphers require a fixed-size static IV; the full nonce is + * formed later using the record sequence number. + * + * We deliberately export only the static portion of the IV here, + * rather than constructing the full per-record nonce. The AEAD + * record layer computes the final nonce dynamically using the + * sequence number, ensuring uniqueness and preventing reuse. + * + * Embedding the full nonce at this stage would render the IV + * values meaningless—since they could not be recomputed once the + * sequence counter advances—and would deny the caller flexibility + * to derive the nonce at any time. + * + * By exposing only the static IV, the user remains free to + * calculate the final nonce as needed, typically by XORing or + * concatenating the sequence number as defined in RFC 5116 and + * RFC 8446 §5.3. + */ + keys->iv_len = (key_type == PSA_KEY_TYPE_CHACHA20) ? 12 : 4; + } else +#endif /* MBEDTLS_SSL_HAVE_AEAD */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + if (ssl_mode == MBEDTLS_SSL_MODE_STREAM || ssl_mode == MBEDTLS_SSL_MODE_CBC || + ssl_mode == MBEDTLS_SSL_MODE_CBC_ETM) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } else +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Expand master secret to key block */ + status = mbedtls_ssl_tls_prf(tls_prf_type, secret, secret_len, "key expansion", randbytes, 64, keyblk, + sizeof(keyblk)); + + if (status == PSA_SUCCESS) { + keys->key_len = PSA_BITS_TO_BYTES(keys->key_len); + + /* Partition expanded key block into secrets and IVs */ + const unsigned char *key1 = keyblk; + const unsigned char *key2 = key1 + keys->key_len; + const unsigned char *iv1 = key2 + keys->key_len; + const unsigned char *iv2 = iv1 + keys->iv_len; + + /* Copy derived values into output structure */ + memcpy(keys->client_write_key, key1, keys->key_len); + memcpy(keys->server_write_key, key2, keys->key_len); + memcpy(keys->client_write_iv, iv1, keys->iv_len); + memcpy(keys->server_write_iv, iv2, keys->iv_len); + } + + /* Clear sensitive material from stack */ + mbedtls_platform_zeroize(keyblk, sizeof(keyblk)); + } + + return (status == PSA_SUCCESS) ? 0 : MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} + +/** + * \brief Retrieve the current inbound and outbound sequence numbers. + */ +inline void mbedtls_ssl_get_sequence_numbers(const mbedtls_ssl_context *ssl, + const unsigned char **in, + const unsigned char **out) +{ + *in = ssl->in_ctr; + *out = ssl->cur_out_ctr; +} + #endif /* defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) */ #endif /* MBEDTLS_SSL_TLS_C */ diff --git a/library/ssl_tls13_keys.c b/library/ssl_tls13_keys.c index 865e02c2dc89..64e4ea195c9b 100644 --- a/library/ssl_tls13_keys.c +++ b/library/ssl_tls13_keys.c @@ -1042,7 +1042,7 @@ int mbedtls_ssl_tls13_populate_transform( } MBEDTLS_CHECK_RETURN_CRITICAL -static int ssl_tls13_get_cipher_key_info( +int ssl_tls13_get_cipher_key_info( const mbedtls_ssl_ciphersuite_t *ciphersuite_info, size_t *key_len, size_t *iv_len) { diff --git a/library/ssl_tls13_keys.h b/library/ssl_tls13_keys.h index 1509e9a4d494..fa2ac0c61ee6 100644 --- a/library/ssl_tls13_keys.h +++ b/library/ssl_tls13_keys.h @@ -484,6 +484,21 @@ int mbedtls_ssl_tls13_create_psk_binder(mbedtls_ssl_context *ssl, unsigned char const *transcript, unsigned char *result); +/** + * \brief Retrieve key and IV lengths for a given TLS 1.3 cipher suite. + * + * \param ciphersuite_info [in] The cipher suite information structure. + * \param key_len [out] The resulting key length in bytes. + * \param iv_len [out] The resulting IV length in bytes (always 12 for TLS 1.3 AEAD ciphers). + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int ssl_tls13_get_cipher_key_info( + const mbedtls_ssl_ciphersuite_t *ciphersuite_info, + size_t *key_len, size_t *iv_len); + /** * \bref Setup an SSL transform structure representing the * record protection mechanism used by TLS 1.3 diff --git a/programs/.gitignore b/programs/.gitignore index 004dcf22f73c..3d8664a69f4c 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -9,6 +9,7 @@ hash/md5sum hash/sha1sum hash/sha2sum ssl/dtls_client +ssl/ktls ssl/dtls_server ssl/mini_client ssl/ssl_client1 diff --git a/programs/Makefile b/programs/Makefile index 6c9d4d734258..24752b4c574d 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -45,6 +45,7 @@ APPS = \ ../tf-psa-crypto/programs/test/which_aes \ ssl/dtls_client \ ssl/dtls_server \ + ssl/ktls \ ssl/mini_client \ ssl/ssl_client1 \ ssl/ssl_client2 \ @@ -167,6 +168,10 @@ ssl/dtls_server$(EXEXT): ssl/dtls_server.c $(DEP) echo " CC ssl/dtls_server.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) ssl/dtls_server.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ +ssl/ktls$(EXEXT): ssl/ktls.c $(DEP) + echo " CC ssl/ktls.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) ssl/ktls.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + ssl/ssl_client1$(EXEXT): ssl/ssl_client1.c $(DEP) echo " CC ssl/ssl_client1.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) ssl/ssl_client1.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ diff --git a/programs/README.md b/programs/README.md index b9260bffe908..0215396549ba 100644 --- a/programs/README.md +++ b/programs/README.md @@ -9,6 +9,9 @@ This subdirectory mostly contains sample programs that illustrate specific featu * [`ssl/dtls_server.c`](ssl/dtls_server.c): a simple DTLS server program, which expects one datagram from the client and writes one datagram in response. This program supports DTLS cookies for hello verification. +* [`ssl/ktls.c`](ssl/ktls.c): a demonstration of Kernel TLS (KTLS) integration. +This program shows how to export TLS traffic keys from Mbed TLS and configure Kernel TLS for both transmit and receive paths, enabling zero-copy encryption and decryption at the kernel level. + * [`ssl/mini_client.c`](ssl/mini_client.c): a minimalistic SSL client, which sends a short string and disconnects. This is primarily intended as a benchmark; for a better example of a typical TLS client, see `ssl/ssl_client1.c`. * [`ssl/ssl_client1.c`](ssl/ssl_client1.c): a simple HTTPS client that sends a fixed request and displays the response. diff --git a/programs/ssl/CMakeLists.txt b/programs/ssl/CMakeLists.txt index 65f65b9bdd40..12d943202103 100644 --- a/programs/ssl/CMakeLists.txt +++ b/programs/ssl/CMakeLists.txt @@ -7,6 +7,7 @@ set(libs set(executables dtls_client dtls_server + ktls mini_client ssl_client1 ssl_client2 diff --git a/programs/ssl/ktls.c b/programs/ssl/ktls.c new file mode 100644 index 000000000000..d2953545c223 --- /dev/null +++ b/programs/ssl/ktls.c @@ -0,0 +1,1479 @@ +/* + * KTLS key export demonstration program + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +/* + * Usage: + * ktls_app ip=127.0.0.1 port=4433 crt_filename=/path_to_crt_dir/server.crt + * key_filename=/path_to_key_dir/server.key [key_pwd=secret] debug_level=level + * + * Notes: + * - `ip` : Server IP address (default: 127.0.0.1) + * - `port` : TCP port to bind (default: 4433) + * - `crt_filename`: Path to server certificate + * - `key_filename`: Path to server private key + * - `key_pwd` : Optional password for private key (omit or if unencrypted) + * - `debug_level` : 0 = disabled, 1-4 = increasing verbosity (default: 0) + */ + +/* + * NOTE: + * This example is **not a production web server**. Its purpose is purely + * educational: it demonstrates how to use Mbed TLS to derive and export + * TLS traffic keys and initialization vectors (IVs) for use with the + * Linux Kernel TLS (KTLS) interface, enabling zero-copy encryption/ + * decryption at the kernel level. + * + * The **core of this demonstration** is the `app_export_keys_cb` callback + * and the `enable_ktls` function. Everything else serves merely as + * supporting scaffolding—helpers to set up the socket, manage contexts, + * or handle errors. Focus on these two components for understanding + * the KTLS integration. + * + * Users should ensure their kernel supports KTLS before attempting to + * use this functionality. Basic KTLS (TLS 1.2 TX) is available from + * Linux 4.13+. + * + * To utilize full TLS 1.3 offload (both RX and TX), the system must + * run at least Linux kernel 5.20. + * + * You can verify support with: + * + * $ zgrep KTLS /proc/config.gz + * + * Or check if the TLS module is loaded: + * + * $ lsmod | grep tls + * + * If missing, load it with: + * + * # modprobe tls + * + * To load it automatically on boot, create a file inside + * /etc/modules-load.d/ with the content: + * + * tls + * + * This example is intentionally minimal and synchronous: it accepts + * connections, reads the request header, and sends a static response. + * Its goal is to illustrate **how to hand off encryption keys to KTLS**, + * not to serve arbitrary web traffic securely or efficiently. + */ + +#define DFL_IP "127.0.0.1" +#define DFL_CRT_FILENAME "./tls/server.crt" +#define DFL_KEY_FILENAME "./tls/server.key" +#define DFL_KEY_PWD NULL +#define DFL_PORT 4433 +#define DFL_DEBUG_LEVEL 0 + +#define BUFFER_SIZE (1024 * 1) + +#define GET_REQUEST "GET / HTTP/1.1\r\n\r\n" + +#define GET_RESPONSE \ + "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/plain\r\n" \ + "Content-Length: 21\r\n" \ + "\r\n" \ + "Welcome to Mbed TLS!\n" + +#ifndef __linux__ +#error "this example is for Linux only." +#endif + +#include "mbedtls/build_info.h" +#include "mbedtls/platform.h" + +#ifndef _WIN32 +#include // TCP_NODELAY TCP_ULP SOL_TLS +#endif + +#if !defined(__linux__) || !defined(TCP_ULP) || !defined(SOL_TLS) +int main(void) { + mbedtls_printf("KTLS program designed to run on Linux with KTLS.\n"); + mbedtls_exit(0); +} +#elif !defined(MBEDTLS_X509_CRT_PARSE_C) || (!defined(MBEDTLS_SSL_PROTO_TLS1_2) && !defined(MBEDTLS_SSL_PROTO_TLS1_3)) +int main(void) { + mbedtls_printf("MBEDTLS_X509_CRT_PARSE_C and/or MBEDTLS_SSL_PROTO_TLS1_2 and/or MBEDTLS_SSL_PROTO_TLS1_3 " + "not defined.\n"); + mbedtls_exit(0); +} +#else /*__linux__*/ + +#include "mbedtls/ssl.h" + +#if defined(MBEDTLS_DEBUG_C) +#include "mbedtls/debug.h" +#endif + +#include +#include +#include // inet_ntop inet_pton +#include // close syscall +#include // SIGPIPE +#include // SYS_futex +#include // strlen +#include // TLS_TX TLS_RX +#include // FUTEX_WAIT FUTEX_WAKE + +#define USAGE \ + "\n usage: ktls param=<>...\n" \ + "\n acceptable parameters:\n" \ + " ip=%%s default: 127.0.0.1\n" \ + " port=%%d default: 4433\n" \ + " crt_filename=%%s Server certificate filename\n" \ + " default: ./tls/server.crt\n" \ + " key_filename=%%s Server private key filename\n" \ + " default: ./tls/server.key\n" \ + " key_pwd=%%s Password for key specified by key_file argument\n" \ + " default: none\n" \ + " debug_level=%%d default: 0 (disabled)\n" \ + "\n" + +/* + * global options + */ +struct options { + const char *ip; /* IP address to bind the server to (default: "127.0.0.1") */ + const char *crt_filename; /* Server certificate filename (default: "server.crt") */ + const char *key_filename; /* Server private key filename (default: "server.key") */ + const char *key_pwd; /* Password for key specified by key_file argument if encrypted */ + int debug_level; /* Level of debugging (default: 0) */ + uint16_t port; /* TCP port on which the server listens (default: "4433") */ +} opt; + +typedef int app_send_fn_t(void *ctx, const unsigned char *buf, size_t len); +typedef int app_receive_fn_t(void *ctx, unsigned char *buf, size_t len); + +typedef struct { + unsigned char block[128]; + mbedtls_ssl_key_set key_set; + mbedtls_cipher_type_t cipher_type; + const mbedtls_ssl_context *ssl; + size_t secret_len; + mbedtls_ssl_protocol_version protocol_version; +} exported_keys_t; + +typedef struct { + mbedtls_ssl_config conf; + mbedtls_x509_crt crt; + mbedtls_pk_context key; + struct sockaddr_storage addr; + int fd; +} server_st_t; + +typedef struct { + mbedtls_ssl_config conf; + mbedtls_x509_crt crt; + mbedtls_pk_context key; + mbedtls_ssl_context ssl; + + app_send_fn_t *send_fn; + app_receive_fn_t *recv_fn; + + int fd; + unsigned id; +} client_st_t; + +typedef struct { + const mbedtls_ssl_config *conf; + app_send_fn_t *send_fn; + app_receive_fn_t *recv_fn; + + int client_fd; + unsigned client_id; +} request_args_t; + +typedef struct { + mbedtls_ssl_context *ssl; + int fd; +} send_recv_ctx_t; + +void app_export_keys_cb(void *keys_ctx, mbedtls_ssl_key_export_type type, const unsigned char *secret, + size_t secret_len, const unsigned char *client_random, const unsigned char *server_random, + mbedtls_tls_prf_types tls_prf_type) { + exported_keys_t *e_keys = (exported_keys_t *)keys_ctx; + + switch (type) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + case MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET: { + // TLS 1.2 master secret export: + // - Concatenate server_random || client_random in 'block' + // - The role (client/server) does not change this order + memcpy(e_keys->block, server_random, 32); + memcpy(e_keys->block + 32, client_random, 32); + + int ret = mbedtls_ssl_export_traffic_keys(e_keys->ssl, &e_keys->key_set, &e_keys->cipher_type, secret, + secret_len, e_keys->block, tls_prf_type); + + // Clear temporary memory to avoid leaking sensitive material + mbedtls_platform_zeroize(e_keys->block, 64); + + e_keys->protocol_version = MBEDTLS_SSL_VERSION_TLS1_2; + + if (ret != 0) { + e_keys->protocol_version = MBEDTLS_SSL_VERSION_UNKNOWN; + printf(" failed\n ! mbedtls_ssl_conf_own_cert returned %i.\n", ret); + fflush(stdout); + } + + break; + } +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + case MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_APPLICATION_TRAFFIC_SECRET: { + // TLS 1.3 client application traffic secret: + // - Concatenate client_secret || server_secret in 'block' + // - Order is always client then server, independent of app role + memcpy(e_keys->block, secret, secret_len); + + if (e_keys->secret_len == 0) { + // First part of the secret received; store length + e_keys->secret_len = secret_len; + } else { + // Second part received; combine and export traffic keys + e_keys->secret_len += secret_len; + int ret = mbedtls_ssl_export_traffic_keys(e_keys->ssl, &e_keys->key_set, &e_keys->cipher_type, + e_keys->block, e_keys->secret_len, NULL, tls_prf_type); + + // Clear temporary buffer + mbedtls_platform_zeroize(e_keys->block, e_keys->secret_len); + e_keys->protocol_version = MBEDTLS_SSL_VERSION_TLS1_3; + + if (ret != 0) { + e_keys->protocol_version = MBEDTLS_SSL_VERSION_UNKNOWN; + printf(" failed\n ! mbedtls_ssl_export_traffic_keys returned %i.\n", ret); + fflush(stdout); + } + } + + break; + } + + case MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_APPLICATION_TRAFFIC_SECRET: { + // TLS 1.3 server application traffic secret: + // - Append server_secret to 'block' (after client_secret) + // - Export keys once both client & server secrets are present + memcpy(e_keys->block + secret_len, secret, secret_len); + + if (e_keys->secret_len != 0) { + e_keys->secret_len += secret_len; + int ret = mbedtls_ssl_export_traffic_keys(e_keys->ssl, &e_keys->key_set, &e_keys->cipher_type, + e_keys->block, e_keys->secret_len, NULL, tls_prf_type); + + // Clear temporary buffer + mbedtls_platform_zeroize(e_keys->block, e_keys->secret_len); + e_keys->protocol_version = MBEDTLS_SSL_VERSION_TLS1_3; + + if (ret != 0) { + e_keys->protocol_version = MBEDTLS_SSL_VERSION_UNKNOWN; + printf(" failed\n ! mbedtls_ssl_export_traffic_keys returned %i.\n", ret); + fflush(stdout); + } + } else { + // First part (server secret) received; store length for later combination + e_keys->secret_len = secret_len; + } + + break; + } +#endif + default: { + // Other export types are ignored + } + } +} + +int app_recv(void *ctx, unsigned char *buffer, size_t length) { + send_recv_ctx_t *info = (send_recv_ctx_t *)ctx; + int fd = info->fd; + int read = 0; + + while (1) { + read = (int)recv(fd, buffer, length, MSG_NOSIGNAL); + + if ((read == -1) && (errno == EINTR)) { + continue; // retry + } + + break; + } + + return read; +} + +int app_send(void *ctx, const unsigned char *buffer, size_t length) { + send_recv_ctx_t *info = (send_recv_ctx_t *)ctx; + int fd = info->fd; + int sent = 0; + + while (1) { + sent = (int)send(fd, buffer, length, MSG_NOSIGNAL); + + if ((sent == -1) && (errno == EINTR)) { + continue; // retry + } + + break; + } + + return sent; +} + +int app_tls_recv(void *ctx, unsigned char *buffer, size_t length) { + send_recv_ctx_t *info = (send_recv_ctx_t *)ctx; + return mbedtls_ssl_read(info->ssl, buffer, length); +} + +int app_tls_send(void *ctx, const unsigned char *buffer, size_t length) { + send_recv_ctx_t *info = (send_recv_ctx_t *)ctx; + return mbedtls_ssl_write(info->ssl, buffer, length); +} + +int app_tls_recv_cb(void *ctx, unsigned char *buffer, size_t length) { + int fd = *((int *)ctx); + int read = 0; + + while (1) { + read = (int)recv(fd, buffer, length, MSG_NOSIGNAL); + + if (read == -1) { + if (errno == EINTR) { + continue; // retry + } + + printf("failed to read for fd %i. errno %i\n", fd, errno); + fflush(stdout); + } + + break; + } + + return read; +} + +int app_tls_send_cb(void *ctx, const unsigned char *buffer, size_t length) { + int fd = *((int *)ctx); + int sent = 0; + + while (1) { + sent = (int)send(fd, buffer, length, MSG_NOSIGNAL); + + if (sent == -1) { + if (errno == EINTR) { + continue; // retry + } + + printf("failed to send for fd %i. errno %i\n", fd, errno); + fflush(stdout); + } + + break; + } + + return sent; +} + +/** + * \brief Enable Kernel TLS (KTLS) on a given socket for an Mbed TLS session. + * + * This function configures both the transmit (TX) and receive (RX) channels + * for Kernel TLS, using keys exported from an established Mbed TLS session. + * + * \param endpoint Endpoint role — either MBEDTLS_SSL_IS_CLIENT or MBEDTLS_SSL_IS_SERVER. + * \param e_keys Pointer to an `exported_keys_t` structure containing: + * - `key_set`: AEAD keys for TX/RX, + * - `cipher_type`: cipher in use (AES-GCM, AES-CCM, etc.), + * - `ssl`: pointer to the source Mbed TLS context, + * - `secret_len`: length of exported secrets, + * - `protocol_version`: TLS version in use. + * \param fd File descriptor of the active TCP socket. + * + * \note TLS 1.2 vs TLS 1.3 IV handling for KTLS: + * In TLS 1.2 AEAD modes (e.g., AES-GCM, CCM), each record carries + * an explicit (per-record) IV in the record header. The kernel + * automatically constructs the full nonce internally, combining + * its own per-record sequence number with the static salt. + * Therefore, the IV field is zeroed and its length set to zero. + * + * In TLS 1.3, explicit IVs are removed. Each record IV is derived + * as: + * record_iv = static_iv XOR sequence_number + * Hence, the full static IV must be provided to the kernel during + * KTLS setup. + * + * \return + * - 1 if both TX and RX KTLS channels are successfully configured. + * - 0 if only TX channel is configured. + * - -1 on error (e.g., unsupported cipher, missing ULP support, etc.). + */ +int enable_ktls(int endpoint, exported_keys_t *e_keys, int fd) { + if (e_keys->protocol_version == MBEDTLS_SSL_VERSION_UNKNOWN) { + return -1; + } + + void *crypto_info_ptr; + socklen_t crypto_info_size; + + // Endpoint-specific identifiers + const char *name; + + unsigned char *tx_key; + unsigned char *tx_iv; + unsigned char *tx_salt; + + unsigned char *rx_key; + unsigned char *rx_iv; + unsigned char *rx_salt; + + unsigned char *info_iv; + unsigned char *info_key; + unsigned char *info_salt; + unsigned char *info_rec_seq; + + // Lengths of IV, key, salt, and record sequence for the selected cipher + size_t iv_len; + size_t key_len; + size_t salt_len; + size_t seq_len; + + // Select role-specific key material for TX and RX + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + name = "server"; + tx_key = e_keys->key_set.server_write_key; + tx_iv = e_keys->key_set.server_write_iv; + tx_salt = e_keys->key_set.server_write_iv; + rx_key = e_keys->key_set.client_write_key; + rx_iv = e_keys->key_set.client_write_iv; + rx_salt = e_keys->key_set.client_write_iv; + } else { + name = "client"; + tx_key = e_keys->key_set.client_write_key; + tx_iv = e_keys->key_set.client_write_iv; + tx_salt = e_keys->key_set.client_write_iv; + rx_key = e_keys->key_set.server_write_key; + rx_iv = e_keys->key_set.server_write_iv; + rx_salt = e_keys->key_set.server_write_iv; + } + + // Attempt to enable KTLS user-level protocol (TCP_ULP) + if (setsockopt(fd, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) == 0) { + /* --- Cipher selection and crypto_info structure preparation --- */ + switch (e_keys->cipher_type) { + case MBEDTLS_CIPHER_AES_128_GCM: { + struct tls12_crypto_info_aes_gcm_128 crypto_info; + crypto_info_ptr = &crypto_info; + crypto_info_size = sizeof(crypto_info); + crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; + + // Set protocol version-specific fields + if (e_keys->protocol_version == MBEDTLS_SSL_VERSION_TLS1_3) { + crypto_info.info.version = TLS_1_3_VERSION; + iv_len = TLS_CIPHER_AES_GCM_128_IV_SIZE; + } else { + crypto_info.info.version = TLS_1_2_VERSION; + iv_len = 0; + memset(crypto_info.iv, 0, TLS_CIPHER_AES_GCM_128_IV_SIZE); + } + + key_len = TLS_CIPHER_AES_GCM_128_KEY_SIZE; + salt_len = TLS_CIPHER_AES_GCM_128_SALT_SIZE; + seq_len = TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE; + + tx_iv += salt_len; + rx_iv += salt_len; + + info_iv = crypto_info.iv; + info_key = crypto_info.key; + info_salt = crypto_info.salt; + info_rec_seq = crypto_info.rec_seq; + + break; + } + + case MBEDTLS_CIPHER_AES_256_GCM: { + struct tls12_crypto_info_aes_gcm_256 crypto_info; + crypto_info_ptr = &crypto_info; + crypto_info_size = sizeof(crypto_info); + crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_256; + + // Set protocol version-specific fields + if (e_keys->protocol_version == MBEDTLS_SSL_VERSION_TLS1_3) { + crypto_info.info.version = TLS_1_3_VERSION; + iv_len = TLS_CIPHER_AES_GCM_256_IV_SIZE; + } else { + crypto_info.info.version = TLS_1_2_VERSION; + iv_len = 0; + memset(crypto_info.iv, 0, TLS_CIPHER_AES_GCM_256_IV_SIZE); + } + + key_len = TLS_CIPHER_AES_GCM_256_KEY_SIZE; + salt_len = TLS_CIPHER_AES_GCM_256_SALT_SIZE; + seq_len = TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE; + + tx_iv += salt_len; + rx_iv += salt_len; + + info_iv = crypto_info.iv; + info_key = crypto_info.key; + info_salt = crypto_info.salt; + info_rec_seq = crypto_info.rec_seq; + + break; + } + + case MBEDTLS_CIPHER_CHACHA20_POLY1305: { + struct tls12_crypto_info_chacha20_poly1305 crypto_info; + crypto_info_ptr = &crypto_info; + crypto_info_size = sizeof(crypto_info); + crypto_info.info.cipher_type = TLS_CIPHER_CHACHA20_POLY1305; + + crypto_info.info.version = + ((e_keys->protocol_version == MBEDTLS_SSL_VERSION_TLS1_3) ? TLS_1_3_VERSION : TLS_1_2_VERSION); + + iv_len = TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE; + key_len = TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE; + salt_len = TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE; + seq_len = TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE; + + info_iv = crypto_info.iv; + info_key = crypto_info.key; + info_salt = crypto_info.salt; + info_rec_seq = crypto_info.rec_seq; + + break; + } + + // TLS 1.2 only + case MBEDTLS_CIPHER_ARIA_128_GCM: { + struct tls12_crypto_info_aria_gcm_128 crypto_info; + crypto_info_ptr = &crypto_info; + crypto_info_size = sizeof(crypto_info); + crypto_info.info.cipher_type = TLS_CIPHER_ARIA_GCM_128; + + crypto_info.info.version = TLS_1_2_VERSION; + + iv_len = 0; + memset(crypto_info.iv, 0, TLS_CIPHER_ARIA_GCM_128_IV_SIZE); + + key_len = TLS_CIPHER_ARIA_GCM_128_KEY_SIZE; + salt_len = TLS_CIPHER_ARIA_GCM_128_SALT_SIZE; + seq_len = TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE; + + info_iv = crypto_info.iv; + info_key = crypto_info.key; + info_salt = crypto_info.salt; + info_rec_seq = crypto_info.rec_seq; + + break; + } + + case MBEDTLS_CIPHER_ARIA_256_GCM: { + struct tls12_crypto_info_aria_gcm_256 crypto_info; + crypto_info_ptr = &crypto_info; + crypto_info_size = sizeof(crypto_info); + crypto_info.info.cipher_type = TLS_CIPHER_ARIA_GCM_256; + + crypto_info.info.version = TLS_1_2_VERSION; + + iv_len = 0; + memset(crypto_info.iv, 0, TLS_CIPHER_ARIA_GCM_256_IV_SIZE); + + key_len = TLS_CIPHER_ARIA_GCM_256_KEY_SIZE; + salt_len = TLS_CIPHER_ARIA_GCM_256_SALT_SIZE; + seq_len = TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE; + + info_iv = crypto_info.iv; + info_key = crypto_info.key; + info_salt = crypto_info.salt; + info_rec_seq = crypto_info.rec_seq; + + break; + } + + case MBEDTLS_CIPHER_AES_128_CCM: { + struct tls12_crypto_info_aes_ccm_128 crypto_info; + crypto_info_ptr = &crypto_info; + crypto_info_size = sizeof(crypto_info); + crypto_info.info.cipher_type = TLS_CIPHER_AES_CCM_128; + + crypto_info.info.version = TLS_1_2_VERSION; + + iv_len = 0; + memset(crypto_info.iv, 0, TLS_CIPHER_AES_CCM_128_IV_SIZE); + + key_len = TLS_CIPHER_AES_CCM_128_KEY_SIZE; + salt_len = TLS_CIPHER_AES_CCM_128_SALT_SIZE; + seq_len = TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE; + + info_iv = crypto_info.iv; + info_key = crypto_info.key; + info_salt = crypto_info.salt; + info_rec_seq = crypto_info.rec_seq; + + break; + } + + default: { + // Unsupported cipher type; no KTLS configuration + printf("[%s] failed to configure KTLS for fd %d: cipher not supported.\n", name, fd); + fflush(stdout); + return -1; + } + } + + const unsigned char *in_seq; + const unsigned char *out_seq; + + // Retrieve current TLS record sequence numbers. + mbedtls_ssl_get_sequence_numbers(e_keys->ssl, &in_seq, &out_seq); + + // Configure transmit (TX) channel + memcpy(info_iv, tx_iv, iv_len); + memcpy(info_key, tx_key, key_len); + memcpy(info_salt, tx_salt, salt_len); + memcpy(info_rec_seq, out_seq, seq_len); + + int res = 0; + + if (setsockopt(fd, SOL_TLS, TLS_TX, crypto_info_ptr, crypto_info_size) == 0) { + printf("[%s] KTLS transmit channel successfully configured for fd %i.\n", name, fd); + + // Configure receive (RX) channel + memcpy(info_iv, rx_iv, iv_len); + memcpy(info_key, rx_key, key_len); + memcpy(info_salt, rx_salt, salt_len); + memcpy(info_rec_seq, in_seq, seq_len); + + if (setsockopt(fd, SOL_TLS, TLS_RX, crypto_info_ptr, crypto_info_size) == 0) { + printf("[%s] KTLS receive channel successfully configured for fd %i.\n", name, fd); + ++res; + } else { + printf("[%s] Failed to configure KTLS receive channel for fd %i.\n", name, fd); + } + } else { + printf("[%s] Failed to configure KTLS transmit channel for fd %i.\n", name, fd); + } + + mbedtls_platform_zeroize(crypto_info_ptr, crypto_info_size); + return res; + } + + fprintf(stderr, + "[error] Failed to enable Kernel TLS (TCP_ULP) on socket %d: %s (errno=%i). " + "Ensure the 'tls' kernel module is loaded (try: sudo modprobe tls).\n", + fd, strerror(errno), errno); + + fflush(stdout); + return -1; +} + +void *process_requests(void *arg) { + unsigned char buffer[BUFFER_SIZE]; + request_args_t *request = (request_args_t *)arg; + mbedtls_ssl_context client_ssl; + exported_keys_t e_keys; + send_recv_ctx_t send_recv_ctx; + const size_t response_len = strlen(GET_RESPONSE); + + mbedtls_ssl_init(&client_ssl); + + int ret = mbedtls_ssl_setup(&client_ssl, request->conf); + + if (ret == 0) { + memset(&e_keys, 0, sizeof(exported_keys_t)); + e_keys.ssl = &client_ssl; + + mbedtls_ssl_set_bio(&client_ssl, &request->client_fd, app_tls_send_cb, app_tls_recv_cb, NULL); + mbedtls_ssl_set_export_keys_cb(&client_ssl, app_export_keys_cb, &e_keys); + + ret = mbedtls_ssl_handshake(&client_ssl); + + if (ret == 0) { + request->recv_fn = app_tls_recv; + request->send_fn = app_tls_send; + send_recv_ctx.ssl = &client_ssl; + send_recv_ctx.fd = request->client_fd; + + printf("[server] negotiated ciphersuite for client %u: %s.\n", request->client_id, + mbedtls_ssl_get_ciphersuite(&client_ssl)); + fflush(stdout); + + ret = enable_ktls(MBEDTLS_SSL_IS_SERVER, &e_keys, request->client_fd); + mbedtls_platform_zeroize(&e_keys, sizeof(e_keys)); + + if (ret >= 0) { + request->send_fn = app_send; + + if (ret == 1) { + request->recv_fn = app_recv; + } + } + + unsigned requests = 0; + + do { + int read = 0; + int valid_request = 0; + + do { + read = request->recv_fn(&send_recv_ctx, buffer, BUFFER_SIZE); + + if (read > 0) { + ++requests; + printf("[server] read %i bytes from client %u (request: %u).\n", read, request->client_id, + requests); + fflush(stdout); + } else { + if (read == 0) { + printf("[server] client %u disconnected.\n", request->client_id); + fflush(stdout); + } else if (errno == EAGAIN) { + printf("[server] Client %u timed out due to inactivity.\n", request->client_id); + fflush(stdout); + } else { + printf("[server] failed to recv from client %u.\n", request->client_id); + fflush(stdout); + } + + break; + } + + int offset = 0; + + while (offset < read) { + // Detect end-of-request markers: \n\n or \r\n\r\n + if (buffer[offset] == '\n' && (offset + 1 < read)) { + if (buffer[offset + 1] == '\r') { + if ((offset + 2 < read) && buffer[offset + 2] == '\n') { + valid_request = 1; + break; + } + } else if (buffer[offset + 1] == '\n') { + valid_request = 1; + break; + } + } + ++offset; + } + } while (valid_request == 0); + + if (valid_request == 1) { + const int sent = + request->send_fn(&send_recv_ctx, (const unsigned char *)GET_RESPONSE, response_len); + + if (sent > 0) { + printf("[server] sent %i bytes to client %u (response: %u).\n", sent, request->client_id, + requests); + fflush(stdout); + } else { + printf("[server] failed to send to client %u.\n", request->client_id); + fflush(stdout); + } + + continue; + } + break; + } while (1); + } else { + printf("server -> client %u handshake failed. error code: %i.\n", request->client_id, ret); + fflush(stdout); + } + } else { + printf(" failed\n ! mbedtls_ssl_setup returned %i.\n", ret); + fflush(stdout); + } + + close(request->client_fd); + mbedtls_ssl_free(&client_ssl); + + free(request); + + return NULL; +} + +void cleanup_server_ssl(server_st_t *server_st) { + mbedtls_ssl_config_free(&server_st->conf); + mbedtls_x509_crt_free(&server_st->crt); + mbedtls_pk_free(&server_st->key); +} + +void cleanup_client_ssl(client_st_t *client_st) { + mbedtls_ssl_config_free(&client_st->conf); + mbedtls_ssl_free(&client_st->ssl); +} + +void exit_server(server_st_t *server_st, int ret) { + fflush(stdout); + cleanup_server_ssl(server_st); + mbedtls_psa_crypto_free(); + exit(ret); +} + +void fatal_error(void *arg, const char *msg) { + perror(msg); + exit_server((server_st_t *)arg, EXIT_FAILURE); +} + +pthread_t app_create_thread(void *(*fun)(void *), void *arg) { + pthread_t thread = 0; + pthread_attr_t attr; + pthread_attr_init(&attr); + + const int ret = pthread_create(&thread, &attr, fun, arg); + pthread_attr_destroy(&attr); + + if (ret != 0) { + fatal_error(arg, "pthread_create"); + } + + return thread; +} + +void *server_accept_task(void *arg) { + struct timeval timeout; + struct sockaddr_storage client_addr; + struct sockaddr *client_addr_ptr = (struct sockaddr *)&client_addr; + server_st_t *server_st = (server_st_t *)arg; + const struct sockaddr_in *addr = (struct sockaddr_in *)&server_st->addr; + static socklen_t client_addr_length = sizeof(struct sockaddr_storage); + char ip[64]; + unsigned client_id = 0; + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + if (addr->sin_family == AF_INET) { + inet_ntop(AF_INET, &addr->sin_addr, ip, 64); + printf("Listening on %s:%i\n", ip, opt.port); + } else { + const struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server_st->addr; + inet_ntop(AF_INET6, &addr6->sin6_addr, ip, 64); + printf("Listening on [%s]:%i\n", ip, opt.port); + } + + fflush(stdout); + + do { + const int client_fd = accept(server_st->fd, client_addr_ptr, &client_addr_length); + + if (client_fd != -1) { + ++client_id; + printf("[server] accepted connection (fd: %i, client: %u).\n", client_fd, client_id); + fflush(stdout); + + setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(client_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + request_args_t *request = (request_args_t *)malloc(sizeof(request_args_t)); + request->conf = &server_st->conf; + request->client_fd = client_fd; + request->client_id = client_id; + + pthread_t p_id = app_create_thread(process_requests, request); + + if (p_id != 0) { + pthread_detach(p_id); + } else { + free(request); + } + } else { + break; + } + } while (1); + + printf("server shutdown.\n"); + fflush(stdout); + + close(server_st->fd); + cleanup_server_ssl(server_st); + + return NULL; +} + +void init_server_ssl(server_st_t *server_st) { + mbedtls_ssl_config_init(&server_st->conf); + mbedtls_x509_crt_init(&server_st->crt); + mbedtls_pk_init(&server_st->key); +} + +void init_client_ssl(client_st_t *client_st) { + mbedtls_ssl_config_init(&client_st->conf); + mbedtls_ssl_init(&client_st->ssl); +} + +void configure_server_cert(server_st_t *server_st) { + /* Load certificate chain (PEM) */ + + int ret = mbedtls_x509_crt_parse_file(&server_st->crt, opt.crt_filename); + + if (ret != 0) { + printf(" failed\n ! mbedtls_x509_crt_parse_file returned %i.\n", ret); + exit_server(server_st, ret); + } + + ret = mbedtls_pk_parse_keyfile(&server_st->key, opt.key_filename, opt.key_pwd); + + if (ret != 0) { + printf(" failed\n ! mbedtls_pk_parse_keyfile returned %i.\n", ret); + exit_server(server_st, ret); + } + + ret = mbedtls_ssl_conf_own_cert(&server_st->conf, &server_st->crt, &server_st->key); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_conf_own_cert returned %i.\n", ret); + exit_server(server_st, ret); + } +} + +#if defined(MBEDTLS_DEBUG_C) +pthread_mutex_t debug_mutex; + +void server_debug_cb(void *ctx, int level, const char *file, int line, const char *str) { + ((void)level); + long int thread_id = (long int)pthread_self(); + + pthread_mutex_lock(&debug_mutex); + + ((void)level); + mbedtls_fprintf((FILE *)ctx, "[server debug]: %s:%04d: [ #%ld ] %s", file, line, thread_id, str); + fflush((FILE *)ctx); + + pthread_mutex_unlock(&debug_mutex); +} + +void client_debug_cb(void *ctx, int level, const char *file, int line, const char *str) { + ((void)level); + long int thread_id = (long int)pthread_self(); + + pthread_mutex_lock(&debug_mutex); + + ((void)level); + mbedtls_fprintf((FILE *)ctx, "[client debug]: %s:%04d: [ #%ld ] %s", file, line, thread_id, str); + fflush((FILE *)ctx); + + pthread_mutex_unlock(&debug_mutex); +} +#endif + +void setup_server_ssl(server_st_t *server_st) { + int ret; + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_ssl_conf_dbg(&server_st->conf, server_debug_cb, stdout); +#endif + + ret = mbedtls_ssl_config_defaults(&server_st->conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_config_defaults returned %i.\n", ret); + exit_server(server_st, ret); + } + + mbedtls_ssl_conf_preference_order(&server_st->conf, MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_CLIENT); + mbedtls_ssl_conf_renegotiation(&server_st->conf, MBEDTLS_SSL_RENEGOTIATION_DISABLED); + mbedtls_ssl_conf_session_tickets(&server_st->conf, MBEDTLS_SSL_SESSION_TICKETS_DISABLED); + + configure_server_cert(server_st); + mbedtls_ssl_conf_min_tls_version(&server_st->conf, MBEDTLS_SSL_VERSION_TLS1_2); +} + +void setup_client_ssl(client_st_t *client_st) { + int ret; + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_ssl_conf_dbg(&client_st->conf, client_debug_cb, stdout); +#endif + + ret = mbedtls_ssl_config_defaults(&client_st->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_config_defaults for client returned %i.\n", ret); + fflush(stdout); + return; + } + + mbedtls_ssl_conf_authmode(&client_st->conf, MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_renegotiation(&client_st->conf, MBEDTLS_SSL_RENEGOTIATION_DISABLED); + mbedtls_ssl_conf_session_tickets(&client_st->conf, MBEDTLS_SSL_SESSION_TICKETS_DISABLED); +} + +pthread_t run_server(server_st_t *server_st, int *started) { + const int enable = 1; + int ret; + + int is_v6 = 0; + const char *ip = opt.ip; + + if (ip) { + while (*ip) { + if (*ip == ':') { + is_v6 = 1; + break; + } + ++ip; + } + } + + if (is_v6 == 1) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&server_st->addr; + addr->sin6_family = AF_INET6; + + addr->sin6_port = htons(opt.port); + ret = inet_pton(AF_INET6, opt.ip, &addr->sin6_addr); + } else { + struct sockaddr_in *addr = (struct sockaddr_in *)&server_st->addr; + addr->sin_family = AF_INET; + + addr->sin_port = htons(opt.port); + ret = inet_pton(AF_INET, opt.ip, &addr->sin_addr); + } + + if (ret != 1) { + fatal_error(server_st, " incorrect ip address used"); + } + + server_st->fd = socket(is_v6 == 1 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + + if (server_st->fd == -1) { + fatal_error(server_st, "run_server socket"); + } + + ret = setsockopt(server_st->fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); + + if (ret == -1) { + close(server_st->fd); + fatal_error(server_st, "setsockopt(SO_REUSEADDR)"); + } + + setsockopt(server_st->fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); + + ret = bind(server_st->fd, (struct sockaddr *)&server_st->addr, sizeof(server_st->addr)); + + if (ret == -1) { + close(server_st->fd); + fatal_error(server_st, "bind"); + } + + ret = listen(server_st->fd, 8); // no need for bigger queue here + + if (ret == -1) { + close(server_st->fd); + fatal_error(server_st, "listen"); + } + + // Wake the waiting client thread once the server socket is listening + __atomic_store_n(started, 1, __ATOMIC_RELEASE); + long syscall_ret = syscall(SYS_futex, started, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0); + + if (syscall_ret == -1) { + close(server_st->fd); + fatal_error(server_st, "SYS_futex"); + } + + return app_create_thread(server_accept_task, server_st); +} + +void client_send_and_receive(client_st_t *client_st) { + unsigned char buffer[BUFFER_SIZE]; + exported_keys_t e_keys; + send_recv_ctx_t send_recv_ctx; + const size_t response_len = strlen(GET_RESPONSE); + const size_t request_len = strlen(GET_REQUEST); + + memset(&e_keys, 0, sizeof(exported_keys_t)); + e_keys.ssl = &client_st->ssl; + + mbedtls_ssl_set_bio(&client_st->ssl, &client_st->fd, app_tls_send_cb, app_tls_recv_cb, NULL); + // Register key export callback to demonstrate KTLS key extraction + mbedtls_ssl_set_export_keys_cb(&client_st->ssl, app_export_keys_cb, &e_keys); + + int ret = mbedtls_ssl_handshake(&client_st->ssl); + + if (ret == 0) { + const unsigned max_requests_count = 5; + unsigned requests = 0; + + client_st->recv_fn = app_tls_recv; + client_st->send_fn = app_tls_send; + send_recv_ctx.ssl = &client_st->ssl; + send_recv_ctx.fd = client_st->fd; + + while (requests < max_requests_count) { + int response_length = response_len; + + ++requests; + + const int sent = client_st->send_fn(&send_recv_ctx, (const unsigned char *)GET_REQUEST, request_len); + + if (sent > 0) { + printf("[client %u] sent %i bytes (request: %u).\n", client_st->id, sent, requests); + fflush(stdout); + } else { + printf("[client %u] failed to send.\n", client_st->id); + fflush(stdout); + } + + if (requests == 3) { + /* + * Mid-session KTLS activation. + * + * This transition occurs after several encrypted + * application records have already been exchanged — + * sequence numbers are therefore non-zero. From this + * point onward, encryption and decryption are handled + * directly by the Linux Kernel TLS (KTLS). + * + * The active traffic keys and IVs are exported from + * Mbed TLS and installed into the socket via TLS_TX and + * TLS_RX. User-space record handling ceases entirely, + * yet the session state remains uninterrupted. + * + * In the event log, this is reflected by: + * [client] KTLS transmit channel successfully configured... + * [client] KTLS receive channel successfully configured... + * indicating that kernel-level crypto now manages the + * connection mid-flight. + */ + ret = enable_ktls(MBEDTLS_SSL_IS_CLIENT, &e_keys, client_st->fd); + mbedtls_platform_zeroize(&e_keys, sizeof(e_keys)); + + if (ret >= 0) { + client_st->send_fn = app_send; + + if (ret == 1) { + client_st->recv_fn = app_recv; + } + } + } + + do { + const int read = client_st->recv_fn(&send_recv_ctx, buffer, BUFFER_SIZE); + + if (read > 0) { + response_length -= read; + printf("[client %u] read %i bytes (response: %u).\n", client_st->id, read, requests); + fflush(stdout); + + if (response_length == 0) { + break; + } + + continue; + } + + printf("[client %u] failed to recv.\n", client_st->id); + fflush(stdout); + requests = max_requests_count; + break; + } while (1); + } + } else { + printf("client %u -> server handshake failed. error code: %i.\n", client_st->id, ret); + fflush(stdout); + } +} + +void start_request(client_st_t *client_st, const struct sockaddr_storage *server_sockaddr, unsigned *client_id) { + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + const int enable = 1; + int ret; + + client_st->fd = socket(AF_INET, SOCK_STREAM, 0); + + if (client_st->fd == -1) { + perror("send_requests socket"); + mbedtls_ssl_session_reset(&client_st->ssl); + return; + } + + ret = connect(client_st->fd, (struct sockaddr *)server_sockaddr, sizeof(struct sockaddr_storage)); + + if (ret == -1) { + perror("connect"); + close(client_st->fd); + mbedtls_ssl_session_reset(&client_st->ssl); + return; + } + + setsockopt(client_st->fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); + setsockopt(client_st->fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(client_st->fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + ++(*client_id); + client_st->id = *client_id; + + client_send_and_receive(client_st); + close(client_st->fd); + mbedtls_ssl_session_reset(&client_st->ssl); +} + +void send_requests(const struct sockaddr_storage *server_sockaddr) { + unsigned client_id = 0; + client_st_t client_st; + int ret; + + static int ciphers[3] = {0, 0, 0}; + + memset(&client_st, 0, sizeof(client_st_t)); + + init_client_ssl(&client_st); + setup_client_ssl(&client_st); + mbedtls_ssl_conf_ciphersuites(&client_st.conf, ciphers); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* TLS 1.3 */ + mbedtls_ssl_conf_max_tls_version(&client_st.conf, MBEDTLS_SSL_VERSION_TLS1_3); + + /* TLS 1.3 CHACHA20_POLY1305 */ + ciphers[0] = MBEDTLS_TLS1_3_CHACHA20_POLY1305_SHA256; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + /* TLS 1.3 AES_256_GCM */ + ciphers[0] = MBEDTLS_TLS1_3_AES_256_GCM_SHA384; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + /* TLS 1.3 AES_128_GCM */ + ciphers[0] = MBEDTLS_TLS1_3_AES_128_GCM_SHA256; + + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* TLS 1.2 */ + + mbedtls_ssl_conf_max_tls_version(&client_st.conf, MBEDTLS_SSL_VERSION_TLS1_2); + + // /* TLS 1.2 CHACHA20_POLY1305 */ + ciphers[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256; + ciphers[1] = MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + // /* TLS 1.2 AES_256_GCM */ + ciphers[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + ciphers[1] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + // /* TLS 1.2 AES_128_GCM */ + ciphers[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + ciphers[1] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + /* TLS 1.2 AES_128_CCM */ + ciphers[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM; + ciphers[1] = MBEDTLS_TLS_PSK_WITH_AES_128_CCM; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + /* TLS 1.2 ARIA_128_GCM */ + ciphers[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256; + ciphers[1] = MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); + + // /* TLS 1.2 ARIA_128_GCM */ + ciphers[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384; + ciphers[1] = MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384; + ret = mbedtls_ssl_setup(&client_st.ssl, &client_st.conf); + + if (ret != 0) { + printf(" failed\n ! mbedtls_ssl_setup for client returned %i.\n", ret); + fflush(stdout); + return; + } + + start_request(&client_st, server_sockaddr, &client_id); +#endif + + cleanup_client_ssl(&client_st); +} + +int main(int argc, char *argv[]) { + // Initialize options with defaults + opt.ip = DFL_IP; + opt.crt_filename = DFL_CRT_FILENAME; + opt.key_filename = DFL_KEY_FILENAME; + opt.key_pwd = DFL_KEY_PWD; + opt.port = DFL_PORT; + opt.debug_level = DFL_DEBUG_LEVEL; + + // Help requested + if (argc == 2 && (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) { + printf(USAGE); + fflush(stdout); + return 0; + } + + for (int i = 1; i < argc; i++) { + char *param = argv[i]; + char *value = strchr(param, '='); + + if (value == NULL) { + printf("Parameter requires a value: '%s'\n", param); + printf(USAGE); + return 1; + } + + *value++ = '\0'; // Split into key=value + + if (strcmp(param, "ip") == 0) { + opt.ip = value; + } else if (strcmp(param, "crt_filename") == 0) { + opt.crt_filename = value; + } else if (strcmp(param, "key_filename") == 0) { + opt.key_filename = value; + } else if (strcmp(param, "key_pwd") == 0) { + opt.key_pwd = value; + } else if (strcmp(param, "port") == 0) { + char *endptr; + unsigned long val = strtoul(value, &endptr, 10); + if (*endptr != '\0' || val > 65535) { + fprintf(stderr, "Invalid integer value for port: '%s'\n", value); + return 1; + } + opt.port = (uint16_t)val; + } else if (strcmp(param, "debug_level") == 0) { + char *endptr; + unsigned long val = strtoul(value, &endptr, 10); + if (*endptr != '\0' || val > 4) { + fprintf(stderr, "Invalid integer value for debug_level: '%s'\n", value); + return 1; + } + opt.debug_level = (int)val; + } else { + fprintf(stderr, "Unknown parameter: '%s'\n", param); + printf(USAGE); + fflush(stdout); + return 1; + } + } + + server_st_t server_st; + memset(&server_st, 0, sizeof(server_st_t)); + + int server_started = 0; + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(opt.debug_level); +#endif + + int ret = psa_crypto_init(); + + if (ret != PSA_SUCCESS) { + exit(MBEDTLS_ERR_SSL_HW_ACCEL_FAILED); + } + + init_server_ssl(&server_st); + setup_server_ssl(&server_st); + signal(SIGPIPE, SIG_IGN); /* Ignore SIGPIPE */ + + pthread_t server_thread = run_server(&server_st, &server_started); + + const int expected = 0; + + while (1) { + const int current = __atomic_load_n(&server_started, __ATOMIC_ACQUIRE); + + if (current != expected) { + break; + } + + const long syscall_ret = + syscall(SYS_futex, &server_started, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, expected, NULL, NULL, 0); + + if (syscall_ret == -1) { + if ((errno == EINTR) || (errno == EAGAIN)) { + continue; // retry + } + + fatal_error(&server_st, "futex_wait"); + } + + break; + } + + send_requests(&server_st.addr); + + if (server_thread != 0) { + pthread_join(server_thread, NULL); + } + + mbedtls_psa_crypto_free(); + + return 0; +} + +#endif diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index fa61b0f435c1..0e27474545a4 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3329,6 +3329,10 @@ TLS 1.2 Keying Material Exporter: Handshake not done depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY ssl_tls_exporter_too_early:MBEDTLS_SSL_VERSION_TLS1_2:1:MBEDTLS_SSL_SERVER_CERTIFICATE +TLS 1.2 Export traffic keys +depends_on:MBEDTLS_SSL_PROTO_TLS1_2 +test_export_traffic_keys_tls_1_2 + TLS 1.3 Keying Material Exporter: Consistent results, no context depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:MBEDTLS_X509_RSASSA_PSS_SUPPORT ssl_tls_exporter_consistent_result:MBEDTLS_SSL_VERSION_TLS1_3:24:0 @@ -3364,3 +3368,7 @@ ssl_tls_exporter_rejects_bad_parameters:MBEDTLS_SSL_VERSION_TLS1_3:24:250:10 TLS 1.3 Keying Material Exporter: Handshake not done depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:MBEDTLS_X509_RSASSA_PSS_SUPPORT ssl_tls_exporter_too_early:MBEDTLS_SSL_VERSION_TLS1_3:1:MBEDTLS_SSL_SERVER_CERTIFICATE + +TLS 1.3 Export traffic keys +depends_on:MBEDTLS_SSL_PROTO_TLS1_3 +test_export_traffic_keys_tls_1_3 diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 5b6500898e2a..08d94992f247 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5936,3 +5936,426 @@ exit: MD_OR_USE_PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_2 */ +void test_export_traffic_keys_tls_1_2(void) +{ + mbedtls_ssl_key_set key_set; + mbedtls_ssl_context ssl; + mbedtls_cipher_type_t cipher_type; + mbedtls_ssl_session session; + + USE_PSA_INIT(); + + mbedtls_ssl_init(&ssl); + mbedtls_ssl_session_init(&session); + ssl.session = &session; + ssl.tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + + { + session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_SHA256; + + const size_t secret_len = 48; + const size_t expected_key_len = 32; + const size_t expected_iv_len = 12; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_CHACHA20_POLY1305; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {112, 105, 147, 201, 72, 76, 51, 251, 243, 159, 11, 141, 137, 105, 63, 66, + 135, 32, 111, 39, 93, 51, 231, 175, 12, 155, 63, 30, 54, 246, 102, 86, + 30, 16, 157, 169, 236, 254, 71, 35, 50, 184, 175, 91, 119, 18, 106, 163}; + + const unsigned char randbytes[] = { + 104, 239, 9, 103, 120, 47, 166, 196, 243, 146, 16, 174, 29, 142, 152, 164, 214, 88, 192, 9, 111, 8, + 119, 97, 68, 79, 87, 78, 71, 82, 68, 1, 104, 239, 9, 103, 245, 80, 118, 222, 167, 210, 56, 238, + 165, 66, 111, 113, 226, 111, 19, 1, 197, 145, 183, 203, 3, 50, 71, 5, 87, 235, 29, 249}; + + const unsigned char expected_server_key[] = {40, 9, 95, 97, 133, 89, 235, 94, 76, 94, 185, + 113, 228, 214, 113, 187, 196, 211, 184, 131, 227, 129, + 168, 159, 69, 237, 2, 99, 191, 220, 120, 106}; + const unsigned char expected_client_key[] = {244, 235, 99, 119, 220, 51, 164, 111, 180, 170, 192, + 38, 167, 183, 172, 6, 50, 21, 151, 24, 154, 157, + 91, 46, 6, 252, 208, 118, 209, 116, 201, 31}; + const unsigned char expected_server_iv[] = {239, 17, 62, 69, 149, 174, 93, 34, 137, 193, 156, 233}; + const unsigned char expected_client_iv[] = {35, 2, 194, 106, 200, 254, 191, 158, 109, 99, 208, 77}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, randbytes, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + { + session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_SHA384; + + const size_t secret_len = 48; + const size_t expected_key_len = 32; + const size_t expected_iv_len = 4; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_AES_256_GCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {251, 193, 91, 147, 255, 124, 205, 53, 107, 173, 153, 78, 233, 183, 226, 87, + 116, 66, 133, 152, 35, 34, 29, 31, 104, 29, 225, 97, 127, 70, 72, 70, + 80, 155, 42, 96, 78, 168, 69, 9, 85, 106, 128, 33, 45, 224, 117, 223}; + + const unsigned char randbytes[] = { + 104, 239, 9, 103, 28, 36, 104, 0, 181, 107, 60, 199, 125, 85, 64, 247, 126, 52, 81, 51, 170, 156, + 166, 206, 68, 79, 87, 78, 71, 82, 68, 1, 104, 239, 9, 103, 172, 177, 26, 145, 104, 177, 153, 102, + 195, 101, 135, 232, 42, 31, 144, 180, 251, 210, 178, 75, 163, 108, 10, 35, 102, 89, 246, 28}; + + const unsigned char expected_server_key[] = {187, 234, 138, 103, 92, 215, 48, 78, 213, 5, 87, + 157, 77, 142, 28, 25, 165, 45, 176, 184, 38, 89, + 238, 112, 140, 204, 182, 50, 29, 21, 105, 240}; + const unsigned char expected_client_key[] = {160, 87, 54, 140, 29, 15, 34, 175, 21, 113, 119, + 188, 102, 79, 123, 244, 225, 224, 90, 14, 70, 123, + 74, 221, 57, 0, 222, 6, 129, 1, 185, 176}; + const unsigned char expected_server_iv[] = {191, 60, 67, 216}; + const unsigned char expected_client_iv[] = {45, 136, 133, 56}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, randbytes, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + { + session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_SHA256; + + const size_t secret_len = 48; + const size_t expected_key_len = 16; + const size_t expected_iv_len = 4; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_AES_128_GCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {89, 44, 241, 155, 32, 36, 250, 23, 38, 180, 213, 178, 92, 111, 220, 61, + 220, 140, 217, 41, 246, 242, 156, 63, 178, 179, 125, 17, 66, 77, 6, 44, + 236, 187, 64, 112, 104, 201, 193, 24, 102, 70, 214, 4, 147, 22, 117, 67}; + + const unsigned char randbytes[] = { + 104, 239, 9, 103, 119, 154, 211, 249, 69, 209, 69, 99, 63, 166, 91, 140, 44, 164, 180, 101, 235, 62, + 117, 149, 68, 79, 87, 78, 71, 82, 68, 1, 104, 239, 9, 103, 11, 58, 87, 201, 5, 146, 9, 254, + 66, 227, 211, 31, 173, 59, 99, 53, 168, 227, 72, 227, 178, 50, 63, 249, 43, 11, 185, 174}; + + const unsigned char expected_server_key[] = {59, 157, 119, 219, 174, 221, 254, 233, + 152, 149, 68, 26, 152, 50, 81, 50}; + const unsigned char expected_client_key[] = {190, 227, 211, 111, 60, 91, 138, 45, + 16, 175, 143, 212, 113, 133, 92, 112}; + const unsigned char expected_server_iv[] = {154, 166, 99, 205}; + const unsigned char expected_client_iv[] = {100, 7, 246, 47}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, randbytes, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + { + session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_SHA256; + + const size_t secret_len = 48; + const size_t expected_key_len = 16; + const size_t expected_iv_len = 4; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_AES_128_CCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {148, 184, 235, 179, 139, 197, 225, 85, 105, 30, 121, 1, 77, 204, 43, 38, + 184, 194, 106, 71, 121, 247, 107, 215, 13, 206, 139, 96, 171, 84, 165, 28, + 27, 57, 165, 33, 142, 20, 33, 92, 7, 177, 70, 102, 137, 193, 8, 246}; + + const unsigned char randbytes[] = { + 104, 239, 9, 103, 52, 127, 204, 66, 196, 4, 201, 15, 22, 212, 148, 40, 8, 133, 244, 58, 220, 31, + 116, 100, 68, 79, 87, 78, 71, 82, 68, 1, 104, 239, 9, 103, 63, 253, 82, 129, 88, 173, 235, 155, + 21, 167, 14, 124, 162, 17, 179, 28, 185, 222, 236, 183, 153, 235, 56, 24, 77, 210, 216, 53}; + + const unsigned char expected_server_key[] = {140, 73, 197, 206, 4, 226, 237, 172, + 43, 70, 222, 162, 23, 216, 95, 92}; + const unsigned char expected_client_key[] = {42, 15, 104, 95, 13, 76, 111, 16, + 33, 224, 240, 28, 245, 199, 176, 34}; + const unsigned char expected_server_iv[] = {211, 27, 99, 229}; + const unsigned char expected_client_iv[] = {63, 102, 139, 99}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, randbytes, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + { + session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_SHA256; + + const size_t secret_len = 48; + const size_t expected_key_len = 16; + const size_t expected_iv_len = 4; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_ARIA_128_GCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {86, 37, 122, 100, 241, 79, 116, 184, 224, 101, 78, 4, 172, 210, 75, 46, + 20, 210, 199, 179, 227, 233, 69, 12, 92, 141, 98, 46, 122, 166, 146, 74, + 2, 42, 62, 170, 128, 177, 111, 182, 235, 166, 190, 153, 158, 99, 119, 83}; + + const unsigned char randbytes[] = { + 104, 239, 9, 103, 31, 203, 112, 250, 131, 133, 129, 241, 51, 54, 130, 152, 144, 70, 109, 83, 86, 91, + 69, 97, 68, 79, 87, 78, 71, 82, 68, 1, 104, 239, 9, 103, 149, 234, 65, 6, 53, 79, 255, 218, + 232, 131, 35, 218, 105, 138, 33, 159, 21, 2, 113, 197, 164, 46, 158, 4, 115, 218, 11, 50}; + + const unsigned char expected_server_key[] = {72, 133, 252, 88, 46, 115, 132, 96, + 140, 75, 252, 126, 40, 173, 42, 64}; + const unsigned char expected_client_key[] = {189, 7, 251, 84, 185, 79, 134, 77, + 153, 199, 207, 152, 134, 219, 154, 148}; + const unsigned char expected_server_iv[] = {121, 212, 219, 209}; + const unsigned char expected_client_iv[] = {34, 252, 102, 234}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, randbytes, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + { + session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_SHA384; + + const size_t secret_len = 48; + const size_t expected_key_len = 32; + const size_t expected_iv_len = 4; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_ARIA_256_GCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {36, 196, 199, 74, 200, 173, 97, 178, 101, 51, 220, 252, 103, 65, 233, 161, + 245, 6, 24, 242, 128, 109, 52, 253, 158, 90, 12, 170, 237, 254, 64, 2, + 220, 229, 111, 241, 175, 147, 37, 119, 236, 217, 39, 224, 109, 233, 113, 222}; + + const unsigned char randbytes[] = { + 104, 239, 9, 103, 215, 165, 117, 79, 67, 243, 200, 73, 90, 238, 105, 202, 30, 244, 196, 233, 157, 61, + 128, 116, 68, 79, 87, 78, 71, 82, 68, 1, 104, 239, 9, 103, 17, 93, 139, 98, 199, 40, 192, 74, + 5, 191, 201, 197, 21, 117, 36, 94, 36, 136, 168, 36, 239, 143, 165, 41, 74, 188, 170, 153}; + + const unsigned char expected_server_key[] = {154, 105, 11, 241, 40, 205, 112, 144, 119, 57, 251, + 148, 2, 77, 248, 32, 46, 194, 55, 1, 236, 223, + 141, 12, 105, 126, 23, 245, 206, 11, 89, 152}; + const unsigned char expected_client_key[] = {27, 142, 24, 235, 130, 118, 136, 127, 77, 67, 51, + 167, 218, 221, 191, 169, 252, 116, 147, 22, 13, 17, + 105, 162, 87, 103, 126, 49, 13, 190, 42, 75}; + const unsigned char expected_server_iv[] = {222, 16, 98, 107}; + const unsigned char expected_client_iv[] = {250, 24, 34, 51}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, randbytes, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_3 */ +void test_export_traffic_keys_tls_1_3(void) +{ + mbedtls_ssl_key_set key_set; + mbedtls_ssl_context ssl; + mbedtls_cipher_type_t cipher_type; + mbedtls_ssl_session session; + + USE_PSA_INIT(); + + mbedtls_ssl_init(&ssl); + mbedtls_ssl_session_init(&session); + ssl.tls_version = MBEDTLS_SSL_VERSION_UNKNOWN; + session.ciphersuite = 0; + + { + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, NULL, 0, NULL, MBEDTLS_SSL_TLS_PRF_NONE); + + TEST_EQUAL(ret, MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + + ssl.session = &session; + + { + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, NULL, 0, NULL, MBEDTLS_SSL_TLS_PRF_NONE); + + TEST_EQUAL(ret, MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + + ssl.tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + + { + session.ciphersuite = MBEDTLS_TLS1_3_CHACHA20_POLY1305_SHA256; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; + + const size_t secret_len = 64; + const size_t expected_key_len = 32; + const size_t expected_iv_len = 12; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_CHACHA20_POLY1305; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {64, 59, 29, 110, 233, 21, 144, 215, 31, 179, 10, 13, 2, 7, 7, 20, + 148, 116, 241, 175, 81, 133, 200, 76, 89, 64, 173, 115, 162, 188, 233, 17, + 137, 27, 115, 91, 224, 158, 26, 244, 224, 64, 89, 10, 34, 164, 228, 193, + 233, 63, 188, 102, 88, 27, 201, 56, 14, 166, 169, 195, 222, 158, 55, 214}; + + const unsigned char expected_server_key[] = {80, 196, 54, 28, 207, 197, 129, 204, 73, 59, 192, + 57, 203, 188, 214, 94, 102, 14, 59, 147, 88, 231, + 239, 147, 52, 172, 41, 214, 215, 165, 13, 85}; + const unsigned char expected_client_key[] = {243, 91, 84, 253, 235, 197, 244, 52, 43, 55, 241, + 48, 42, 166, 87, 226, 64, 129, 237, 218, 28, 183, + 206, 61, 112, 180, 99, 174, 198, 117, 136, 97}; + const unsigned char expected_server_iv[] = {15, 33, 207, 101, 231, 168, 231, 86, 111, 131, 94, 115}; + const unsigned char expected_client_iv[] = {40, 211, 47, 221, 106, 219, 128, 102, 103, 108, 226, 18}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = + mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, NULL, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + ssl.session = NULL; + ssl.session_negotiate = &session; + + { + session.ciphersuite = MBEDTLS_TLS1_3_AES_256_GCM_SHA384; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; + + const size_t secret_len = 96; + const size_t expected_key_len = 32; + const size_t expected_iv_len = 12; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_AES_256_GCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {242, 205, 244, 158, 158, 29, 5, 222, 62, 194, 124, 137, 89, 120, 66, 226, + 50, 34, 202, 239, 155, 151, 228, 28, 198, 88, 252, 118, 183, 225, 233, 72, + 83, 156, 63, 214, 3, 111, 21, 246, 147, 176, 255, 12, 186, 190, 102, 172, + 9, 98, 80, 141, 3, 166, 85, 96, 53, 119, 176, 124, 154, 75, 216, 177, + 86, 211, 0, 83, 8, 97, 77, 1, 76, 222, 52, 193, 79, 46, 118, 247, + 247, 239, 70, 239, 42, 47, 232, 94, 228, 240, 220, 120, 218, 220, 29, 187}; + + const unsigned char expected_server_key[] = {7, 165, 59, 66, 12, 220, 88, 180, 207, 39, 225, + 181, 121, 222, 231, 151, 2, 172, 115, 242, 9, 96, + 106, 102, 66, 235, 47, 171, 157, 40, 47, 124}; + + const unsigned char expected_client_key[] = { + 23, 182, 251, 20, 99, 82, 44, 161, 112, 8, 253, 193, 228, 168, 255, 193, + 84, 59, 115, 216, 93, 179, 127, 27, 204, 144, 82, 204, 155, 234, 235, 108, + }; + + const unsigned char expected_server_iv[] = {86, 139, 138, 15, 83, 177, 206, 18, 20, 13, 153, 13}; + const unsigned char expected_client_iv[] = {243, 54, 158, 185, 195, 5, 122, 11, 88, 198, 64, 243}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, NULL, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + { + session.ciphersuite = MBEDTLS_TLS1_3_AES_128_GCM_SHA256; + + const mbedtls_tls_prf_types tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; + + const size_t secret_len = 64; + const size_t expected_key_len = 16; + const size_t expected_iv_len = 12; + const mbedtls_cipher_type_t expected_cipher_type = MBEDTLS_CIPHER_AES_128_GCM; + cipher_type = MBEDTLS_CIPHER_NONE; + + const unsigned char secret[] = {169, 134, 220, 49, 95, 239, 131, 244, 252, 139, 64, 149, 234, 235, 185, 82, + 243, 88, 143, 141, 82, 28, 135, 18, 196, 250, 245, 163, 190, 116, 164, 188, + 80, 55, 17, 171, 125, 249, 80, 241, 155, 158, 213, 116, 68, 153, 219, 94, + 92, 172, 58, 113, 122, 162, 215, 135, 37, 49, 35, 255, 79, 8, 222, 40}; + + const unsigned char expected_server_key[] = {204, 178, 149, 146, 252, 156, 245, 94, + 89, 40, 155, 46, 178, 111, 107, 219}; + + const unsigned char expected_client_key[] = { + 159, 7, 2, 51, 111, 158, 159, 102, 84, 120, 6, 171, 210, 166, 238, 136, + }; + + const unsigned char expected_server_iv[] = {207, 226, 136, 162, 58, 86, 72, 199, 24, 135, 123, 170}; + const unsigned char expected_client_iv[] = {11, 69, 197, 4, 63, 108, 140, 220, 2, 15, 6, 216}; + + memset(&key_set, 0, sizeof(key_set)); + int ret = mbedtls_ssl_export_traffic_keys(&ssl, &key_set, &cipher_type, secret, secret_len, NULL, tls_prf_type); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(cipher_type, expected_cipher_type); + + TEST_MEMORY_COMPARE(key_set.server_write_key, key_set.key_len, expected_server_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.client_write_key, key_set.key_len, expected_client_key, expected_key_len); + TEST_MEMORY_COMPARE(key_set.server_write_iv, key_set.iv_len, expected_server_iv, expected_iv_len); + TEST_MEMORY_COMPARE(key_set.client_write_iv, key_set.iv_len, expected_client_iv, expected_iv_len); + } + + USE_PSA_DONE(); +} +/* END_CASE */