tls13: Add definition of mbedtls_ssl_{write,read}_early_data

Signed-off-by: Ronald Cron <ronald.cron@arm.com>
This commit is contained in:
Ronald Cron 2022-10-26 18:49:09 +02:00
parent ef25a99f20
commit 4a8c9e2cff
3 changed files with 326 additions and 16 deletions

View file

@ -478,3 +478,175 @@ outbound message on server side as well.
* 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.

View file

@ -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_key_cert mbedtls_ssl_key_cert;
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_send_alert_message( mbedtls_ssl_context *ssl,
*/
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
*

View file

@ -1183,11 +1183,11 @@ int mbedtls_ssl_tls13_write_client_hello_exts( mbedtls_ssl_context *ssl,
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
{