diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 08c628a3a..78395d2a6 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -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, + +/* + * 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, + +/* + * 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, + +/* + * 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. * @@ -1673,33 +1718,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 */ @@ -5150,10 +5191,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 @@ -5206,17 +5243,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 @@ -5237,9 +5300,11 @@ int mbedtls_ssl_read_early_data(mbedtls_ssl_context *ssl, * 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 + * \p ssl and any subsequent call to this API will return this + * error code. But this does not preclude for using it with * mbedtls_ssl_write(), mbedtls_ssl_read() or - * mbedtls_ssl_handshake(). + * mbedtls_ssl_handshake() and the handshake can be + * completed by calling one of these APIs. * * \note This function may write early data only if the SSL context * has been configured for the handshake with a PSK for which diff --git a/library/ssl_debug_helpers.h b/library/ssl_debug_helpers.h index 2b0e73772..a8e31409f 100644 --- a/library/ssl_debug_helpers.h +++ b/library/ssl_debug_helpers.h @@ -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); diff --git a/library/ssl_misc.h b/library/ssl_misc.h index 942d4ad22..d8844fcc3 100644 --- a/library/ssl_misc.h +++ b/library/ssl_misc.h @@ -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 @@ -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) @@ -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 */ diff --git a/library/ssl_msg.c b/library/ssl_msg.c index c2e64c609..2a6d4341b 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -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 */ diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c index 1e8df1b39..88d6c9e01 100644 --- a/library/ssl_tls13_client.c +++ b/library/ssl_tls13_client.c @@ -1180,7 +1180,15 @@ int mbedtls_ssl_tls13_write_client_hello_exts(mbedtls_ssl_context *ssl, #endif #if defined(MBEDTLS_SSL_EARLY_DATA) - if (ssl->handshake->hello_retry_request_count == 0) { + /* In the first ClientHello, write the early data indication extension if + * necessary and update the early data status. + * If an HRR has been received and thus we are currently writing the + * second ClientHello, the second ClientHello must not contain an early + * data extension and the early data status must stay as it is: + * MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT or + * MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED. + */ + if (!ssl->handshake->hello_retry_request_flag) { if (mbedtls_ssl_conf_tls13_is_some_psk_enabled(ssl) && ssl_tls13_early_data_has_valid_ticket(ssl) && ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED) { @@ -1495,7 +1503,7 @@ static int ssl_tls13_preprocess_server_hello(mbedtls_ssl_context *ssl, * to a HelloRetryRequest), it MUST abort the handshake with an * "unexpected_message" alert. */ - if (handshake->hello_retry_request_count > 0) { + if (handshake->hello_retry_request_flag) { MBEDTLS_SSL_DEBUG_MSG(1, ("Multiple HRRs received")); MBEDTLS_SSL_PEND_FATAL_ALERT( MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE, @@ -1517,7 +1525,7 @@ static int ssl_tls13_preprocess_server_hello(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; } - handshake->hello_retry_request_count++; + handshake->hello_retry_request_flag = 1; break; } @@ -1672,7 +1680,7 @@ static int ssl_tls13_parse_server_hello(mbedtls_ssl_context *ssl, * proposed in the HRR, we abort the handshake and send an * "illegal_parameter" alert. */ - else if ((!is_hrr) && (handshake->hello_retry_request_count > 0) && + else if ((!is_hrr) && handshake->hello_retry_request_flag && (cipher_suite != ssl->session_negotiate->ciphersuite)) { fatal_alert = MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER; } @@ -2270,6 +2278,7 @@ cleanup: } +#if defined(MBEDTLS_SSL_EARLY_DATA) /* * Handler for MBEDTLS_SSL_END_OF_EARLY_DATA * @@ -2308,6 +2317,32 @@ cleanup: return ret; } +int mbedtls_ssl_get_early_data_status(mbedtls_ssl_context *ssl) +{ + if ((ssl->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) || + (!mbedtls_ssl_is_handshake_over(ssl))) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + switch (ssl->early_data_status) { + case MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT: + return MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT; + break; + + case MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED: + return MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED; + break; + + case MBEDTLS_SSL_EARLY_DATA_STATUS_SERVER_FINISHED_RECEIVED: + return MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED; + break; + + default: + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +} +#endif /* MBEDTLS_SSL_EARLY_DATA */ + #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) /* * STATE HANDLING: CertificateRequest @@ -3030,9 +3065,11 @@ int mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl) ret = ssl_tls13_process_server_finished(ssl); break; +#if defined(MBEDTLS_SSL_EARLY_DATA) case MBEDTLS_SSL_END_OF_EARLY_DATA: ret = ssl_tls13_write_end_of_early_data(ssl); break; +#endif case MBEDTLS_SSL_CLIENT_CERTIFICATE: ret = ssl_tls13_write_client_certificate(ssl); @@ -3061,23 +3098,17 @@ int mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl) */ #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO: - ret = 0; - if (ssl->handshake->ccs_count == 0) { - ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); - if (ret != 0) { - break; - } + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret != 0) { + break; } mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); break; case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED: - ret = 0; - if (ssl->handshake->ccs_count == 0) { - ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); - if (ret != 0) { - break; - } + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret != 0) { + break; } mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE); break; diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c index 064f6160d..bc737047a 100644 --- a/library/ssl_tls13_generic.c +++ b/library/ssl_tls13_generic.c @@ -1379,6 +1379,12 @@ int mbedtls_ssl_tls13_write_change_cipher_spec(mbedtls_ssl_context *ssl) MBEDTLS_SSL_DEBUG_MSG(2, ("=> write change cipher spec")); + /* Only one CCS to send. */ + if (ssl->handshake->ccs_sent) { + ret = 0; + goto cleanup; + } + /* Write CCS message */ MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_change_cipher_spec_body( ssl, ssl->out_msg, @@ -1390,7 +1396,7 @@ int mbedtls_ssl_tls13_write_change_cipher_spec(mbedtls_ssl_context *ssl) /* Dispatch message */ MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_write_record(ssl, 0)); - ssl->handshake->ccs_count++; + ssl->handshake->ccs_sent = 1; cleanup: diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c index 141144675..3a968aa96 100644 --- a/library/ssl_tls13_server.c +++ b/library/ssl_tls13_server.c @@ -1531,7 +1531,7 @@ static int ssl_tls13_parse_client_hello(mbedtls_ssl_context *ssl, const unsigned char *extension_data_end; uint32_t allowed_exts = MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CH; - if (ssl->handshake->hello_retry_request_count > 0) { + if (ssl->handshake->hello_retry_request_flag) { /* Do not accept early data extension in 2nd ClientHello */ allowed_exts &= ~MBEDTLS_SSL_EXT_MASK(EARLY_DATA); } @@ -2427,7 +2427,7 @@ MBEDTLS_CHECK_RETURN_CRITICAL static int ssl_tls13_prepare_hello_retry_request(mbedtls_ssl_context *ssl) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - if (ssl->handshake->hello_retry_request_count > 0) { + if (ssl->handshake->hello_retry_request_flag) { MBEDTLS_SSL_DEBUG_MSG(1, ("Too many HRRs")); MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); @@ -2474,7 +2474,7 @@ static int ssl_tls13_write_hello_retry_request(mbedtls_ssl_context *ssl) MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg(ssl, buf_len, msg_len)); - ssl->handshake->hello_retry_request_count++; + ssl->handshake->hello_retry_request_flag = 1; #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) /* The server sends a dummy change_cipher_spec record immediately @@ -3477,12 +3477,9 @@ int mbedtls_ssl_tls13_handshake_server_step(mbedtls_ssl_context *ssl) break; case MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO: - ret = 0; - if (ssl->handshake->ccs_count == 0) { - ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); - if (ret != 0) { - break; - } + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret != 0) { + break; } mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS); break; diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 05bb2ffdd..8a9aff975 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -52,7 +52,7 @@ int main(void) #define DFL_KEY_OPAQUE 0 #define DFL_KEY_PWD "" #define DFL_PSK "" -#define DFL_EARLY_DATA "" +#define DFL_EARLY_DATA -1 #define DFL_PSK_OPAQUE 0 #define DFL_PSK_IDENTITY "Client_identity" #define DFL_ECJPAKE_PW NULL @@ -347,9 +347,8 @@ int main(void) #if defined(MBEDTLS_SSL_EARLY_DATA) #define USAGE_EARLY_DATA \ - " early_data=%%s The file path to read early data from\n" \ - " default: \"\" (do nothing)\n" \ - " option: a file path\n" + " early_data=%%d default: library default\n" \ + " options: 0 (disabled), 1 (enabled)\n" #else #define USAGE_EARLY_DATA "" #endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_PROTO_TLS1_3 */ @@ -544,7 +543,7 @@ struct options { int reproducible; /* make communication reproducible */ int skip_close_notify; /* skip sending the close_notify alert */ #if defined(MBEDTLS_SSL_EARLY_DATA) - const char *early_data; /* the path of the file to read early data from */ + int early_data; /* early data enablement flag */ #endif int query_config_mode; /* whether to read config */ int use_srtp; /* Support SRTP */ @@ -717,9 +716,64 @@ exit: return ret; } +/* + * Build HTTP request + */ +static int build_http_request(unsigned char *buf, size_t buf_size, size_t *request_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len, tail_len, request_size; + + ret = mbedtls_snprintf((char *) buf, buf_size, GET_REQUEST, opt.request_page); + if (ret < 0) { + return ret; + } + + len = (size_t) ret; + tail_len = strlen(GET_REQUEST_END); + if (opt.request_size != DFL_REQUEST_SIZE) { + request_size = (size_t) opt.request_size; + } else { + request_size = len + tail_len; + } + + if (request_size > buf_size) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* Add padding to GET request to reach opt.request_size in length */ + if (opt.request_size != DFL_REQUEST_SIZE && + len + tail_len < request_size) { + memset(buf + len, 'A', request_size - len - tail_len); + len = request_size - tail_len; + } + + strncpy((char *) buf + len, GET_REQUEST_END, buf_size - len); + len += tail_len; + + /* Truncate if request size is smaller than the "natural" size */ + if (opt.request_size != DFL_REQUEST_SIZE && + len > request_size) { + len = request_size; + + /* Still end with \r\n unless that's really not possible */ + if (len >= 2) { + buf[len - 2] = '\r'; + } + if (len >= 1) { + buf[len - 1] = '\n'; + } + } + + *request_len = len; + + return 0; +} + int main(int argc, char *argv[]) { - int ret = 0, len, tail_len, i, written, frags, retry_left; + int ret = 0, i; + size_t len, written, frags, retry_left; int query_config_ret = 0; mbedtls_net_context server_fd; io_ctx_t io_ctx; @@ -742,10 +796,6 @@ int main(int argc, char *argv[]) size_t cid_renego_len = 0; #endif -#if defined(MBEDTLS_SSL_EARLY_DATA) - FILE *early_data_fp = NULL; -#endif /* MBEDTLS_SSL_EARLY_DATA */ - #if defined(MBEDTLS_SSL_ALPN) const char *alpn_list[ALPN_LIST_SIZE]; #endif @@ -1201,7 +1251,15 @@ usage: #if defined(MBEDTLS_SSL_PROTO_TLS1_3) #if defined(MBEDTLS_SSL_EARLY_DATA) else if (strcmp(p, "early_data") == 0) { - opt.early_data = q; + switch (atoi(q)) { + case 0: + opt.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED; + break; + case 1: + opt.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED; + break; + default: goto usage; + } } #endif /* MBEDTLS_SSL_EARLY_DATA */ @@ -1968,16 +2026,9 @@ usage: } #if defined(MBEDTLS_SSL_EARLY_DATA) - int early_data_enabled = MBEDTLS_SSL_EARLY_DATA_DISABLED; - if (strlen(opt.early_data) > 0) { - if ((early_data_fp = fopen(opt.early_data, "rb")) == NULL) { - mbedtls_printf("failed\n ! Cannot open '%s' for reading.\n", - opt.early_data); - goto exit; - } - early_data_enabled = MBEDTLS_SSL_EARLY_DATA_ENABLED; + if (opt.early_data != DFL_EARLY_DATA) { + mbedtls_ssl_conf_early_data(&conf, opt.early_data); } - mbedtls_ssl_conf_early_data(&conf, early_data_enabled); #endif /* MBEDTLS_SSL_EARLY_DATA */ if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { @@ -2448,32 +2499,9 @@ send_request: mbedtls_printf(" > Write to server:"); fflush(stdout); - len = mbedtls_snprintf((char *) buf, sizeof(buf) - 1, GET_REQUEST, - opt.request_page); - tail_len = (int) strlen(GET_REQUEST_END); - - /* Add padding to GET request to reach opt.request_size in length */ - if (opt.request_size != DFL_REQUEST_SIZE && - len + tail_len < opt.request_size) { - memset(buf + len, 'A', opt.request_size - len - tail_len); - len += opt.request_size - len - tail_len; - } - - strncpy((char *) buf + len, GET_REQUEST_END, sizeof(buf) - len - 1); - len += tail_len; - - /* Truncate if request size is smaller than the "natural" size */ - if (opt.request_size != DFL_REQUEST_SIZE && - len > opt.request_size) { - len = opt.request_size; - - /* Still end with \r\n unless that's really not possible */ - if (len >= 2) { - buf[len - 2] = '\r'; - } - if (len >= 1) { - buf[len - 1] = '\n'; - } + ret = build_http_request(buf, sizeof(buf) - 1, &len); + if (ret != 0) { + goto exit; } if (opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM) { @@ -2545,8 +2573,11 @@ send_request: } buf[written] = '\0'; - mbedtls_printf(" %d bytes written in %d fragments\n\n%s\n", - written, frags, (char *) buf); + mbedtls_printf( + " %" MBEDTLS_PRINTF_SIZET " bytes written in %" MBEDTLS_PRINTF_SIZET " fragments\n\n%s\n", + written, + frags, + (char *) buf); /* Send a non-empty request if request_size == 0 */ if (len == 0) { @@ -2653,7 +2684,9 @@ send_request: len = ret; buf[len] = '\0'; - mbedtls_printf(" < Read from server: %d bytes read\n\n%s", len, (char *) buf); + mbedtls_printf(" < Read from server: %" MBEDTLS_PRINTF_SIZET " bytes read\n\n%s", + len, + (char *) buf); fflush(stdout); /* End of message should be detected according to the syntax of the * application protocol (eg HTTP), just use a dummy test here. */ @@ -2712,7 +2745,9 @@ send_request: len = ret; buf[len] = '\0'; - mbedtls_printf(" < Read from server: %d bytes read\n\n%s", len, (char *) buf); + mbedtls_printf(" < Read from server: %" MBEDTLS_PRINTF_SIZET " bytes read\n\n%s", + len, + (char *) buf); ret = 0; } @@ -3002,6 +3037,54 @@ reconnect: goto exit; } + ret = build_http_request(buf, sizeof(buf) - 1, &len); + if (ret != 0) { + goto exit; + } + +#if defined(MBEDTLS_SSL_EARLY_DATA) + if (ssl.conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED) { + frags = 0; + written = 0; + do { + while ((ret = mbedtls_ssl_write_early_data(&ssl, buf + written, + len - written)) < 0) { + if (ret == MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA) { + goto end_of_early_data; + } + if (ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE && + ret != MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) { + mbedtls_printf(" failed\n ! mbedtls_ssl_write returned -0x%x\n\n", + (unsigned int) -ret); + goto exit; + } + + /* For event-driven IO, wait for socket to become available */ + if (opt.event == 1 /* level triggered IO */) { +#if defined(MBEDTLS_TIMING_C) + idle(&server_fd, &timer, ret); +#else + idle(&server_fd, ret); +#endif + } + } + + frags++; + written += ret; + } while (written < len); + } + +end_of_early_data: + + buf[written] = '\0'; + mbedtls_printf( + " %" MBEDTLS_PRINTF_SIZET " bytes of early data written in %" MBEDTLS_PRINTF_SIZET " fragments\n\n%s\n", + written, + frags, + (char *) buf); +#endif /* MBEDTLS_SSL_EARLY_DATA */ + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && @@ -3035,12 +3118,6 @@ exit: mbedtls_ssl_config_free(&conf); mbedtls_ssl_session_free(&saved_session); -#if defined(MBEDTLS_SSL_EARLY_DATA) - if (early_data_fp != NULL) { - fclose(early_data_fp); - } -#endif - if (session_data != NULL) { mbedtls_platform_zeroize(session_data, session_data_len); } diff --git a/tests/include/test/ssl_helpers.h b/tests/include/test/ssl_helpers.h index 9a078f645..5b071f75a 100644 --- a/tests/include/test/ssl_helpers.h +++ b/tests/include/test/ssl_helpers.h @@ -608,9 +608,7 @@ int mbedtls_test_get_tls13_ticket( mbedtls_test_handshake_test_options *client_options, mbedtls_test_handshake_test_options *server_options, mbedtls_ssl_session *session); -#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_SRV_C && - MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS && - MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +#endif #define ECJPAKE_TEST_PWD "bla" diff --git a/tests/opt-testcases/tls13-misc.sh b/tests/opt-testcases/tls13-misc.sh index 4e6bf876d..ad062dccf 100755 --- a/tests/opt-testcases/tls13-misc.sh +++ b/tests/opt-testcases/tls13-misc.sh @@ -263,7 +263,7 @@ requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ run_test "TLS 1.3 m->G: EarlyData: basic check, good" \ "$G_NEXT_SRV -d 10 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+ECDHE-PSK:+PSK \ --earlydata --maxearlydata 16384 --disable-client-cert" \ - "$P_CLI debug_level=4 early_data=$EARLY_DATA_INPUT reco_mode=1 reconnect=1 reco_delay=900" \ + "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1 reco_delay=900" \ 0 \ -c "received max_early_data_size: 16384" \ -c "Reconnecting with saved session" \ @@ -277,6 +277,31 @@ run_test "TLS 1.3 m->G: EarlyData: basic check, good" \ -s "END OF EARLY DATA (5) was received." \ -s "early data accepted" +requires_gnutls_tls1_3 +requires_config_enabled MBEDTLS_DEBUG_C +requires_config_enabled MBEDTLS_SSL_CLI_C +requires_all_configs_enabled MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED \ + MBEDTLS_SSL_EARLY_DATA +requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +run_test "TLS 1.3 m->G: EarlyData: write early data, good" \ + "$G_NEXT_SRV -d 10 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+ECDHE-PSK:+PSK --earlydata --disable-client-cert" \ + "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1 reco_delay=900" \ + 0 \ + -c "Reconnecting with saved session" \ + -c "NewSessionTicket: early_data(42) extension received." \ + -c "ClientHello: early_data(42) extension exists." \ + -c "EncryptedExtensions: early_data(42) extension received." \ + -c "EncryptedExtensions: early_data(42) extension exists." \ + -c "<= write early_data" \ + -c "<= write EndOfEarlyData" \ + -s "Parsing extension 'Early Data/42' (0 bytes)" \ + -s "Sending extension Early Data/42 (0 bytes)" \ + -s "END OF EARLY DATA (5) was received." \ + -s "early data accepted" \ + -s "decrypted early data with length" + requires_gnutls_tls1_3 requires_config_enabled MBEDTLS_DEBUG_C requires_config_enabled MBEDTLS_SSL_CLI_C @@ -287,7 +312,7 @@ requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED run_test "TLS 1.3 m->G: EarlyData: no early_data in NewSessionTicket, good" \ "$G_NEXT_SRV -d 10 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+ECDHE-PSK:+PSK --disable-client-cert" \ - "$P_CLI debug_level=4 early_data=$EARLY_DATA_INPUT reco_mode=1 reconnect=1" \ + "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1" \ 0 \ -c "Reconnecting with saved session" \ -C "NewSessionTicket: early_data(42) extension received." \ diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index 69ccf26ee..385682ae1 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3274,14 +3274,17 @@ elliptic_curve_get_properties TLS 1.3 resume session with ticket tls13_resume_session_with_ticket -TLS 1.3 early data, early data accepted -tls13_early_data:TEST_EARLY_DATA_ACCEPTED +TLS 1.3 read early data, early data accepted +tls13_read_early_data:TEST_EARLY_DATA_ACCEPTED -TLS 1.3 early data, server rejects early data -tls13_early_data:TEST_EARLY_DATA_SERVER_REJECTS +TLS 1.3 read early data, no early data indication +tls13_read_early_data:TEST_EARLY_DATA_NO_INDICATION_SENT -TLS 1.3 early data, discard after HRR -tls13_early_data:TEST_EARLY_DATA_HRR +TLS 1.3 read early data, server rejects early data +tls13_read_early_data:TEST_EARLY_DATA_SERVER_REJECTS + +TLS 1.3 read early data, discard after HRR +tls13_read_early_data:TEST_EARLY_DATA_HRR TLS 1.3 cli, early data status, early data accepted tls13_cli_early_data_status:TEST_EARLY_DATA_ACCEPTED @@ -3294,3 +3297,15 @@ tls13_cli_early_data_status:TEST_EARLY_DATA_SERVER_REJECTS TLS 1.3 cli, early data status, hello retry request tls13_cli_early_data_status:TEST_EARLY_DATA_HRR + +TLS 1.3 write early data, early data accepted +tls13_write_early_data:TEST_EARLY_DATA_ACCEPTED + +TLS 1.3 write early data, no early data indication +tls13_write_early_data:TEST_EARLY_DATA_NO_INDICATION_SENT + +TLS 1.3 write early data, server rejects early data +tls13_write_early_data:TEST_EARLY_DATA_SERVER_REJECTS + +TLS 1.3 write early data, hello retry request +tls13_write_early_data:TEST_EARLY_DATA_HRR diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 0e798f4bc..d327828bc 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -18,49 +18,6 @@ #define TEST_EARLY_DATA_SERVER_REJECTS 2 #define TEST_EARLY_DATA_HRR 3 -#if (!defined(MBEDTLS_SSL_PROTO_TLS1_2)) && \ - defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C) && \ - defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_DEBUG_C) && \ - defined(MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE) && \ - defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) && \ - defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) && \ - defined(MBEDTLS_MD_CAN_SHA256) && \ - defined(MBEDTLS_ECP_HAVE_SECP256R1) && defined(MBEDTLS_ECP_HAVE_SECP384R1) && \ - defined(MBEDTLS_PK_CAN_ECDSA_VERIFY) && defined(MBEDTLS_SSL_SESSION_TICKETS) -/* - * The implementation of the function should be based on - * mbedtls_ssl_write_early_data() eventually. The current version aims at - * removing the dependency on mbedtls_ssl_write_early_data() for the - * development and testing of reading early data. - */ -static int write_early_data(mbedtls_ssl_context *ssl, - unsigned char *buf, size_t len) -{ - int ret = mbedtls_ssl_get_max_out_record_payload(ssl); - - TEST_ASSERT(ret > 0); - TEST_ASSERT(len <= (size_t) ret); - - ret = mbedtls_ssl_flush_output(ssl); - TEST_EQUAL(ret, 0); - TEST_EQUAL(ssl->out_left, 0); - - ssl->out_msglen = len; - ssl->out_msgtype = MBEDTLS_SSL_MSG_APPLICATION_DATA; - if (len > 0) { - memcpy(ssl->out_msg, buf, len); - } - - ret = mbedtls_ssl_write_record(ssl, 1); - TEST_EQUAL(ret, 0); - - ret = len; - -exit: - return ret; -} -#endif - /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -3624,12 +3581,12 @@ exit: /* END_CASE */ /* - * The !MBEDTLS_SSL_PROTO_TLS1_2 dependency of tls13_early_data() below is + * The !MBEDTLS_SSL_PROTO_TLS1_2 dependency of tls13_read_early_data() below is * a temporary workaround to not run the test in Windows-2013 where there is * an issue with mbedtls_vsnprintf(). */ /* BEGIN_CASE depends_on:!MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_DEBUG_C:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_ECP_HAVE_SECP384R1:MBEDTLS_PK_CAN_ECDSA_VERIFY:MBEDTLS_SSL_SESSION_TICKETS */ -void tls13_early_data(int scenario) +void tls13_read_early_data(int scenario) { int ret = -1; unsigned char buf[64]; @@ -3676,6 +3633,10 @@ void tls13_early_data(int scenario) case TEST_EARLY_DATA_ACCEPTED: break; + case TEST_EARLY_DATA_NO_INDICATION_SENT: + client_options.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED; + break; + case TEST_EARLY_DATA_SERVER_REJECTS: mbedtls_debug_set_threshold(3); server_pattern.pattern = @@ -3723,12 +3684,16 @@ void tls13_early_data(int scenario) &(client_ep.ssl), &(server_ep.ssl), MBEDTLS_SSL_SERVER_HELLO), 0); - TEST_ASSERT(client_ep.ssl.early_data_status != - MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT); + ret = mbedtls_ssl_write_early_data(&(client_ep.ssl), + (unsigned char *) early_data, + early_data_len); - ret = write_early_data(&(client_ep.ssl), (unsigned char *) early_data, - early_data_len); - TEST_EQUAL(ret, early_data_len); + if (client_ep.ssl.early_data_status != + MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT) { + TEST_EQUAL(ret, early_data_len); + } else { + TEST_EQUAL(ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + } ret = mbedtls_test_move_handshake_to_state( &(server_ep.ssl), &(client_ep.ssl), @@ -3743,12 +3708,20 @@ void tls13_early_data(int scenario) TEST_MEMORY_COMPARE(buf, early_data_len, early_data, early_data_len); break; + case TEST_EARLY_DATA_NO_INDICATION_SENT: + TEST_EQUAL(ret, 0); + TEST_EQUAL(server_ep.ssl.handshake->early_data_accepted, 0); + break; + case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */ case TEST_EARLY_DATA_HRR: TEST_EQUAL(ret, 0); TEST_EQUAL(server_ep.ssl.handshake->early_data_accepted, 0); TEST_EQUAL(server_pattern.counter, 1); break; + + default: + TEST_FAIL("Unknown scenario."); } TEST_EQUAL(mbedtls_test_move_handshake_to_state( @@ -3869,6 +3842,11 @@ void tls13_cli_early_data_status(int scenario) (ret == MBEDTLS_ERR_SSL_WANT_WRITE)); } + if (client_ep.ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { + TEST_EQUAL(mbedtls_ssl_get_early_data_status(&(client_ep.ssl)), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + switch (client_ep.ssl.state) { case MBEDTLS_SSL_CLIENT_HELLO: switch (scenario) { @@ -3880,7 +3858,7 @@ void tls13_cli_early_data_status(int scenario) break; case TEST_EARLY_DATA_HRR: - if (client_ep.ssl.handshake->hello_retry_request_count == 0) { + if (!client_ep.ssl.handshake->hello_retry_request_flag) { TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN); } else { @@ -3888,6 +3866,9 @@ void tls13_cli_early_data_status(int scenario) MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); } break; + + default: + TEST_FAIL("Unknown scenario."); } break; @@ -3905,7 +3886,7 @@ void tls13_cli_early_data_status(int scenario) break; case TEST_EARLY_DATA_HRR: - if (client_ep.ssl.handshake->hello_retry_request_count == 0) { + if (!client_ep.ssl.handshake->hello_retry_request_flag) { TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_CAN_WRITE); } else { @@ -3913,6 +3894,9 @@ void tls13_cli_early_data_status(int scenario) MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); } break; + + default: + TEST_FAIL("Unknown scenario."); } break; @@ -3933,6 +3917,9 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); break; + + default: + TEST_FAIL("Unknown scenario."); } break; @@ -3953,6 +3940,9 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); break; + + default: + TEST_FAIL("Unknown scenario."); } break; @@ -3979,6 +3969,9 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); break; + + default: + TEST_FAIL("Unknown scenario."); } break; @@ -3999,12 +3992,14 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); break; + + default: + TEST_FAIL("Unknown scenario."); } break; #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) case MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO: - TEST_ASSERT(scenario != TEST_EARLY_DATA_NO_INDICATION_SENT); switch (scenario) { case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */ case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */ @@ -4012,6 +4007,9 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_SENT); break; + + default: + TEST_FAIL("Unexpected or unknown scenario."); } break; @@ -4022,7 +4020,6 @@ void tls13_cli_early_data_status(int scenario) break; case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED: - TEST_ASSERT(scenario != TEST_EARLY_DATA_ACCEPTED); switch (scenario) { case TEST_EARLY_DATA_NO_INDICATION_SENT: TEST_EQUAL(client_ep.ssl.early_data_status, @@ -4034,6 +4031,9 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); break; + + default: + TEST_FAIL("Unexpected or unknown scenario."); } break; #endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ @@ -4057,6 +4057,9 @@ void tls13_cli_early_data_status(int scenario) TEST_EQUAL(client_ep.ssl.early_data_status, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); break; + + default: + TEST_FAIL("Unknown scenario."); } break; @@ -4065,8 +4068,30 @@ void tls13_cli_early_data_status(int scenario) } } while (client_ep.ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER); + ret = mbedtls_ssl_get_early_data_status(&(client_ep.ssl)); + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: + TEST_EQUAL(ret, MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED); + break; + + case TEST_EARLY_DATA_NO_INDICATION_SENT: + TEST_EQUAL(ret, MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT); + break; + + case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */ + case TEST_EARLY_DATA_HRR: + TEST_EQUAL(ret, MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED); + break; + + default: + TEST_FAIL("Unknown scenario."); + } + + ret = mbedtls_ssl_get_early_data_status(&(server_ep.ssl)); + TEST_EQUAL(ret, MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + #if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) - TEST_EQUAL(client_ep.ssl.handshake->ccs_count, 1); + TEST_EQUAL(client_ep.ssl.handshake->ccs_sent, 1); #endif exit: @@ -4078,3 +4103,348 @@ exit: PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_ECP_HAVE_SECP384R1:MBEDTLS_PK_CAN_ECDSA_VERIFY:MBEDTLS_SSL_SESSION_TICKETS */ +void tls13_write_early_data(int scenario) +{ + int ret = -1; + mbedtls_test_ssl_endpoint client_ep, server_ep; + mbedtls_test_handshake_test_options client_options; + mbedtls_test_handshake_test_options server_options; + mbedtls_ssl_session saved_session; + uint16_t group_list[3] = { + MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, + MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1, + MBEDTLS_SSL_IANA_TLS_GROUP_NONE + }; + int beyond_first_hello = 0; + + mbedtls_platform_zeroize(&client_ep, sizeof(client_ep)); + mbedtls_platform_zeroize(&server_ep, sizeof(server_ep)); + mbedtls_test_init_handshake_options(&client_options); + mbedtls_test_init_handshake_options(&server_options); + mbedtls_ssl_session_init(&saved_session); + + PSA_INIT(); + + /* + * Run first handshake to get a ticket from the server. + */ + client_options.pk_alg = MBEDTLS_PK_ECDSA; + client_options.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED; + server_options.pk_alg = MBEDTLS_PK_ECDSA; + server_options.early_data = MBEDTLS_SSL_EARLY_DATA_ENABLED; + if (scenario == TEST_EARLY_DATA_HRR) { + client_options.group_list = group_list; + server_options.group_list = group_list; + } + + ret = mbedtls_test_get_tls13_ticket(&client_options, &server_options, + &saved_session); + TEST_EQUAL(ret, 0); + + /* + * Prepare for handshake with the ticket. + */ + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: + break; + + case TEST_EARLY_DATA_NO_INDICATION_SENT: + client_options.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED; + break; + + case TEST_EARLY_DATA_SERVER_REJECTS: + server_options.early_data = MBEDTLS_SSL_EARLY_DATA_DISABLED; + break; + + case TEST_EARLY_DATA_HRR: + server_options.group_list = group_list + 1; + break; + + default: + TEST_FAIL("Unknown scenario."); + } + + ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT, + &client_options, NULL, NULL, NULL); + TEST_EQUAL(ret, 0); + + ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER, + &server_options, NULL, NULL, NULL); + TEST_EQUAL(ret, 0); + + mbedtls_ssl_conf_session_tickets_cb(&server_ep.conf, + mbedtls_test_ticket_write, + mbedtls_test_ticket_parse, + NULL); + + ret = mbedtls_test_mock_socket_connect(&(client_ep.socket), + &(server_ep.socket), 1024); + TEST_EQUAL(ret, 0); + + ret = mbedtls_ssl_set_session(&(client_ep.ssl), &saved_session); + TEST_EQUAL(ret, 0); + + /* + * Run handshakes going one state further in the handshake sequence at each + * loop up to the point where we reach the MBEDTLS_SSL_HANDSHAKE_OVER + * state. For each reached handshake state, check the result of the call + * to mbedtls_ssl_write_early_data(), make sure we can complete the + * handshake successfully and then reset the connection to restart the + * handshake from scratch. + */ + do { + int client_state = client_ep.ssl.state; + int previous_client_state; + const char *early_data_string = "This is early data."; + const unsigned char *early_data = (const unsigned char *) early_data_string; + size_t early_data_len = strlen(early_data_string); + int write_early_data_ret, read_early_data_ret; + unsigned char read_buf[64]; + + write_early_data_ret = mbedtls_ssl_write_early_data(&(client_ep.ssl), + early_data, + early_data_len); + + if (scenario == TEST_EARLY_DATA_NO_INDICATION_SENT) { + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, client_state); + goto complete_handshake; + } + + switch (client_state) { + case MBEDTLS_SSL_HELLO_REQUEST: /* Intentional fallthrough */ + case MBEDTLS_SSL_CLIENT_HELLO: + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */ + case TEST_EARLY_DATA_SERVER_REJECTS: + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_HELLO); + break; + + case TEST_EARLY_DATA_HRR: + if (!client_ep.ssl.handshake->hello_retry_request_flag) { + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_HELLO); + } else { + beyond_first_hello = 1; + TEST_EQUAL(write_early_data_ret, + MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_CLIENT_HELLO); + } + break; + + default: + TEST_FAIL("Unknown scenario."); + } + break; + + case MBEDTLS_SSL_SERVER_HELLO: + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */ + case TEST_EARLY_DATA_SERVER_REJECTS: + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_HELLO); + break; + + case TEST_EARLY_DATA_HRR: + if (!client_ep.ssl.handshake->hello_retry_request_flag) { + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_HELLO); + } else { + TEST_EQUAL(write_early_data_ret, + MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_HELLO); + } + break; + + default: + TEST_FAIL("Unknown scenario."); + } + break; + + case MBEDTLS_SSL_ENCRYPTED_EXTENSIONS: + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */ + case TEST_EARLY_DATA_SERVER_REJECTS: + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS); + break; + + case TEST_EARLY_DATA_HRR: + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS); + break; + + default: + TEST_FAIL("Unknown scenario."); + } + break; + + case MBEDTLS_SSL_SERVER_FINISHED: + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_FINISHED); + break; + + case TEST_EARLY_DATA_SERVER_REJECTS: + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_FINISHED); + break; + + case TEST_EARLY_DATA_HRR: + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_FINISHED); + break; + + default: + TEST_FAIL("Unknown scenario."); + } + break; + + case MBEDTLS_SSL_END_OF_EARLY_DATA: + TEST_EQUAL(scenario, TEST_EARLY_DATA_ACCEPTED); + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_END_OF_EARLY_DATA); + break; + +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + case MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO: + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */ + case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */ + case TEST_EARLY_DATA_HRR: + TEST_EQUAL(write_early_data_ret, early_data_len); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_SERVER_HELLO); + break; + default: + TEST_FAIL("Unknown scenario."); + } + break; + + case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO: + TEST_EQUAL(scenario, TEST_EARLY_DATA_HRR); + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO); + break; + + case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED: + switch (scenario) { + case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */ + case TEST_EARLY_DATA_HRR: + TEST_EQUAL(write_early_data_ret, + MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, + MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED); + break; + default: + TEST_FAIL("Unexpected or unknown scenario."); + } + break; +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + + case MBEDTLS_SSL_CLIENT_CERTIFICATE: /* Intentional fallthrough */ + case MBEDTLS_SSL_CLIENT_FINISHED: /* Intentional fallthrough */ + case MBEDTLS_SSL_FLUSH_BUFFERS: /* Intentional fallthrough */ + case MBEDTLS_SSL_HANDSHAKE_WRAPUP: /* Intentional fallthrough */ + case MBEDTLS_SSL_HANDSHAKE_OVER: + switch (scenario) { + case TEST_EARLY_DATA_ACCEPTED: /* Intentional fallthrough */ + case TEST_EARLY_DATA_SERVER_REJECTS: /* Intentional fallthrough */ + case TEST_EARLY_DATA_HRR: + TEST_EQUAL(write_early_data_ret, MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA); + TEST_EQUAL(client_ep.ssl.state, client_state); + break; + default: + TEST_FAIL("Unknown scenario."); + } + break; + + default: + TEST_FAIL("Unexpected state."); + } + +complete_handshake: + do { + ret = mbedtls_test_move_handshake_to_state( + &(server_ep.ssl), &(client_ep.ssl), + MBEDTLS_SSL_HANDSHAKE_OVER); + + if (ret == MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA) { + read_early_data_ret = mbedtls_ssl_read_early_data( + &(server_ep.ssl), read_buf, sizeof(read_buf)); + + TEST_EQUAL(read_early_data_ret, early_data_len); + } + } while (ret == MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA); + + TEST_EQUAL(ret, 0); + TEST_EQUAL(mbedtls_test_move_handshake_to_state( + &(client_ep.ssl), &(server_ep.ssl), + MBEDTLS_SSL_HANDSHAKE_OVER), 0); + + mbedtls_test_mock_socket_close(&(client_ep.socket)); + mbedtls_test_mock_socket_close(&(server_ep.socket)); + + ret = mbedtls_ssl_session_reset(&(client_ep.ssl)); + TEST_EQUAL(ret, 0); + + ret = mbedtls_ssl_set_session(&(client_ep.ssl), &saved_session); + TEST_EQUAL(ret, 0); + + ret = mbedtls_ssl_session_reset(&(server_ep.ssl)); + TEST_EQUAL(ret, 0); + + ret = mbedtls_test_mock_socket_connect(&(client_ep.socket), + &(server_ep.socket), 1024); + TEST_EQUAL(ret, 0); + + previous_client_state = client_state; + if (previous_client_state == MBEDTLS_SSL_HANDSHAKE_OVER) { + break; + } + + /* In case of HRR scenario, once we have been through it, move over + * the first ClientHello and ServerHello otherwise we just keep playing + * this first part of the handshake with HRR. + */ + if ((scenario == TEST_EARLY_DATA_HRR) && (beyond_first_hello)) { + TEST_ASSERT(mbedtls_test_move_handshake_to_state( + &(client_ep.ssl), &(server_ep.ssl), + MBEDTLS_SSL_SERVER_HELLO) == 0); + TEST_ASSERT(mbedtls_test_move_handshake_to_state( + &(client_ep.ssl), &(server_ep.ssl), + MBEDTLS_SSL_CLIENT_HELLO) == 0); + } + + TEST_EQUAL(mbedtls_test_move_handshake_to_state( + &(client_ep.ssl), &(server_ep.ssl), + previous_client_state), 0); + + /* Progress the handshake from at least one state */ + while (client_ep.ssl.state == previous_client_state) { + ret = mbedtls_ssl_handshake_step(&(client_ep.ssl)); + TEST_ASSERT((ret == 0) || + (ret == MBEDTLS_ERR_SSL_WANT_READ) || + (ret == MBEDTLS_ERR_SSL_WANT_WRITE)); + if (client_ep.ssl.state != previous_client_state) { + break; + } + ret = mbedtls_ssl_handshake_step(&(server_ep.ssl)); + TEST_ASSERT((ret == 0) || + (ret == MBEDTLS_ERR_SSL_WANT_READ) || + (ret == MBEDTLS_ERR_SSL_WANT_WRITE)); + } + } while (1); + +exit: + mbedtls_test_ssl_endpoint_free(&client_ep, NULL); + mbedtls_test_ssl_endpoint_free(&server_ep, NULL); + mbedtls_test_free_handshake_options(&client_options); + mbedtls_test_free_handshake_options(&server_options); + mbedtls_ssl_session_free(&saved_session); + PSA_DONE(); +} +/* END_CASE */