// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "quiche/quic/core/crypto/quic_crypto_server_config.h"

#include <algorithm>
#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <utility>

#include "absl/base/attributes.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "openssl/sha.h"
#include "openssl/ssl.h"
#include "quiche/quic/core/crypto/aes_128_gcm_12_decrypter.h"
#include "quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
#include "quiche/quic/core/crypto/cert_compressor.h"
#include "quiche/quic/core/crypto/certificate_view.h"
#include "quiche/quic/core/crypto/chacha20_poly1305_encrypter.h"
#include "quiche/quic/core/crypto/channel_id.h"
#include "quiche/quic/core/crypto/crypto_framer.h"
#include "quiche/quic/core/crypto/crypto_handshake_message.h"
#include "quiche/quic/core/crypto/crypto_utils.h"
#include "quiche/quic/core/crypto/curve25519_key_exchange.h"
#include "quiche/quic/core/crypto/key_exchange.h"
#include "quiche/quic/core/crypto/p256_key_exchange.h"
#include "quiche/quic/core/crypto/proof_source.h"
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/crypto/quic_hkdf.h"
#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/quic/core/crypto/tls_server_connection.h"
#include "quiche/quic/core/proto/crypto_server_config_proto.h"
#include "quiche/quic/core/proto/source_address_token_proto.h"
#include "quiche/quic/core/quic_clock.h"
#include "quiche/quic/core/quic_connection_context.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_socket_address_coder.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_hostname_utils.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/quic/platform/api/quic_testvalue.h"
#include "quiche/common/platform/api/quiche_reference_counted.h"

namespace quic {

namespace {

// kMultiplier is the multiple of the CHLO message size that a REJ message
// must stay under when the client doesn't present a valid source-address
// token. This is used to protect QUIC from amplification attacks.
// TODO(rch): Reduce this to 2 again once b/25933682 is fixed.
const size_t kMultiplier = 3;

const int kMaxTokenAddresses = 4;

std::string DeriveSourceAddressTokenKey(
    absl::string_view source_address_token_secret) {
  QuicHKDF hkdf(source_address_token_secret, absl::string_view() /* no salt */,
                "QUIC source address token key",
                CryptoSecretBoxer::GetKeySize(), 0 /* no fixed IV needed */,
                0 /* no subkey secret */);
  return std::string(hkdf.server_write_key());
}

// Default source for creating KeyExchange objects.
class DefaultKeyExchangeSource : public KeyExchangeSource {
 public:
  DefaultKeyExchangeSource() = default;
  ~DefaultKeyExchangeSource() override = default;

  std::unique_ptr<AsynchronousKeyExchange> Create(
      std::string /*server_config_id*/, bool /* is_fallback */, QuicTag type,
      absl::string_view private_key) override {
    if (private_key.empty()) {
      QUIC_LOG(WARNING) << "Server config contains key exchange method without "
                           "corresponding private key of type "
                        << QuicTagToString(type);
      return nullptr;
    }

    std::unique_ptr<SynchronousKeyExchange> ka =
        CreateLocalSynchronousKeyExchange(type, private_key);
    if (!ka) {
      QUIC_LOG(WARNING) << "Failed to create key exchange method of type "
                        << QuicTagToString(type);
    }
    return ka;
  }
};

// Returns true if the PDMD field from the client hello demands an X509
// certificate.
bool ClientDemandsX509Proof(const CryptoHandshakeMessage& client_hello) {
  QuicTagVector their_proof_demands;

  if (client_hello.GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) {
    return false;
  }

  for (const QuicTag tag : their_proof_demands) {
    if (tag == kX509) {
      return true;
    }
  }
  return false;
}

std::string FormatCryptoHandshakeMessageForTrace(
    const CryptoHandshakeMessage* message) {
  if (message == nullptr) {
    return "<null message>";
  }

  std::string s = QuicTagToString(message->tag());

  // Append the reasons for REJ.
  if (const auto it = message->tag_value_map().find(kRREJ);
      it != message->tag_value_map().end()) {
    const std::string& value = it->second;
    // The value is a vector of uint32_t(s).
    if (value.size() % sizeof(uint32_t) == 0) {
      absl::StrAppend(&s, " RREJ:[");
      // Append comma-separated list of reasons to |s|.
      for (size_t j = 0; j < value.size(); j += sizeof(uint32_t)) {
        uint32_t reason;
        memcpy(&reason, value.data() + j, sizeof(reason));
        if (j > 0) {
          absl::StrAppend(&s, ",");
        }
        absl::StrAppend(&s, CryptoUtils::HandshakeFailureReasonToString(
                                static_cast<HandshakeFailureReason>(reason)));
      }
      absl::StrAppend(&s, "]");
    } else {
      absl::StrAppendFormat(&s, " RREJ:[unexpected length:%u]", value.size());
    }
  }

  return s;
}

}  // namespace

// static
std::unique_ptr<KeyExchangeSource> KeyExchangeSource::Default() {
  return std::make_unique<DefaultKeyExchangeSource>();
}

class ValidateClientHelloHelper {
 public:
  // Note: stores a pointer to a unique_ptr, and std::moves the unique_ptr when
  // ValidationComplete is called.
  ValidateClientHelloHelper(
      quiche::QuicheReferenceCountedPointer<
          ValidateClientHelloResultCallback::Result>
          result,
      std::unique_ptr<ValidateClientHelloResultCallback>* done_cb)
      : result_(std::move(result)), done_cb_(done_cb) {}
  ValidateClientHelloHelper(const ValidateClientHelloHelper&) = delete;
  ValidateClientHelloHelper& operator=(const ValidateClientHelloHelper&) =
      delete;

  ~ValidateClientHelloHelper() {
    QUIC_BUG_IF(quic_bug_12963_1, done_cb_ != nullptr)
        << "Deleting ValidateClientHelloHelper with a pending callback.";
  }

  void ValidationComplete(
      QuicErrorCode error_code, const char* error_details,
      std::unique_ptr<ProofSource::Details> proof_source_details) {
    result_->error_code = error_code;
    result_->error_details = error_details;
    (*done_cb_)->Run(std::move(result_), std::move(proof_source_details));
    DetachCallback();
  }

  void DetachCallback() {
    QUIC_BUG_IF(quic_bug_10630_1, done_cb_ == nullptr)
        << "Callback already detached.";
    done_cb_ = nullptr;
  }

