Merge pull request #4517 from hanno-arm/ticket_api_3_0
Implement 3.0-API for SSL session resumption
diff --git a/docs/3.0-migration-guide.d/remove-ssl-get-session_pointer.md b/docs/3.0-migration-guide.d/remove-ssl-get-session_pointer.md
new file mode 100644
index 0000000..a4a4895
--- /dev/null
+++ b/docs/3.0-migration-guide.d/remove-ssl-get-session_pointer.md
@@ -0,0 +1,23 @@
+Remove the SSL API mbedtls_ssl_get_session_pointer()
+-----------------------------------------------------------------
+
+This affects two classes of users:
+
+1. Users who manually inspect parts of the current session through
+ direct structure field access.
+
+2. Users of session resumption who query the current session
+ via `mbedtls_ssl_get_session_pointer()` prior to saving or exporting
+ it via `mbedtls_ssl_session_copy()` or `mbedtls_ssl_session_save()`,
+ respectively.
+
+Migration paths:
+
+1. Mbed TLS 3.0 does not offer a migration path for the usecase 1: Like many
+ other Mbed TLS structures, the structure of `mbedtls_ssl_session` is no
+ longer part of the public API in Mbed TLS 3.0, and direct structure field
+ access is no longer supported. Please see the corresponding migration guide.
+
+2. Users should replace calls to `mbedtls_ssl_get_session_pointer()` by
+ calls to `mbedtls_ssl_get_session()` as demonstrated in the example
+ program `programs/ssl/ssl_client2.c`.
diff --git a/docs/3.0-migration-guide.d/ssl-ticket-api.md b/docs/3.0-migration-guide.d/ssl-ticket-api.md
new file mode 100644
index 0000000..23c53d6
--- /dev/null
+++ b/docs/3.0-migration-guide.d/ssl-ticket-api.md
@@ -0,0 +1,30 @@
+Modified semantics of mbedtls_ssl_{get,set}_session()
+-----------------------------------------------------------------
+
+This affects users who call `mbedtls_ssl_get_session()` or
+`mbedtls_ssl_set_session()` multiple times on the same SSL context
+representing an established TLS 1.2 connection.
+Those users will now observe the second call to fail with
+`MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE`.
+
+Migration path:
+- Exporting the same TLS 1.2 connection multiple times via
+ `mbedtls_ssl_get_session()` leads to multiple copies of
+ the same session. This use of `mbedtls_ssl_get_session()`
+ is discouraged, and the following should be considered:
+ * If the various session copies are later loaded into
+ fresh SSL contexts via `mbedtls_ssl_set_session()`,
+ export via `mbedtls_ssl_get_session()` only once and
+ load the same session into different contexts via
+ `mbedtls_ssl_set_session()`. Since `mbedtls_ssl_set_session()`
+ makes a copy of the session that's being loaded, this
+ is functionally equivalent.
+ * If the various session copies are later serialized
+ via `mbedtls_ssl_session_save()`, export and serialize
+ the session only once via `mbedtls_ssl_get_session()` and
+ `mbedtls_ssl_session_save()` and make copies of the raw
+ data instead.
+- Calling `mbedtls_ssl_set_session()` multiple times in Mbed TLS 2.x
+ is not useful since subsequent calls overwrite the effect of previous
+ calls. Applications achieve equivalent functional behaviour by
+ issuing only the very last call to `mbedtls_ssl_set_session()`.
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 8c44287..603615b 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -933,6 +933,8 @@
unsigned char MBEDTLS_PRIVATE(id)[32]; /*!< session identifier */
unsigned char MBEDTLS_PRIVATE(master)[48]; /*!< the master secret */
+ unsigned char exported;
+
#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 */
@@ -2391,18 +2393,49 @@
#if defined(MBEDTLS_SSL_CLI_C)
/**
- * \brief Request resumption of session (client-side only)
- * Session data is copied from presented session structure.
+ * \brief Load a session for session resumption.
*
- * \param ssl SSL context
- * \param session session context
+ * Sessions loaded through this call will be considered
+ * for session resumption in the next handshake.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed,
- * MBEDTLS_ERR_SSL_BAD_INPUT_DATA if used server-side or
- * arguments are otherwise invalid
+ * \note Even if this call succeeds, it is not guaranteed that
+ * the next handshake will indeed be shortened through the
+ * use of session resumption: The server is always free
+ * to reject any attempt for resumption and fall back to
+ * a full handshake.
+ *
+ * \note This function can handle a variety of mechanisms for session
+ * resumption: For TLS 1.2, both session ID-based resumption and
+ * ticket-based resumption will be considered. For TLS 1.3,
+ * once implemented, sessions equate to tickets, and loading
+ * one or more sessions via this call will lead to their
+ * corresponding tickets being advertised as resumption PSKs
+ * by the client.
+ *
+ * \note Calling this function multiple times will only be useful
+ * once TLS 1.3 is supported. For TLS 1.2 connections, this
+ * function should be called at most once.
+ *
+ * \param ssl The SSL context representing the connection which should
+ * be attempted to be setup using session resumption. This
+ * must be initialized via mbedtls_ssl_init() and bound to
+ * an SSL configuration via mbedtls_ssl_setup(), but
+ * the handshake must not yet have been started.
+ * \param session The session to be considered for session resumption.
+ * This must be a session previously exported via
+ * mbedtls_ssl_get_session(), and potentially serialized and
+ * deserialized through mbedtls_ssl_session_save() and
+ * mbedtls_ssl_session_load() in the meantime.
+ *
+ * \return \c 0 if successful.
+ * \return \c MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE if the session
+ * could not be loaded because of an implementation limitation.
+ * This error is non-fatal, and has no observable effect on
+ * the SSL context or the session that was attempted to be loaded.
+ * \return Another negative error code on other kinds of failure.
*
* \sa mbedtls_ssl_get_session()
+ * \sa mbedtls_ssl_session_load()
*/
int mbedtls_ssl_set_session( mbedtls_ssl_context *ssl, const mbedtls_ssl_session *session );
#endif /* MBEDTLS_SSL_CLI_C */
@@ -2451,7 +2484,6 @@
* 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
@@ -2475,23 +2507,6 @@
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)
@@ -3642,32 +3657,41 @@
#if defined(MBEDTLS_SSL_CLI_C)
/**
- * \brief Save session in order to resume it later (client-side only)
- * Session data is copied to presented session structure.
+ * \brief Export a session in order to resume it later.
*
+ * \param ssl The SSL context representing the connection for which to
+ * to export a session structure for later resumption.
+ * \param session The target structure in which to store the exported session.
+ * This must have been initialized with mbedtls_ssl_init_session()
+ * but otherwise be unused.
*
- * \param ssl SSL context
- * \param session session context
+ * \note This function can handle a variety of mechanisms for session
+ * resumption: For TLS 1.2, both session ID-based resumption and
+ * ticket-based resumption will be considered. For TLS 1.3,
+ * once implemented, sessions equate to tickets, and calling
+ * this function multiple times will export the available
+ * tickets one a time until no further tickets are available,
+ * in which case MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE will
+ * be returned.
*
- * \return 0 if successful,
- * MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed,
- * MBEDTLS_ERR_SSL_BAD_INPUT_DATA if used server-side or
- * arguments are otherwise invalid.
+ * \note Calling this function multiple times will only be useful
+ * once TLS 1.3 is supported. For TLS 1.2 connections, this
+ * function should be called at most once.
*
- * \note Only the server certificate is copied, and not the full chain,
- * so you should not attempt to validate the certificate again
- * by calling \c mbedtls_x509_crt_verify() on it.
- * Instead, you should use the results from the verification
- * in the original handshake by calling \c mbedtls_ssl_get_verify_result()
- * after loading the session again into a new SSL context
- * using \c mbedtls_ssl_set_session().
- *
- * \note Once the session object is not needed anymore, you should
- * free it by calling \c mbedtls_ssl_session_free().
+ * \return \c 0 if successful. In this case, \p session can be used for
+ * session resumption by passing it to mbedtls_ssl_set_session(),
+ * and serialized for storage via mbedtls_ssl_session_save().
+ * \return #MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE if no further session
+ * is available for export.
+ * This error is a non-fatal, and has no observable effect on
+ * the SSL context or the destination session.
+ * \return Another negative error code on other kinds of failure.
*
* \sa mbedtls_ssl_set_session()
+ * \sa mbedtls_ssl_session_save()
*/
-int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl, mbedtls_ssl_session *session );
+int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl,
+ mbedtls_ssl_session *session );
#endif /* MBEDTLS_SSL_CLI_C */
/**
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 5c1bc32..2961637 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3504,6 +3504,9 @@
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
}
+ if( ssl->handshake->resume == 1 )
+ return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+
if( ( ret = mbedtls_ssl_session_copy( ssl->session_negotiate,
session ) ) != 0 )
return( ret );
@@ -4465,6 +4468,8 @@
int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl,
mbedtls_ssl_session *dst )
{
+ int ret;
+
if( ssl == NULL ||
dst == NULL ||
ssl->session == NULL ||
@@ -4473,18 +4478,30 @@
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
}
- return( mbedtls_ssl_session_copy( dst, ssl->session ) );
+ /* Since Mbed TLS 3.0, mbedtls_ssl_get_session() is no longer
+ * idempotent: Each session can only be exported once.
+ *
+ * (This is in preparation for TLS 1.3 support where we will
+ * need the ability to export multiple sessions (aka tickets),
+ * which will be achieved by calling mbedtls_ssl_get_session()
+ * multiple times until it fails.)
+ *
+ * Check whether we have already exported the current session,
+ * and fail if so.
+ */
+ if( ssl->session->exported == 1 )
+ return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+
+ ret = mbedtls_ssl_session_copy( dst, ssl->session );
+ if( ret != 0 )
+ return( ret );
+
+ /* Remember that we've exported the session. */
+ ssl->session->exported = 1;
+ return( 0 );
}
#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.
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 6501c49..322cef8 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -2163,6 +2163,8 @@
if( opt.reco_mode == 1 )
{
+ mbedtls_ssl_session exported_session;
+
/* free any previously saved data */
if( session_data != NULL )
{
@@ -2171,27 +2173,40 @@
session_data = NULL;
}
+ mbedtls_ssl_session_init( &exported_session );
+ ret = mbedtls_ssl_get_session( &ssl, &exported_session );
+ if( ret != 0 )
+ {
+ mbedtls_printf(
+ "failed\n ! mbedtls_ssl_get_session() returned -%#02x\n",
+ (unsigned) -ret );
+ goto exit;
+ }
+
/* get size of the buffer needed */
- mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer( &ssl ),
- NULL, 0, &session_data_len );
+ mbedtls_ssl_session_save( &exported_session, 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 );
+ mbedtls_ssl_session_free( &exported_session );
ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
goto exit;
}
/* actually save session data */
- if( ( ret = mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer( &ssl ),
+ if( ( ret = mbedtls_ssl_session_save( &exported_session,
session_data, session_data_len,
&session_data_len ) ) != 0 )
{
mbedtls_printf( " failed\n ! mbedtls_ssl_session_saved returned -0x%04x\n\n",
(unsigned int) -ret );
+ mbedtls_ssl_session_free( &exported_session );
goto exit;
}
+
+ mbedtls_ssl_session_free( &exported_session );
}
else
{