Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b62732e
tls13: cli: Add mbedtls_ssl_write_early_data() API
xkqian Nov 30, 2023
54a3829
ssl_client2: Simplify early_data option
ronald-cron-arm Jan 25, 2024
4e1bd47
ssl_client2: Move code to build http request
ronald-cron-arm Jan 23, 2024
ccfaefa
ssl_client2: Switch from int to size_t
ronald-cron-arm Jan 25, 2024
2fe0ec8
ssl_client2: Add buffer overflow check
ronald-cron-arm Jan 23, 2024
a556189
ssl_client2: Add support for early data writing
ronald-cron-arm Jan 23, 2024
30bb7ce
Add test case for early data writing
xkqian Nov 30, 2023
2fbbba9
tests: ssl: Add write early data unit test
ronald-cron-arm Jan 26, 2024
8fe2b01
tests: write early data: Add "not sent" scenario
ronald-cron-arm Jan 26, 2024
05600e2
tests: write early data: Add "server rejects" scenario
ronald-cron-arm Jan 26, 2024
b3d42fd
tests: write early data: Add HRR scenario
ronald-cron-arm Jan 26, 2024
e273f72
tls13: client: Improve CCS handling
ronald-cron-arm Feb 13, 2024
5fbd270
tls13: Use a flag not a counter for CCS and HRR handling
ronald-cron-arm Feb 14, 2024
84dfbf4
tls13: client: Add comment about early data in 2nd ClientHello
ronald-cron-arm Feb 14, 2024
b9a9b1f
tls13: Fix/Improve comments
ronald-cron-arm Feb 14, 2024
d6d32b9
tls13: Improve declaration and doc of early data status
ronald-cron-arm Feb 14, 2024
24da991
tests: ssl: early data: Add systematic default case in scenario switches
ronald-cron-arm Feb 15, 2024
4922190
tls13: write_early_data: Add endpoint check
ronald-cron-arm Feb 21, 2024
d406924
Improve comments/documentation
ronald-cron-arm Feb 21, 2024
b4fd47e
ssl_client2: Default to library default for early data enablement
ronald-cron-arm Feb 21, 2024
0aead12
ssl_client2: Improve loop writing early data
ronald-cron-arm Feb 21, 2024
bf5e909
tests: write early data: Check we can complete handshake after writing
ronald-cron-arm Feb 21, 2024
0004600
tests: write early data: Inverse loop over state logic
ronald-cron-arm Feb 21, 2024
e21c2d2
tls13: cli: Add missing MBEDTLS_SSL_EARLY_DATA guards
ronald-cron-arm Feb 21, 2024
9f2c3c0
tls13: cli: Add mbedtls_ssl_get_early_data_status() API
ronald-cron-arm Feb 21, 2024
86d288c
tests: ssl: Rename tls13_early_data to tls13_read_early_data
ronald-cron-arm Feb 22, 2024
110303f
tests: read early data: Add no early data indication sent scenario
ronald-cron-arm Feb 22, 2024
7d158f4
tests: read early data: Use write API to send early data
ronald-cron-arm Feb 22, 2024
8f1de7e
tls13: Improve documentation
ronald-cron-arm Feb 22, 2024
f19989d
tls13: Improve sanity check in get_early_data_status
ronald-cron-arm Feb 22, 2024
dcb09ca
tests: write early data: Improve get_early_data_status testing
ronald-cron-arm Feb 22, 2024
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
111 changes: 87 additions & 24 deletions include/mbedtls/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,51 @@ typedef enum {
}
mbedtls_ssl_states;

/*
* Early data status, client side only.
*/

