Merge remote-tracking branch 'origin/pr/574' into baremetal
diff --git a/ChangeLog b/ChangeLog
index 1196de5..e769dc2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -20,6 +20,10 @@
changed its IP or port. The feature is enabled at compile-time by setting
MBEDTLS_SSL_DTLS_CONNECTION_ID (disabled by default), and at run-time
through the new APIs mbedtls_ssl_conf_cid() and mbedtls_ssl_set_cid().
+ * Add new API functions mbedtls_ssl_session_save() and
+ mbedtls_ssl_session_load() to allow serializing a session, for example to
+ store it in non-volatile storage, and later using it for TLS session
+ resumption.
Bugfix
* Server's RSA certificate in certs.c was SHA-1 signed. In the default
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 7571a3f..7f073af 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. */
/*
@@ -810,6 +811,14 @@
/*
* This structure is used for storing current session data.
+ *
+ * Note: when changing this definition, we need to check and update:
+ * - in tests/suites/test_suite_ssl.function:
+ * ssl_populate_session() and ssl_serialize_session_save_load()
+ * - in library/ssl_tls.c:
+ * mbedtls_ssl_session_init() and mbedtls_ssl_session_free()
+ * mbedtls_ssl_session_save() and ssl_session_load()
+ * ssl_session_copy()
*/
struct mbedtls_ssl_session
{
@@ -2146,6 +2155,90 @@
#endif /* MBEDTLS_SSL_CLI_C */
/**
+ * \brief Load serialized session data into a session structure.
+ * On client, this can be used for loading saved sessions
+ * before resuming them with mbedstls_ssl_set_session().
+ * On server, this can be used for alternative implementations
+ * of session cache or session tickets.
+ *
+ * \warning If a peer certificate chain is associated with the session,
+ * the serialized state will only contain the peer's
+ * end-entity certificate and the result of the chain
+ * verification (unless verification was disabled), but not
+ * the rest of the chain.
+ *
+ * \see mbedtls_ssl_session_save()
+ * \see mbedtls_ssl_set_session()
+ *
+ * \param session The session structure to be populated. It must have been
+ * initialised with mbedtls_ssl_session_init() but not
+ * populated yet.
+ * \param buf The buffer holding the serialized session data. It must be a
+ * readable buffer of at least \p len bytes.
+ * \param len The size of the serialized data in bytes.
+ *
+ * \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).
+ */
+int mbedtls_ssl_session_load( mbedtls_ssl_session *session,
+ const unsigned char *buf,
+ size_t len );
+
+/**
+ * \brief Save session structure as serialized data in a buffer.
+ * On client, this can be used for saving session data,
+ * potentially in non-volatile storage, for resuming later.
+ * On server, this can be used for alternative implementations
+ * of session cache or session tickets.
+ *
+ * \see mbedtls_ssl_session_load()
+ * \see mbedtls_ssl_get_session_pointer()
+ *
+ * \param session The session structure to be saved.
+ * \param buf The buffer to write the serialized data to. It must be a
+ * writeable buffer of at least \p len bytes, or may be \c
+ * NULL if \p len is \c 0.
+ * \param buf_len The number of bytes available for writing in \p buf.
+ * \param olen The size in bytes of the data that has been or would have
+ * been written. It must point to a valid \c size_t.
+ *
+ * \note \p olen is updated to the correct value regardless of
+ * whether \p buf_len was large enough. This makes it possible
+ * to determine the necessary size by calling this function
+ * with \p buf set to \c NULL and \p buf_len to \c 0.
+ *
+ * \return \c 0 if successful.
+ * \return #MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL if \p buf is too small.
+ */
+int mbedtls_ssl_session_save( const mbedtls_ssl_session *session,
+ unsigned char *buf,
+ size_t buf_len,
+ size_t *olen );
+
+/**
+ * \brief Get a pointer to the current session structure, for example
+ * to serialize it.
+ *
+ * \warning Ownership of the session remains with the SSL context, and
+ * the returned pointer is only guaranteed to be valid until
+ * the next API call operating on the same \p ssl context.
+ *
+ * \see mbedtls_ssl_session_save()
+ *
+ * \param ssl The SSL context.
+ *
+ * \return A pointer to the current session if successful.
+ * \return \c NULL if no session is active.
+ */
+const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer( const mbedtls_ssl_context *ssl );
+
+/**
* \brief Set the list of allowed ciphersuites and the preference
* order. First in the list has the highest preference.
* (Overrides all version-specific lists)
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_ticket.c b/library/ssl_ticket.c
index 8492c19..5fe693c 100644
--- a/library/ssl_ticket.c
+++ b/library/ssl_ticket.c
@@ -157,115 +157,6 @@
}
/*
- * Serialize a session in the following format:
- * 0 . n-1 session structure, n = sizeof(mbedtls_ssl_session)
- * n . n+2 peer_cert length = m (0 if no certificate)
- * n+3 . n+2+m peer cert ASN.1
- */
-static int ssl_save_session( const mbedtls_ssl_session *session,
- unsigned char *buf, size_t buf_len,
- size_t *olen )
-{
- unsigned char *p = buf;
- size_t left = buf_len;
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
- size_t cert_len;
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
- if( left < sizeof( mbedtls_ssl_session ) )
- return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
-
- memcpy( p, session, sizeof( mbedtls_ssl_session ) );
- p += sizeof( mbedtls_ssl_session );
- left -= sizeof( mbedtls_ssl_session );
-
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
- if( session->peer_cert == NULL )
- cert_len = 0;
- else
- cert_len = session->peer_cert->raw.len;
-
- if( left < 3 + cert_len )
- return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
-
- *p++ = (unsigned char)( ( cert_len >> 16 ) & 0xFF );
- *p++ = (unsigned char)( ( cert_len >> 8 ) & 0xFF );
- *p++ = (unsigned char)( ( cert_len ) & 0xFF );
-
- if( session->peer_cert != NULL )
- memcpy( p, session->peer_cert->raw.p, cert_len );
-
- p += cert_len;
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
- *olen = p - buf;
-
- return( 0 );
-}
-
-/*
- * Unserialise session, see ssl_save_session()
- */
-static int ssl_load_session( 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_X509_CRT_PARSE_C)
- size_t cert_len;
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
- if( sizeof( mbedtls_ssl_session ) > (size_t)( end - p ) )
- return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
-
- memcpy( session, p, sizeof( mbedtls_ssl_session ) );
- p += sizeof( mbedtls_ssl_session );
-
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
- if( 3 > (size_t)( end - p ) )
- return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
-
- cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
- p += 3;
-
- if( cert_len == 0 )
- {
- session->peer_cert = NULL;
- }
- else
- {
- int ret;
-
- if( cert_len > (size_t)( end - p ) )
- return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
-
- session->peer_cert = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) );
-
- if( session->peer_cert == NULL )
- return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
-
- mbedtls_x509_crt_init( session->peer_cert );
-
- if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert,
- p, cert_len ) ) != 0 )
- {
- mbedtls_x509_crt_free( session->peer_cert );
- mbedtls_free( session->peer_cert );
- session->peer_cert = NULL;
- return( ret );
- }
-
- p += cert_len;
- }
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
-
- if( p != end )
- return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
-
- return( 0 );
-}
-
-/*
* Create session ticket, with the following structure:
*
* struct {
@@ -323,8 +214,9 @@
goto cleanup;
/* Dump session state */
- if( ( ret = ssl_save_session( session,
- state, end - state, &clear_len ) ) != 0 ||
+ if( ( ret = mbedtls_ssl_session_save( session,
+ state, end - state,
+ &clear_len ) ) != 0 ||
(unsigned long) clear_len > 65535 )
{
goto cleanup;
@@ -441,7 +333,7 @@
}
/* Actually load session */
- if( ( ret = ssl_load_session( session, ticket, clear_len ) ) != 0 )
+ if( ( ret = mbedtls_ssl_session_load( session, ticket, clear_len ) ) != 0 )
goto cleanup;
#if defined(MBEDTLS_HAVE_TIME)
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 81bed84..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>
@@ -8767,6 +8768,493 @@
}
#endif /* MBEDTLS_SSL_CLI_C */
+const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer( const mbedtls_ssl_context *ssl )
+{
+ if( ssl == NULL )
+ return( NULL );
+
+ 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
+ * 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
+ *
+ * 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.
+ */
+int mbedtls_ssl_session_save( const mbedtls_ssl_session *session,
+ unsigned char *buf,
+ size_t buf_len,
+ size_t *olen )
+{
+ unsigned char *p = buf;
+ size_t used = 0;
+#if defined(MBEDTLS_HAVE_TIME)
+ uint64_t start;
+#endif
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ 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
+ */
+#if defined(MBEDTLS_HAVE_TIME)
+ used += 8;
+
+ if( used <= buf_len )
+ {
+ start = (uint64_t) session->start;
+
+ *p++ = (unsigned char)( ( start >> 56 ) & 0xFF );
+ *p++ = (unsigned char)( ( start >> 48 ) & 0xFF );
+ *p++ = (unsigned char)( ( start >> 40 ) & 0xFF );
+ *p++ = (unsigned char)( ( start >> 32 ) & 0xFF );
+ *p++ = (unsigned char)( ( start >> 24 ) & 0xFF );
+ *p++ = (unsigned char)( ( start >> 16 ) & 0xFF );
+ *p++ = (unsigned char)( ( start >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( start ) & 0xFF );
+ }
+#endif /* MBEDTLS_HAVE_TIME */
+
+ /*
+ * Basic mandatory fields
+ */
+ used += 2 /* ciphersuite */
+ + 1 /* compression */
+ + 1 /* id_len */
+ + sizeof( session->id )
+ + sizeof( session->master )
+ + 4; /* verify_result */
+
+ if( used <= buf_len )
+ {
+ *p++ = (unsigned char)( ( session->ciphersuite >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->ciphersuite ) & 0xFF );
+
+ *p++ = (unsigned char)( session->compression & 0xFF );
+
+ *p++ = (unsigned char)( session->id_len & 0xFF );
+ memcpy( p, session->id, 32 );
+ p += 32;
+
+ memcpy( p, session->master, 48 );
+ p += 48;
+
+ *p++ = (unsigned char)( ( session->verify_result >> 24 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->verify_result >> 16 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->verify_result >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->verify_result ) & 0xFF );
+ }
+
+ /*
+ * Peer's end-entity certificate
+ */
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ if( session->peer_cert == NULL )
+ cert_len = 0;
+ else
+ cert_len = session->peer_cert->raw.len;
+
+ used += 3 + cert_len;
+
+ if( used <= buf_len )
+ {
+ *p++ = (unsigned char)( ( cert_len >> 16 ) & 0xFF );
+ *p++ = (unsigned char)( ( cert_len >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( cert_len ) & 0xFF );
+
+ if( session->peer_cert != NULL )
+ {
+ memcpy( p, session->peer_cert->raw.p, cert_len );
+ p += cert_len;
+ }
+ }
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
+ /*
+ * Session ticket if any, plus associated data
+ */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
+ used += 3 + session->ticket_len + 4; /* len + ticket + lifetime */
+
+ if( used <= buf_len )
+ {
+ *p++ = (unsigned char)( ( session->ticket_len >> 16 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->ticket_len >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->ticket_len ) & 0xFF );
+
+ if( session->ticket != NULL )
+ {
+ memcpy( p, session->ticket, session->ticket_len );
+ p += session->ticket_len;
+ }
+
+ *p++ = (unsigned char)( ( session->ticket_lifetime >> 24 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->ticket_lifetime >> 16 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->ticket_lifetime >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( session->ticket_lifetime ) & 0xFF );
+ }
+#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
+
+ /*
+ * Misc extension-related info
+ */
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+ used += 1;
+
+ if( used <= buf_len )
+ *p++ = session->mfl_code;
+#endif
+
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+ used += 1;
+
+ if( used <= buf_len )
+ *p++ = (unsigned char)( ( session->trunc_hmac ) & 0xFF );
+#endif
+
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+ used += 1;
+
+ if( used <= buf_len )
+ *p++ = (unsigned char)( ( session->encrypt_then_mac ) & 0xFF );
+#endif
+
+ /* Done */
+ *olen = used;
+
+ if( used > buf_len )
+ return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
+
+ return( 0 );
+}
+
+/*
+ * Unserialize session, see mbedtls_ssl_session_save() for format.
+ *
+ * This internal version is wrapped by a public function that cleans up in
+ * case of error.
+ */
+static int ssl_session_load( 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
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ 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
+ */
+#if defined(MBEDTLS_HAVE_TIME)
+ if( 8 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ start = ( (uint64_t) p[0] << 56 ) |
+ ( (uint64_t) p[1] << 48 ) |
+ ( (uint64_t) p[2] << 40 ) |
+ ( (uint64_t) p[3] << 32 ) |
+ ( (uint64_t) p[4] << 24 ) |
+ ( (uint64_t) p[5] << 16 ) |
+ ( (uint64_t) p[6] << 8 ) |
+ ( (uint64_t) p[7] );
+ p += 8;
+
+ session->start = (time_t) start;
+#endif /* MBEDTLS_HAVE_TIME */
+
+ /*
+ * Basic mandatory fields
+ */
+ if( 2 + 1 + 1 + 32 + 48 + 4 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->ciphersuite = ( p[0] << 8 ) | p[1];
+ p += 2;
+
+ session->compression = *p++;
+
+ session->id_len = *p++;
+ memcpy( session->id, p, 32 );
+ p += 32;
+
+ memcpy( session->master, p, 48 );
+ p += 48;
+
+ session->verify_result = ( (uint32_t) p[0] << 24 ) |
+ ( (uint32_t) p[1] << 16 ) |
+ ( (uint32_t) p[2] << 8 ) |
+ ( (uint32_t) p[3] );
+ p += 4;
+
+ /* Immediately clear invalid pointer values that have been read, in case
+ * we exit early before we replaced them with valid ones. */
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ session->peer_cert = NULL;
+#endif
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
+ session->ticket = NULL;
+#endif
+
+ /*
+ * Peer certificate
+ */
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ if( 3 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
+ p += 3;
+
+ if( cert_len == 0 )
+ {
+ session->peer_cert = NULL;
+ }
+ else
+ {
+ int ret;
+
+ if( cert_len > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->peer_cert = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) );
+
+ if( session->peer_cert == NULL )
+ return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+
+ mbedtls_x509_crt_init( session->peer_cert );
+
+ if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert,
+ p, cert_len ) ) != 0 )
+ {
+ mbedtls_x509_crt_free( session->peer_cert );
+ mbedtls_free( session->peer_cert );
+ session->peer_cert = NULL;
+ return( ret );
+ }
+
+ p += cert_len;
+ }
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
+ /*
+ * Session ticket and associated data
+ */
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
+ if( 3 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->ticket_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
+ p += 3;
+
+ if( session->ticket_len != 0 )
+ {
+ if( session->ticket_len > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->ticket = mbedtls_calloc( 1, session->ticket_len );
+ if( session->ticket == NULL )
+ return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+
+ memcpy( session->ticket, p, session->ticket_len );
+ p += session->ticket_len;
+ }
+
+ if( 4 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->ticket_lifetime = ( (uint32_t) p[0] << 24 ) |
+ ( (uint32_t) p[1] << 16 ) |
+ ( (uint32_t) p[2] << 8 ) |
+ ( (uint32_t) p[3] );
+ p += 4;
+#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
+
+ /*
+ * Misc extension-related info
+ */
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+ if( 1 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->mfl_code = *p++;
+#endif
+
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+ if( 1 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->trunc_hmac = *p++;
+#endif
+
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+ if( 1 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->encrypt_then_mac = *p++;
+#endif
+
+ /* Done, should have consumed entire buffer */
+ if( p != end )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ return( 0 );
+}
+
+/*
+ * Unserialize session: public wrapper for error cleaning
+ */
+int mbedtls_ssl_session_load( mbedtls_ssl_session *session,
+ const unsigned char *buf,
+ size_t len )
+{
+ int ret = ssl_session_load( session, buf, len );
+
+ if( ret != 0 )
+ mbedtls_ssl_session_free( session );
+
+ return( ret );
+}
+
/*
* Perform a single step of the SSL handshake
*/
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 5ac3985..38c94be 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -36,6 +36,8 @@
#define mbedtls_fprintf fprintf
#define mbedtls_snprintf snprintf
#define mbedtls_exit exit
+#define mbedtls_calloc calloc
+#define mbedtls_free free
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif
@@ -103,6 +105,7 @@
#define DFL_DHMLEN -1
#define DFL_RECONNECT 0
#define DFL_RECO_DELAY 0
+#define DFL_RECO_MODE 1
#define DFL_CID_ENABLED 0
#define DFL_CID_VALUE ""
#define DFL_CID_ENABLED_RENEGO -1
@@ -309,8 +312,11 @@
" allow_legacy=%%d default: (library default: no)\n" \
USAGE_RENEGO \
" exchanges=%%d default: 1\n" \
- " reconnect=%%d default: 0 (disabled)\n" \
+ " reconnect=%%d number of reconnections using session resumption\n" \
+ " default: 0 (disabled)\n" \
" reco_delay=%%d default: 0 seconds\n" \
+ " reco_mode=%%d 0: copy session, 1: serialize session\n" \
+ " default: 1\n" \
" reconnect_hard=%%d default: 0 (disabled)\n" \
USAGE_TICKETS \
USAGE_MAX_FRAG_LEN \
@@ -392,6 +398,7 @@
int dhmlen; /* minimum DHM params len in bits */
int reconnect; /* attempt to resume session */
int reco_delay; /* delay in seconds before resuming session */
+ int reco_mode; /* how to keep the session around */
int reconnect_hard; /* unexpectedly reconnect from the same port */
int tickets; /* enable / disable session tickets */
const char *curves; /* list of supported elliptic curves */
@@ -716,6 +723,8 @@
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_ssl_session saved_session;
+ unsigned char *session_data = NULL;
+ size_t session_data_len = 0;
#if defined(MBEDTLS_TIMING_C)
mbedtls_timing_delay_context timer;
#endif
@@ -805,6 +814,7 @@
opt.dhmlen = DFL_DHMLEN;
opt.reconnect = DFL_RECONNECT;
opt.reco_delay = DFL_RECO_DELAY;
+ opt.reco_mode = DFL_RECO_MODE;
opt.reconnect_hard = DFL_RECONNECT_HARD;
opt.tickets = DFL_TICKETS;
opt.alpn_string = DFL_ALPN_STRING;
@@ -971,6 +981,12 @@
if( opt.reco_delay < 0 )
goto usage;
}
+ else if( strcmp( p, "reco_mode" ) == 0 )
+ {
+ opt.reco_mode = atoi( q );
+ if( opt.reco_mode < 0 )
+ goto usage;
+ }
else if( strcmp( p, "reconnect_hard" ) == 0 )
{
opt.reconnect_hard = atoi( q );
@@ -1863,14 +1879,55 @@
mbedtls_printf(" . Saving session for reuse..." );
fflush( stdout );
- if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 )
+ if( opt.reco_mode == 1 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n",
- -ret );
- goto exit;
+ /* free any previously saved data */
+ if( session_data != NULL )
+ {
+ mbedtls_platform_zeroize( session_data, session_data_len );
+ mbedtls_free( session_data );
+ session_data = NULL;
+ }
+
+ /* get size of the buffer needed */
+ mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer( &ssl ),
+ NULL, 0, &session_data_len );
+ session_data = mbedtls_calloc( 1, session_data_len );
+ if( session_data == NULL )
+ {
+ mbedtls_printf( " failed\n ! alloc %u bytes for session data\n",
+ (unsigned) session_data_len );
+ ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
+ goto exit;
+ }
+
+ /* actually save session data */
+ if( ( ret = mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer( &ssl ),
+ session_data, session_data_len,
+ &session_data_len ) ) != 0 )
+ {
+ mbedtls_printf( " failed\n ! mbedtls_ssl_session_saved returned -0x%04x\n\n",
+ -ret );
+ goto exit;
+ }
+ }
+ else
+ {
+ if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 )
+ {
+ mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n",
+ -ret );
+ goto exit;
+ }
}
mbedtls_printf( " ok\n" );
+
+ if( opt.reco_mode == 1 )
+ {
+ mbedtls_printf( " [ Saved %u bytes of session data]\n",
+ (unsigned) session_data_len );
+ }
}
#if defined(MBEDTLS_X509_CRT_PARSE_C)
@@ -2306,10 +2363,22 @@
goto exit;
}
+ if( opt.reco_mode == 1 )
+ {
+ if( ( ret = mbedtls_ssl_session_load( &saved_session,
+ session_data,
+ session_data_len ) ) != 0 )
+ {
+ mbedtls_printf( " failed\n ! mbedtls_ssl_session_load returned -0x%x\n\n",
+ -ret );
+ goto exit;
+ }
+ }
+
if( ( ret = mbedtls_ssl_set_session( &ssl, &saved_session ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_conf_session returned %d\n\n",
- ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_set_session returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -2376,6 +2445,9 @@
mbedtls_ssl_config_free( &conf );
mbedtls_ctr_drbg_free( &ctr_drbg );
mbedtls_entropy_free( &entropy );
+ if( session_data != NULL )
+ mbedtls_platform_zeroize( session_data, session_data_len );
+ mbedtls_free( session_data );
#if defined(_WIN32)
mbedtls_printf( " + Press Enter to exit this program.\n" );
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 5150cd6..9af040f 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -702,7 +702,7 @@
component_test_small_mbedtls_ssl_dtls_max_buffering () {
msg "build: small MBEDTLS_SSL_DTLS_MAX_BUFFERING #1"
- scripts/config.pl set MBEDTLS_SSL_DTLS_MAX_BUFFERING 240
+ scripts/config.pl set MBEDTLS_SSL_DTLS_MAX_BUFFERING 190
CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
make
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 2ea177c..da89642 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -2071,6 +2071,20 @@
-S "a session has been resumed" \
-C "a session has been resumed"
+run_test "Session resume using tickets: session copy" \
+ "$P_SRV debug_level=3 tickets=1 cache_max=0" \
+ "$P_CLI debug_level=3 tickets=1 reconnect=1 reco_mode=0" \
+ 0 \
+ -c "client hello, adding session ticket extension" \
+ -s "found session ticket extension" \
+ -s "server hello, adding session ticket extension" \
+ -c "found session_ticket extension" \
+ -c "parse new session ticket" \
+ -S "session successfully restored from cache" \
+ -s "session successfully restored from ticket" \
+ -s "a session has been resumed" \
+ -c "a session has been resumed"
+
run_test "Session resume using tickets: openssl server" \
"$O_SRV" \
"$P_CLI debug_level=3 tickets=1 reconnect=1" \
@@ -2136,6 +2150,20 @@
-S "a session has been resumed" \
-C "a session has been resumed"
+run_test "Session resume using tickets, DTLS: session copy" \
+ "$P_SRV debug_level=3 dtls=1 tickets=1 cache_max=0" \
+ "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1 reco_mode=0" \
+ 0 \
+ -c "client hello, adding session ticket extension" \
+ -s "found session ticket extension" \
+ -s "server hello, adding session ticket extension" \
+ -c "found session_ticket extension" \
+ -c "parse new session ticket" \
+ -S "session successfully restored from cache" \
+ -s "session successfully restored from ticket" \
+ -s "a session has been resumed" \
+ -c "a session has been resumed"
+
run_test "Session resume using tickets, DTLS: openssl server" \
"$O_SRV -dtls1" \
"$P_CLI dtls=1 debug_level=3 tickets=1 reconnect=1" \
@@ -2232,6 +2260,15 @@
-s "a session has been resumed" \
-c "a session has been resumed"
+run_test "Session resume using cache: session copy" \
+ "$P_SRV debug_level=3 tickets=0" \
+ "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_mode=0" \
+ 0 \
+ -s "session successfully restored from cache" \
+ -S "session successfully restored from ticket" \
+ -s "a session has been resumed" \
+ -c "a session has been resumed"
+
run_test "Session resume using cache: openssl client" \
"$P_SRV debug_level=3 tickets=0" \
"( $O_CLI -sess_out $SESSION; \
@@ -2327,6 +2364,15 @@
-s "a session has been resumed" \
-c "a session has been resumed"
+run_test "Session resume using cache, DTLS: session copy" \
+ "$P_SRV dtls=1 debug_level=3 tickets=0" \
+ "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 reco_mode=0" \
+ 0 \
+ -s "session successfully restored from cache" \
+ -S "session successfully restored from ticket" \
+ -s "a session has been resumed" \
+ -c "a session has been resumed"
+
run_test "Session resume using cache, DTLS: openssl client" \
"$P_SRV dtls=1 debug_level=3 tickets=0" \
"( $O_CLI -dtls1 -sess_out $SESSION; \
@@ -7665,11 +7711,11 @@
# without fragmentation or be reassembled within the bounds of
# MBEDTLS_SSL_DTLS_MAX_BUFFERING. Achieve this by testing with a PSK-based
# handshake, omitting CRTs.
-requires_config_value_at_least "MBEDTLS_SSL_DTLS_MAX_BUFFERING" 240
-requires_config_value_at_most "MBEDTLS_SSL_DTLS_MAX_BUFFERING" 280
+requires_config_value_at_least "MBEDTLS_SSL_DTLS_MAX_BUFFERING" 190
+requires_config_value_at_most "MBEDTLS_SSL_DTLS_MAX_BUFFERING" 230
run_test "DTLS reordering: Buffer encrypted Finished message, drop for fragmented NewSessionTicket" \
-p "$P_PXY delay_srv=NewSessionTicket delay_srv=NewSessionTicket delay_ccs=1" \
- "$P_SRV mtu=190 dgram_packing=0 psk=abc123 psk_identity=foo cookies=0 dtls=1 debug_level=2" \
+ "$P_SRV mtu=140 response_size=90 dgram_packing=0 psk=abc123 psk_identity=foo cookies=0 dtls=1 debug_level=2" \
"$P_CLI dgram_packing=0 dtls=1 debug_level=2 force_ciphersuite=TLS-PSK-WITH-AES-128-CCM-8 psk=abc123 psk_identity=foo" \
0 \
-s "Buffer record from epoch 1" \
diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data
index 5b2025f..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
@@ -8713,3 +8725,95 @@
Record crypt, little space, NULL cipher, SSL3, MD5, short tag, EtM
depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SSL_PROTO_SSL3:MBEDTLS_MD5_C:MBEDTLS_SSL_ENCRYPT_THEN_MAC
ssl_crypt_record_small:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_MD5:1:1:MBEDTLS_SSL_MINOR_VERSION_0:0:0
+
+Session serialization, save-load: no ticket, no cert
+ssl_serialize_session_save_load:0:""
+
+Session serialization, save-load: small ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_save_load:42:""
+
+Session serialization, save-load: large ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_save_load:1023:""
+
+Session serialization, save-load: no ticket, cert
+depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_save_load:0:"data_files/server5.crt"
+
+Session serialization, save-load: small ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_save_load:42:"data_files/server5.crt"
+
+Session serialization, save-load: large ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_save_load:1023:"data_files/server5.crt"
+
+Session serialization, load-save: no ticket, no cert
+ssl_serialize_session_load_save:0:""
+
+Session serialization, load-save: small ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_load_save:42:""
+
+Session serialization, load-save: large ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_load_save:1023:""
+
+Session serialization, load-save: no ticket, cert
+depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_load_save:0:"data_files/server5.crt"
+
+Session serialization, load-save: small ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_load_save:42:"data_files/server5.crt"
+
+Session serialization, load-save: large ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_load_save:1023:"data_files/server5.crt"
+
+Session serialization, save buffer size: no ticket, no cert
+ssl_serialize_session_save_buf_size:0:""
+
+Session serialization, save buffer size: small ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_save_buf_size:42:""
+
+Session serialization, save buffer size: large ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_save_buf_size:1023:""
+
+Session serialization, save buffer size: no ticket, cert
+depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_save_buf_size:0:"data_files/server5.crt"
+
+Session serialization, save buffer size: small ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_save_buf_size:42:"data_files/server5.crt"
+
+Session serialization, save buffer size: large ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_save_buf_size:1023:"data_files/server5.crt"
+
+Session serialization, load buffer size: no ticket, no cert
+ssl_serialize_session_load_buf_size:0:""
+
+Session serialization, load buffer size: small ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_load_buf_size:42:""
+
+Session serialization, load buffer size: large ticket, no cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C
+ssl_serialize_session_load_buf_size:1023:""
+
+Session serialization, load buffer size: no ticket, cert
+depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_load_buf_size:0:"data_files/server5.crt"
+
+Session serialization, load buffer size: small ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_load_buf_size:42:"data_files/server5.crt"
+
+Session serialization, load buffer size: large ticket, cert
+depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO
+ssl_serialize_session_load_buf_size:1023:"data_files/server5.crt"
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index d599b6d..65f5852 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -266,6 +266,68 @@
return( ret );
}
+/*
+ * 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 )
+{
+#if defined(MBEDTLS_HAVE_TIME)
+ session->start = mbedtls_time( NULL ) - 42;
+#endif
+ session->ciphersuite = 0xabcd;
+ session->compression = 1;
+ session->id_len = sizeof( session->id );
+ memset( session->id, 66, session->id_len );
+ memset( session->master, 17, sizeof( session->master ) );
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_FS_IO)
+ if( strlen( crt_file ) != 0 )
+ {
+ int ret;
+
+ session->peer_cert = mbedtls_calloc( 1, sizeof( *session->peer_cert ) );
+ if( session->peer_cert == NULL )
+ return( -1 );
+
+ ret = mbedtls_x509_crt_parse_file( session->peer_cert, crt_file );
+ if( ret != 0 )
+ return( ret );
+ }
+#else
+ (void) crt_file;
+#endif
+ session->verify_result = 0xdeadbeef;
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
+ if( ticket_len != 0 )
+ {
+ session->ticket = mbedtls_calloc( 1, ticket_len );
+ if( session->ticket == NULL )
+ return( -1 );
+ memset( session->ticket, 33, ticket_len );
+ }
+ session->ticket_len = ticket_len;
+ session->ticket_lifetime = 86401;
+#else
+ (void) ticket_len;
+#endif
+
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+ session->mfl_code = 1;
+#endif
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+ session->trunc_hmac = 1;
+#endif
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+ session->encrypt_then_mac = 1;
+#endif
+
+ return( 0 );
+}
+
/* END_HEADER */
/* BEGIN_DEPENDENCIES
@@ -575,3 +637,280 @@
mbedtls_free( buf );
}
/* END_CASE */
+
+/* BEGIN_CASE */
+void ssl_serialize_session_save_load( int ticket_len, char *crt_file )
+{
+ mbedtls_ssl_session original, restored;
+ unsigned char *buf = NULL;
+ size_t len;
+
+ /*
+ * Test that a save-load pair is the identity
+ */
+
+ mbedtls_ssl_session_init( &original );
+ mbedtls_ssl_session_init( &restored );
+
+ /* Prepare a dummy session to work on */
+ TEST_ASSERT( ssl_populate_session( &original, ticket_len, crt_file ) == 0 );
+
+ /* Serialize it */
+ TEST_ASSERT( mbedtls_ssl_session_save( &original, NULL, 0, &len )
+ == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
+ TEST_ASSERT( ( buf = mbedtls_calloc( 1, len ) ) != NULL );
+ TEST_ASSERT( mbedtls_ssl_session_save( &original, buf, len, &len )
+ == 0 );
+
+ /* Restore session from serialized data */
+ TEST_ASSERT( mbedtls_ssl_session_load( &restored, buf, len) == 0 );
+
+ /*
+ * Make sure both session structures are identical
+ */
+#if defined(MBEDTLS_HAVE_TIME)
+ TEST_ASSERT( original.start == restored.start );
+#endif
+ TEST_ASSERT( original.ciphersuite == restored.ciphersuite );
+ TEST_ASSERT( original.compression == restored.compression );
+ TEST_ASSERT( original.id_len == restored.id_len );
+ TEST_ASSERT( memcmp( original.id,
+ restored.id, sizeof( original.id ) ) == 0 );
+ TEST_ASSERT( memcmp( original.master,
+ restored.master, sizeof( original.master ) ) == 0 );
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ TEST_ASSERT( ( original.peer_cert == NULL ) ==
+ ( restored.peer_cert == NULL ) );
+ if( original.peer_cert != NULL )
+ {
+ TEST_ASSERT( original.peer_cert->raw.len ==
+ restored.peer_cert->raw.len );
+ TEST_ASSERT( memcmp( original.peer_cert->raw.p,
+ restored.peer_cert->raw.p,
+ original.peer_cert->raw.len ) == 0 );
+ }
+#endif
+ TEST_ASSERT( original.verify_result == restored.verify_result );
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
+ TEST_ASSERT( original.ticket_len == restored.ticket_len );
+ if( original.ticket_len != 0 )
+ {
+ TEST_ASSERT( original.ticket != NULL );
+ TEST_ASSERT( restored.ticket != NULL );
+ TEST_ASSERT( memcmp( original.ticket,
+ restored.ticket, original.ticket_len ) == 0 );
+ }
+ TEST_ASSERT( original.ticket_lifetime == restored.ticket_lifetime );
+#endif
+
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+ TEST_ASSERT( original.mfl_code == restored.mfl_code );
+#endif
+
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+ TEST_ASSERT( original.trunc_hmac == restored.trunc_hmac );
+#endif
+
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+ TEST_ASSERT( original.encrypt_then_mac == restored.encrypt_then_mac );
+#endif
+
+exit:
+ mbedtls_ssl_session_free( &original );
+ mbedtls_ssl_session_free( &restored );
+ mbedtls_free( buf );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void ssl_serialize_session_load_save( int ticket_len, char *crt_file )
+{
+ mbedtls_ssl_session session;
+ unsigned char *buf1 = NULL, *buf2 = NULL;
+ size_t len0, len1, len2;
+
+ /*
+ * Test that a load-save pair is the identity
+ */
+
+ mbedtls_ssl_session_init( &session );
+
+ /* Prepare a dummy session to work on */
+ TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 );
+
+ /* Get desired buffer size for serializing */
+ TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &len0 )
+ == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
+
+ /* Allocate first buffer */
+ buf1 = mbedtls_calloc( 1, len0 );
+ TEST_ASSERT( buf1 != NULL );
+
+ /* Serialize to buffer and free live session */
+ TEST_ASSERT( mbedtls_ssl_session_save( &session, buf1, len0, &len1 )
+ == 0 );
+ TEST_ASSERT( len0 == len1 );
+ mbedtls_ssl_session_free( &session );
+
+ /* Restore session from serialized data */
+ TEST_ASSERT( mbedtls_ssl_session_load( &session, buf1, len1 ) == 0 );
+
+ /* Allocate second buffer and serialize to it */
+ buf2 = mbedtls_calloc( 1, len0 );
+ TEST_ASSERT( buf2 != NULL );
+ TEST_ASSERT( mbedtls_ssl_session_save( &session, buf2, len0, &len2 )
+ == 0 );
+
+ /* Make sure both serialized versions are identical */
+ TEST_ASSERT( len1 == len2 );
+ TEST_ASSERT( memcmp( buf1, buf2, len1 ) == 0 );
+
+exit:
+ mbedtls_ssl_session_free( &session );
+ mbedtls_free( buf1 );
+ mbedtls_free( buf2 );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void ssl_serialize_session_save_buf_size( int ticket_len, char *crt_file )
+{
+ mbedtls_ssl_session session;
+ unsigned char *buf = NULL;
+ size_t good_len, bad_len, test_len;
+
+ /*
+ * Test that session_save() fails cleanly on small buffers
+ */
+
+ 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( mbedtls_ssl_session_save( &session, NULL, 0, &good_len )
+ == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
+
+ /* Try all possible bad lengths */
+ for( bad_len = 1; bad_len < good_len; bad_len++ )
+ {
+ /* Allocate exact size so that asan/valgrind can detect any overwrite */
+ mbedtls_free( buf );
+ TEST_ASSERT( ( buf = mbedtls_calloc( 1, bad_len ) ) != NULL );
+ TEST_ASSERT( mbedtls_ssl_session_save( &session, buf, bad_len,
+ &test_len )
+ == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
+ TEST_ASSERT( test_len == good_len );
+ }
+
+exit:
+ mbedtls_ssl_session_free( &session );
+ mbedtls_free( buf );
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void ssl_serialize_session_load_buf_size( int ticket_len, char *crt_file )
+{
+ mbedtls_ssl_session session;
+ unsigned char *good_buf = NULL, *bad_buf = NULL;
+ size_t good_len, bad_len;
+
+ /*
+ * Test that session_load() fails cleanly on small buffers
+ */
+
+ mbedtls_ssl_session_init( &session );
+
+ /* Prepare serialized session data */
+ TEST_ASSERT( ssl_populate_session( &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 );
+ TEST_ASSERT( mbedtls_ssl_session_save( &session, good_buf, good_len,
+ &good_len ) == 0 );
+ mbedtls_ssl_session_free( &session );
+
+ /* Try all possible bad lengths */
+ for( bad_len = 0; bad_len < good_len; bad_len++ )
+ {
+ /* Allocate exact size so that asan/valgrind can detect any overread */
+ mbedtls_free( bad_buf );
+ bad_buf = mbedtls_calloc( 1, bad_len ? bad_len : 1 );
+ TEST_ASSERT( bad_buf != NULL );
+ memcpy( bad_buf, good_buf, bad_len );
+
+ TEST_ASSERT( mbedtls_ssl_session_load( &session, bad_buf, bad_len )
+ == MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+ }
+
+exit:
+ mbedtls_ssl_session_free( &session );
+ mbedtls_free( good_buf );
+ 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 */