tls13: Add definition of mbedtls_ssl_{write,read}_early_data
Signed-off-by: Ronald Cron <ronald.cron@arm.com>
diff --git a/docs/architecture/tls13-support.md b/docs/architecture/tls13-support.md
index f30590b..85482ba 100644
--- a/docs/architecture/tls13-support.md
+++ b/docs/architecture/tls13-support.md
@@ -478,3 +478,175 @@
* state change: the state change is done in the main state handler to ease
the navigation of the state machine transitions.
+
+
+Writing and reading early or 0-RTT data
+---------------------------------------
+
+An application function to write and send a buffer of data to a server through
+TLS may plausibly look like:
+
+```
+int write_data( mbedtls_ssl_context *ssl,
+ const unsigned char *data_to_write,
+ size_t data_to_write_len,
+ size_t *data_written )
+{
+ *data_written = 0;
+
+ while( *data_written < data_to_write_len )
+ {
+ ret = mbedtls_ssl_write( ssl, data_to_write + *data_written,
+ data_to_write_len - *data_written );
+
+ if( ret < 0 &&
+ ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ {
+ return( ret );
+ }
+
+ *data_written += ret;
+ }
+
+ return( 0 );
+}
+```
+where ssl is the SSL context to use, data_to_write the address of the data
+buffer and data_to_write_len the number of data bytes. The handshake may
+not be completed, not even started for the SSL context ssl when the function is
+called and in that case the mbedtls_ssl_write() API takes care transparently of
+completing the handshake before to write and send data to the server. The
+mbedtls_ssl_write() may not been able to write and send all data in one go thus
+the need for a loop calling it as long as there are still data to write and
+send.
+
+An application function to write and send early data and only early data,
+data sent during the first flight of client messages while the handshake is in
+its initial phase, would look completely similar but the call to
+mbedtls_ssl_write_early_data() instead of mbedtls_ssl_write().
+```
+int write_early_data( mbedtls_ssl_context *ssl,
+ const unsigned char *data_to_write,
+ size_t data_to_write_len,
+ size_t *data_written )
+{
+ *data_written = 0;
+
+ while( *data_written < data_to_write_len )
+ {
+ ret = mbedtls_ssl_write_early_data( ssl, data_to_write + *data_written,
+ data_to_write_len - *data_written );
+
+ if( ret < 0 &&
+ ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ {
+ return( ret );
+ }
+
+ *data_written += ret;
+ }
+
+ return( 0 );
+}
+```
+Note that compared to write_data(), write_early_data() can also return
+MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA and that should be handled
+specifically by the user of write_early_data(). A fresh SSL context (typically
+just after a call to mbedtls_ssl_setup() or mbedtls_ssl_session_reset()) would
+be expected when calling `write_early_data`.
+
+All together, code to write and send a buffer of data as long as possible as
+early data and then as standard post-handshake application data could
+plausibly look like:
+
+```
+ret = write_early_data( ssl, data_to_write, data_to_write_len,
+ &early_data_written );
+if( ret < 0 &&
+ ret != MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA )
+{
+ goto error;
+}
+
+ret = write_data( ssl, data_to_write + early_data_written,
+ data_to_write_len - early_data_written, &data_written );
+if( ret < 0 )
+ goto error;
+
+data_written += early_data_written;
+```
+
+Finally, taking into account that the server may reject early data, application
+code to write and send a buffer of data could plausibly look like:
+```
+ret = write_early_data( ssl, data_to_write, data_to_write_len,
+ &early_data_written );
+if( ret < 0 &&
+ ret != MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA )
+{
+ goto error;
+}
+
+/*
+ * Make sure the handshake is completed as it is a requisite to
+ * mbedtls_ssl_get_early_data_status().
+ */
+while( !mbedtls_ssl_is_handshake_over( ssl ) )
+{
+ ret = mbedtls_ssl_handshake( ssl );
+ if( ret < 0 &&
+ ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ {
+ goto error;
+ }
+}
+
+ret = mbedtls_ssl_get_early_data_status( ssl );
+if( ret < 0 )
+ goto error;
+
+if( ret == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED )
+ early_data_written = 0;
+
+ret = write_data( ssl, data_to_write + early_data_written,
+ data_to_write_len - early_data_written, &data_written );
+if( ret < 0 )
+ goto error;
+
+data_written += early_data_written;
+```
+
+Basically, the same holds for reading early data on the server side without the
+complication of possible rejection. An application function to read early data
+into a given buffer could plausibly look like:
+```
+int read_early_data( mbedtls_ssl_context *ssl,
+ unsigned char *buffer,
+ size_t buffer_size,
+ size_t *data_len )
+{
+ *data_len = 0;
+
+ while( *data_len < buffer_size )
+ {
+ ret = mbedtls_ssl_read_early_data( ssl, buffer + *data_len,
+ buffer_size - *data_len );
+
+ if( ret < 0 &&
+ ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ {
+ return( ret );
+ }
+
+ *data_len += ret;
+ }
+
+ return( 0 );
+}
+```
+with again calls to read_early_data() expected to be done with a fresh SSL
+context.
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index dddaaea..ea58661 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -96,15 +96,16 @@
/* Error space gap */
/** Processing of the Certificate handshake message failed. */
#define MBEDTLS_ERR_SSL_BAD_CERTIFICATE -0x7A00
+/* Error space gap */
/**
* Received NewSessionTicket Post Handshake Message.
* This error code is experimental and may be changed or removed without notice.
*/
#define MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET -0x7B00
-/* Error space gap */
-/* Error space gap */
-/* Error space gap */
-/* Error space gap */
+/** Not possible to read early data */
+#define MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA -0x7B80
+/** Not possible to write early data */
+#define MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA -0x7C00
/* Error space gap */
/* Error space gap */
/* Error space gap */
@@ -806,14 +807,6 @@
typedef struct mbedtls_ssl_flight_item mbedtls_ssl_flight_item;
#endif
-#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN 0
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT 1
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_INDICATION_SENT 2
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED 3
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED 4
-#endif
-
#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS)
typedef uint8_t mbedtls_ssl_tls13_ticket_flags;
@@ -4897,6 +4890,151 @@
*/
int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl );
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+
+#if defined(MBEDTLS_SSL_SRV_C)
+/**
+ * \brief Read at most 'len' application data bytes while performing
+ * the handshake (early data).
+ *
+ * \note This function behaves mainly as mbedtls_ssl_read(). The
+ * specification of mbedtls_ssl_read() relevant to TLS 1.3
+ * (thus not the parts specific to (D)TLS 1.2) applies to this
+ * function and the present documentation is restricted to the
+ * differences with mbedtls_ssl_read().
+ *
+ * \param ssl SSL context
+ * \param buf buffer that will hold the data
+ * \param len maximum number of bytes to read
+ *
+ * \return One additional specific return value:
+ * #MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA.
+ *
+ * #MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA is returned when it
+ * is not possible to read early data for the SSL context
+ * \p ssl.
+ *
+ * It may have been possible and it is not possible
+ * anymore because the server received the End of Early Data
+ * message or the maximum number of allowed early data for the
+ * PSK in use has been reached.
+ *
+ * It may never have been possible and will never be possible
+ * for the SSL context \p ssl because the use of early data
+ * is disabled for that context or more generally the context
+ * is not suitably configured to enable early data or the
+ * client does not use early data or the first call to the
+ * function was done while the handshake was already too
+ * advanced to gather and accept early data.
+ *
+ * It is not possible to read early data for the SSL context
+ * \p ssl but this does not preclude for using it with
+ * mbedtls_ssl_write(), mbedtls_ssl_read() or
+ * mbedtls_ssl_handshake().
+ *
+ * \note When a server wants to retrieve early data, it is expected
+ * that this function starts the handshake for the SSL context
+ * \p ssl. But this is not mandatory.
+ *
+ */
+int mbedtls_ssl_read_early_data( mbedtls_ssl_context *ssl,
+ unsigned char *buf, size_t len );
+#endif /* MBEDTLS_SSL_SRV_C */
+
+#if defined(MBEDTLS_SSL_CLI_C)
+/**
+ * \brief Try to write exactly 'len' application data bytes while
+ * performing the handshake (early data).
+ *
+ * \note This function behaves mainly as mbedtls_ssl_write(). The
+ * specification of mbedtls_ssl_write() relevant to TLS 1.3
+ * (thus not the parts specific to (D)TLS1.2) applies to this
+ * function and the present documentation is restricted to the
+ * differences with mbedtls_ssl_write().
+ *
+ * \param ssl SSL context
+ * \param buf buffer holding the data
+ * \param len how many bytes must be written
+ *
+ * \return One additional specific return value:
+ * #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA.
+ *
+ * #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA is returned when it
+ * is not possible to write early data for the SSL context
+ * \p ssl.
+ *
+ * It may have been possible and it is not possible
+ * anymore because the client received the server Finished
+ * message, the server rejected early data or the maximum
+ * number of allowed early data for the PSK in use has been
+ * reached.
+ *
+ * It may never have been possible and will never be possible
+ * for the SSL context \p ssl because the use of early data
+ * is disabled for that context or more generally the context
+ * is not suitably configured to enable early data or the first
+ * call to the function was done while the handshake was
+ * already completed.
+ *
+ * It is not possible to write early data for the SSL context
+ * \p ssl but this does not preclude for using it with
+ * mbedtls_ssl_write(), mbedtls_ssl_read() or
+ * mbedtls_ssl_handshake().
+ *
+ * \note This function may write early data only if the SSL context
+ * has been configured for the handshake with a PSK for which
+ * early data is allowed.
+ *
+ * \note To maximize the number of early data that can be written in
+ * the course of the handshake, it is expected that this
+ * function starts the handshake for the SSL context \p ssl.
+ * But this is not mandatory.
+ *
+ * \note This function does not provide any information on whether
+ * the server has accepted or will accept early data or not.
+ * When it returns a positive value, it just means that it
+ * has written early data to the server. To know whether the
+ * server has accepted early data or not, you should call
+ * mbedtls_ssl_get_early_data_status() with the handshake
+ * completed.
+ */
+int mbedtls_ssl_write_early_data( mbedtls_ssl_context *ssl,
+ const unsigned char *buf, size_t len );
+
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT 0
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED 1
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED 2
+/**
+ * \brief Get the status of the negotiation of the use of early data.
+ *
+ * \param ssl The SSL context to query
+ *
+ * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if this function is called
+ * from the server-side.
+ *
+ * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if this function is called
+ * prior to completion of the handshake.
+ *
+ * \return #MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT if the client has
+ * not indicated the use of early data to the server.
+ *
+ * \return #MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED if the client has
+ * indicated the use of early data and the server has accepted
+ * it.
+ *
+ * \return #MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED if the client has
+ * indicated the use of early data but the server has rejected
+ * it. In this situation, the client may want to re-send the
+ * early data it may have tried to send by calling
+ * mbedtls_ssl_write_early_data() as ordinary post-handshake
+ * application data by calling mbedtls_ssl_write().
+ *
+ */
+int mbedtls_ssl_get_early_data_status( mbedtls_ssl_context *ssl );
+#endif /* MBEDTLS_SSL_CLI_C */
+
+#endif /* MBEDTLS_SSL_EARLY_DATA */
+
/**
* \brief Free referenced items in an SSL context and clear memory
*
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index 9d2e69e..0109f77 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -1183,11 +1183,11 @@
return( ret );
p += ext_len;
- /* Initializes the status to `indication sent`. It will be updated to
- * `accepted` or `rejected` depending on whether the EncryptedExtension
- * message will contain an early data indication extension or not.
+ /* Initializes the status to `rejected`. It will be updated to
+ * `accepted` if the EncryptedExtension message contain an early data
+ * indication extension.
*/
- ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_INDICATION_SENT;
+ ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
}
else
{