#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
typedef enum {
/*
* The client has not sent the first ClientHello yet, it is unknown if the
* client will send an early data indication extension or not.
*/
MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this gives the idea that this is a state that track the actual early data not the "early data indication extension" and later on when reading the documentation or the code you realise that it's not, so maybe it's clearer to mention it in the name like with adding "EXT" or "IND" or in any other way that indicates it.


/*
* See documentation of mbedtls_ssl_get_early_data_status().
*/
MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT,
MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED,
MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED,
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me that those three states are overlapping with the rest of the states for example "MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED" and "MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED" are both the same state as "MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED" but we still need to know the information of if the server accepted early data or not so maybe this can be split to maybe "Result" and "State" for example? or something close maybe.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually should we just use different enums for the return value of this function and the internal state? I think that would be clearer.


/*
* The client has sent an early data indication extension in its first
* ClientHello, it has not received the response (ServerHello or
* HelloRetryRequest) from the server yet. The transform to protect early data
* is not set and early data cannot be sent yet.
*/
MBEDTLS_SSL_EARLY_DATA_STATUS_SENT,

/*
* The client has sent an early data indication extension in its first
* ClientHello, it has not received the response (ServerHello or
* HelloRetryRequest) from the server yet. The transform to protect early data
* has been set and early data can be written now.
*/
MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE,
Copy link
Contributor

Choose a reason for hiding this comment

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

From the first glance it seems that "MBEDTLS_SSL_EARLY_DATA_STATUS_SENT" is the same as "MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE" but it's not because of sending changeCipher compatibility so maybe it's better to mention it here in the comments why it's not the case since I was only able to know from the ticket discussion.


/*
* The client has sent an early data indication extension in its first
* ClientHello, the server has accepted them and the client has received the
* server Finished message. It cannot send early data to the server anymore.
*/
MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED,
} mbedtls_ssl_early_data_status;

#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_CLI_C */

/**
* \brief Callback type: send data on the network.
*
Expand Down Expand Up @@ -1657,33 +1702,29 @@ struct mbedtls_ssl_context {
#endif /* MBEDTLS_SSL_RENEGOTIATION */

/**
* Maximum TLS version to be negotiated, then negotiated TLS version.
* Maximum TLS version to be negotiated, then negotiated TLS version.
*
* It is initialized as the configured maximum TLS version to be
* negotiated by mbedtls_ssl_setup().
* It is initialized as the configured maximum TLS version to be
* negotiated by mbedtls_ssl_setup().
*
* When renegotiating or resuming a session, it is overwritten in the
* ClientHello writing preparation stage with the previously negotiated
* TLS version.
* When renegotiating or resuming a session, it is overwritten in the
* ClientHello writing preparation stage with the previously negotiated
* TLS version.
*
* On client side, it is updated to the TLS version selected by the server
* for the handshake when the ServerHello is received.
* On client side, it is updated to the TLS version selected by the server
* for the handshake when the ServerHello is received.
*
* On server side, it is updated to the TLS version the server selects for
* the handshake when the ClientHello is received.
* On server side, it is updated to the TLS version the server selects for
* the handshake when the ClientHello is received.
*/
mbedtls_ssl_protocol_version MBEDTLS_PRIVATE(tls_version);

#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
/**
* Status of the negotiation of the use of early data.
* See the documentation of mbedtls_ssl_get_early_data_status() for more
* information.
*
* Reset to #MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT when the context is
* reset.
* Status of the negotiation of the use of early data. Reset to
* MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN when the context is reset.
*/
int MBEDTLS_PRIVATE(early_data_status);
mbedtls_ssl_early_data_status MBEDTLS_PRIVATE(early_data_status);
#endif

unsigned MBEDTLS_PRIVATE(badmac_seen); /*!< records with a bad MAC received */
Expand Down Expand Up @@ -5106,10 +5147,6 @@ int mbedtls_ssl_close_notify(mbedtls_ssl_context *ssl);

#if defined(MBEDTLS_SSL_EARLY_DATA)

#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT 1
#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED 2
#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED 3

