Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS 1.3: Add definition of mbedtls_ssl_{write,read}_early_data #6621

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions docs/architecture/tls13-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
162 changes: 150 additions & 12 deletions include/mbedtls/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 );
Comment on lines +4940 to +4941
Copy link
Contributor

@yuhaoth yuhaoth Nov 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a internal buffer for early data, before it is called, early data should be saved.

In this case, should we change mbedtls_ssl_read ? If early data buffer is not empty, we should return early data .

Copy link
Contributor Author

@ronald-cron-arm ronald-cron-arm Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan is for mbedtls_ssl_read_early_data() to be called when the handshake is not started and for mbedtls_ssl_read_early_data() to initiate it. Thus when we receive early data while the handshake is on-going we have a buffer (the buffer passed to mbedtls_ssl_read_early_data()) to store the early data. See the pseudo-code I have added in tls13-support.md.

Otherwise mbedtls_ssl_read does not and cannot return early data as it reads data when the handshake is over.

Copy link
Contributor

@yuhaoth yuhaoth Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the early data buffer is not empty when handshake is over, we should free it , right? it must be read by mbedtls_ssl_read_early_data.

#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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_DO means SSL context allow early data, any negative value means not allow.
MBEDTLS_SSL_EARLY_DATA_STATUS_ALLOW_SEND, I am not sure if it should be expose to user. that means mbedtls_ssl_write_early_data can called without error report. If not expose that, we should call mbedtls_ssl_handshake_step inside mbedtls_ssl_write_early_data

Suggested change
#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT 0
#define MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_DO 0
#define MBEDTLS_SSL_EARLY_DATA_STATUS_ALLOW_SEND 3

Copy link
Contributor Author

@ronald-cron-arm ronald-cron-arm Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SSL context is set-up by the application and if it wants to send early data it has to set it up with a proper PSK, enabling early data with mbedtls_ssl_tls13_conf_early_data() ... Thus the application is aware if the SSL context potentially allow early data or not. That's why there is currently no API to tell it so.

Setting-up the SSL context is not trivial though thus we should at least have clear debug logs when early data have been enabled through mbedtls_ssl_tls13_conf_early_data() but eventually early data cannot be sent.

If not expose that, we should call mbedtls_ssl_handshake_step inside mbedtls_ssl_write_early_data

That's the plan like in mbedtls_ssl_write() to call mbedtls_ssl_handshake() in mbedtls_ssl_write_early_data().

Copy link
Contributor

@yuhaoth yuhaoth Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more question. Is below code expected ?

int mbedtls_ssl_get_early_data_status( mbedtls_ssl_context *ssl )
{
    if( is_server || handshake_is_not_over )
        return( BAD_INPUT_DATA );
    return( ssl->early_data_status );
}

#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.
yuhaoth marked this conversation as resolved.
Show resolved Hide resolved
*
* \return #MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED if the client has
* indicated the use of early data and the server has accepted
* it.
yuhaoth marked this conversation as resolved.
Show resolved Hide resolved
*
* \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().
yuhaoth marked this conversation as resolved.
Show resolved Hide resolved
*
*/
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
*
Expand Down
8 changes: 4 additions & 4 deletions library/ssl_tls13_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
yuhaoth marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
Expand Down