in ssl/tls13_client.cc [171:332]
static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
assert(ssl->s3->have_version);
SSLMessage msg;
if (!ssl->method->get_message(ssl, &msg)) {
return ssl_hs_read_message;
}
// Queue up a ChangeCipherSpec for whenever we next send something. This
// will be before the second ClientHello. If we offered early data, this was
// already done.
if (!hs->early_data_offered &&
!ssl->method->add_change_cipher_spec(ssl)) {
return ssl_hs_error;
}
ParsedServerHello server_hello;
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
// The cipher suite must be one we offered. We currently offer all supported
// TLS 1.3 ciphers, so check the version.
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
if (cipher == nullptr ||
SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
hs->new_cipher = cipher;
const bool is_hrr = is_hello_retry_request(server_hello);
if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) ||
(is_hrr && !hs->transcript.UpdateForHelloRetryRequest())) {
return ssl_hs_error;
}
if (hs->selected_ech_config) {
if (!hs->inner_transcript.InitHash(ssl_protocol_version(ssl),
hs->new_cipher) ||
(is_hrr && !hs->inner_transcript.UpdateForHelloRetryRequest())) {
return ssl_hs_error;
}
}
// Determine which ClientHello the server is responding to. Run
// |check_ech_confirmation| unconditionally, so we validate the extension
// contents.
bool ech_accepted;
if (!check_ech_confirmation(hs, &ech_accepted, &alert, server_hello)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
if (hs->selected_ech_config) {
ssl->s3->ech_status = ech_accepted ? ssl_ech_accepted : ssl_ech_rejected;
}
if (!is_hrr) {
hs->tls13_state = state_read_server_hello;
return ssl_hs_ok;
}
// The ECH extension, if present, was already parsed by
// |check_ech_confirmation|.
SSLExtension cookie(TLSEXT_TYPE_cookie), key_share(TLSEXT_TYPE_key_share),
supported_versions(TLSEXT_TYPE_supported_versions),
ech_unused(TLSEXT_TYPE_encrypted_client_hello,
hs->selected_ech_config || hs->config->ech_grease_enabled);
if (!ssl_parse_extensions(
&server_hello.extensions, &alert,
{&cookie, &key_share, &supported_versions, &ech_unused},
/*ignore_unknown=*/false)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
if (!cookie.present && !key_share.present) {
OPENSSL_PUT_ERROR(SSL, SSL_R_EMPTY_HELLO_RETRY_REQUEST);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
if (cookie.present) {
CBS cookie_value;
if (!CBS_get_u16_length_prefixed(&cookie.data, &cookie_value) ||
CBS_len(&cookie_value) == 0 ||
CBS_len(&cookie.data) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return ssl_hs_error;
}
if (!hs->cookie.CopyFrom(cookie_value)) {
return ssl_hs_error;
}
}
if (key_share.present) {
uint16_t group_id;
if (!CBS_get_u16(&key_share.data, &group_id) ||
CBS_len(&key_share.data) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return ssl_hs_error;
}
// The group must be supported.
if (!tls1_check_group_id(hs, group_id)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
return ssl_hs_error;
}
// Check that the HelloRetryRequest does not request a key share that was
// provided in the initial ClientHello.
if (hs->key_shares[0]->GroupID() == group_id ||
(hs->key_shares[1] && hs->key_shares[1]->GroupID() == group_id)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
return ssl_hs_error;
}
if (!ssl_setup_key_shares(hs, group_id)) {
return ssl_hs_error;
}
}
// Although we now know whether ClientHelloInner was used, we currently
// maintain both transcripts up to ServerHello. We could swap transcripts
// early, but then ClientHello construction and |check_ech_confirmation|
// become more complex.
if (!ssl_hash_message(hs, msg)) {
return ssl_hs_error;
}
if (ssl->s3->ech_status == ssl_ech_accepted &&
!hs->inner_transcript.Update(msg.raw)) {
return ssl_hs_error;
}
// HelloRetryRequest should be the end of the flight.
if (ssl->method->has_unprocessed_handshake_data(ssl)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESS_HANDSHAKE_DATA);
return ssl_hs_error;
}
ssl->method->next_message(ssl);
ssl->s3->used_hello_retry_request = true;
hs->tls13_state = state_send_second_client_hello;
// 0-RTT is rejected if we receive a HelloRetryRequest.
if (hs->in_early_data) {
ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
if (!close_early_data(hs, ssl_encryption_initial)) {
return ssl_hs_error;
}
return ssl_hs_early_data_rejected;
}
return ssl_hs_ok;
}