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

[0-RTT][RFC] Create new API for sending early data #387

Open
wants to merge 3 commits into
base: tls13-prototype
Choose a base branch
from
Open
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
120 changes: 119 additions & 1 deletion include/mbedtls/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,14 @@
#define MBEDTLS_SSL_EARLY_DATA_OFF 0
#define MBEDTLS_SSL_EARLY_DATA_ON 1

#define MBEDTLS_SSL_EARLY_DATA_OLD_API 0
#define MBEDTLS_SSL_EARLY_DATA_NEW_API 1

#define MBEDTLS_SSL_EARLY_DATA_STATE_DISABLED 0
#define MBEDTLS_SSL_EARLY_DATA_STATE_ENABLED 1
#define MBEDTLS_SSL_EARLY_DATA_STATE_OFF 2 /* early_data extension sent, cannot send early_data */
#define MBEDTLS_SSL_EARLY_DATA_STATE_ON 3 /* early_data extension sent, can send early_data */

#define MBEDTLS_SSL_FORCE_RR_CHECK_OFF 0
#define MBEDTLS_SSL_FORCE_RR_CHECK_ON 1

Expand Down Expand Up @@ -862,6 +870,42 @@ typedef struct mbedtls_ssl_flight_item mbedtls_ssl_flight_item;
* of 0-RTT and the server has accepted it.
*/
int mbedtls_ssl_get_early_data_status( mbedtls_ssl_context *ssl );

/**
* \brief Get information about the eligibility of sending early data
* in a 0-RTT handshake
*
* \param ssl The SSL context to query.
*
* \returns #MBEDTLS_SSL_EARLY_DATA_STATE_DISABLED if early data is
* disabled for the session.
* \returns #MBEDTLS_SSL_EARLY_DATA_STATE_ENABLED if early data is
* enabled for the session.
* \returns #MBEDTLS_SSL_EARLY_DATA_STATE_ON if the client has provided
* early data indication.
* \returns #MBEDTLS_SSL_EARLY_DATA_STATE_OFF if early data indication
* was provided but the session can no longer send early data,
* (e.g. EndOfEarlyData message has been sent).
*
* \note This function may only be used if the new 0-RTT API is
* enabled by passing #MBEDTLS_SSL_EARLY_DATA_NEW_API to
* \c mbedtls_ssl_conf_early_data().
*/
int mbedtls_ssl_get_early_data_state( mbedtls_ssl_context *ssl );

/**
* \brief Get current status of the handshake.
*
* \param ssl The SSL context to query.
*
* \returns \c 1 if the handshake is over.
* \returns \c 0 if the handshake is still in progress.
*
* \note If you need to determine whether to defer sending data until
* the connection is replay-safe, this function can be used to
* check if the handshake is complete.
*/
int mbedtls_ssl_is_init_finished( mbedtls_ssl_context *ssl );
#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_ZERO_RTT && MBEDTLS_SSL_CLI_C */

typedef enum
Expand Down Expand Up @@ -1592,7 +1636,12 @@ struct mbedtls_ssl_config
* - MBEDTLS_SSL_EARLY_DATA_DISABLED,
* - MBEDTLS_SSL_EARLY_DATA_ENABLED
*/
int early_data_enabled;
int MBEDTLS_PRIVATE(early_data_enabled);
/*!< Early data api:
* - MBEDTLS_SSL_EARLY_DATA_OLD_API,
* - MBEDTLS_SSL_EARLY_DATA_NEW_API
*/
int MBEDTLS_PRIVATE(early_data_api);
#if defined(MBEDTLS_SSL_SRV_C)
/* Max number of bytes of early data acceptable by the server. */
size_t max_early_data;
Expand Down Expand Up @@ -2084,6 +2133,7 @@ void mbedtls_ssl_conf_authmode( mbedtls_ssl_config *conf, int authmode );
#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_ZERO_RTT)
void mbedtls_ssl_conf_early_data( mbedtls_ssl_config* conf, int early_data,
size_t max_early_data,
int early_data_api,
int(*early_data_callback)( mbedtls_ssl_context*,
const unsigned char*,
size_t ) );
Expand Down Expand Up @@ -5058,6 +5108,74 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len )
*/
int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len );