#if defined(MBEDTLS_SSL_SRV_C)
/**
* \brief Read at most 'len' bytes of early data
Expand Down Expand Up @@ -5162,17 +5199,43 @@ int mbedtls_ssl_read_early_data(mbedtls_ssl_context *ssl,
* \brief Try to write exactly 'len' application data bytes while
* performing the handshake (early data).
*
* \warning Early data is defined in the TLS 1.3 specification, RFC 8446.
* IMPORTANT NOTE from section 2.3 of the specification:
*
* The security properties for 0-RTT data are weaker than
* those for other kinds of TLS data. Specifically:
* - This data is not forward secret, as it is encrypted
* solely under keys derived using the offered PSK.
* - There are no guarantees of non-replay between connections.
* Protection against replay for ordinary TLS 1.3 1-RTT data
* is provided via the server's Random value, but 0-RTT data
* does not depend on the ServerHello and therefore has
* weaker guarantees. This is especially relevant if the
* data is authenticated either with TLS client
* authentication or inside the application protocol. The
* same warnings apply to any use of the
* early_exporter_master_secret.
*
* \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().
* function and the present documentation is mainly restricted
* to the differences with mbedtls_ssl_write(). One noticeable
* difference though is that mbedtls_ssl_write() aims to
* complete the handshake before to write application data
* while mbedtls_ssl_write_early() aims to drive the handshake
* just past the point where it is not possible to send early
* data anymore.
*
* \param ssl SSL context
* \param buf buffer holding the data
* \param len how many bytes must be written
*
* \return One additional specific return value:
* \return The (non-negative) number of bytes actually written if
* successful (may be less than \p len).
*
* \return One additional specific error code compared to
* mbedtls_ssl_write():
* #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA.
*
* #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA is returned when it
Expand Down
4 changes: 4 additions & 0 deletions library/ssl_debug_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@

const char *mbedtls_ssl_states_str(mbedtls_ssl_states in);

#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
const char *mbedtls_ssl_early_data_status_str(mbedtls_ssl_early_data_status in);
#endif

const char *mbedtls_ssl_protocol_version_str(mbedtls_ssl_protocol_version in);

const char *mbedtls_tls_prf_types_str(mbedtls_tls_prf_types in);
Expand Down
77 changes: 25 additions & 52 deletions library/ssl_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,21 +665,21 @@ struct mbedtls_ssl_handshake_params {
#if defined(MBEDTLS_SSL_CLI_C)
/** Minimum TLS version to be negotiated.
*
* It is set up in the ClientHello writing preparation stage and used
* throughout the ClientHello writing. Not relevant anymore as soon as
* the protocol version has been negotiated thus as soon as the
* ServerHello is received.
* For a fresh handshake not linked to any previous handshake, it is
* equal to the configured minimum minor version to be negotiated. When
* renegotiating or resuming a session, it is equal to the previously
* negotiated minor version.
* It is set up in the ClientHello writing preparation stage and used
* throughout the ClientHello writing. Not relevant anymore as soon as
* the protocol version has been negotiated thus as soon as the
* ServerHello is received.
* For a fresh handshake not linked to any previous handshake, it is
* equal to the configured minimum minor version to be negotiated. When
* renegotiating or resuming a session, it is equal to the previously
* negotiated minor version.
*
* There is no maximum TLS version field in this handshake context.
* From the start of the handshake, we need to define a current protocol
* version for the record layer which we define as the maximum TLS
* version to be negotiated. The `tls_version` field of the SSL context is
* used to store this maximum value until it contains the actual
* negotiated value.
* There is no maximum TLS version field in this handshake context.
* From the start of the handshake, we need to define a current protocol
* version for the record layer which we define as the maximum TLS
* version to be negotiated. The `tls_version` field of the SSL context is
* used to store this maximum value until it contains the actual
* negotiated value.
*/
mbedtls_ssl_protocol_version min_tls_version;
#endif
Expand Down Expand Up @@ -730,16 +730,21 @@ struct mbedtls_ssl_handshake_params {
#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
uint8_t key_exchange_mode; /*!< Selected key exchange mode */

/** Number of HelloRetryRequest messages received/sent from/to the server. */
uint8_t hello_retry_request_count;
/**
* Flag indicating if, in the course of the current handshake, an
* HelloRetryRequest message has been sent by the server or received by
* the client (<> 0) or not (0).
*/
uint8_t hello_retry_request_flag;

#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
/**
* Number of dummy change_cipher_spec (CCS) record sent. Used to send only
* one CCS per handshake without having to complicate the handshake state
* transitions.
* Flag indicating if, in the course of the current handshake, a dummy
* change_cipher_spec (CCS) record has already been sent. Used to send only
* one CCS per handshake while not complicating the handshake state
* transitions for that purpose.
*/
uint8_t ccs_count;
uint8_t ccs_sent;
#endif

#if defined(MBEDTLS_SSL_SRV_C)
Expand Down Expand Up @@ -2145,38 +2150,6 @@ int mbedtls_ssl_tls13_write_early_data_ext(mbedtls_ssl_context *ssl,
unsigned char *buf,
const unsigned char *end,
size_t *out_len);

#if defined(MBEDTLS_SSL_CLI_C)
/*
* The client has not sent the first ClientHello yet, it is unknown if the
* client will send an early data indication extension or not.
*/
#define MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN 0

/*
* The client has sent an early data indication extension in its first
* ClientHello, it has not received the response (ServerHello or
* HelloRetryRequest) from the server yet. The transform to protect early data
* is not set and early data cannot be sent yet.
*/
#define MBEDTLS_SSL_EARLY_DATA_STATUS_SENT 4

/*
* The client has sent an early data indication extension in its first
* ClientHello, it has not received the response (ServerHello or
* HelloRetryRequest) from the server yet. The transform to protect early data
* has been set and early data can be written now.
*/
#define MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE 5

/*
* The client has sent an early data indication extension in its first
* ClientHello, the server has accepted them and the client has received the
* server Finished message. It cannot send early data to the server anymore.
*/
#define MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED 6
#endif /* MBEDTLS_SSL_CLI_C */

#endif /* MBEDTLS_SSL_EARLY_DATA */

#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
Expand Down
88 changes: 88 additions & 0 deletions library/ssl_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -6058,6 +6058,94 @@ int mbedtls_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t
return ret;
}

