Merge remote-tracking branch 'origin/pr/567' into baremetal
diff --git a/include/mbedtls/error.h b/include/mbedtls/error.h
index 765fd42..31f294f 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 d435a69..df62069 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 @@
  * \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 0a9baeb..546fa49 100644
--- a/library/error.c
+++ b/library/error.c
@@ -525,6 +525,8 @@
             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 876457c..b61453f 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 <string.h>
 
@@ -8776,22 +8777,112 @@
 }
 
 /*
+ * 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
@@ -8812,6 +8903,19 @@
 #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
      */
 #if defined(MBEDTLS_HAVE_TIME)
@@ -8965,6 +9069,20 @@
 #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
      */
 #if defined(MBEDTLS_HAVE_TIME)
diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data
index aca26a9..edb87d8 100644
--- a/tests/suites/test_suite_ssl.data
+++ b/tests/suites/test_suite_ssl.data
@@ -58,6 +58,18 @@
 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 1b55018..65f5852 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -851,3 +851,66 @@
     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 */