Merge pull request #4806 from hanno-arm/ssl_session_serialization_version
Store TLS version in SSL session structure
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 610aa14..6be6c9c 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -927,6 +927,11 @@
unsigned char MBEDTLS_PRIVATE(exported);
+ /* This field is temporarily duplicated with mbedtls_ssl_context.minor_ver.
+ * Once runtime negotiation of TLS 1.2 and TLS 1.3 is implemented, it needs
+ * to be studied whether one of them can be removed. */
+ 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 */
@@ -1245,6 +1250,10 @@
#endif /* MBEDTLS_SSL_RENEGOTIATION */
int MBEDTLS_PRIVATE(major_ver); /*!< equal to MBEDTLS_SSL_MAJOR_VERSION_3 */
+
+ /* This field is temporarily duplicated with mbedtls_ssl_context.minor_ver.
+ * Once runtime negotiation of TLS 1.2 and TLS 1.3 is implemented, it needs
+ * to be studied whether one of them can be removed. */
int MBEDTLS_PRIVATE(minor_ver); /*!< one of MBEDTLS_SSL_MINOR_VERSION_x macros */
unsigned MBEDTLS_PRIVATE(badmac_seen); /*!< records with a bad MAC received */
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index ef391b3..e0a1c24 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -2024,6 +2024,7 @@
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 ad98850..d82ec04 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -1392,6 +1392,7 @@
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 fe3b5e2..bb5ddc4 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -655,7 +655,6 @@
* - [in] ciphersuite
* - [in] master
* - [in] encrypt_then_mac
- * - [in] trunc_hmac
* - [in] compression
* - [in] tls_prf: pointer to PRF to use for key derivation
* - [in] randbytes: buffer holding ServerHello.random + ClientHello.random
@@ -4488,8 +4487,6 @@
#define SSL_SERIALIZED_SESSION_CONFIG_MFL 0
#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
-#define SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC 0
-
#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
#define SSL_SERIALIZED_SESSION_CONFIG_ETM 1
#else
@@ -4506,9 +4503,8 @@
#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_ETM_BIT 4
+#define SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT 5
#define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG \
( (uint16_t) ( \
@@ -4516,7 +4512,6 @@
( 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 ) ) )
@@ -4532,44 +4527,61 @@
* 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]; // library version: major, minor, patch
+ * opaque session_format[2]; // library-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.
*
- * 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.
+ * // 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; // Protocol-version. 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;
+ *
*/
-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 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
@@ -4579,23 +4591,6 @@
#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
*/
@@ -4738,9 +4733,61 @@
*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 );
@@ -4764,13 +4811,10 @@
* 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
@@ -4780,22 +4824,8 @@
#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
@@ -4980,6 +5010,54 @@
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 081e8a4..72b0cdb 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -1435,13 +1435,14 @@
* 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 @@
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;
@@ -4005,7 +4006,7 @@
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 )
@@ -4023,6 +4024,7 @@
#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 );
@@ -4101,7 +4103,7 @@
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 )
@@ -4151,7 +4153,7 @@
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 );
@@ -4187,7 +4189,7 @@
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 );
@@ -4232,6 +4234,7 @@
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,