diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 3a14a5830..4f6b448e9 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -927,6 +927,8 @@ struct mbedtls_ssl_session unsigned char exported; + unsigned char MBEDTLS_PRIVATE(minor_ver); /*!< The TLS version used in the session. */ + #if defined(MBEDTLS_X509_CRT_PARSE_C) #if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) mbedtls_x509_crt *MBEDTLS_PRIVATE(peer_cert); /*!< peer X.509 cert chain */ diff --git a/library/ssl_cli.c b/library/ssl_cli.c index ef391b306..e0a1c24ec 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -2024,6 +2024,7 @@ static int ssl_parse_server_hello( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_BUF( 3, "server hello, version", buf + 0, 2 ); mbedtls_ssl_read_version( &ssl->major_ver, &ssl->minor_ver, ssl->conf->transport, buf + 0 ); + ssl->session_negotiate->minor_ver = ssl->minor_ver; if( ssl->major_ver < ssl->conf->min_major_ver || ssl->minor_ver < ssl->conf->min_minor_ver || diff --git a/library/ssl_srv.c b/library/ssl_srv.c index ad98850f0..d82ec0471 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -1392,6 +1392,7 @@ read_record_header: mbedtls_ssl_read_version( &ssl->major_ver, &ssl->minor_ver, ssl->conf->transport, buf ); + ssl->session_negotiate->minor_ver = ssl->minor_ver; ssl->handshake->max_major_ver = ssl->major_ver; ssl->handshake->max_minor_ver = ssl->minor_ver; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 1cfda4a08..95d646875 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -4550,44 +4550,62 @@ static unsigned char ssl_serialized_session_header[] = { * 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. + * struct { * - * Note: When updating the format, remember to keep - * these version+format bytes. + * opaque mbedtls_version[3]; // major, minor, patch + * opaque session_format[2]; // version-specific 16-bit field determining + * // the format of the remaining + * // serialized data. * - * // 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 - * opaque session_id[32]; - * 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 - * 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 + * 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. + * + * uint8_t minor_ver; // Possible values: + * // - TLS 1.2 (MBEDTLS_SSL_MINOR_VERSION_3) + * + * select (serialized_session.minor_ver) { + * + * case MBEDTLS_SSL_MINOR_VERSION_3: // TLS 1.2 + * serialized_session_tls12 data; + * + * }; + * + * } serialized_session; * - * 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 - * together in one block. */ -static int ssl_session_save( const mbedtls_ssl_session *session, - unsigned char omit_header, - unsigned char *buf, - size_t buf_len, - size_t *olen ) + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +/* Serialization of TLS 1.2 sessions: + * + * struct { + * uint64 start_time; + * 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 + * 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 + * 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 + * } serialized_session_tls12; + * + */ +static size_t ssl_session_save_tls12( const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len ) { unsigned char *p = buf; size_t used = 0; + #if defined(MBEDTLS_HAVE_TIME) uint64_t start; #endif @@ -4597,23 +4615,6 @@ static int ssl_session_save( const mbedtls_ssl_session *session, #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ #endif /* MBEDTLS_X509_CRT_PARSE_C */ - - if( !omit_header ) - { - /* - * 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 */ @@ -4756,9 +4757,61 @@ static int ssl_session_save( const mbedtls_ssl_session *session, *p++ = (unsigned char)( ( session->encrypt_then_mac ) & 0xFF ); #endif - /* Done */ - *olen = used; + return( used ); +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ +static int ssl_session_save( const mbedtls_ssl_session *session, + unsigned char omit_header, + unsigned char *buf, + size_t buf_len, + size_t *olen ) +{ + unsigned char *p = buf; + size_t used = 0; + + if( !omit_header ) + { + /* + * Add Mbed TLS 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 ); + } + } + + /* + * TLS version identifier + */ + used += 1; + if( used <= buf_len ) + { + *p++ = session->minor_ver; + } + + /* Forward to version-specific serialization routine. */ + switch( session->minor_ver ) + { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + case MBEDTLS_SSL_MINOR_VERSION_3: + { + size_t remaining_len = used <= buf_len ? buf_len - used : 0; + used += ssl_session_save_tls12( session, p, remaining_len ); + break; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + default: + return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE ); + } + + *olen = used; if( used > buf_len ) return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); @@ -4782,13 +4835,10 @@ int mbedtls_ssl_session_save( const mbedtls_ssl_session *session, * This internal version is wrapped by a public function that cleans up in * case of error, and has an extra option omit_header. */ -static int ssl_session_load( mbedtls_ssl_session *session, - unsigned char omit_header, - const unsigned char *buf, - size_t len ) +static int ssl_session_load_tls12( mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len ) { - const unsigned char *p = buf; - const unsigned char * const end = buf + len; #if defined(MBEDTLS_HAVE_TIME) uint64_t start; #endif @@ -4798,22 +4848,8 @@ static int ssl_session_load( mbedtls_ssl_session *session, #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ #endif /* MBEDTLS_X509_CRT_PARSE_C */ - if( !omit_header ) - { - /* - * 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 ); - } + const unsigned char *p = buf; + const unsigned char * const end = buf + len; /* * Time @@ -4998,6 +5034,54 @@ static int ssl_session_load( mbedtls_ssl_session *session, return( 0 ); } +static int ssl_session_load( mbedtls_ssl_session *session, + unsigned char omit_header, + const unsigned char *buf, + size_t len ) +{ + const unsigned char *p = buf; + const unsigned char * const end = buf + len; + + if( !omit_header ) + { + /* + * Check Mbed TLS 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 ); + } + + /* + * TLS version identifier + */ + if( 1 > (size_t)( end - p ) ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + session->minor_ver = *p++; + + /* Dispatch according to TLS version. */ + switch( session->minor_ver ) + { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + case MBEDTLS_SSL_MINOR_VERSION_3: /* TLS 1.2 */ + { + size_t remaining_len = ( end - p ); + return( ssl_session_load_tls12( session, p, remaining_len ) ); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + default: + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + } +} + /* * Deserialize session: public wrapper for error cleaning */ diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 98ed11fd4..8f742b86c 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -1435,13 +1435,14 @@ cleanup: * Populate a session structure for serialization tests. * Choose dummy values, mostly non-0 to distinguish from the init default. */ -static int ssl_populate_session( mbedtls_ssl_session *session, - int ticket_len, - const char *crt_file ) +static int ssl_populate_session_tls12( mbedtls_ssl_session *session, + int ticket_len, + const char *crt_file ) { #if defined(MBEDTLS_HAVE_TIME) session->start = mbedtls_time( NULL ) - 42; #endif + session->minor_ver = MBEDTLS_SSL_MINOR_VERSION_3; session->ciphersuite = 0xabcd; session->compression = 1; session->id_len = sizeof( session->id ); @@ -1449,7 +1450,7 @@ static int ssl_populate_session( mbedtls_ssl_session *session, memset( session->master, 17, sizeof( session->master ) ); #if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_FS_IO) - if( strlen( crt_file ) != 0 ) + if( crt_file != NULL && strlen( crt_file ) != 0 ) { mbedtls_x509_crt tmp_crt; int ret; @@ -4008,7 +4009,7 @@ void ssl_serialize_session_save_load( int ticket_len, char *crt_file ) mbedtls_ssl_session_init( &restored ); /* Prepare a dummy session to work on */ - TEST_ASSERT( ssl_populate_session( &original, ticket_len, crt_file ) == 0 ); + TEST_ASSERT( ssl_populate_session_tls12( &original, ticket_len, crt_file ) == 0 ); /* Serialize it */ TEST_ASSERT( mbedtls_ssl_session_save( &original, NULL, 0, &len ) @@ -4026,6 +4027,7 @@ void ssl_serialize_session_save_load( int ticket_len, char *crt_file ) #if defined(MBEDTLS_HAVE_TIME) TEST_ASSERT( original.start == restored.start ); #endif + TEST_ASSERT( original.minor_ver == restored.minor_ver ); TEST_ASSERT( original.ciphersuite == restored.ciphersuite ); TEST_ASSERT( original.compression == restored.compression ); TEST_ASSERT( original.id_len == restored.id_len ); @@ -4104,7 +4106,7 @@ void ssl_serialize_session_load_save( int ticket_len, char *crt_file ) mbedtls_ssl_session_init( &session ); /* Prepare a dummy session to work on */ - TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 ); + TEST_ASSERT( ssl_populate_session_tls12( &session, ticket_len, crt_file ) == 0 ); /* Get desired buffer size for serializing */ TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &len0 ) @@ -4154,7 +4156,7 @@ void ssl_serialize_session_save_buf_size( int ticket_len, char *crt_file ) mbedtls_ssl_session_init( &session ); /* Prepare dummy session and get serialized size */ - TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 ); + TEST_ASSERT( ssl_populate_session_tls12( &session, ticket_len, crt_file ) == 0 ); TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &good_len ) == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); @@ -4190,7 +4192,7 @@ void ssl_serialize_session_load_buf_size( int ticket_len, char *crt_file ) mbedtls_ssl_session_init( &session ); /* Prepare serialized session data */ - TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 ); + TEST_ASSERT( ssl_populate_session_tls12( &session, ticket_len, crt_file ) == 0 ); TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &good_len ) == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); TEST_ASSERT( ( good_buf = mbedtls_calloc( 1, good_len ) ) != NULL ); @@ -4235,6 +4237,7 @@ void ssl_session_serialize_version_check( int corrupt_major, corrupt_config == 1 }; mbedtls_ssl_session_init( &session ); + ssl_populate_session_tls12( &session, 0, NULL ); /* Infer length of serialized session. */ TEST_ASSERT( mbedtls_ssl_session_save( &session,