Store TLS version in SSL session structure

Instances of `mbedtls_ssl_session` represent data enabling session resumption.

With the introduction of TLS 1.3, the format of this data changes. We therefore
need TLS-version field as part of `mbedtlsl_ssl_session` which allows distinguish
1.2 and 1.3 sessions.

This commit introduces such a TLS-version field to mbedtls_ssl_session.

The change has a few ramifications:

- Session serialization/deserialization routines need to be adjusted.

  This is achieved by adding the TLS-version after the header of
  Mbed TLS version+config, and by having the subsequent structure
  of the serialized data depend on the value of this field.

  The details are described in terms of the RFC 8446 presentation language.

  The 1.2 session (de)serialization are moved into static helper functions,
  while the top-level session (de)serialization only parses the Mbed TLS
  version+config header and the TLS-version field, and dispatches according
  to the found version.

  This way, it will be easy to add support for TLS 1.3 sessions in the future.

- Tests for session serialization need to be adjusted

- Once we add support for TLS 1.3, with runtime negotiation of 1.2 vs. 1.3,
  we will need to have some logic comparing the TLS version of the proposed session
  to the negotiated TLS version. For now, however, we only support TLS 1.2,
  and no such logic is needed. Instead, we just store the TLS version in the
  session structure at the same point when we populate mbedtls_ssl_context.minor_ver.

The change introduces some overlap between `mbedtls_ssl_session.minor_ver` and
`mbedtls_ssl_context.minor_ver`, which should be studied and potentially resolved.
However, with both fields being private and explicitly marked so, this can happen
in a later change.

Signed-off-by: Hanno Becker <hanno.becker@arm.com>
This commit is contained in:
Hanno Becker 2021-07-23 06:25:48 +01:00
parent ac6cc9fbc4
commit fadbdbb576
5 changed files with 171 additions and 80 deletions

View file

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

View file

@ -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 ||

View file

@ -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;

View file

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

View file

@ -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,