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/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 */