#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
int mbedtls_ssl_write_early_data(mbedtls_ssl_context *ssl,
const unsigned char *buf, size_t len)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
const struct mbedtls_ssl_config *conf;
int written_data_len = 0;

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

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

if (conf->endpoint != MBEDTLS_SSL_IS_CLIENT) {
return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
}

if ((!mbedtls_ssl_conf_is_tls13_enabled(conf)) ||
(conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) ||
(conf->early_data_enabled != MBEDTLS_SSL_EARLY_DATA_ENABLED)) {
return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
}

if (ssl->tls_version != MBEDTLS_SSL_VERSION_TLS1_3) {
return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
}

/*
* If we are at the beginning of the handshake, the early data status being
* equal to MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN or
* MBEDTLS_SSL_EARLY_DATA_STATUS_SENT advance the handshake just
* enough to be able to send early data if possible. That way, we can
* guarantee that when starting the handshake with this function we will
* send at least one record of early data. Note that when the status is
* MBEDTLS_SSL_EARLY_DATA_STATUS_SENT and not yet
* MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE, we cannot send early data yet
* as the early data outbound transform has not been set as we may have to
* first send a dummy CCS in clear.
*/
if ((ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN) ||
(ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_SENT)) {
while ((ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN) ||
(ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_SENT)) {
ret = mbedtls_ssl_handshake_step(ssl);
if (ret != 0) {
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake_step", ret);
return ret;
}

ret = mbedtls_ssl_flush_output(ssl);
if (ret != 0) {
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flush_output", ret);
return ret;
}
}
} else {
/*
* If we are past the point where we can send early data, return
* immediatly. Otherwise, progress the handshake as much as possible to
* not delay it too much. If we reach a point where we can still send
* early data, then we will send some.
*/
if ((ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE) &&
(ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED)) {
return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
}

ret = mbedtls_ssl_handshake(ssl);
if ((ret != 0) && (ret != MBEDTLS_ERR_SSL_WANT_READ)) {
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake", ret);
return ret;
}
}

if ((ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE) &&
(ssl->early_data_status != MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED)) {
return MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA;
}

written_data_len = ssl_write_real(ssl, buf, len);

MBEDTLS_SSL_DEBUG_MSG(2, ("<= write early_data, len=%d", written_data_len));

return written_data_len;
}
#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_CLI_C */

/*
* Notify the peer that the connection is being closed
*/
Expand Down
Loading