 private:
  quiche::QuicheReferenceCountedPointer<
      ValidateClientHelloResultCallback::Result>
      result_;
  std::unique_ptr<ValidateClientHelloResultCallback>* done_cb_;
};

// static
const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";

ClientHelloInfo::ClientHelloInfo(const QuicIpAddress& in_client_ip,
                                 QuicWallTime in_now)
    : client_ip(in_client_ip), now(in_now), valid_source_address_token(false) {}

ClientHelloInfo::ClientHelloInfo(const ClientHelloInfo& other) = default;

ClientHelloInfo::~ClientHelloInfo() {}

PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {}

PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {}

ValidateClientHelloResultCallback::Result::Result(
    const CryptoHandshakeMessage& in_client_hello, QuicIpAddress in_client_ip,
    QuicWallTime in_now)
    : client_hello(in_client_hello),
      info(in_client_ip, in_now),
      error_code(QUIC_NO_ERROR) {}

ValidateClientHelloResultCallback::Result::~Result() {}

ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {}

ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {}

ProcessClientHelloResultCallback::ProcessClientHelloResultCallback() {}

ProcessClientHelloResultCallback::~ProcessClientHelloResultCallback() {}

QuicCryptoServerConfig::ConfigOptions::ConfigOptions()
    : expiry_time(QuicWallTime::Zero()),
      channel_id_enabled(false),
      p256(false) {}

QuicCryptoServerConfig::ConfigOptions::ConfigOptions(
    const ConfigOptions& other) = default;

QuicCryptoServerConfig::ConfigOptions::~ConfigOptions() {}

QuicCryptoServerConfig::ProcessClientHelloContext::
    ~ProcessClientHelloContext() {
  if (done_cb_ != nullptr) {
    QUIC_LOG(WARNING)
        << "Deleting ProcessClientHelloContext with a pending callback.";
  }
}

void QuicCryptoServerConfig::ProcessClientHelloContext::Fail(
    QuicErrorCode error, const std::string& error_details) {
  QUIC_TRACEPRINTF("ProcessClientHello failed: error=%s, details=%s",
                   QuicErrorCodeToString(error), error_details);
  done_cb_->Run(error, error_details, nullptr, nullptr, nullptr);
  done_cb_ = nullptr;
}

void QuicCryptoServerConfig::ProcessClientHelloContext::Succeed(
    std::unique_ptr<CryptoHandshakeMessage> message,
    std::unique_ptr<DiversificationNonce> diversification_nonce,
    std::unique_ptr<ProofSource::Details> proof_source_details) {
  QUIC_TRACEPRINTF("ProcessClientHello succeeded: %s",
                   FormatCryptoHandshakeMessageForTrace(message.get()));

  done_cb_->Run(QUIC_NO_ERROR, std::string(), std::move(message),
                std::move(diversification_nonce),
                std::move(proof_source_details));
  done_cb_ = nullptr;
}

QuicCryptoServerConfig::QuicCryptoServerConfig(
    absl::string_view source_address_token_secret,
    QuicRandom* server_nonce_entropy, std::unique_ptr<ProofSource> proof_source,
    std::unique_ptr<KeyExchangeSource> key_exchange_source)
    : replay_protection_(true),
      chlo_multiplier_(kMultiplier),
      configs_lock_(),
      primary_config_(nullptr),
      next_config_promotion_time_(QuicWallTime::Zero()),
      proof_source_(std::move(proof_source)),
      key_exchange_source_(std::move(key_exchange_source)),
      ssl_ctx_(TlsServerConnection::CreateSslCtx(proof_source_.get())),
      source_address_token_future_secs_(3600),
      source_address_token_lifetime_secs_(86400),
      enable_serving_sct_(false),
      rejection_observer_(nullptr),
      pad_rej_(true),
      pad_shlo_(true),
      validate_chlo_size_(true),
      validate_source_address_token_(true) {
  QUICHE_DCHECK(proof_source_.get());
  source_address_token_boxer_.SetKeys(
      {DeriveSourceAddressTokenKey(source_address_token_secret)});

  // Generate a random key and orbit for server nonces.
  server_nonce_entropy->RandBytes(server_nonce_orbit_,
                                  sizeof(server_nonce_orbit_));
  const size_t key_size = server_nonce_boxer_.GetKeySize();
  std::unique_ptr<uint8_t[]> key_bytes(new uint8_t[key_size]);
  server_nonce_entropy->RandBytes(key_bytes.get(), key_size);

  server_nonce_boxer_.SetKeys(
      {std::string(reinterpret_cast<char*>(key_bytes.get()), key_size)});
}

QuicCryptoServerConfig::~QuicCryptoServerConfig() {}

// static
QuicServerConfigProtobuf QuicCryptoServerConfig::GenerateConfig(
    QuicRandom* rand, const QuicClock* clock, const ConfigOptions& options) {
  CryptoHandshakeMessage msg;

  const std::string curve25519_private_key =
      Curve25519KeyExchange::NewPrivateKey(rand);
  std::unique_ptr<Curve25519KeyExchange> curve25519 =
      Curve25519KeyExchange::New(curve25519_private_key);
  absl::string_view curve25519_public_value = curve25519->public_value();

  std::string encoded_public_values;
  // First three bytes encode the length of the public value.
  QUICHE_DCHECK_LT(curve25519_public_value.size(), (1U << 24));
  encoded_public_values.push_back(
      static_cast<char>(curve25519_public_value.size()));
  encoded_public_values.push_back(
      static_cast<char>(curve25519_public_value.size() >> 8));
  encoded_public_values.push_back(
      static_cast<char>(curve25519_public_value.size() >> 16));
  encoded_public_values.append(curve25519_public_value.data(),
                               curve25519_public_value.size());

  std::string p256_private_key;
  if (options.p256) {
    p256_private_key = P256KeyExchange::NewPrivateKey();
    std::unique_ptr<P256KeyExchange> p256(
        P256KeyExchange::New(p256_private_key));
    absl::string_view p256_public_value = p256->public_value();

    QUICHE_DCHECK_LT(p256_public_value.size(), (1U << 24));
    encoded_public_values.push_back(
        static_cast<char>(p256_public_value.size()));
    encoded_public_values.push_back(
        static_cast<char>(p256_public_value.size() >> 8));
    encoded_public_values.push_back(
        static_cast<char>(p256_public_value.size() >> 16));
    encoded_public_values.append(p256_public_value.data(),
                                 p256_public_value.size());
  }

  msg.set_tag(kSCFG);
  if (options.p256) {
    msg.SetVector(kKEXS, QuicTagVector{kC255, kP256});
  } else {
    msg.SetVector(kKEXS, QuicTagVector{kC255});
  }
  msg.SetVector(kAEAD, QuicTagVector{kAESG, kCC20});
  msg.SetStringPiece(kPUBS, encoded_public_values);

  if (options.expiry_time.IsZero()) {
    const QuicWallTime now = clock->WallNow();
    const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds(
        60 * 60 * 24 * 180 /* 180 days, ~six months */));
    const uint64_t expiry_seconds = expiry.ToUNIXSeconds();
    msg.SetValue(kEXPY, expiry_seconds);
  } else {
    msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds());
  }

  char orbit_bytes[kOrbitSize];
  if (options.orbit.size() == sizeof(orbit_bytes)) {
    memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes));
  } else {
    QUICHE_DCHECK(options.orbit.empty());
    rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
  }
  msg.SetStringPiece(kORBT,
                     absl::string_view(orbit_bytes, sizeof(orbit_bytes)));

  if (options.channel_id_enabled) {
    msg.SetVector(kPDMD, QuicTagVector{kCHID});
  }

  if (options.id.empty()) {
    // We need to ensure that the SCID changes whenever the server config does
    // thus we make it a hash of the rest of the server config.
    std::unique_ptr<QuicData> serialized =
        CryptoFramer::ConstructHandshakeMessage(msg);

    uint8_t scid_bytes[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const uint8_t*>(serialized->data()),
           serialized->length(), scid_bytes);
    // The SCID is a truncated SHA-256 digest.
    static_assert(16 <= SHA256_DIGEST_LENGTH, "SCID length too high.");
    msg.SetStringPiece(
        kSCID,
        absl::string_view(reinterpret_cast<const char*>(scid_bytes), 16));
  } else {
    msg.SetStringPiece(kSCID, options.id);
  }
  // Don't put new tags below this point. The SCID generation should hash over
  // everything but itself and so extra tags should be added prior to the
  // preceding if block.

  std::unique_ptr<QuicData> serialized =
      CryptoFramer::ConstructHandshakeMessage(msg);

  QuicServerConfigProtobuf config;
  config.set_config(std::string(serialized->AsStringPiece()));
  QuicServerConfigProtobuf::PrivateKey* curve25519_key = config.add_key();
  curve25519_key->set_tag(kC255);
  curve25519_key->set_private_key(curve25519_private_key);

  if (options.p256) {
    QuicServerConfigProtobuf::PrivateKey* p256_key = config.add_key();
    p256_key->set_tag(kP256);
    p256_key->set_private_key(p256_private_key);
  }

  return config;
}

std::unique_ptr<CryptoHandshakeMessage> QuicCryptoServerConfig::AddConfig(
    const QuicServerConfigProtobuf& protobuf, const QuicWallTime now) {
  std::unique_ptr<CryptoHandshakeMessage> msg =
      CryptoFramer::ParseMessage(protobuf.config());

  if (!msg) {
    QUIC_LOG(WARNING) << "Failed to parse server config message";
    return nullptr;
  }

  quiche::QuicheReferenceCountedPointer<Config> config =
      ParseConfigProtobuf(protobuf, /* is_fallback = */ false);
  if (!config) {
    QUIC_LOG(WARNING) << "Failed to parse server config message";
    return nullptr;
  }

  {
    QuicWriterMutexLock locked(&configs_lock_);
    if (configs_.find(config->id) != configs_.end()) {
      QUIC_LOG(WARNING) << "Failed to add config because another with the same "
                           "server config id already exists: "
                        << absl::BytesToHexString(config->id);
      return nullptr;
    }

    configs_[config->id] = config;
    SelectNewPrimaryConfig(now);
    QUICHE_DCHECK(primary_config_.get());
    QUICHE_DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
                     primary_config_.get());
  }

  return msg;
}

std::unique_ptr<CryptoHandshakeMessage>
QuicCryptoServerConfig::AddDefaultConfig(QuicRandom* rand,
                                         const QuicClock* clock,
                                         const ConfigOptions& options) {
  return AddConfig(GenerateConfig(rand, clock, options), clock->WallNow());
}

