diff --git a/ChangeLog.d/add-tls-exporter.txt b/ChangeLog.d/add-tls-exporter.txt new file mode 100644 index 000000000..1aea653e0 --- /dev/null +++ b/ChangeLog.d/add-tls-exporter.txt @@ -0,0 +1,6 @@ +Features + * Add the function mbedtls_ssl_export_keying_material() which allows the + client and server to extract additional shared symmetric keys from an SSL + session, according to the TLS-Exporter specification in RFC 8446 and 5705. + This requires MBEDTLS_SSL_KEYING_MATERIAL_EXPORT to be defined in + mbedtls_config.h. diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index 45feb5ecc..d2831367c 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -1763,6 +1763,20 @@ */ #define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE +/** + * \def MBEDTLS_SSL_KEYING_MATERIAL_EXPORT + * + * When this option is enabled, the client and server can extract additional + * shared symmetric keys after an SSL handshake using the function + * mbedtls_ssl_export_keying_material(). + * + * The process for deriving the keys is specified in RFC 5705 for TLS 1.2 and + * in RFC 8446, Section 7.5, for TLS 1.3. + * + * Comment this macro to disable mbedtls_ssl_export_keying_material(). + */ +#define MBEDTLS_SSL_KEYING_MATERIAL_EXPORT + /** * \def MBEDTLS_SSL_RENEGOTIATION * diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index f9b103e38..cc9da3417 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -729,6 +729,14 @@ union mbedtls_ssl_premaster_secret { /* Length in number of bytes of the TLS sequence number */ #define MBEDTLS_SSL_SEQUENCE_NUMBER_LEN 8 +/* Helper to state that client_random and server_random need to be stored + * after the handshake is complete. This is required for context serialization + * and for the keying material exporter in TLS 1.2. */ +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) || \ + (defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) && defined(MBEDTLS_SSL_PROTO_TLS1_2)) +#define MBEDTLS_SSL_KEEP_RANDBYTES +#endif + #ifdef __cplusplus extern "C" { #endif @@ -5767,6 +5775,41 @@ int mbedtls_ssl_tls_prf(const mbedtls_tls_prf_types prf, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen); +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) +/* Maximum value for key_len in mbedtls_ssl_export_keying material. Depending on the TLS + * version and the negotiated ciphersuite, larger keys could in principle be exported, + * but for simplicity, we define one limit that works in all cases. TLS 1.3 with SHA256 + * has the strictest limit: 255 blocks of SHA256 output, or 8160 bytes. */ +#define MBEDTLS_SSL_EXPORT_MAX_KEY_LEN 8160 + +/** + * \brief TLS-Exporter to derive shared symmetric keys between server and client. + * + * \param ssl 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 at most + * MBEDTLS_SSL_EXPORT_MAX_KEY_LEN (8160). + * \param label Label for which to generate the key of length label_len. + * \param label_len Length of label in bytes. Must be at most 249 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 TLS 1.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. + * \return MBEDTLS_ERR_SSL_BAD_INPUT_DATA if the handshake is not yet completed. + * \return An SSL-specific error on failure. + */ +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); +#endif #ifdef __cplusplus } #endif diff --git a/library/ssl_misc.h b/library/ssl_misc.h index 29254958f..faa1b5ec0 100644 --- a/library/ssl_misc.h +++ b/library/ssl_misc.h @@ -1166,14 +1166,15 @@ struct mbedtls_ssl_transform { unsigned char out_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX]; #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ -#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) +#if defined(MBEDTLS_SSL_KEEP_RANDBYTES) /* We need the Hello random bytes in order to re-derive keys from the - * Master Secret and other session info, - * see ssl_tls12_populate_transform() */ + * Master Secret and other session info and for the keying material + * exporter in TLS 1.2. + * See ssl_tls12_populate_transform() */ unsigned char randbytes[MBEDTLS_SERVER_HELLO_RANDOM_LEN + MBEDTLS_CLIENT_HELLO_RANDOM_LEN]; /*!< ServerHello.random+ClientHello.random */ -#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ +#endif /* defined(MBEDTLS_SSL_KEEP_RANDBYTES) */ }; /* diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 8d451778d..ce14c7d7b 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" @@ -6768,7 +6769,7 @@ static psa_status_t setup_psa_key_derivation(psa_key_derivation_operation_t *der MBEDTLS_CHECK_RETURN_CRITICAL static int tls_prf_generic(mbedtls_md_type_t md_type, const unsigned char *secret, size_t slen, - const char *label, + const char *label, size_t label_len, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen) { @@ -6808,7 +6809,7 @@ static int tls_prf_generic(mbedtls_md_type_t md_type, NULL, 0, random, rlen, (unsigned char const *) label, - (size_t) strlen(label), + label_len, NULL, 0, dlen); if (status != PSA_SUCCESS) { @@ -6848,7 +6849,7 @@ static int tls_prf_generic(mbedtls_md_type_t md_type, MBEDTLS_CHECK_RETURN_CRITICAL static int tls_prf_generic(mbedtls_md_type_t md_type, const unsigned char *secret, size_t slen, - const char *label, + const char *label, size_t label_len, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen) { @@ -6869,14 +6870,14 @@ static int tls_prf_generic(mbedtls_md_type_t md_type, md_len = mbedtls_md_get_size(md_info); - tmp_len = md_len + strlen(label) + rlen; + tmp_len = md_len + label_len + rlen; tmp = mbedtls_calloc(1, tmp_len); if (tmp == NULL) { ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; goto exit; } - nb = strlen(label); + nb = label_len; memcpy(tmp + md_len, label, nb); memcpy(tmp + md_len + nb, random, rlen); nb += rlen; @@ -6959,7 +6960,7 @@ static int tls_prf_sha256(const unsigned char *secret, size_t slen, unsigned char *dstbuf, size_t dlen) { return tls_prf_generic(MBEDTLS_MD_SHA256, secret, slen, - label, random, rlen, dstbuf, dlen); + label, strlen(label), random, rlen, dstbuf, dlen); } #endif /* MBEDTLS_MD_CAN_SHA256*/ @@ -6971,7 +6972,7 @@ static int tls_prf_sha384(const unsigned char *secret, size_t slen, unsigned char *dstbuf, size_t dlen) { return tls_prf_generic(MBEDTLS_MD_SHA384, secret, slen, - label, random, rlen, dstbuf, dlen); + label, strlen(label), random, rlen, dstbuf, dlen); } #endif /* MBEDTLS_MD_CAN_SHA384*/ @@ -8668,7 +8669,7 @@ static int ssl_tls12_populate_transform(mbedtls_ssl_transform *transform, #endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ transform->tls_version = tls_version; -#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) +#if defined(MBEDTLS_SSL_KEEP_RANDBYTES) memcpy(transform->randbytes, randbytes, sizeof(transform->randbytes)); #endif @@ -10053,4 +10054,134 @@ int mbedtls_ssl_verify_certificate(mbedtls_ssl_context *ssl, } #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +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; + unsigned char *prf_input = NULL; + + /* 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. */ + const size_t randbytes_len = MBEDTLS_CLIENT_HELLO_RANDOM_LEN + MBEDTLS_SERVER_HELLO_RANDOM_LEN; + size_t prf_input_len = randbytes_len; + if (use_context) { + if (context_len > UINT16_MAX) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* This does not overflow a 32-bit size_t because the current value of + * prf_input_len is 64 (length of client_random + server_random) and + * context_len fits into two bytes (checked above). */ + prf_input_len += sizeof(uint16_t) + context_len; + } + + prf_input = mbedtls_calloc(prf_input_len, sizeof(unsigned char)); + if (prf_input == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(prf_input, + ssl->transform->randbytes + MBEDTLS_SERVER_HELLO_RANDOM_LEN, + MBEDTLS_CLIENT_HELLO_RANDOM_LEN); + memcpy(prf_input + MBEDTLS_CLIENT_HELLO_RANDOM_LEN, + ssl->transform->randbytes, + MBEDTLS_SERVER_HELLO_RANDOM_LEN); + if (use_context) { + MBEDTLS_PUT_UINT16_BE(context_len, prf_input, randbytes_len); + memcpy(prf_input + randbytes_len + sizeof(uint16_t), context, context_len); + } + ret = tls_prf_generic(hash_alg, ssl->session->master, sizeof(ssl->session->master), + label, label_len, + prf_input, prf_input_len, + out, key_len); + mbedtls_free(prf_input); + return ret; +} +#endif /* defined(MBEDTLS_SSL_PROTO_TLS1_2) */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +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; + + /* The length of the label must be at most 249 bytes to fit into the HkdfLabel + * struct as defined in RFC 8446, Section 7.1. + * + * The length of the context is unlimited even though the context field in the + * struct can only hold up to 255 bytes. This is because we place a *hash* of + * the context in the field. */ + if (label_len > 249) { + 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); +} +#endif /* defined(MBEDTLS_SSL_PROTO_TLS1_3) */ + +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)) { + /* TODO: Change this to a more appropriate error code when one is available. */ + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (key_len > MBEDTLS_SSL_EXPORT_MAX_KEY_LEN) { + 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)) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + 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); +#endif +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + 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); +#endif + default: + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } +} + +#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 739414ea2..fd559a7d7 100644 --- a/library/ssl_tls13_keys.c +++ b/library/ssl_tls13_keys.c @@ -58,15 +58,16 @@ struct mbedtls_ssl_tls13_labels_struct const mbedtls_ssl_tls13_labels = * }; * * Parameters: - * - desired_length: Length of expanded key material - * Even though the standard allows expansion to up to - * 2**16 Bytes, TLS 1.3 never uses expansion to more than - * 255 Bytes, so we require `desired_length` to be at most - * 255. This allows us to save a few Bytes of code by - * hardcoding the writing of the high bytes. + * - desired_length: Length of expanded key material. + * The length field can hold numbers up to 2**16, but HKDF + * can only generate outputs of up to 255 * HASH_LEN bytes. + * It is the caller's responsibility to ensure that this + * limit is not exceeded. In TLS 1.3, SHA256 is the hash + * function with the smallest block size, so a length + * <= 255 * 32 = 8160 is always safe. * - (label, label_len): label + label length, without "tls13 " prefix * The label length MUST be less than or equal to - * MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN + * MBEDTLS_SSL_TLS1_3_HKDF_LABEL_MAX_LABEL_LEN. * It is the caller's responsibility to ensure this. * All (label, label length) pairs used in TLS 1.3 * can be obtained via MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(). @@ -93,7 +94,7 @@ static const char tls13_label_prefix[6] = "tls13 "; #define SSL_TLS1_3_KEY_SCHEDULE_MAX_HKDF_LABEL_LEN \ SSL_TLS1_3_KEY_SCHEDULE_HKDF_LABEL_LEN( \ sizeof(tls13_label_prefix) + \ - MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN, \ + MBEDTLS_SSL_TLS1_3_HKDF_LABEL_MAX_LABEL_LEN, \ MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_CONTEXT_LEN) static void ssl_tls13_hkdf_encode_label( @@ -109,15 +110,13 @@ static void ssl_tls13_hkdf_encode_label( unsigned char *p = dst; - /* Add the size of the expanded key material. - * We're hardcoding the high byte to 0 here assuming that we never use - * TLS 1.3 HKDF key expansion to more than 255 Bytes. */ -#if MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN > 255 -#error "The implementation of ssl_tls13_hkdf_encode_label() is not fit for the \ - value of MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN" + /* Add the size of the expanded key material. */ +#if MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN > UINT16_MAX +#error "The desired key length must fit into an uint16 but \ + MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN is greater than UINT16_MAX" #endif - *p++ = 0; + *p++ = MBEDTLS_BYTE_1(desired_length); *p++ = MBEDTLS_BYTE_0(desired_length); /* Add label incl. prefix */ @@ -151,7 +150,7 @@ int mbedtls_ssl_tls13_hkdf_expand_label( psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; - if (label_len > MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN) { + if (label_len > MBEDTLS_SSL_TLS1_3_HKDF_LABEL_MAX_LABEL_LEN) { /* Should never happen since this is an internal * function, and we know statically which labels * are allowed. */ @@ -1882,4 +1881,37 @@ int mbedtls_ssl_tls13_export_handshake_psk(mbedtls_ssl_context *ssl, } #endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) +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]; + int ret = 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; + } + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + hkdf_secret, + hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(exporter), + context_value, + context_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED, + out, + out_len); + +exit: + mbedtls_platform_zeroize(hkdf_secret, sizeof(hkdf_secret)); + return ret; +} +#endif /* defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) */ + #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/library/ssl_tls13_keys.h b/library/ssl_tls13_keys.h index d3a4c6c99..14f6e4876 100644 --- a/library/ssl_tls13_keys.h +++ b/library/ssl_tls13_keys.h @@ -60,8 +60,9 @@ extern const struct mbedtls_ssl_tls13_labels_struct mbedtls_ssl_tls13_labels; mbedtls_ssl_tls13_labels.LABEL, \ MBEDTLS_SSL_TLS1_3_LBL_LEN(LABEL) -#define MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN \ - sizeof(union mbedtls_ssl_tls13_labels_union) +/* Maximum length of the label field in the HkdfLabel struct defined in + * RFC 8446, Section 7.1, excluding the "tls13 " prefix. */ +#define MBEDTLS_SSL_TLS1_3_HKDF_LABEL_MAX_LABEL_LEN 249 /* The maximum length of HKDF contexts used in the TLS 1.3 standard. * Since contexts are always hashes of message transcripts, this can @@ -70,13 +71,11 @@ extern const struct mbedtls_ssl_tls13_labels_struct mbedtls_ssl_tls13_labels; PSA_HASH_MAX_SIZE /* Maximum desired length for expanded key material generated - * by HKDF-Expand-Label. - * - * Warning: If this ever needs to be increased, the implementation - * ssl_tls13_hkdf_encode_label() in ssl_tls13_keys.c needs to be - * adjusted since it currently assumes that HKDF key expansion - * is never used with more than 255 Bytes of output. */ -#define MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN 255 + * by HKDF-Expand-Label. This algorithm can output up to 255 * hash_size + * bytes of key material where hash_size is the output size of the + * underlying hash function. */ +#define MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN \ + (255 * MBEDTLS_TLS1_3_MD_MAX_SIZE) /** * \brief The \c HKDF-Expand-Label function from @@ -646,6 +645,23 @@ 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(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, + uint8_t *out, const size_t out_len); + #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ #endif /* MBEDTLS_SSL_TLS1_3_KEYS_H */ diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index fc45e9fac..ed72095da 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -105,6 +105,8 @@ int main(void) #define DFL_NSS_KEYLOG 0 #define DFL_NSS_KEYLOG_FILE NULL #define DFL_SKIP_CLOSE_NOTIFY 0 +#define DFL_EXP_LABEL NULL +#define DFL_EXP_LEN 20 #define DFL_QUERY_CONFIG_MODE 0 #define DFL_USE_SRTP 0 #define DFL_SRTP_FORCE_PROFILE 0 @@ -375,6 +377,16 @@ int main(void) #define USAGE_TLS1_3_KEY_EXCHANGE_MODES "" #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) +#define USAGE_EXPORT \ + " exp_label=%%s Label to input into TLS-Exporter\n" \ + " default: None (don't try to export a key)\n" \ + " exp_len=%%d Length of key to extract from TLS-Exporter \n" \ + " default: 20\n" +#else +#define USAGE_EXPORT "" +#endif /* defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) */ + /* USAGE is arbitrarily split to stay under the portable string literal * length limit: 4095 bytes in C99. */ #define USAGE1 \ @@ -465,6 +477,7 @@ int main(void) " otherwise. The expansion of the macro\n" \ " is printed if it is defined\n" \ USAGE_SERIALIZATION \ + USAGE_EXPORT \ "\n" /* @@ -553,6 +566,8 @@ struct options { * after renegotiation */ int reproducible; /* make communication reproducible */ int skip_close_notify; /* skip sending the close_notify alert */ + const char *exp_label; /* label to input into mbedtls_ssl_export_keying_material() */ + int exp_len; /* Length of key to export using mbedtls_ssl_export_keying_material() */ #if defined(MBEDTLS_SSL_EARLY_DATA) int early_data; /* early data enablement flag */ #endif @@ -1004,6 +1019,8 @@ int main(int argc, char *argv[]) opt.nss_keylog = DFL_NSS_KEYLOG; opt.nss_keylog_file = DFL_NSS_KEYLOG_FILE; opt.skip_close_notify = DFL_SKIP_CLOSE_NOTIFY; + opt.exp_label = DFL_EXP_LABEL; + opt.exp_len = DFL_EXP_LEN; opt.query_config_mode = DFL_QUERY_CONFIG_MODE; opt.use_srtp = DFL_USE_SRTP; opt.force_srtp_profile = DFL_SRTP_FORCE_PROFILE; @@ -1460,6 +1477,10 @@ usage: if (opt.skip_close_notify < 0 || opt.skip_close_notify > 1) { goto usage; } + } else if (strcmp(p, "exp_label") == 0) { + opt.exp_label = q; + } else if (strcmp(p, "exp_len") == 0) { + opt.exp_len = atoi(q); } else if (strcmp(p, "use_srtp") == 0) { opt.use_srtp = atoi(q); } else if (strcmp(p, "srtp_force_profile") == 0) { @@ -2560,6 +2581,33 @@ usage: } #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) + if (opt.exp_label != NULL && opt.exp_len > 0) { + unsigned char *exported_key = mbedtls_calloc((size_t) opt.exp_len, sizeof(unsigned char)); + if (exported_key == NULL) { + mbedtls_printf("Could not allocate %d bytes\n", opt.exp_len); + ret = 3; + goto exit; + } + ret = mbedtls_ssl_export_keying_material(&ssl, exported_key, (size_t) opt.exp_len, + opt.exp_label, strlen(opt.exp_label), + NULL, 0, 0); + if (ret != 0) { + mbedtls_free(exported_key); + goto exit; + } + mbedtls_printf("Exporting key of length %d with label \"%s\": 0x", + opt.exp_len, + opt.exp_label); + for (i = 0; i < opt.exp_len; i++) { + mbedtls_printf("%02X", exported_key[i]); + } + mbedtls_printf("\n\n"); + fflush(stdout); + mbedtls_free(exported_key); + } +#endif /* defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) */ + /* * 6. Write the GET request */ diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 5de734f7e..e9539499d 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -71,6 +71,8 @@ int main(void) #define DFL_NBIO 0 #define DFL_EVENT 0 #define DFL_READ_TIMEOUT 0 +#define DFL_EXP_LABEL NULL +#define DFL_EXP_LEN 20 #define DFL_CA_FILE "" #define DFL_CA_PATH "" #define DFL_CRT_FILE "" @@ -476,6 +478,16 @@ int main(void) #define USAGE_SERIALIZATION "" #endif +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) +#define USAGE_EXPORT \ + " exp_label=%%s Label to input into TLS-Exporter\n" \ + " default: None (don't try to export a key)\n" \ + " exp_len=%%d Length of key to extract from TLS-Exporter \n" \ + " default: 20\n" +#else +#define USAGE_EXPORT "" +#endif + #define USAGE_KEY_OPAQUE_ALGS \ " key_opaque_algs=%%s Allowed opaque key 1 algorithms.\n" \ " comma-separated pair of values among the following:\n" \ @@ -583,6 +595,7 @@ int main(void) " otherwise. The expansion of the macro\n" \ " is printed if it is defined\n" \ USAGE_SERIALIZATION \ + USAGE_EXPORT \ "\n" #define PUT_UINT64_BE(out_be, in_le, i) \ @@ -610,6 +623,8 @@ struct options { int nbio; /* should I/O be blocking? */ int event; /* loop or event-driven IO? level or edge triggered? */ uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */ + const char *exp_label; /* label to input into mbedtls_ssl_export_keying_material() */ + int exp_len; /* Length of key to export using mbedtls_ssl_export_keying_material() */ int response_size; /* pad response with header to requested size */ uint16_t buffer_size; /* IO buffer size */ const char *ca_file; /* the file with the CA certificate(s) */ @@ -1699,6 +1714,8 @@ int main(int argc, char *argv[]) opt.cid_val = DFL_CID_VALUE; opt.cid_val_renego = DFL_CID_VALUE_RENEGO; opt.read_timeout = DFL_READ_TIMEOUT; + opt.exp_label = DFL_EXP_LABEL; + opt.exp_len = DFL_EXP_LEN; opt.ca_file = DFL_CA_FILE; opt.ca_path = DFL_CA_PATH; opt.crt_file = DFL_CRT_FILE; @@ -1877,6 +1894,10 @@ usage: } } else if (strcmp(p, "read_timeout") == 0) { opt.read_timeout = atoi(q); + } else if (strcmp(p, "exp_label") == 0) { + opt.exp_label = q; + } else if (strcmp(p, "exp_len") == 0) { + opt.exp_len = atoi(q); } else if (strcmp(p, "buffer_size") == 0) { opt.buffer_size = atoi(q); if (opt.buffer_size < 1) { @@ -3642,6 +3663,33 @@ handshake: mbedtls_printf("\n"); } +#if defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) + if (opt.exp_label != NULL && opt.exp_len > 0) { + unsigned char *exported_key = mbedtls_calloc((size_t) opt.exp_len, sizeof(unsigned char)); + if (exported_key == NULL) { + mbedtls_printf("Could not allocate %d bytes\n", opt.exp_len); + ret = 3; + goto exit; + } + ret = mbedtls_ssl_export_keying_material(&ssl, exported_key, (size_t) opt.exp_len, + opt.exp_label, strlen(opt.exp_label), + NULL, 0, 0); + if (ret != 0) { + mbedtls_free(exported_key); + goto exit; + } + mbedtls_printf("Exporting key of length %d with label \"%s\": 0x", + opt.exp_len, + opt.exp_label); + for (i = 0; i < opt.exp_len; i++) { + mbedtls_printf("%02X", exported_key[i]); + } + mbedtls_printf("\n\n"); + fflush(stdout); + mbedtls_free(exported_key); + } +#endif /* defined(MBEDTLS_SSL_KEYING_MATERIAL_EXPORT) */ + #if defined(MBEDTLS_SSL_DTLS_SRTP) else if (opt.use_srtp != 0) { size_t j = 0; diff --git a/tests/include/test/ssl_helpers.h b/tests/include/test/ssl_helpers.h index 2da73ca3c..33ed2a883 100644 --- a/tests/include/test/ssl_helpers.h +++ b/tests/include/test/ssl_helpers.h @@ -596,6 +596,14 @@ int mbedtls_test_ssl_exchange_data( mbedtls_ssl_context *ssl_2, int msg_len_2, const int expected_fragments_2); +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +int mbedtls_test_ssl_do_handshake_with_endpoints( + mbedtls_test_ssl_endpoint *server_ep, + mbedtls_test_ssl_endpoint *client_ep, + mbedtls_test_handshake_test_options *options, + mbedtls_ssl_protocol_version proto); +#endif /* defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) */ + #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) void mbedtls_test_ssl_perform_handshake( mbedtls_test_handshake_test_options *options); diff --git a/tests/src/test_helpers/ssl_helpers.c b/tests/src/test_helpers/ssl_helpers.c index 58b8362f0..cf051d112 100644 --- a/tests/src/test_helpers/ssl_helpers.c +++ b/tests/src/test_helpers/ssl_helpers.c @@ -2037,6 +2037,63 @@ exit: } #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +int mbedtls_test_ssl_do_handshake_with_endpoints( + mbedtls_test_ssl_endpoint *server_ep, + mbedtls_test_ssl_endpoint *client_ep, + mbedtls_test_handshake_test_options *options, + mbedtls_ssl_protocol_version proto) +{ + enum { BUFFSIZE = 1024 }; + + int ret = -1; + + mbedtls_platform_zeroize(server_ep, sizeof(mbedtls_test_ssl_endpoint)); + mbedtls_platform_zeroize(client_ep, sizeof(mbedtls_test_ssl_endpoint)); + + mbedtls_test_init_handshake_options(options); + options->server_min_version = proto; + options->client_min_version = proto; + options->server_max_version = proto; + options->client_max_version = proto; + + ret = mbedtls_test_ssl_endpoint_init(client_ep, MBEDTLS_SSL_IS_CLIENT, options, + NULL, NULL, NULL); + if (ret != 0) { + return ret; + } + ret = mbedtls_test_ssl_endpoint_init(server_ep, MBEDTLS_SSL_IS_SERVER, options, + NULL, NULL, NULL); + if (ret != 0) { + return ret; + } + + ret = mbedtls_test_mock_socket_connect(&client_ep->socket, &server_ep->socket, BUFFSIZE); + if (ret != 0) { + return ret; + } + + ret = mbedtls_test_move_handshake_to_state(&server_ep->ssl, + &client_ep->ssl, + MBEDTLS_SSL_HANDSHAKE_OVER); + if (ret != 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + return ret; + } + ret = mbedtls_test_move_handshake_to_state(&client_ep->ssl, + &server_ep->ssl, + MBEDTLS_SSL_HANDSHAKE_OVER); + if (ret != 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + return ret; + } + if (!mbedtls_ssl_is_handshake_over(&client_ep->ssl) || + !mbedtls_ssl_is_handshake_over(&server_ep->ssl)) { + return -1; + } + + return 0; +} +#endif /* defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) */ + #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) void mbedtls_test_ssl_perform_handshake( mbedtls_test_handshake_test_options *options) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 61cb366c1..16e267519 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -1230,6 +1230,26 @@ check_server_hello_time() { fi } +# Extract the exported key from the output. +get_exported_key() { + OUTPUT="$1" + EXPORTED_KEY1=$(sed -n '/Exporting key of length 20 with label ".*": /s/.*: //p' $OUTPUT) +} + +# Check that the exported key from the output matches the one obtained in get_exported_key(). +check_exported_key() { + OUTPUT="$1" + EXPORTED_KEY2=$(sed -n '/Exporting key of length 20 with label ".*": /s/.*: //p' $OUTPUT) + test "$EXPORTED_KEY1" = "$EXPORTED_KEY2" +} + +# Check that the exported key from the output matches the one obtained in get_exported_key(). +check_exported_key_openssl() { + OUTPUT="$1" + EXPORTED_KEY2=0x$(sed -n '/Keying material: /s/.*: //p' $OUTPUT) + test "$EXPORTED_KEY1" = "$EXPORTED_KEY2" +} + # Get handshake memory usage from server or client output and put it into the variable specified by the first argument handshake_memory_get() { OUTPUT_VARIABLE="$1" @@ -1977,6 +1997,46 @@ run_tests_memory_after_handshake() run_test_memory_after_handshake_with_mfl 512 "$MEMORY_USAGE_MFL_16K" } +run_test_export_keying_material() { + unset EXPORTED_KEY1 + unset EXPORTED_KEY2 + TLS_VERSION="$1" + + case $TLS_VERSION in + tls12) TLS_VERSION_PRINT="TLS 1.2";; + tls13) TLS_VERSION_PRINT="TLS 1.3";; + esac + + run_test "$TLS_VERSION_PRINT: Export keying material" \ + "$P_SRV debug_level=4 force_version=$TLS_VERSION exp_label=test-label" \ + "$P_CLI debug_level=4 force_version=$TLS_VERSION exp_label=test-label" \ + 0 \ + -s "Exporting key of length 20 with label \".*\": 0x" \ + -c "Exporting key of length 20 with label \".*\": 0x" \ + -f get_exported_key \ + -F check_exported_key +} + +run_test_export_keying_material_openssl_compat() { + unset EXPORTED_KEY1 + unset EXPORTED_KEY2 + TLS_VERSION="$1" + + case $TLS_VERSION in + tls12) TLS_VERSION_PRINT="TLS 1.2"; OPENSSL_CLIENT="$O_CLI";; + tls13) TLS_VERSION_PRINT="TLS 1.3"; OPENSSL_CLIENT="$O_NEXT_CLI";; + esac + + run_test "$TLS_VERSION_PRINT: Export keying material (OpenSSL compatibility)" \ + "$P_SRV debug_level=4 force_version=$TLS_VERSION exp_label=test-label" \ + "$OPENSSL_CLIENT -keymatexport test-label" \ + 0 \ + -s "Exporting key of length 20 with label \".*\": 0x" \ + -c "Keying material exporter:" \ + -F get_exported_key \ + -f check_exported_key_openssl +} + cleanup() { rm -f $CLI_OUT $SRV_OUT $PXY_OUT $SESSION rm -f context_srv.txt @@ -3138,6 +3198,24 @@ run_test "Saving the serialized context to a file" \ 0 \ -s "Save serialized context to a file... ok" \ -c "Save serialized context to a file... ok" + +requires_config_enabled MBEDTLS_SSL_KEYING_MATERIAL_EXPORT +requires_protocol_version tls12 +run_test_export_keying_material tls12 + +requires_config_enabled MBEDTLS_SSL_KEYING_MATERIAL_EXPORT +requires_protocol_version tls12 +run_test_export_keying_material_openssl_compat tls12 + +requires_config_enabled MBEDTLS_SSL_KEYING_MATERIAL_EXPORT +requires_protocol_version tls13 +run_test_export_keying_material tls13 + +requires_config_enabled MBEDTLS_SSL_KEYING_MATERIAL_EXPORT +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED +requires_openssl_tls1_3_with_compatible_ephemeral +run_test_export_keying_material_openssl_compat tls13 + rm -f context_srv.txt rm -f context_cli.txt diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index b6a843b77..92bda3efd 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -2904,6 +2904,21 @@ SSL TLS 1.3 Key schedule: Derive-Secret( ., "res master", hash) depends_on:PSA_WANT_ALG_SHA_256 ssl_tls13_derive_secret:PSA_ALG_SHA_256:"e2d32d4ed66dd37897a0e80c84107503ce58bf8aad4cb55a5002d77ecb890ece":tls13_label_res_master:"c3c122e0bd907a4a3ff6112d8fd53dbf89c773d9552e8b6b9d56d361b3a97bf6":32:MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED:"5e95bdf1f89005ea2e9aa0ba85e728e3c19c5fe0c699e3f5bee59faebd0b5406" +SSL TLS 1.3 Exporter +# Based on the "exp master" key from RFC 8448, expected result calculated with a HMAC-SHA256 calculator. +depends_on:PSA_WANT_ALG_SHA_256 +ssl_tls13_exporter:PSA_ALG_SHA_256:"3fd93d4ffddc98e64b14dd107aedf8ee4add23f4510f58a4592d0b201bee56b4":"test":"context value":32:"83d0fac39f87c1b4fbcd261369f31149c535391a9199bd4c5daf89fe259c2e94" + +SSL TLS 1.3 Exporter, 0-byte label and context +# Expected output taken from OpenSSL. +depends_on:PSA_WANT_ALG_SHA_384 +ssl_tls13_exporter:PSA_ALG_SHA_384:"9f355772f34017927ecc81d16e653c7408f945e7f62dc632d3f59e6310ef49401e62a2e3be886e3f930d4bf6300ce30a":"":"":20:"18268580D7C6769194794A84B7A3EE35317DB88A" + +SSL TLS 1.3 Exporter, 249-byte label and 0-byte context +# Expected output taken from OpenSSL. +depends_on:PSA_WANT_ALG_SHA_384 +ssl_tls13_exporter:PSA_ALG_SHA_384:"c453aeae318ebae00617c430a0066cf586593a4b0150219107420798933cf9e6e4434337cccc2cae5429dc4f77401e39":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678":"":20:"259531766AAA10FBAB6BF2D11D23264B321743D9" + SSL TLS 1.3 Key schedule: Early secrets derivation helper # Vector from RFC 8448 depends_on:PSA_WANT_ALG_SHA_256 @@ -3445,3 +3460,67 @@ tls13_srv_max_early_data_size:TEST_EARLY_DATA_HRR:3:3 TLS 1.3 srv, max early data size, HRR, 98, wsz=49 tls13_srv_max_early_data_size:TEST_EARLY_DATA_HRR:97:0 + +TLS 1.2 Keying Material Exporter: Consistent results, no context +depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY +ssl_tls_exporter_consistent_result:MBEDTLS_SSL_VERSION_TLS1_2:24:0 + +TLS 1.2 Keying Material Exporter: Consistent results, with context +depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY +ssl_tls_exporter_consistent_result:MBEDTLS_SSL_VERSION_TLS1_2:24:1 + +TLS 1.2 Keying Material Exporter: Consistent results, large keys +depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY +ssl_tls_exporter_consistent_result:MBEDTLS_SSL_VERSION_TLS1_2:255 * 32:0 + +TLS 1.2 Keying Material Exporter: Uses label +depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY +ssl_tls_exporter_uses_label:MBEDTLS_SSL_VERSION_TLS1_2 + +TLS 1.2 Keying Material Exporter: Uses context +depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY +ssl_tls_exporter_uses_context:MBEDTLS_SSL_VERSION_TLS1_2 + +TLS 1.2 Keying Material Exporter: Context too long +depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY +ssl_tls_exporter_rejects_bad_parameters:MBEDTLS_SSL_VERSION_TLS1_2:24:251:UINT16_MAX + 1 + +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.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 + +TLS 1.3 Keying Material Exporter: Consistent results, with 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:1 + +TLS 1.3 Keying Material Exporter: Consistent results, large keys +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:255 * 32:0 + +TLS 1.3 Keying Material Exporter: Uses label +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_uses_label:MBEDTLS_SSL_VERSION_TLS1_3 + +TLS 1.3 Keying Material Exporter: Uses 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_uses_context:MBEDTLS_SSL_VERSION_TLS1_3 + +TLS 1.3 Keying Material Exporter: Uses length +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_tls13_exporter_uses_length + +TLS 1.3 Keying Material Exporter: Exported key too long +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_rejects_bad_parameters:MBEDTLS_SSL_VERSION_TLS1_3:255 * 32 + 1:20:20 + +TLS 1.3 Keying Material Exporter: Label too long +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_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 diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index f189affe8..8fa5120d4 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -1964,6 +1964,37 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT */ +void ssl_tls13_exporter(int hash_alg, + data_t *secret, + char *label, + char *context_value, + int desired_length, + data_t *expected) +{ + unsigned char dst[100]; + + /* Check sanity of test parameters. */ + TEST_ASSERT((size_t) desired_length <= sizeof(dst)); + TEST_ASSERT((size_t) desired_length == expected->len); + + PSA_INIT(); + + TEST_ASSERT(mbedtls_ssl_tls13_exporter( + (psa_algorithm_t) hash_alg, + secret->x, secret->len, + (unsigned char *) label, strlen(label), + (unsigned char *) context_value, strlen(context_value), + dst, desired_length) == 0); + + TEST_MEMORY_COMPARE(dst, desired_length, + expected->x, desired_length); + +exit: + PSA_DONE(); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_3 */ void ssl_tls13_derive_early_secrets(int hash_alg, data_t *secret, @@ -5695,3 +5726,262 @@ exit: PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */ +void ssl_tls_exporter_consistent_result(int proto, int exported_key_length, int use_context) +{ + /* Test that the client and server generate the same key. */ + + int ret = -1; + uint8_t *key_buffer_server = NULL; + uint8_t *key_buffer_client = NULL; + mbedtls_test_ssl_endpoint client_ep, server_ep; + mbedtls_test_handshake_test_options options; + + MD_OR_USE_PSA_INIT(); + + ret = mbedtls_test_ssl_do_handshake_with_endpoints(&server_ep, &client_ep, &options, proto); + TEST_ASSERT(ret == 0); + + TEST_ASSERT(exported_key_length > 0); + TEST_CALLOC(key_buffer_server, exported_key_length); + TEST_CALLOC(key_buffer_client, exported_key_length); + + memset(key_buffer_server, 0, exported_key_length); + memset(key_buffer_client, 0, exported_key_length); + + char label[] = "test-label"; + unsigned char context[128] = { 0 }; + ret = mbedtls_ssl_export_keying_material(&server_ep.ssl, + key_buffer_server, (size_t) exported_key_length, + label, sizeof(label), + context, sizeof(context), use_context); + TEST_ASSERT(ret == 0); + ret = mbedtls_ssl_export_keying_material(&client_ep.ssl, + key_buffer_client, (size_t) exported_key_length, + label, sizeof(label), + context, sizeof(context), use_context); + TEST_ASSERT(ret == 0); + TEST_ASSERT(memcmp(key_buffer_server, key_buffer_client, (size_t) exported_key_length) == 0); + +exit: + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_free_handshake_options(&options); + mbedtls_free(key_buffer_server); + mbedtls_free(key_buffer_client); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */ +void ssl_tls_exporter_uses_label(int proto) +{ + /* Test that the client and server export different keys when using different labels. */ + + int ret = -1; + mbedtls_test_ssl_endpoint client_ep, server_ep; + mbedtls_test_handshake_test_options options; + + MD_OR_USE_PSA_INIT(); + + ret = mbedtls_test_ssl_do_handshake_with_endpoints(&server_ep, &client_ep, &options, proto); + TEST_ASSERT(ret == 0); + + char label_server[] = "test-label-server"; + char label_client[] = "test-label-client"; + uint8_t key_buffer_server[24] = { 0 }; + uint8_t key_buffer_client[24] = { 0 }; + unsigned char context[128] = { 0 }; + ret = mbedtls_ssl_export_keying_material(&server_ep.ssl, + key_buffer_server, sizeof(key_buffer_server), + label_server, sizeof(label_server), + context, sizeof(context), 1); + TEST_ASSERT(ret == 0); + ret = mbedtls_ssl_export_keying_material(&client_ep.ssl, + key_buffer_client, sizeof(key_buffer_client), + label_client, sizeof(label_client), + context, sizeof(context), 1); + TEST_ASSERT(ret == 0); + TEST_ASSERT(memcmp(key_buffer_server, key_buffer_client, sizeof(key_buffer_server)) != 0); + +exit: + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_free_handshake_options(&options); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */ +void ssl_tls_exporter_uses_context(int proto) +{ + /* Test that the client and server export different keys when using different contexts. */ + + int ret = -1; + mbedtls_test_ssl_endpoint client_ep, server_ep; + mbedtls_test_handshake_test_options options; + + MD_OR_USE_PSA_INIT(); + + ret = mbedtls_test_ssl_do_handshake_with_endpoints(&server_ep, &client_ep, &options, proto); + TEST_ASSERT(ret == 0); + + char label[] = "test-label"; + uint8_t key_buffer_server[24] = { 0 }; + uint8_t key_buffer_client[24] = { 0 }; + unsigned char context_server[128] = { 0 }; + unsigned char context_client[128] = { 23 }; + ret = mbedtls_ssl_export_keying_material(&server_ep.ssl, + key_buffer_server, sizeof(key_buffer_server), + label, sizeof(label), + context_server, sizeof(context_server), 1); + TEST_ASSERT(ret == 0); + ret = mbedtls_ssl_export_keying_material(&client_ep.ssl, + key_buffer_client, sizeof(key_buffer_client), + label, sizeof(label), + context_client, sizeof(context_client), 1); + TEST_ASSERT(ret == 0); + TEST_ASSERT(memcmp(key_buffer_server, key_buffer_client, sizeof(key_buffer_server)) != 0); + +exit: + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_free_handshake_options(&options); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */ +void ssl_tls13_exporter_uses_length(void) +{ + /* In TLS 1.3, when two keys are exported with the same parameters except one is shorter, + * the shorter key should NOT be a prefix of the longer one. */ + + int ret = -1; + mbedtls_test_ssl_endpoint client_ep, server_ep; + mbedtls_test_handshake_test_options options; + + MD_OR_USE_PSA_INIT(); + + ret = mbedtls_test_ssl_do_handshake_with_endpoints(&server_ep, + &client_ep, + &options, + MBEDTLS_SSL_VERSION_TLS1_3); + TEST_ASSERT(ret == 0); + + char label[] = "test-label"; + uint8_t key_buffer_server[16] = { 0 }; + uint8_t key_buffer_client[24] = { 0 }; + unsigned char context[128] = { 0 }; + ret = mbedtls_ssl_export_keying_material(&server_ep.ssl, + key_buffer_server, sizeof(key_buffer_server), + label, sizeof(label), + context, sizeof(context), 1); + TEST_ASSERT(ret == 0); + ret = mbedtls_ssl_export_keying_material(&client_ep.ssl, + key_buffer_client, sizeof(key_buffer_client), + label, sizeof(label), + context, sizeof(context), 1); + TEST_ASSERT(ret == 0); + TEST_ASSERT(memcmp(key_buffer_server, key_buffer_client, sizeof(key_buffer_server)) != 0); + +exit: + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_free_handshake_options(&options); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */ +void ssl_tls_exporter_rejects_bad_parameters( + int proto, int exported_key_length, int label_length, int context_length) +{ + int ret = -1; + uint8_t *key_buffer = NULL; + char *label = NULL; + uint8_t *context = NULL; + mbedtls_test_ssl_endpoint client_ep, server_ep; + mbedtls_test_handshake_test_options options; + + TEST_ASSERT(exported_key_length > 0); + TEST_ASSERT(label_length > 0); + TEST_ASSERT(context_length > 0); + TEST_CALLOC(key_buffer, exported_key_length); + TEST_CALLOC(label, label_length); + TEST_CALLOC(context, context_length); + + MD_OR_USE_PSA_INIT(); + + ret = mbedtls_test_ssl_do_handshake_with_endpoints(&server_ep, &client_ep, &options, proto); + TEST_ASSERT(ret == 0); + + ret = mbedtls_ssl_export_keying_material(&client_ep.ssl, + key_buffer, exported_key_length, + label, label_length, + context, context_length, 1); + TEST_ASSERT(ret == MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + +exit: + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_free_handshake_options(&options); + mbedtls_free(key_buffer); + mbedtls_free(label); + mbedtls_free(context); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */ +void ssl_tls_exporter_too_early(int proto, int check_server, int state) +{ + enum { BUFFSIZE = 1024 }; + + int ret = -1; + mbedtls_test_ssl_endpoint server_ep, client_ep; + + mbedtls_test_handshake_test_options options; + mbedtls_test_init_handshake_options(&options); + options.server_min_version = proto; + options.client_min_version = proto; + options.server_max_version = proto; + options.client_max_version = proto; + + MD_OR_USE_PSA_INIT(); + + ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER, &options, + NULL, NULL, NULL); + TEST_ASSERT(ret == 0); + ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT, &options, + NULL, NULL, NULL); + TEST_ASSERT(ret == 0); + + ret = mbedtls_test_mock_socket_connect(&client_ep.socket, &server_ep.socket, BUFFSIZE); + TEST_ASSERT(ret == 0); + + if (check_server) { + ret = mbedtls_test_move_handshake_to_state(&server_ep.ssl, &client_ep.ssl, state); + } else { + ret = mbedtls_test_move_handshake_to_state(&client_ep.ssl, &server_ep.ssl, state); + } + TEST_ASSERT(ret == 0 || ret == MBEDTLS_ERR_SSL_WANT_READ || MBEDTLS_ERR_SSL_WANT_WRITE); + + char label[] = "test-label"; + uint8_t key_buffer[24] = { 0 }; + ret = mbedtls_ssl_export_keying_material(check_server ? &server_ep.ssl : &client_ep.ssl, + key_buffer, sizeof(key_buffer), + label, sizeof(label), + NULL, 0, 0); + + /* FIXME: A more appropriate error code should be created for this case. */ + TEST_ASSERT(ret == MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + +exit: + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_free_handshake_options(&options); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */