diff --git a/include/mbedtls/error.h b/include/mbedtls/error.h index 765fd42f8..31f294f70 100644 --- a/include/mbedtls/error.h +++ b/include/mbedtls/error.h @@ -100,6 +100,7 @@ * ECP 4 10 (Started from top) * MD 5 5 * HKDF 5 1 (Started from top) + * SSL 5 1 (Started from 0x5F00) * CIPHER 6 8 (Started from 0x6080) * SSL 6 24 (Started from top, plus 0x6000) * SSL 7 32 diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index d435a694b..df620692f 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -123,6 +123,7 @@ #define MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS -0x6500 /**< The asynchronous operation is not completed yet. */ #define MBEDTLS_ERR_SSL_EARLY_MESSAGE -0x6480 /**< Internal-only message signaling that a message arrived early. */ #define MBEDTLS_ERR_SSL_UNEXPECTED_CID -0x6000 /**< An encrypted DTLS-frame with an unexpected CID was received. */ +#define MBEDTLS_ERR_SSL_VERSION_MISMATCH -0x5F00 /**< An operation failed due to an unexpected version or configuration. */ #define MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS -0x7000 /**< A cryptographic operation is in progress. Try again later. */ /* @@ -2179,6 +2180,9 @@ int mbedtls_ssl_set_session( mbedtls_ssl_context *ssl, const mbedtls_ssl_session * \return \c 0 if successful. * \return #MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed. * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if input data is invalid. + * \return #MBEDTLS_ERR_SSL_VERSION_MISMATCH if the serialized data + * was generated in a different version or configuration of + * Mbed TLS. * \return Another negative value for other kinds of errors (for * example, unsupported features in the embedded certificate). */ diff --git a/library/error.c b/library/error.c index 0a9baebb2..546fa49df 100644 --- a/library/error.c +++ b/library/error.c @@ -525,6 +525,8 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) mbedtls_snprintf( buf, buflen, "SSL - Internal-only message signaling that a message arrived early" ); if( use_ret == -(MBEDTLS_ERR_SSL_UNEXPECTED_CID) ) mbedtls_snprintf( buf, buflen, "SSL - An encrypted DTLS-frame with an unexpected CID was received" ); + if( use_ret == -(MBEDTLS_ERR_SSL_VERSION_MISMATCH) ) + mbedtls_snprintf( buf, buflen, "SSL - An operation failed due to an unexpected version or configuration" ); if( use_ret == -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) ) mbedtls_snprintf( buf, buflen, "SSL - A cryptographic operation is in progress. Try again later" ); #endif /* MBEDTLS_SSL_TLS_C */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 876457c4f..b61453fe5 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -47,6 +47,7 @@ #include "mbedtls/ssl.h" #include "mbedtls/ssl_internal.h" #include "mbedtls/platform_util.h" +#include "mbedtls/version.h" #include @@ -8775,23 +8776,113 @@ const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer( const mbedtls_ssl_co return( ssl->session ); } +/* + * Define ticket header determining Mbed TLS version + * and structure of the ticket. + */ + +/* + * Define bitflag determining compile-time settings influencing + * structure of serialized SSL sessions. + */ + +#if defined(MBEDTLS_HAVE_TIME) +#define SSL_SERIALIZED_SESSION_CONFIG_TIME 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_TIME 0 +#endif /* MBEDTLS_HAVE_TIME */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#define SSL_SERIALIZED_SESSION_CONFIG_CRT 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_CRT 0 +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET 0 +#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +#define SSL_SERIALIZED_SESSION_CONFIG_MFL 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_MFL 0 +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC) +#define SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC 0 +#endif /* MBEDTLS_SSL_TRUNCATED_HMAC */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +#define SSL_SERIALIZED_SESSION_CONFIG_ETM 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_ETM 0 +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +#define SSL_SERIALIZED_SESSION_CONFIG_TICKET 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_TICKET 0 +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#define SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT 0 +#define SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT 1 +#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT 2 +#define SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT 3 +#define SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC_BIT 4 +#define SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT 5 +#define SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT 6 + +#define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG \ + ( (uint16_t) ( \ + ( SSL_SERIALIZED_SESSION_CONFIG_TIME << SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT ) | \ + ( SSL_SERIALIZED_SESSION_CONFIG_CRT << SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT ) | \ + ( SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET << SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT ) | \ + ( SSL_SERIALIZED_SESSION_CONFIG_MFL << SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT ) | \ + ( SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC << SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC_BIT ) | \ + ( SSL_SERIALIZED_SESSION_CONFIG_ETM << SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT ) | \ + ( SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT ) ) ) + +static unsigned char ssl_serialized_session_header[] = { + MBEDTLS_VERSION_MAJOR, + MBEDTLS_VERSION_MINOR, + MBEDTLS_VERSION_PATCH, + ( SSL_SERIALIZED_SESSION_CONFIG_BITFLAG >> 8 ) & 0xFF, + ( SSL_SERIALIZED_SESSION_CONFIG_BITFLAG >> 0 ) & 0xFF, +}; + /* * Serialize a session in the following format: * (in the presentation language of TLS, RFC 8446 section 3) * + * opaque mbedtls_version[3]; // major, minor, patch + * opaque session_format[2]; // version-specific 16-bit field determining + * // the format of the remaining + * // serialized data. + * + * Note: When updating the format, remember to keep + * these version+format bytes. + * + * // In this version, `session_format` determines + * // the setting of those compile-time + * // configuration options which influence + * // the structure of mbedtls_ssl_session. * uint64 start_time; - * uint8 ciphersuite[2]; // defined by the standard - * uint8 compression; // 0 or 1 - * uint8 session_id_len; // at most 32 + * uint8 ciphersuite[2]; // defined by the standard + * uint8 compression; // 0 or 1 + * uint8 session_id_len; // at most 32 * opaque session_id[32]; - * opaque master[48]; // fixed length in the standard + * opaque master[48]; // fixed length in the standard * uint32 verify_result; - * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert - * opaque ticket<0..2^24-1>; // length 0 means no ticket + * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert + * opaque ticket<0..2^24-1>; // length 0 means no ticket * uint32 ticket_lifetime; - * uint8 mfl_code; // up to 255 according to standard - * uint8 trunc_hmac; // 0 or 1 - * uint8 encrypt_then_mac; // 0 or 1 + * uint8 mfl_code; // up to 255 according to standard + * uint8 trunc_hmac; // 0 or 1 + * uint8 encrypt_then_mac; // 0 or 1 * * The order is the same as in the definition of the structure, except * verify_result is put before peer_cert so that all mandatory fields come @@ -8811,6 +8902,19 @@ int mbedtls_ssl_session_save( const mbedtls_ssl_session *session, size_t cert_len; #endif + /* + * Add version identifier + */ + + used += sizeof( ssl_serialized_session_header ); + + if( used <= buf_len ) + { + memcpy( p, ssl_serialized_session_header, + sizeof( ssl_serialized_session_header ) ); + p += sizeof( ssl_serialized_session_header ); + } + /* * Time */ @@ -8964,6 +9068,20 @@ static int ssl_session_load( mbedtls_ssl_session *session, size_t cert_len; #endif /* MBEDTLS_X509_CRT_PARSE_C */ + /* + * Check version identifier + */ + + if( (size_t)( end - p ) < sizeof( ssl_serialized_session_header ) ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + if( memcmp( p, ssl_serialized_session_header, + sizeof( ssl_serialized_session_header ) ) != 0 ) + { + return( MBEDTLS_ERR_SSL_VERSION_MISMATCH ); + } + p += sizeof( ssl_serialized_session_header ); + /* * Time */ diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index aca26a9cc..edb87d888 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -58,6 +58,18 @@ ssl_dtls_replay:"abcd12340000abcd12340100":"abcd123400ff":0 SSL SET_HOSTNAME memory leak: call ssl_set_hostname twice ssl_set_hostname_twice:"server0":"server1" +SSL session serialization: Wrong major version +ssl_session_serialize_version_check:1:0:0:0 + +SSL session serialization: Wrong minor version +ssl_session_serialize_version_check:0:1:0:0 + +SSL session serialization: Wrong patch version +ssl_session_serialize_version_check:0:0:1:0 + +SSL session serialization: Wrong config +ssl_session_serialize_version_check:0:0:0:1 + Record crypt, AES-128-CBC, 1.2, SHA-384 depends_on:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_AES_C:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SHA512_C ssl_crypt_record:MBEDTLS_CIPHER_AES_128_CBC:MBEDTLS_MD_SHA384:0:0:MBEDTLS_SSL_MINOR_VERSION_3:0:0 diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 1b5501848..65f585274 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -851,3 +851,66 @@ exit: mbedtls_free( bad_buf ); } /* END_CASE */ + +/* BEGIN_CASE */ +void ssl_session_serialize_version_check( int corrupt_major, + int corrupt_minor, + int corrupt_patch, + int corrupt_config ) +{ + unsigned char serialized_session[ 2048 ]; + size_t serialized_session_len; + unsigned cur_byte; + mbedtls_ssl_session session; + uint8_t should_corrupt_byte[] = { corrupt_major == 1, + corrupt_minor == 1, + corrupt_patch == 1, + corrupt_config == 1, + corrupt_config == 1 }; + + mbedtls_ssl_session_init( &session ); + + /* Infer length of serialized session. */ + TEST_ASSERT( mbedtls_ssl_session_save( &session, + serialized_session, + sizeof( serialized_session ), + &serialized_session_len ) == 0 ); + + mbedtls_ssl_session_free( &session ); + + /* Without any modification, we should be able to successfully + * de-serialize the session - double-check that. */ + TEST_ASSERT( mbedtls_ssl_session_load( &session, + serialized_session, + serialized_session_len ) == 0 ); + mbedtls_ssl_session_free( &session ); + + /* Go through the bytes in the serialized session header and + * corrupt them bit-by-bit. */ + for( cur_byte = 0; cur_byte < sizeof( should_corrupt_byte ); cur_byte++ ) + { + int cur_bit; + unsigned char * const byte = &serialized_session[ cur_byte ]; + + if( should_corrupt_byte[ cur_byte ] == 0 ) + continue; + + for( cur_bit = 0; cur_bit < CHAR_BIT; cur_bit++ ) + { + unsigned char const corrupted_bit = 0x1u << cur_bit; + /* Modify a single bit in the serialized session. */ + *byte ^= corrupted_bit; + + /* Attempt to deserialize */ + TEST_ASSERT( mbedtls_ssl_session_load( &session, + serialized_session, + serialized_session_len ) == + MBEDTLS_ERR_SSL_VERSION_MISMATCH ); + + /* Undo the change */ + *byte ^= corrupted_bit; + } + } + +} +/* END_CASE */