bool QuicCryptoServerConfig::SetConfigs(
    const std::vector<QuicServerConfigProtobuf>& protobufs,
    const QuicServerConfigProtobuf* fallback_protobuf, const QuicWallTime now) {
  std::vector<quiche::QuicheReferenceCountedPointer<Config>> parsed_configs;
  for (auto& protobuf : protobufs) {
    quiche::QuicheReferenceCountedPointer<Config> config =
        ParseConfigProtobuf(protobuf, /* is_fallback = */ false);
    if (!config) {
      QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors";
      return false;
    }

    parsed_configs.push_back(config);
  }

  quiche::QuicheReferenceCountedPointer<Config> fallback_config;
  if (fallback_protobuf != nullptr) {
    fallback_config =
        ParseConfigProtobuf(*fallback_protobuf, /* is_fallback = */ true);
    if (!fallback_config) {
      QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors";
      return false;
    }
    QUIC_LOG(INFO) << "Fallback config has scid "
                   << absl::BytesToHexString(fallback_config->id);
    parsed_configs.push_back(fallback_config);
  } else {
    QUIC_LOG(INFO) << "No fallback config provided";
  }

  if (parsed_configs.empty()) {
    QUIC_LOG(WARNING)
        << "Rejecting QUIC configs because new config list is empty.";
    return false;
  }

  QUIC_LOG(INFO) << "Updating configs:";

  QuicWriterMutexLock locked(&configs_lock_);
  ConfigMap new_configs;

  for (const quiche::QuicheReferenceCountedPointer<Config>& config :
       parsed_configs) {
    auto it = configs_.find(config->id);
    if (it != configs_.end()) {
      QUIC_LOG(INFO) << "Keeping scid: " << absl::BytesToHexString(config->id)
                     << " orbit: "
                     << absl::BytesToHexString(absl::string_view(
                            reinterpret_cast<const char*>(config->orbit),
                            kOrbitSize))
                     << " new primary_time "
                     << config->primary_time.ToUNIXSeconds()
                     << " old primary_time "
                     << it->second->primary_time.ToUNIXSeconds()
                     << " new priority " << config->priority << " old priority "
                     << it->second->priority;
      // Update primary_time and priority.
      it->second->primary_time = config->primary_time;
      it->second->priority = config->priority;
      new_configs.insert(*it);
    } else {
      QUIC_LOG(INFO) << "Adding scid: " << absl::BytesToHexString(config->id)
                     << " orbit: "
                     << absl::BytesToHexString(absl::string_view(
                            reinterpret_cast<const char*>(config->orbit),
                            kOrbitSize))
                     << " primary_time " << config->primary_time.ToUNIXSeconds()
                     << " priority " << config->priority;
      new_configs.emplace(config->id, config);
    }
  }

  configs_ = std::move(new_configs);
  fallback_config_ = fallback_config;
  SelectNewPrimaryConfig(now);
  QUICHE_DCHECK(primary_config_.get());
  QUICHE_DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
                   primary_config_.get());

  return true;
}

void QuicCryptoServerConfig::SetSourceAddressTokenKeys(
    const std::vector<std::string>& keys) {
  // TODO(b/208866709)
  source_address_token_boxer_.SetKeys(keys);
}

std::vector<std::string> QuicCryptoServerConfig::GetConfigIds() const {
  QuicReaderMutexLock locked(&configs_lock_);
  std::vector<std::string> scids;
  for (auto it = configs_.begin(); it != configs_.end(); ++it) {
    scids.push_back(it->first);
  }
  return scids;
}

