static enum ssl_hs_wait_t do_select_session()

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;
}