static enum ssl_hs_wait_t do_read_hello_retry_request()

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