void QuicCryptoServerConfig::ValidateClientHello(
    const CryptoHandshakeMessage& client_hello,
    const QuicSocketAddress& client_address,
    const QuicSocketAddress& server_address, QuicTransportVersion version,
    const QuicClock* clock,
    quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig> signed_config,
    std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const {
  const QuicWallTime now(clock->WallNow());

  quiche::QuicheReferenceCountedPointer<
      ValidateClientHelloResultCallback::Result>
      result(new ValidateClientHelloResultCallback::Result(
          client_hello, client_address.host(), now));

  absl::string_view requested_scid;
  // We ignore here the return value from GetStringPiece. If there is no SCID
  // tag, EvaluateClientHello will discover that because GetCurrentConfigs will
  // not have found the requested config (i.e. because none of the configs will
  // have an empty string as its id).
  client_hello.GetStringPiece(kSCID, &requested_scid);
  Configs configs;
  if (!GetCurrentConfigs(now, requested_scid,
                         /* old_primary_config = */ nullptr, &configs)) {
    result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
    result->error_details = "No configurations loaded";
  }
  signed_config->config = configs.primary;

  if (result->error_code == QUIC_NO_ERROR) {
    // QUIC requires a new proof for each CHLO so clear any existing proof.
    signed_config->chain = nullptr;
    signed_config->proof.signature = "";
    signed_config->proof.leaf_cert_scts = "";
    EvaluateClientHello(server_address, client_address, version, configs,
                        result, std::move(done_cb));
  } else {
    done_cb->Run(result, /* details = */ nullptr);
  }
}

class QuicCryptoServerConfig::ProcessClientHelloCallback
    : public ProofSource::Callback {
 public:
  ProcessClientHelloCallback(const QuicCryptoServerConfig* config,
                             std::unique_ptr<ProcessClientHelloContext> context,
                             const Configs& configs)
      : config_(config), context_(std::move(context)), configs_(configs) {}

  void Run(
      bool ok,
      const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
      const QuicCryptoProof& proof,
      std::unique_ptr<ProofSource::Details> details) override {
    if (ok) {
      context_->signed_config()->chain = chain;
      context_->signed_config()->proof = proof;
    }
    config_->ProcessClientHelloAfterGetProof(!ok, std::move(details),
                                             std::move(context_), configs_);
  }

 private:
  const QuicCryptoServerConfig* config_;
  std::unique_ptr<ProcessClientHelloContext> context_;
  const Configs configs_;
};

class QuicCryptoServerConfig::ProcessClientHelloAfterGetProofCallback
    : public AsynchronousKeyExchange::Callback {
 public:
  ProcessClientHelloAfterGetProofCallback(
      const QuicCryptoServerConfig* config,
      std::unique_ptr<ProofSource::Details> proof_source_details,
      QuicTag key_exchange_type, std::unique_ptr<CryptoHandshakeMessage> out,
      absl::string_view public_value,
      std::unique_ptr<ProcessClientHelloContext> context,
      const Configs& configs)
      : config_(config),
        proof_source_details_(std::move(proof_source_details)),
        key_exchange_type_(key_exchange_type),
        out_(std::move(out)),
        public_value_(public_value),
        context_(std::move(context)),
        configs_(configs) {}

  void Run(bool ok) override {
    config_->ProcessClientHelloAfterCalculateSharedKeys(
        !ok, std::move(proof_source_details_), key_exchange_type_,
        std::move(out_), public_value_, std::move(context_), configs_);
  }

 private:
  const QuicCryptoServerConfig* config_;
  std::unique_ptr<ProofSource::Details> proof_source_details_;
  const QuicTag key_exchange_type_;
  std::unique_ptr<CryptoHandshakeMessage> out_;
  const std::string public_value_;
  std::unique_ptr<ProcessClientHelloContext> context_;
  const Configs configs_;
  std::unique_ptr<ProcessClientHelloResultCallback> done_cb_;
};

class QuicCryptoServerConfig::SendRejectWithFallbackConfigCallback
    : public ProofSource::Callback {
 public:
  SendRejectWithFallbackConfigCallback(
      const QuicCryptoServerConfig* config,
      std::unique_ptr<ProcessClientHelloContext> context,
      quiche::QuicheReferenceCountedPointer<Config> fallback_config)
      : config_(config),
        context_(std::move(context)),
        fallback_config_(fallback_config) {}

  // Capture |chain| and |proof| into the signed config, and then invoke
  // SendRejectWithFallbackConfigAfterGetProof.
  void Run(
      bool ok,
      const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
      const QuicCryptoProof& proof,
      std::unique_ptr<ProofSource::Details> details) override {
    if (ok) {
      context_->signed_config()->chain = chain;
      context_->signed_config()->proof = proof;
    }
    config_->SendRejectWithFallbackConfigAfterGetProof(
        !ok, std::move(details), std::move(context_), fallback_config_);
  }

 private:
  const QuicCryptoServerConfig* config_;
  std::unique_ptr<ProcessClientHelloContext> context_;
  quiche::QuicheReferenceCountedPointer<Config> fallback_config_;
};

void QuicCryptoServerConfig::ProcessClientHello(
    quiche::QuicheReferenceCountedPointer<
        ValidateClientHelloResultCallback::Result>
        validate_chlo_result,
    bool reject_only, QuicConnectionId connection_id,
    const QuicSocketAddress& server_address,
    const QuicSocketAddress& client_address, ParsedQuicVersion version,
    const ParsedQuicVersionVector& supported_versions, const QuicClock* clock,
    QuicRandom* rand, QuicCompressedCertsCache* compressed_certs_cache,
    quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
        params,
    quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig> signed_config,
    QuicByteCount total_framing_overhead, QuicByteCount chlo_packet_size,
    std::shared_ptr<ProcessClientHelloResultCallback> done_cb) const {
  QUICHE_DCHECK(done_cb);
  auto context = std::make_unique<ProcessClientHelloContext>(
      validate_chlo_result, reject_only, connection_id, server_address,
      client_address, version, supported_versions, clock, rand,
      compressed_certs_cache, params, signed_config, total_framing_overhead,
      chlo_packet_size, std::move(done_cb));

  // Verify that various parts of the CHLO are valid
  std::string error_details;
  QuicErrorCode valid = CryptoUtils::ValidateClientHello(
      context->client_hello(), context->version(),
      context->supported_versions(), &error_details);
  if (valid != QUIC_NO_ERROR) {
    context->Fail(valid, error_details);
    return;
  }

  absl::string_view requested_scid;
  context->client_hello().GetStringPiece(kSCID, &requested_scid);
  Configs configs;
  if (!GetCurrentConfigs(context->clock()->WallNow(), requested_scid,
                         signed_config->config, &configs)) {
    context->Fail(QUIC_CRYPTO_INTERNAL_ERROR, "No configurations loaded");
    return;
  }

  if (context->validate_chlo_result()->error_code != QUIC_NO_ERROR) {
    context->Fail(context->validate_chlo_result()->error_code,
                  context->validate_chlo_result()->error_details);
    return;
  }

  if (!ClientDemandsX509Proof(context->client_hello())) {
    context->Fail(QUIC_UNSUPPORTED_PROOF_DEMAND, "Missing or invalid PDMD");
    return;
  }

  // No need to get a new proof if one was already generated.
  if (!context->signed_config()->chain) {
    const std::string chlo_hash = CryptoUtils::HashHandshakeMessage(
        context->client_hello(), Perspective::IS_SERVER);
    const QuicSocketAddress context_server_address = context->server_address();
    const std::string sni = std::string(context->info().sni);
    const QuicTransportVersion transport_version = context->transport_version();

    auto cb = std::make_unique<ProcessClientHelloCallback>(
        this, std::move(context), configs);

    QUICHE_DCHECK(proof_source_.get());
    proof_source_->GetProof(context_server_address, client_address, sni,
                            configs.primary->serialized, transport_version,
                            chlo_hash, std::move(cb));
    return;
  }

  ProcessClientHelloAfterGetProof(
      /* found_error = */ false, /* proof_source_details = */ nullptr,
      std::move(context), configs);
}

void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof(
    bool found_error,
    std::unique_ptr<ProofSource::Details> proof_source_details,
    std::unique_ptr<ProcessClientHelloContext> context,
    const Configs& configs) const {
  QUIC_BUG_IF(quic_bug_12963_2,
              !QuicUtils::IsConnectionIdValidForVersion(
                  context->connection_id(), context->transport_version()))
      << "ProcessClientHelloAfterGetProof: attempted to use connection ID "
      << context->connection_id() << " which is invalid with version "
      << context->version();

  if (context->info().reject_reasons.empty()) {
    if (!context->signed_config() || !context->signed_config()->chain) {
      // No chain.
      context->validate_chlo_result()->info.reject_reasons.push_back(
          SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
    } else if (!ValidateExpectedLeafCertificate(
                   context->client_hello(),
                   context->signed_config()->chain->certs)) {
      // Has chain but leaf is invalid.
      context->validate_chlo_result()->info.reject_reasons.push_back(
          INVALID_EXPECTED_LEAF_CERTIFICATE);
    }
  }

  if (found_error) {
    context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
    return;
  }

  auto out_diversification_nonce = std::make_unique<DiversificationNonce>();

  absl::string_view cert_sct;
  if (context->client_hello().GetStringPiece(kCertificateSCTTag, &cert_sct) &&
      cert_sct.empty()) {
    context->params()->sct_supported_by_client = true;
  }

  auto out = std::make_unique<CryptoHandshakeMessage>();
  if (!context->info().reject_reasons.empty() || !configs.requested) {
    BuildRejectionAndRecordStats(*context, *configs.primary,
                                 context->info().reject_reasons, out.get());
    context->Succeed(std::move(out), std::move(out_diversification_nonce),
                     std::move(proof_source_details));
    return;
  }

  if (context->reject_only()) {
    context->Succeed(std::move(out), std::move(out_diversification_nonce),
                     std::move(proof_source_details));
    return;
  }

  QuicTagVector their_aeads;
  QuicTagVector their_key_exchanges;
  if (context->client_hello().GetTaglist(kAEAD, &their_aeads) !=
          QUIC_NO_ERROR ||
      context->client_hello().GetTaglist(kKEXS, &their_key_exchanges) !=
          QUIC_NO_ERROR ||
      their_aeads.size() != 1 || their_key_exchanges.size() != 1) {
    context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                  "Missing or invalid AEAD or KEXS");
    return;
  }

  size_t key_exchange_index;
  if (!FindMutualQuicTag(configs.requested->aead, their_aeads,
                         &context->params()->aead, nullptr) ||
      !FindMutualQuicTag(configs.requested->kexs, their_key_exchanges,
                         &context->params()->key_exchange,
                         &key_exchange_index)) {
    context->Fail(QUIC_CRYPTO_NO_SUPPORT, "Unsupported AEAD or KEXS");
    return;
  }

  absl::string_view public_value;
  if (!context->client_hello().GetStringPiece(kPUBS, &public_value)) {
    context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                  "Missing public value");
    return;
  }

  // Allow testing a specific adversarial case in which a client sends a public
  // value of incorrect size.
  AdjustTestValue("quic::QuicCryptoServerConfig::public_value_adjust",
                  &public_value);

  const AsynchronousKeyExchange* key_exchange =
      configs.requested->key_exchanges[key_exchange_index].get();
  std::string* initial_premaster_secret =
      &context->params()->initial_premaster_secret;
  auto cb = std::make_unique<ProcessClientHelloAfterGetProofCallback>(
      this, std::move(proof_source_details), key_exchange->type(),
      std::move(out), public_value, std::move(context), configs);
  key_exchange->CalculateSharedKeyAsync(public_value, initial_premaster_secret,
                                        std::move(cb));
}

