From bd81c9d0f710e62a5a493d1c053a87c2db78f78a Mon Sep 17 00:00:00 2001 From: Max Fillinger Date: Mon, 22 Jul 2024 14:43:56 +0200 Subject: [PATCH] Implement TLS-Exporter feature The TLS-Exporter is a function to derive shared symmetric keys for the server and client from the secrets generated during the handshake. It is defined in RFC 8446, Section 7.5 for TLS 1.3 and in RFC 5705 for TLS 1.2. Signed-off-by: Max Fillinger --- include/mbedtls/ssl.h | 24 ++++++++++ library/ssl_tls.c | 95 ++++++++++++++++++++++++++++++++++++++++ library/ssl_tls13_keys.c | 34 ++++++++++++++ library/ssl_tls13_keys.h | 16 +++++++ 4 files changed, 169 insertions(+) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 9a02a6a8c2..5bd0b04903 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -5388,6 +5388,30 @@ int mbedtls_ssl_tls_prf(const mbedtls_tls_prf_types prf, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen); + /** + * \brief TLS-Exporter to derive shared symmetric keys between server and client. + * + * \param ctx SSL context from which to export keys. Must have finished the handshake. + * \param out Output buffer of length at least key_len bytes. + * \param key_len Length of the key to generate in bytes. Must be < 2^16 in TLS 1.3. + * \param label Label for which to generate the key of length label_len. + * \param label_len Length of label in bytes. Must be < 251 in TLS 1.3. + * \param context Context of the key. Can be NULL if context_len or use_context is 0. + * \param context_len Length of context. Must be < 2^16 in TLS1.2. + * \param use_context Indicates if a context should be used in deriving the key. + * + * \note TLS 1.2 makes a distinction between a 0-length context and no context. + * This is why the use_context argument exists. TLS 1.3 does not make + * this distinction. If use_context is 0 and TLS 1.3 is used, context and + * context_len are ignored and a 0-length context is used. + * + * \return 0 on success. An SSL specific error on failure. + */ + int mbedtls_ssl_export_keying_material(mbedtls_ssl_context *ssl, + uint8_t *out, size_t key_len, + const char *label, size_t label_len, + const unsigned char *context, size_t context_len, + int use_context); #ifdef __cplusplus } #endif diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 94de3430cc..4c7ce1ee96 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -18,6 +18,7 @@ #include "mbedtls/ssl.h" #include "ssl_client.h" #include "ssl_debug_helpers.h" +#include "ssl_tls13_keys.h" #include "debug_internal.h" #include "mbedtls/error.h" @@ -8929,4 +8930,98 @@ int mbedtls_ssl_verify_certificate(mbedtls_ssl_context *ssl, } #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +static int mbedtls_ssl_tls12_export_keying_material(const mbedtls_ssl_context *ssl, + const mbedtls_md_type_t hash_alg, + uint8_t *out, const size_t key_len, + const char *label, const size_t label_len, + const unsigned char *context, const size_t context_len, + const int use_context) +{ + int ret = 0; + size_t prf_input_len = use_context ? 64 + 2 + context_len : 64; + unsigned char *prf_input = NULL; + char *label_str = NULL; + + if (use_context && context_len >= (1 << 16)) { + ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + goto exit; + } + + prf_input = mbedtls_calloc(prf_input_len, sizeof(unsigned char)); + label_str = mbedtls_calloc(label_len + 1, sizeof(char)); + if (prf_input == NULL || label_str == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto exit; + } + + memcpy(label_str, label, label_len); + label_str[label_len] = '\0'; + + /* The input to the PRF is client_random, then server_random. + * If a context is provided, this is then followed by the context length + * as a 16-bit big-endian integer, and then the context itself. */ + memcpy(prf_input, ssl->transform->randbytes + 32, 32); + memcpy(prf_input + 32, ssl->transform->randbytes, 32); + if (use_context) { + prf_input[64] = (unsigned char)((context_len >> 8) & 0xff); + prf_input[65] = (unsigned char)(context_len & 0xff); + memcpy(prf_input + 66, context, context_len); + } + ret = tls_prf_generic(hash_alg, ssl->session->master, 48, label_str, + prf_input, prf_input_len, + out, key_len); + +exit: + mbedtls_free(prf_input); + mbedtls_free(label_str); + return ret; +} + +static int mbedtls_ssl_tls13_export_keying_material(mbedtls_ssl_context *ssl, + const mbedtls_md_type_t hash_alg, + uint8_t *out, const size_t key_len, + const char *label, const size_t label_len, + const unsigned char *context, const size_t context_len) +{ + const psa_algorithm_t psa_hash_alg = mbedtls_md_psa_alg_from_type(hash_alg); + const size_t hash_len = PSA_HASH_LENGTH(hash_alg); + const unsigned char *secret = ssl->session->app_secrets.exporter_master_secret; + + if (key_len > 0xff || label_len > 250) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + return mbedtls_ssl_tls13_exporter(psa_hash_alg, secret, hash_len, + (const unsigned char *)label, label_len, + context, context_len, out, key_len); +} + +int mbedtls_ssl_export_keying_material(mbedtls_ssl_context *ssl, + uint8_t *out, const size_t key_len, + const char *label, const size_t label_len, + const unsigned char *context, const size_t context_len, + const int use_context) +{ + if (!mbedtls_ssl_is_handshake_over(ssl)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + int ciphersuite_id = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl); + const mbedtls_ssl_ciphersuite_t *ciphersuite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id); + const mbedtls_md_type_t hash_alg = ciphersuite->mac; + + switch (mbedtls_ssl_get_version_number(ssl)) { + case MBEDTLS_SSL_VERSION_TLS1_2: + return mbedtls_ssl_tls12_export_keying_material(ssl, hash_alg, out, key_len, + label, label_len, + context, context_len, use_context); + case MBEDTLS_SSL_VERSION_TLS1_3: + return mbedtls_ssl_tls13_export_keying_material(ssl, hash_alg, out, key_len, label, label_len, + use_context ? context : NULL, + use_context ? context_len : 0); + default: + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } +} + #endif /* MBEDTLS_SSL_TLS_C */ diff --git a/library/ssl_tls13_keys.c b/library/ssl_tls13_keys.c index a421a06de4..38b342ea8b 100644 --- a/library/ssl_tls13_keys.c +++ b/library/ssl_tls13_keys.c @@ -1824,4 +1824,38 @@ int mbedtls_ssl_tls13_export_handshake_psk(mbedtls_ssl_context *ssl, } #endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ +int mbedtls_ssl_tls13_exporter(const psa_algorithm_t hash_alg, + const unsigned char *secret, const size_t secret_len, + const unsigned char *label, const size_t label_len, + const unsigned char *context_value, const size_t context_len, + unsigned char *out, const size_t out_len) +{ + size_t hash_len = PSA_HASH_LENGTH(hash_alg); + unsigned char hkdf_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char hashed_context[PSA_HASH_MAX_SIZE]; + size_t hashed_context_len = 0; + int ret = 0; + psa_status_t status = 0; + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, secret, secret_len, label, label_len, NULL, 0, + MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED, hkdf_secret, hash_len); + if (ret != 0) { + goto exit; + } + + status = psa_hash_compute(hash_alg, context_value, context_len, hashed_context, hash_len, &hashed_context_len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + ret = mbedtls_ssl_tls13_hkdf_expand_label(hash_alg, hkdf_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(exporter), + hashed_context, hashed_context_len, + out, out_len); + +exit: + mbedtls_platform_zeroize(hkdf_secret, sizeof(hkdf_secret)); + return ret; +} + #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/library/ssl_tls13_keys.h b/library/ssl_tls13_keys.h index d3a4c6c992..41604c7e29 100644 --- a/library/ssl_tls13_keys.h +++ b/library/ssl_tls13_keys.h @@ -646,6 +646,22 @@ int mbedtls_ssl_tls13_export_handshake_psk(mbedtls_ssl_context *ssl, size_t *psk_len); #endif +/** + * \brief Calculate TLS-Exporter function as defined in RFC 8446, Section 7.5. + * + * \param[in] hash_alg The hash algorithm. + * \param[in] secret The secret to use. (Should be the exporter master secret.) + * \param[in] secret_len Length of secret. + * \param[in] label The label of the exported key. + * \param[in] label_len The length of label. + * \param[out] out The output buffer for the exported key. Must have room for at least out_len bytes. + * \param[in] out_len Length of the key to generate. +int mbedtls_ssl_tls13_exporter(psa_algorithm_t hash_alg, + const unsigned char *secret, size_t secret_len, + const unsigned char *label, size_t label_len, + const unsigned char *context_value, size_t context_len, + unsigned char *out, size_t out_len); + #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ #endif /* MBEDTLS_SSL_TLS1_3_KEYS_H */