in ssl/tls13_server.cc [386:572]
static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
SSLMessage msg;
SSL_CLIENT_HELLO client_hello;
if (!hs->GetClientHello(&msg, &client_hello)) {
return ssl_hs_error;
}
uint8_t alert = SSL_AD_DECODE_ERROR;
UniquePtr<SSL_SESSION> session;
bool offered_ticket = false;
switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew,
&offered_ticket, msg, &client_hello)) {
case ssl_ticket_aead_ignore_ticket:
assert(!session);
if (!ssl_get_new_session(hs)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
break;
case ssl_ticket_aead_success:
// Carry over authentication information from the previous handshake into
// a fresh session.
hs->new_session =
SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
if (hs->new_session == nullptr) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
ssl->s3->session_reused = true;
hs->can_release_private_key = true;
// Resumption incorporates fresh key material, so refresh the timeout.
ssl_session_renew_timeout(ssl, hs->new_session.get(),
ssl->session_ctx->session_psk_dhe_timeout);
break;
case ssl_ticket_aead_error:
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
case ssl_ticket_aead_retry:
hs->tls13_state = state13_select_session;
return ssl_hs_pending_ticket;
}
// Negotiate ALPS now, after ALPN is negotiated and |hs->new_session| is
// initialized.
if (!ssl_negotiate_alps(hs, &alert, &client_hello)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
// Record connection properties in the new session.
hs->new_session->cipher = hs->new_cipher;
if (!tls1_get_shared_group(hs, &hs->new_session->group_id)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_GROUP);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
return ssl_hs_error;
}
// Determine if we need HelloRetryRequest.
bool found_key_share;
if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share,
/*out_key_share=*/nullptr, &alert,
&client_hello)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
// Determine if we're negotiating 0-RTT.
if (!ssl->enable_early_data) {
ssl->s3->early_data_reason = ssl_early_data_disabled;
} else if (!offered_ticket) {
ssl->s3->early_data_reason = ssl_early_data_no_session_offered;
} else if (!session) {
ssl->s3->early_data_reason = ssl_early_data_session_not_resumed;
} else if (session->ticket_max_early_data == 0) {
ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
} else if (!hs->early_data_offered) {
ssl->s3->early_data_reason = ssl_early_data_peer_declined;
} else if (hs->channel_id_negotiated) {
// Channel ID is incompatible with 0-RTT.
ssl->s3->early_data_reason = ssl_early_data_channel_id;
} else if (MakeConstSpan(ssl->s3->alpn_selected) != session->early_alpn) {
// The negotiated ALPN must match the one in the ticket.
ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
} else if (hs->new_session->has_application_settings !=
session->has_application_settings ||
MakeConstSpan(hs->new_session->local_application_settings) !=
session->local_application_settings) {
ssl->s3->early_data_reason = ssl_early_data_alps_mismatch;
} else if (ssl->s3->ticket_age_skew < -kMaxTicketAgeSkewSeconds ||
kMaxTicketAgeSkewSeconds < ssl->s3->ticket_age_skew) {
ssl->s3->early_data_reason = ssl_early_data_ticket_age_skew;
} else if (!quic_ticket_compatible(session.get(), hs->config)) {
ssl->s3->early_data_reason = ssl_early_data_quic_parameter_mismatch;
} else if (!found_key_share) {
ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
} else if (hs->custom_extensions.received) {
ssl->s3->early_data_reason = ssl_early_data_unsupported_with_custom_extension;
} else {
// |ssl_session_is_resumable| forbids cross-cipher resumptions even if the
// PRF hashes match.
assert(hs->new_cipher == session->cipher);
ssl->s3->early_data_reason = ssl_early_data_accepted;
ssl->s3->early_data_accepted = true;
}
// Store the ALPN and ALPS values in the session for 0-RTT. Note the peer
// applications settings are not generally known until client
// EncryptedExtensions.
if (!hs->new_session->early_alpn.CopyFrom(ssl->s3->alpn_selected)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
// The peer applications settings are usually received later, in
// EncryptedExtensions. But, in 0-RTT handshakes, we carry over the
// values from |session|. Do this now, before |session| is discarded.
if (ssl->s3->early_data_accepted &&
hs->new_session->has_application_settings &&
!hs->new_session->peer_application_settings.CopyFrom(
session->peer_application_settings)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
// Copy the QUIC early data context to the session.
if (ssl->enable_early_data && ssl->quic_method) {
if (!hs->new_session->quic_early_data_context.CopyFrom(
hs->config->quic_early_data_context)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
}
if (ssl->ctx->dos_protection_cb != NULL &&
ssl->ctx->dos_protection_cb(&client_hello) == 0) {
// Connection rejected for DOS reasons.
OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
size_t hash_len = EVP_MD_size(
ssl_get_handshake_digest(ssl_protocol_version(ssl), hs->new_cipher));
// Set up the key schedule and incorporate the PSK into the running secret.
if (!tls13_init_key_schedule(
hs, ssl->s3->session_reused
? MakeConstSpan(hs->new_session->secret,
hs->new_session->secret_length)
: MakeConstSpan(kZeroes, hash_len)) ||
!ssl_hash_message(hs, msg)) {
return ssl_hs_error;
}
if (ssl->s3->early_data_accepted) {
if (!tls13_derive_early_secret(hs)) {
return ssl_hs_error;
}
} else if (hs->early_data_offered) {
ssl->s3->skip_early_data = true;
}
if (!found_key_share) {
ssl->method->next_message(ssl);
if (!hs->transcript.UpdateForHelloRetryRequest()) {
return ssl_hs_error;
}
hs->tls13_state = state13_send_hello_retry_request;
return ssl_hs_ok;
}
if (!resolve_ecdhe_secret(hs, &client_hello)) {
return ssl_hs_error;
}
ssl->method->next_message(ssl);
hs->ech_client_hello_buf.Reset();
hs->tls13_state = state13_send_server_hello;
return ssl_hs_ok;
}