void QuicCryptoServerConfig::ProcessClientHelloAfterCalculateSharedKeys(
    bool found_error,
    std::unique_ptr<ProofSource::Details> proof_source_details,
    QuicTag key_exchange_type, std::unique_ptr<CryptoHandshakeMessage> out,
    absl::string_view public_value,
    std::unique_ptr<ProcessClientHelloContext> context,
    const Configs& configs) const {
  QUIC_BUG_IF(quic_bug_12963_3,
              !QuicUtils::IsConnectionIdValidForVersion(
                  context->connection_id(), context->transport_version()))
      << "ProcessClientHelloAfterCalculateSharedKeys:"
         " attempted to use connection ID "
      << context->connection_id() << " which is invalid with version "
      << context->version();

  if (found_error) {
    // If we are already using the fallback config, or there is no fallback
    // config to use, just bail out of the handshake.
    if (configs.fallback == nullptr ||
        context->signed_config()->config == configs.fallback) {
      context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                    "Failed to calculate shared key");
    } else {
      SendRejectWithFallbackConfig(std::move(context), configs.fallback);
    }
    return;
  }

  if (!context->info().sni.empty()) {
    context->params()->sni =
        QuicHostnameUtils::NormalizeHostname(context->info().sni);
  }

  std::string hkdf_suffix;
  const QuicData& client_hello_serialized =
      context->client_hello().GetSerialized();
  hkdf_suffix.reserve(context->connection_id().length() +
                      client_hello_serialized.length() +
                      configs.requested->serialized.size());
  hkdf_suffix.append(context->connection_id().data(),
                     context->connection_id().length());
  hkdf_suffix.append(client_hello_serialized.data(),
                     client_hello_serialized.length());
  hkdf_suffix.append(configs.requested->serialized);
  QUICHE_DCHECK(proof_source_.get());
  if (context->signed_config()->chain->certs.empty()) {
    context->Fail(QUIC_CRYPTO_INTERNAL_ERROR, "Failed to get certs");
    return;
  }
  hkdf_suffix.append(context->signed_config()->chain->certs[0]);

  absl::string_view cetv_ciphertext;
  if (configs.requested->channel_id_enabled &&
      context->client_hello().GetStringPiece(kCETV, &cetv_ciphertext)) {
    CryptoHandshakeMessage client_hello_copy(context->client_hello());
    client_hello_copy.Erase(kCETV);
    client_hello_copy.Erase(kPAD);

    const QuicData& client_hello_copy_serialized =
        client_hello_copy.GetSerialized();
    std::string hkdf_input;
    hkdf_input.append(QuicCryptoConfig::kCETVLabel,
                      strlen(QuicCryptoConfig::kCETVLabel) + 1);
    hkdf_input.append(context->connection_id().data(),
                      context->connection_id().length());
    hkdf_input.append(client_hello_copy_serialized.data(),
                      client_hello_copy_serialized.length());
    hkdf_input.append(configs.requested->serialized);

    CrypterPair crypters;
    if (!CryptoUtils::DeriveKeys(
            context->version(), context->params()->initial_premaster_secret,
            context->params()->aead, context->info().client_nonce,
            context->info().server_nonce, pre_shared_key_, hkdf_input,
            Perspective::IS_SERVER, CryptoUtils::Diversification::Never(),
            &crypters, nullptr /* subkey secret */)) {
      context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
                    "Symmetric key setup failed");
      return;
    }

    char plaintext[kMaxOutgoingPacketSize];
    size_t plaintext_length = 0;
    const bool success = crypters.decrypter->DecryptPacket(
        0 /* packet number */, absl::string_view() /* associated data */,
        cetv_ciphertext, plaintext, &plaintext_length, kMaxOutgoingPacketSize);
    if (!success) {
      context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                    "CETV decryption failure");
      return;
    }
    std::unique_ptr<CryptoHandshakeMessage> cetv(CryptoFramer::ParseMessage(
        absl::string_view(plaintext, plaintext_length)));
    if (!cetv) {
      context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "CETV parse error");
      return;
    }

    absl::string_view key, signature;
    if (cetv->GetStringPiece(kCIDK, &key) &&
        cetv->GetStringPiece(kCIDS, &signature)) {
      if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) {
        context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                      "ChannelID signature failure");
        return;
      }

      context->params()->channel_id = std::string(key);
    }
  }

  std::string hkdf_input;
  size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
  hkdf_input.reserve(label_len + hkdf_suffix.size());
  hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
  hkdf_input.append(hkdf_suffix);

  auto out_diversification_nonce = std::make_unique<DiversificationNonce>();
  context->rand()->RandBytes(out_diversification_nonce->data(),
                             out_diversification_nonce->size());
  CryptoUtils::Diversification diversification =
      CryptoUtils::Diversification::Now(out_diversification_nonce.get());
  if (!CryptoUtils::DeriveKeys(
          context->version(), context->params()->initial_premaster_secret,
          context->params()->aead, context->info().client_nonce,
          context->info().server_nonce, pre_shared_key_, hkdf_input,
          Perspective::IS_SERVER, diversification,
          &context->params()->initial_crypters,
          &context->params()->initial_subkey_secret)) {
    context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
                  "Symmetric key setup failed");
    return;
  }

  std::string forward_secure_public_value;
  std::unique_ptr<SynchronousKeyExchange> forward_secure_key_exchange =
      CreateLocalSynchronousKeyExchange(key_exchange_type, context->rand());
  if (!forward_secure_key_exchange) {
    QUIC_DLOG(WARNING) << "Failed to create keypair";
    context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                  "Failed to create keypair");
    return;
  }

  forward_secure_public_value =
      std::string(forward_secure_key_exchange->public_value());
  if (!forward_secure_key_exchange->CalculateSharedKeySync(
          public_value, &context->params()->forward_secure_premaster_secret)) {
    context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                  "Invalid public value");
    return;
  }

  std::string forward_secure_hkdf_input;
  label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
  forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size());
  forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel,
                                   label_len);
  forward_secure_hkdf_input.append(hkdf_suffix);

  std::string shlo_nonce;
  shlo_nonce = NewServerNonce(context->rand(), context->info().now);
  out->SetStringPiece(kServerNonceTag, shlo_nonce);

  if (!CryptoUtils::DeriveKeys(
          context->version(),
          context->params()->forward_secure_premaster_secret,
          context->params()->aead, context->info().client_nonce,
          shlo_nonce.empty() ? context->info().server_nonce : shlo_nonce,
          pre_shared_key_, forward_secure_hkdf_input, Perspective::IS_SERVER,
          CryptoUtils::Diversification::Never(),
          &context->params()->forward_secure_crypters,
          &context->params()->subkey_secret)) {
    context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
                  "Symmetric key setup failed");
    return;
  }

  out->set_tag(kSHLO);
  out->SetVersionVector(kVER, context->supported_versions());
  out->SetStringPiece(
      kSourceAddressTokenTag,
      NewSourceAddressToken(*configs.requested->source_address_token_boxer,
                            context->info().source_address_tokens,
                            context->client_address().host(), context->rand(),
                            context->info().now, nullptr));
  QuicSocketAddressCoder address_coder(context->client_address());
  out->SetStringPiece(kCADR, address_coder.Encode());
  out->SetStringPiece(kPUBS, forward_secure_public_value);

  context->Succeed(std::move(out), std::move(out_diversification_nonce),
                   std::move(proof_source_details));
}

void QuicCryptoServerConfig::SendRejectWithFallbackConfig(
    std::unique_ptr<ProcessClientHelloContext> context,
    quiche::QuicheReferenceCountedPointer<Config> fallback_config) const {
  // We failed to calculate a shared initial key, likely because we tried to use
  // a remote key-exchange service which could not be reached.  We want to send
  // a REJ which tells the client to use a different ServerConfig which
  // corresponds to a local keypair.  To generate the REJ we need to request a
  // new proof.
  const std::string chlo_hash = CryptoUtils::HashHandshakeMessage(
      context->client_hello(), Perspective::IS_SERVER);
  const QuicSocketAddress server_address = context->server_address();
  const std::string sni(context->info().sni);
  const QuicTransportVersion transport_version = context->transport_version();

  const QuicSocketAddress& client_address = context->client_address();
  auto cb = std::make_unique<SendRejectWithFallbackConfigCallback>(
      this, std::move(context), fallback_config);
  proof_source_->GetProof(server_address, client_address, sni,
                          fallback_config->serialized, transport_version,
                          chlo_hash, std::move(cb));
}

void QuicCryptoServerConfig::SendRejectWithFallbackConfigAfterGetProof(
    bool found_error,
    std::unique_ptr<ProofSource::Details> proof_source_details,
    std::unique_ptr<ProcessClientHelloContext> context,
    quiche::QuicheReferenceCountedPointer<Config> fallback_config) const {
  if (found_error) {
    context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
    return;
  }

  auto out = std::make_unique<CryptoHandshakeMessage>();
  BuildRejectionAndRecordStats(*context, *fallback_config,
                               {SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE},
                               out.get());

  context->Succeed(std::move(out), std::make_unique<DiversificationNonce>(),
                   std::move(proof_source_details));
}

quiche::QuicheReferenceCountedPointer<QuicCryptoServerConfig::Config>
QuicCryptoServerConfig::GetConfigWithScid(
    absl::string_view requested_scid) const {
  configs_lock_.AssertReaderHeld();

  if (!requested_scid.empty()) {
    auto it = configs_.find((std::string(requested_scid)));
    if (it != configs_.end()) {
      // We'll use the config that the client requested in order to do
      // key-agreement.
      return quiche::QuicheReferenceCountedPointer<Config>(it->second);
    }
  }

  return quiche::QuicheReferenceCountedPointer<Config>();
}

bool QuicCryptoServerConfig::GetCurrentConfigs(
    const QuicWallTime& now, absl::string_view requested_scid,
    quiche::QuicheReferenceCountedPointer<Config> old_primary_config,
    Configs* configs) const {
  QuicReaderMutexLock locked(&configs_lock_);

  if (!primary_config_) {
    return false;
  }

  if (IsNextConfigReady(now)) {
    configs_lock_.ReaderUnlock();
    configs_lock_.WriterLock();
    SelectNewPrimaryConfig(now);
    QUICHE_DCHECK(primary_config_.get());
    QUICHE_DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
                     primary_config_.get());
    configs_lock_.WriterUnlock();
    configs_lock_.ReaderLock();
  }

  if (old_primary_config != nullptr) {
    configs->primary = old_primary_config;
  } else {
    configs->primary = primary_config_;
  }
  configs->requested = GetConfigWithScid(requested_scid);
  configs->fallback = fallback_config_;

  return true;
}

// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
// Config's based on their primary_time.
// static
bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
    const quiche::QuicheReferenceCountedPointer<Config>& a,
    const quiche::QuicheReferenceCountedPointer<Config>& b) {
  if (a->primary_time.IsBefore(b->primary_time) ||
      b->primary_time.IsBefore(a->primary_time)) {
    // Primary times differ.
    return a->primary_time.IsBefore(b->primary_time);
  } else if (a->priority != b->priority) {
    // Primary times are equal, sort backwards by priority.
    return a->priority < b->priority;
  } else {
    // Primary times and priorities are equal, sort by config id.
    return a->id < b->id;
  }
}