#if defined(MBEDTLS_ZERO_RTT)
/** TODO: Add complete documentation. */
/**
* \brief Try to write exactly 'len' early data bytes
*
* \warning This function will do partial writes in some cases. If the
* return value is non-negative but less than length, the
* function must be called again with updated arguments:
* buf + ret, len - ret (if ret is the return value) until
* it returns a value equal to the last 'len' argument.
*
* \param ssl SSL context
* \param buf buffer holding the data
* \param len how many bytes must be written
*
* \return The (non-negative) number of bytes actually written if
* successful (may be less than \p len).
* \return #MBEDTLS_ERR_SSL_WANT_READ or #MBEDTLS_ERR_SSL_WANT_WRITE
* if the handshake is incomplete and waiting for data to
* be available for reading from or writing to the underlying
* transport - in this case you may call this function again
* when the underlying transport is ready for the operation to
* send additional early data.
*
* \return Another SSL error code - in this case you must stop using
* the context (see below).
*
* \warning If this function returns something other than
* a non-negative value,
* #MBEDTLS_ERR_SSL_WANT_READ,
* #MBEDTLS_ERR_SSL_WANT_WRITE,
* #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS or
* #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS,
* you must stop using the SSL context for reading or writing,
* and either free it or call \c mbedtls_ssl_session_reset()
* on it before re-using it for a new connection; the current
* connection must be closed.
*
* \warning 0-RTT can be subject to replay attacks and should only be
* used for data that is replay-safe.
* \c mbedtls_ssl_is_init_finished() can be used if you need to
* ensure a request is sent only when the connection is
* replay-safe.
*
* \note This function will implicitly begin the handshake until it
* reaches the early data stage, at which point it will send
* the early data.
*
* \note This function can be called multiple times throughout the
* duration of the handshake, but it must be the first IO
* function called on a new connection to guarantee the
* handshake does not complete atomically before the initial
* early data is sent. After a successful return, you should
* call \c mbedtls_ssl_handshake() to try and complete the
* handshake.
*
* \note This function may not be called if
* \c mbedtls_ssl_get_early_data_state() returns
* #MBEDTLS_SSL_EARLY_DATA_STATE_DISABLED or
* #MBEDTLS_SSL_EARLY_DATA_STATE_OFF.
*
* \note This function may only be used if the new 0-RTT API is
* enabled by passing #MBEDTLS_SSL_EARLY_DATA_NEW_API to
* \c mbedtls_ssl_conf_early_data().
*/
int mbedtls_ssl_write_early_data( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len );
#endif /* MBEDTLS_ZERO_RTT*/

/**
* \brief Send an alert message
*
Expand Down
9 changes: 5 additions & 4 deletions library/ssl_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -934,11 +934,12 @@ struct mbedtls_ssl_handshake_params
#if defined(MBEDTLS_ZERO_RTT)
mbedtls_ssl_tls13_early_secrets early_secrets;

/*!< Early data indication:
0 -- MBEDTLS_SSL_EARLY_DATA_DISABLED (for no early data), and
1 -- MBEDTLS_SSL_EARLY_DATA_ENABLED (for use early data)
*/
int early_data;
int early_data_write;
int early_data_ready;
#if defined(MBEDTLS_SSL_SRV_C)
int skip_failed_decryption;
#endif /* MBEDTLS_SSL_SRV_C */
#endif /* MBEDTLS_ZERO_RTT */

#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
Expand Down
139 changes: 131 additions & 8 deletions library/ssl_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -4114,10 +4114,21 @@ int mbedtls_ssl_read_record( mbedtls_ssl_context *ssl,
return( ret );
}

if( ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE &&
update_hs_digest == 1 )
if( ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE )
{
mbedtls_ssl_update_handshake_status( ssl );
if( update_hs_digest == 1)
mbedtls_ssl_update_handshake_status( ssl );

#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_ZERO_RTT)
if( ssl->handshake != NULL &&
ssl->handshake->skip_failed_decryption != 0 &&
ssl->transform_in != NULL )
{
/* Record deprotected successfully */
ssl->handshake->skip_failed_decryption = 0;
MBEDTLS_SSL_DEBUG_MSG( 4, ( "disabling skip_failed_decryption" ) );
}
#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_ZERO_RTT */
}
}
else
Expand Down Expand Up @@ -4711,6 +4722,41 @@ static int ssl_buffer_future_record( mbedtls_ssl_context *ssl,

#endif /* MBEDTLS_SSL_PROTO_DTLS */

/*
* RFC 8446:
* "If the client attempts a 0-RTT handshake but the server
* rejects it, the server will generally not have the 0-RTT record
* protection keys and must instead use trial decryption (either with
* the 1-RTT handshake keys or by looking for a cleartext ClientHello in
* the case of a HelloRetryRequest) to find the first non-0-RTT message."
*/
#if defined(MBEDTLS_SSL_SRV_C)
static int ssl_should_drop_record( mbedtls_ssl_context *ssl )
{
#if defined(MBEDTLS_ZERO_RTT) && !defined(MBEDTLS_SSL_USE_MPS)
if( ssl->conf->endpoint != MBEDTLS_SSL_IS_SERVER || ssl->handshake == NULL )
return( 0 );

/*
* Drop record iff:
* 1. Client indicated early data use (skip_failed_decryption).
* 2. Server does not have early data enabled (skip_failed_decryption).
* 3. First non-0-RTT record has not yet been found (skip_failed_decryption).
* 4. 1-RTT handshake keys are in use.
*/
if( ssl->handshake->skip_failed_decryption == 1 &&
ssl->transform_in == ssl->handshake->transform_handshake )
{
return( 1 );
}

#endif /* MBEDTLS_ZERO_RTT && !MBEDTLS_SSL_USE_MPS */
((void) ssl);

return( 0 );
}
#endif /* MBEDTLS_SSL_SRV_C */

static int ssl_get_next_record( mbedtls_ssl_context *ssl )
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
Expand Down Expand Up @@ -4879,15 +4925,25 @@ static int ssl_get_next_record( mbedtls_ssl_context *ssl )
else
#endif
{
/* Error out (and send alert) on invalid records */
#if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES)
if( ret == MBEDTLS_ERR_SSL_INVALID_MAC )
{
#if defined(MBEDTLS_SSL_SRV_C)
if( ssl->handshake != NULL &&
ssl_should_drop_record( ssl ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "invalid record (mac), dropping 0-RTT message" ) );
return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
#endif /* MBEDTLS_SSL_SRV_C */

/* Error out (and send alert) on invalid records */
#if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES)
mbedtls_ssl_send_alert_message( ssl,
MBEDTLS_SSL_ALERT_LEVEL_FATAL,
MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC );
#endif /* MBEDTLS_SSL_ALL_ALERT_MESSAGES */
}
#endif

return( ret );
}
}
Expand Down Expand Up @@ -5886,6 +5942,15 @@ int mbedtls_ssl_write( mbedtls_ssl_context *ssl,
return( ret );
}

