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 <maximilian.fillinger@foxcrypto.com>
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index f9b103e..f1c8bd2 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -5767,6 +5767,30 @@
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 8d45177..3c1c5cf 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -19,6 +19,7 @@
#include "ssl_client.h"
#include "ssl_debug_helpers.h"
#include "ssl_misc.h"
+#include "ssl_tls13_keys.h"
#include "debug_internal.h"
#include "mbedtls/error.h"
@@ -10053,4 +10054,98 @@
}
#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 739414e..bbaa7c4 100644
--- a/library/ssl_tls13_keys.c
+++ b/library/ssl_tls13_keys.c
@@ -1882,4 +1882,38 @@
}
#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 d3a4c6c..41604c7 100644
--- a/library/ssl_tls13_keys.h
+++ b/library/ssl_tls13_keys.h
@@ -646,6 +646,22 @@
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 */