void QuicCryptoServerConfig::SelectNewPrimaryConfig(
    const QuicWallTime now) const {
  std::vector<quiche::QuicheReferenceCountedPointer<Config>> configs;
  configs.reserve(configs_.size());

  for (auto it = configs_.begin(); it != configs_.end(); ++it) {
    // TODO(avd) Exclude expired configs?
    configs.push_back(it->second);
  }

  if (configs.empty()) {
    if (primary_config_ != nullptr) {
      QUIC_BUG(quic_bug_10630_2)
          << "No valid QUIC server config. Keeping the current config.";
    } else {
      QUIC_BUG(quic_bug_10630_3) << "No valid QUIC server config.";
    }
    return;
  }

  std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);

  quiche::QuicheReferenceCountedPointer<Config> best_candidate = configs[0];

  for (size_t i = 0; i < configs.size(); ++i) {
    const quiche::QuicheReferenceCountedPointer<Config> config(configs[i]);
    if (!config->primary_time.IsAfter(now)) {
      if (config->primary_time.IsAfter(best_candidate->primary_time)) {
        best_candidate = config;
      }
      continue;
    }

    // This is the first config with a primary_time in the future. Thus the
    // previous Config should be the primary and this one should determine the
    // next_config_promotion_time_.
    quiche::QuicheReferenceCountedPointer<Config> new_primary = best_candidate;
    if (i == 0) {
      // We need the primary_time of the next config.
      if (configs.size() > 1) {
        next_config_promotion_time_ = configs[1]->primary_time;
      } else {
        next_config_promotion_time_ = QuicWallTime::Zero();
      }
    } else {
      next_config_promotion_time_ = config->primary_time;
    }

    if (primary_config_) {
      primary_config_->is_primary = false;
    }
    primary_config_ = new_primary;
    new_primary->is_primary = true;
    QUIC_DLOG(INFO) << "New primary config.  orbit: "
                    << absl::BytesToHexString(
                           absl::string_view(reinterpret_cast<const char*>(
                                                 primary_config_->orbit),
                                             kOrbitSize));
    if (primary_config_changed_cb_ != nullptr) {
      primary_config_changed_cb_->Run(primary_config_->id);
    }

    return;
  }

  // All config's primary times are in the past. We should make the most recent
  // and highest priority candidate primary.
  quiche::QuicheReferenceCountedPointer<Config> new_primary = best_candidate;
  if (primary_config_) {
    primary_config_->is_primary = false;
  }
  primary_config_ = new_primary;
  new_primary->is_primary = true;
  QUIC_DLOG(INFO) << "New primary config.  orbit: "
                  << absl::BytesToHexString(absl::string_view(
                         reinterpret_cast<const char*>(primary_config_->orbit),
                         kOrbitSize))
                  << " scid: " << absl::BytesToHexString(primary_config_->id);
  next_config_promotion_time_ = QuicWallTime::Zero();
  if (primary_config_changed_cb_ != nullptr) {
    primary_config_changed_cb_->Run(primary_config_->id);
  }
}

void QuicCryptoServerConfig::EvaluateClientHello(
    const QuicSocketAddress& /*server_address*/,
    const QuicSocketAddress& /*client_address*/,
    QuicTransportVersion /*version*/, const Configs& configs,
    quiche::QuicheReferenceCountedPointer<
        ValidateClientHelloResultCallback::Result>
        client_hello_state,
    std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const {
  ValidateClientHelloHelper helper(client_hello_state, &done_cb);

  const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello;
  ClientHelloInfo* info = &(client_hello_state->info);

  if (client_hello.GetStringPiece(kSNI, &info->sni) &&
      !QuicHostnameUtils::IsValidSNI(info->sni)) {
    helper.ValidationComplete(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
                              "Invalid SNI name", nullptr);
    return;
  }

  client_hello.GetStringPiece(kUAID, &info->user_agent_id);

  HandshakeFailureReason source_address_token_error = MAX_FAILURE_REASON;
  if (validate_source_address_token_) {
    absl::string_view srct;
    if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
      Config& config =
          configs.requested != nullptr ? *configs.requested : *configs.primary;
      source_address_token_error =
          ParseSourceAddressToken(*config.source_address_token_boxer, srct,
                                  info->source_address_tokens);

      if (source_address_token_error == HANDSHAKE_OK) {
        source_address_token_error = ValidateSourceAddressTokens(
            info->source_address_tokens, info->client_ip, info->now,
            &client_hello_state->cached_network_params);
      }
      info->valid_source_address_token =
          (source_address_token_error == HANDSHAKE_OK);
    } else {
      source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
    }
  } else {
    source_address_token_error = HANDSHAKE_OK;
    info->valid_source_address_token = true;
  }

  if (!configs.requested) {
    absl::string_view requested_scid;
    if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
      info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
    } else {
      info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
    }
    // No server config with the requested ID.
    helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
    return;
  }

  if (!client_hello.GetStringPiece(kNONC, &info->client_nonce)) {
    info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
    // Report no client nonce as INCHOATE_HELLO_FAILURE.
    helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
    return;
  }

  if (source_address_token_error != HANDSHAKE_OK) {
    info->reject_reasons.push_back(source_address_token_error);
    // No valid source address token.
  }

  if (info->client_nonce.size() != kNonceSize) {
    info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
    // Invalid client nonce.
    QUIC_LOG_FIRST_N(ERROR, 2)
        << "Invalid client nonce: " << client_hello.DebugString();
    QUIC_DLOG(INFO) << "Invalid client nonce.";
  }

  // Server nonce is optional, and used for key derivation if present.
  client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);

  // If the server nonce is empty and we're requiring handshake confirmation
  // for DoS reasons then we must reject the CHLO.
  if (GetQuicReloadableFlag(quic_require_handshake_confirmation) &&
      info->server_nonce.empty()) {
    QUIC_RELOADABLE_FLAG_COUNT(quic_require_handshake_confirmation);
    info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE);
  }
  helper.ValidationComplete(QUIC_NO_ERROR, "",
                            std::unique_ptr<ProofSource::Details>());
}

void QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
    QuicTransportVersion version, absl::string_view chlo_hash,
    const SourceAddressTokens& previous_source_address_tokens,
    const QuicSocketAddress& server_address,
    const QuicSocketAddress& client_address, const QuicClock* clock,
    QuicRandom* rand, QuicCompressedCertsCache* compressed_certs_cache,
    const QuicCryptoNegotiatedParameters& params,
    const CachedNetworkParameters* cached_network_params,
    std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
  std::string serialized;
  std::string source_address_token;
  {
    QuicReaderMutexLock locked(&configs_lock_);
    serialized = primary_config_->serialized;
    source_address_token = NewSourceAddressToken(
        *primary_config_->source_address_token_boxer,
        previous_source_address_tokens, client_address.host(), rand,
        clock->WallNow(), cached_network_params);
  }

  CryptoHandshakeMessage message;
  message.set_tag(kSCUP);
  message.SetStringPiece(kSCFG, serialized);
  message.SetStringPiece(kSourceAddressTokenTag, source_address_token);

  auto proof_source_cb =
      std::make_unique<BuildServerConfigUpdateMessageProofSourceCallback>(
          this, compressed_certs_cache, params, std::move(message),
          std::move(cb));

  proof_source_->GetProof(server_address, client_address, params.sni,
                          serialized, version, chlo_hash,
                          std::move(proof_source_cb));
}

QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
    ~BuildServerConfigUpdateMessageProofSourceCallback() {}

QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
    BuildServerConfigUpdateMessageProofSourceCallback(
        const QuicCryptoServerConfig* config,
        QuicCompressedCertsCache* compressed_certs_cache,
        const QuicCryptoNegotiatedParameters& params,
        CryptoHandshakeMessage message,
        std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb)
    : config_(config),
      compressed_certs_cache_(compressed_certs_cache),
      client_cached_cert_hashes_(params.client_cached_cert_hashes),
      sct_supported_by_client_(params.sct_supported_by_client),
      sni_(params.sni),
      message_(std::move(message)),
      cb_(std::move(cb)) {}

void QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
    Run(bool ok,
        const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
        const QuicCryptoProof& proof,
        std::unique_ptr<ProofSource::Details> details) {
  config_->FinishBuildServerConfigUpdateMessage(
      compressed_certs_cache_, client_cached_cert_hashes_,
      sct_supported_by_client_, sni_, ok, chain, proof.signature,
      proof.leaf_cert_scts, std::move(details), std::move(message_),
      std::move(cb_));
}