int mbedtls_ssl_write_early_data( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len )
{
( ( void ) buf );
( ( void ) len );

MBEDTLS_SSL_DEBUG_MSG( 1, ( "new 0-RTT api is not compatible with MPS" ) );
return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
}

/*
* Notify the peer that the connection is being closed
*/
Expand Down Expand Up @@ -6217,8 +6282,9 @@ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_

#if defined(MBEDTLS_ZERO_RTT)
/* TODO: What's the purpose of this check? */
if( ( ssl->handshake != NULL ) &&
( ssl->handshake->early_data == MBEDTLS_SSL_EARLY_DATA_OFF ) )
if( ( ssl->conf->early_data_api == MBEDTLS_SSL_EARLY_DATA_NEW_API ) ||
( ( ssl->handshake != NULL ) &&
( ssl->handshake->early_data == MBEDTLS_SSL_EARLY_DATA_OFF ) ) )
#endif /* MBEDTLS_ZERO_RTT */
{
if( mbedtls_ssl_is_handshake_over( ssl ) == 0 )
Expand All @@ -6238,6 +6304,63 @@ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_
return( ret );
}

#if defined(MBEDTLS_ZERO_RTT)
/*
* Write application data as early data (public-facing wrapper)
*/
int mbedtls_ssl_write_early_data( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len )
{
if( ssl->conf->early_data_api == MBEDTLS_SSL_EARLY_DATA_OLD_API )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "new api must be enabled for mbedtls_ssl_write_early_data" ) );
return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
}

int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;

MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write early_data" ) );

if( ssl == NULL || ssl->conf == NULL )
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

/* Advance handshake if necessary */
if( ssl->handshake->early_data_ready == 0 )
{
/* Indicate handshake initiated from an early data write */
ssl->handshake->early_data_write = 1;
ret = mbedtls_ssl_handshake( ssl );

if( ( ssl->handshake->early_data_ready == 0 ) ||
( ret != 0 && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_handshake", ret );
return( ret );
}
}

if( ( mbedtls_ssl_is_init_finished( ssl ) ) ||
( ssl->handshake->early_data != MBEDTLS_SSL_EARLY_DATA_STATE_ON ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "cannot send early_data" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}

#if defined(MBEDTLS_SSL_RENEGOTIATION)
if( ( ret = ssl_check_ctr_renegotiate( ssl ) ) != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "ssl_check_ctr_renegotiate", ret );
return( ret );
}
#endif

ret = ssl_write_real( ssl, buf, len );

MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= write early_data" ) );

return( ret );
}
#endif /* MBEDTLS_ZERO_RTT*/

/*
* Notify the peer that the connection is being closed
*/
Expand Down
8 changes: 8 additions & 0 deletions library/ssl_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,14 @@ static int ssl_handshake_init( mbedtls_ssl_context *ssl )
}
#endif

#if defined(MBEDTLS_ZERO_RTT)
if( ( ssl->conf->early_data_api == MBEDTLS_SSL_EARLY_DATA_NEW_API ) &&
( ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED ) )
{
ssl->handshake->early_data = MBEDTLS_SSL_EARLY_DATA_STATE_ENABLED;
}
#endif

/*
* curve_list is translated to IANA TLS group identifiers here because
* mbedtls_ssl_conf_curves returns void and so can't return
Expand Down
Loading