void QuicCryptoServerConfig::FinishBuildServerConfigUpdateMessage(
    QuicCompressedCertsCache* compressed_certs_cache,
    const std::string& client_cached_cert_hashes, bool sct_supported_by_client,
    const std::string& sni, bool ok,
    const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
    const std::string& signature, const std::string& leaf_cert_sct,
    std::unique_ptr<ProofSource::Details> /*details*/,
    CryptoHandshakeMessage message,
    std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
  if (!ok) {
    cb->Run(false, message);
    return;
  }

  const std::string compressed =
      CompressChain(compressed_certs_cache, chain, client_cached_cert_hashes);

  message.SetStringPiece(kCertificateTag, compressed);
  message.SetStringPiece(kPROF, signature);
  if (sct_supported_by_client && enable_serving_sct_) {
    if (leaf_cert_sct.empty()) {
      QUIC_LOG_EVERY_N_SEC(WARNING, 60)
          << "SCT is expected but it is empty. SNI: " << sni;
    } else {
      message.SetStringPiece(kCertificateSCTTag, leaf_cert_sct);
    }
  }

  cb->Run(true, message);
}

void QuicCryptoServerConfig::BuildRejectionAndRecordStats(
    const ProcessClientHelloContext& context, const Config& config,
    const std::vector<uint32_t>& reject_reasons,
    CryptoHandshakeMessage* out) const {
  BuildRejection(context, config, reject_reasons, out);
  if (rejection_observer_ != nullptr) {
    rejection_observer_->OnRejectionBuilt(reject_reasons, out);
  }
}

void QuicCryptoServerConfig::BuildRejection(
    const ProcessClientHelloContext& context, const Config& config,
    const std::vector<uint32_t>& reject_reasons,
    CryptoHandshakeMessage* out) const {
  const QuicWallTime now = context.clock()->WallNow();

  out->set_tag(kREJ);
  out->SetStringPiece(kSCFG, config.serialized);
  out->SetStringPiece(
      kSourceAddressTokenTag,
      NewSourceAddressToken(
          *config.source_address_token_boxer,
          context.info().source_address_tokens, context.info().client_ip,
          context.rand(), context.info().now,
          &context.validate_chlo_result()->cached_network_params));
  out->SetValue(kSTTL, config.expiry_time.AbsoluteDifference(now).ToSeconds());
  if (replay_protection_) {
    out->SetStringPiece(kServerNonceTag,
                        NewServerNonce(context.rand(), context.info().now));
  }

  // Send client the reject reason for debugging purposes.
  QUICHE_DCHECK_LT(0u, reject_reasons.size());
  out->SetVector(kRREJ, reject_reasons);

  // The client may have requested a certificate chain.
  if (!ClientDemandsX509Proof(context.client_hello())) {
    QUIC_BUG(quic_bug_10630_4)
        << "x509 certificates not supported in proof demand";
    return;
  }

  absl::string_view client_cached_cert_hashes;
  if (context.client_hello().GetStringPiece(kCCRT,
                                            &client_cached_cert_hashes)) {
    context.params()->client_cached_cert_hashes =
        std::string(client_cached_cert_hashes);
  } else {
    context.params()->client_cached_cert_hashes.clear();
  }

  const std::string compressed = CompressChain(
      context.compressed_certs_cache(), context.signed_config()->chain,
      context.params()->client_cached_cert_hashes);

  QUICHE_DCHECK_GT(context.chlo_packet_size(), context.client_hello().size());
  // kREJOverheadBytes is a very rough estimate of how much of a REJ
  // message is taken up by things other than the certificates.
  // STK: 56 bytes
  // SNO: 56 bytes
  // SCFG
  //   SCID: 16 bytes
  //   PUBS: 38 bytes
  const size_t kREJOverheadBytes = 166;
  // max_unverified_size is the number of bytes that the certificate chain,
  // signature, and (optionally) signed certificate timestamp can consume before
  // we will demand a valid source-address token.
  const size_t max_unverified_size =
      chlo_multiplier_ *
          (context.chlo_packet_size() - context.total_framing_overhead()) -
      kREJOverheadBytes;
  static_assert(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
                "overhead calculation may underflow");
  bool should_return_sct =
      context.params()->sct_supported_by_client && enable_serving_sct_;
  const std::string& cert_sct = context.signed_config()->proof.leaf_cert_scts;
  const size_t sct_size = should_return_sct ? cert_sct.size() : 0;
  const size_t total_size = context.signed_config()->proof.signature.size() +
                            compressed.size() + sct_size;
  if (context.info().valid_source_address_token ||
      total_size < max_unverified_size) {
    out->SetStringPiece(kCertificateTag, compressed);
    out->SetStringPiece(kPROF, context.signed_config()->proof.signature);
    if (should_return_sct) {
      if (cert_sct.empty()) {
        // Log SNI and subject name for the leaf cert if its SCT is empty.
        // This is for debugging b/28342827.
        const std::vector<std::string>& certs =
            context.signed_config()->chain->certs;
        std::string ca_subject;
        if (!certs.empty()) {
          std::unique_ptr<CertificateView> view =
              CertificateView::ParseSingleCertificate(certs[0]);
          if (view != nullptr) {
            std::optional<std::string> maybe_ca_subject =
                view->GetHumanReadableSubject();
            if (maybe_ca_subject.has_value()) {
              ca_subject = *maybe_ca_subject;
            }
          }
        }
        QUIC_LOG_EVERY_N_SEC(WARNING, 60)
            << "SCT is expected but it is empty. sni: '"
            << context.params()->sni << "' cert subject: '" << ca_subject
            << "'";
      } else {
        out->SetStringPiece(kCertificateSCTTag, cert_sct);
      }
    }
  } else {
    QUIC_LOG_EVERY_N_SEC(WARNING, 60)
        << "Sending inchoate REJ for hostname: " << context.info().sni
        << " signature: " << context.signed_config()->proof.signature.size()
        << " cert: " << compressed.size() << " sct:" << sct_size
        << " total: " << total_size << " max: " << max_unverified_size;
  }
}

std::string QuicCryptoServerConfig::CompressChain(
    QuicCompressedCertsCache* compressed_certs_cache,
    const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
    const std::string& client_cached_cert_hashes) {
  // Check whether the compressed certs is available in the cache.
  QUICHE_DCHECK(compressed_certs_cache);
  const std::string* cached_value = compressed_certs_cache->GetCompressedCert(
      chain, client_cached_cert_hashes);
  if (cached_value) {
    return *cached_value;
  }
  std::string compressed =
      CertCompressor::CompressChain(chain->certs, client_cached_cert_hashes);
  // Insert the newly compressed cert to cache.
  compressed_certs_cache->Insert(chain, client_cached_cert_hashes, compressed);
  return compressed;
}

quiche::QuicheReferenceCountedPointer<QuicCryptoServerConfig::Config>
QuicCryptoServerConfig::ParseConfigProtobuf(
    const QuicServerConfigProtobuf& protobuf, bool is_fallback) const {
  std::unique_ptr<CryptoHandshakeMessage> msg =
      CryptoFramer::ParseMessage(protobuf.config());

  if (!msg) {
    QUIC_LOG(WARNING) << "Failed to parse server config message";
    return nullptr;
  }

  if (msg->tag() != kSCFG) {
    QUIC_LOG(WARNING) << "Server config message has tag " << msg->tag()
                      << ", but expected " << kSCFG;
    return nullptr;
  }

  quiche::QuicheReferenceCountedPointer<Config> config(new Config);
  config->serialized = protobuf.config();
  config->source_address_token_boxer = &source_address_token_boxer_;

  if (protobuf.has_primary_time()) {
    config->primary_time =
        QuicWallTime::FromUNIXSeconds(protobuf.primary_time());
  }

  config->priority = protobuf.priority();

  absl::string_view scid;
  if (!msg->GetStringPiece(kSCID, &scid)) {
    QUIC_LOG(WARNING) << "Server config message is missing SCID";
    return nullptr;
  }
  if (scid.empty()) {
    QUIC_LOG(WARNING) << "Server config message contains an empty SCID";
    return nullptr;
  }
  config->id = std::string(scid);

  if (msg->GetTaglist(kAEAD, &config->aead) != QUIC_NO_ERROR) {
    QUIC_LOG(WARNING) << "Server config message is missing AEAD";
    return nullptr;
  }

  QuicTagVector kexs_tags;
  if (msg->GetTaglist(kKEXS, &kexs_tags) != QUIC_NO_ERROR) {
    QUIC_LOG(WARNING) << "Server config message is missing KEXS";
    return nullptr;
  }

  absl::string_view orbit;
  if (!msg->GetStringPiece(kORBT, &orbit)) {
    QUIC_LOG(WARNING) << "Server config message is missing ORBT";
    return nullptr;
  }

  if (orbit.size() != kOrbitSize) {
    QUIC_LOG(WARNING) << "Orbit value in server config is the wrong length."
                         " Got "
                      << orbit.size() << " want " << kOrbitSize;
    return nullptr;
  }
  static_assert(sizeof(config->orbit) == kOrbitSize, "incorrect orbit size");
  memcpy(config->orbit, orbit.data(), sizeof(config->orbit));

  QuicTagVector proof_demand_tags;
  if (msg->GetTaglist(kPDMD, &proof_demand_tags) == QUIC_NO_ERROR) {
    for (QuicTag tag : proof_demand_tags) {
      if (tag == kCHID) {
        config->channel_id_enabled = true;
        break;
      }
    }
  }

  for (size_t i = 0; i < kexs_tags.size(); i++) {
    const QuicTag tag = kexs_tags[i];
    std::string private_key;

    config->kexs.push_back(tag);

    for (int j = 0; j < protobuf.key_size(); j++) {
      const QuicServerConfigProtobuf::PrivateKey& key = protobuf.key(i);
      if (key.tag() == tag) {
        private_key = key.private_key();
        break;
      }
    }

    std::unique_ptr<AsynchronousKeyExchange> ka =
        key_exchange_source_->Create(config->id, is_fallback, tag, private_key);
    if (!ka) {
      return nullptr;
    }
    for (const auto& key_exchange : config->key_exchanges) {
      if (key_exchange->type() == tag) {
        QUIC_LOG(WARNING) << "Duplicate key exchange in config: " << tag;
        return nullptr;
      }
    }

    config->key_exchanges.push_back(std::move(ka));
  }

  uint64_t expiry_seconds;
  if (msg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
    QUIC_LOG(WARNING) << "Server config message is missing EXPY";
    return nullptr;
  }
  config->expiry_time = QuicWallTime::FromUNIXSeconds(expiry_seconds);

  return config;
}

void QuicCryptoServerConfig::set_replay_protection(bool on) {
  replay_protection_ = on;
}

void QuicCryptoServerConfig::set_chlo_multiplier(size_t multiplier) {
  chlo_multiplier_ = multiplier;
}

void QuicCryptoServerConfig::set_source_address_token_future_secs(
    uint32_t future_secs) {
  source_address_token_future_secs_ = future_secs;
}

void QuicCryptoServerConfig::set_source_address_token_lifetime_secs(
    uint32_t lifetime_secs) {
  source_address_token_lifetime_secs_ = lifetime_secs;
}

void QuicCryptoServerConfig::set_enable_serving_sct(bool enable_serving_sct) {
  enable_serving_sct_ = enable_serving_sct;
}

void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
    std::unique_ptr<PrimaryConfigChangedCallback> cb) {
  QuicWriterMutexLock locked(&configs_lock_);
  primary_config_changed_cb_ = std::move(cb);
}

std::string QuicCryptoServerConfig::NewSourceAddressToken(
    const CryptoSecretBoxer& crypto_secret_boxer,
    const SourceAddressTokens& previous_tokens, const QuicIpAddress& ip,
    QuicRandom* rand, QuicWallTime now,
    const CachedNetworkParameters* cached_network_params) const {
  SourceAddressTokens source_address_tokens;
  SourceAddressToken* source_address_token = source_address_tokens.add_tokens();
  source_address_token->set_ip(ip.DualStacked().ToPackedString());
  source_address_token->set_timestamp(now.ToUNIXSeconds());
  if (cached_network_params != nullptr) {
    *(source_address_token->mutable_cached_network_parameters()) =
        *cached_network_params;
  }

  // Append previous tokens.
  for (const SourceAddressToken& token : previous_tokens.tokens()) {
    if (source_address_tokens.tokens_size() > kMaxTokenAddresses) {
      break;
    }

    if (token.ip() == source_address_token->ip()) {
      // It's for the same IP address.
      continue;
    }

    if (ValidateSourceAddressTokenTimestamp(token, now) != HANDSHAKE_OK) {
      continue;
    }

    *(source_address_tokens.add_tokens()) = token;
  }

  return crypto_secret_boxer.Box(rand,
                                 source_address_tokens.SerializeAsString());
}

int QuicCryptoServerConfig::NumberOfConfigs() const {
  QuicReaderMutexLock locked(&configs_lock_);
  return configs_.size();
}

ProofSource* QuicCryptoServerConfig::proof_source() const {
  return proof_source_.get();
}

SSL_CTX* QuicCryptoServerConfig::ssl_ctx() const { return ssl_ctx_.get(); }

HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken(
    const CryptoSecretBoxer& crypto_secret_boxer, absl::string_view token,
    SourceAddressTokens& tokens) const {
  std::string storage;
  absl::string_view plaintext;
  if (!crypto_secret_boxer.Unbox(token, &storage, &plaintext)) {
    return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE;
  }

  if (!tokens.ParseFromArray(plaintext.data(), plaintext.size())) {
    // Some clients might still be using the old source token format so
    // attempt to parse that format.
    // TODO(rch): remove this code once the new format is ubiquitous.
    SourceAddressToken old_source_token;
    if (!old_source_token.ParseFromArray(plaintext.data(), plaintext.size())) {
      return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
    }
    *tokens.add_tokens() = old_source_token;
  }

  return HANDSHAKE_OK;
}

HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens(
    const SourceAddressTokens& source_address_tokens, const QuicIpAddress& ip,
    QuicWallTime now, CachedNetworkParameters* cached_network_params) const {
  HandshakeFailureReason reason =
      SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
  for (const SourceAddressToken& token : source_address_tokens.tokens()) {
    reason = ValidateSingleSourceAddressToken(token, ip, now);
    if (reason == HANDSHAKE_OK) {
      if (cached_network_params != nullptr &&
          token.has_cached_network_parameters()) {
        *cached_network_params = token.cached_network_parameters();
      }
      break;
    }
  }
  return reason;
}

HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken(
    const SourceAddressToken& source_address_token, const QuicIpAddress& ip,
    QuicWallTime now) const {
  if (source_address_token.ip() != ip.DualStacked().ToPackedString()) {
    // It's for a different IP address.
    return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
  }

  return ValidateSourceAddressTokenTimestamp(source_address_token, now);
}

HandshakeFailureReason
QuicCryptoServerConfig::ValidateSourceAddressTokenTimestamp(
    const SourceAddressToken& source_address_token, QuicWallTime now) const {
  const QuicWallTime timestamp(
      QuicWallTime::FromUNIXSeconds(source_address_token.timestamp()));
  const QuicTime::Delta delta(now.AbsoluteDifference(timestamp));

  if (now.IsBefore(timestamp) &&
      delta.ToSeconds() > source_address_token_future_secs_) {
    return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE;
  }

  if (now.IsAfter(timestamp) &&
      delta.ToSeconds() > source_address_token_lifetime_secs_) {
    return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE;
  }

  return HANDSHAKE_OK;
}

// kServerNoncePlaintextSize is the number of bytes in an unencrypted server
// nonce.
static const size_t kServerNoncePlaintextSize =
    4 /* timestamp */ + 20 /* random bytes */;

std::string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
                                                   QuicWallTime now) const {
  const uint32_t timestamp = static_cast<uint32_t>(now.ToUNIXSeconds());

  uint8_t server_nonce[kServerNoncePlaintextSize];
  static_assert(sizeof(server_nonce) > sizeof(timestamp), "nonce too small");
  server_nonce[0] = static_cast<uint8_t>(timestamp >> 24);
  server_nonce[1] = static_cast<uint8_t>(timestamp >> 16);
  server_nonce[2] = static_cast<uint8_t>(timestamp >> 8);
  server_nonce[3] = static_cast<uint8_t>(timestamp);
  rand->RandBytes(&server_nonce[sizeof(timestamp)],
                  sizeof(server_nonce) - sizeof(timestamp));

  return server_nonce_boxer_.Box(
      rand, absl::string_view(reinterpret_cast<char*>(server_nonce),
                              sizeof(server_nonce)));
}

bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate(
    const CryptoHandshakeMessage& client_hello,
    const std::vector<std::string>& certs) const {
  if (certs.empty()) {
    return false;
  }

  uint64_t hash_from_client;
  if (client_hello.GetUint64(kXLCT, &hash_from_client) != QUIC_NO_ERROR) {
    return false;
  }
  return CryptoUtils::ComputeLeafCertHash(certs[0]) == hash_from_client;
}

bool QuicCryptoServerConfig::IsNextConfigReady(QuicWallTime now) const {
  return !next_config_promotion_time_.IsZero() &&
         !next_config_promotion_time_.IsAfter(now);
}

QuicCryptoServerConfig::Config::Config()
    : channel_id_enabled(false),
      is_primary(false),
      primary_time(QuicWallTime::Zero()),
      expiry_time(QuicWallTime::Zero()),
      priority(0),
      source_address_token_boxer(nullptr) {}

QuicCryptoServerConfig::Config::~Config() {}

QuicSignedServerConfig::QuicSignedServerConfig() {}
QuicSignedServerConfig::~QuicSignedServerConfig() {}

}  // namespace quic
