// 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.

#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_

#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "openssl/base.h"
#include "quiche/quic/core/crypto/crypto_handshake.h"
#include "quiche/quic/core/crypto/crypto_handshake_message.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/crypto/crypto_secret_boxer.h"
#include "quiche/quic/core/crypto/key_exchange.h"
#include "quiche/quic/core/crypto/proof_source.h"
#include "quiche/quic/core/crypto/quic_compressed_certs_cache.h"
#include "quiche/quic/core/crypto/quic_crypto_proof.h"
#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/quic/core/proto/cached_network_parameters_proto.h"
#include "quiche/quic/core/proto/source_address_token_proto.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/platform/api/quic_export.h"
#include "quiche/quic/platform/api/quic_mutex.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/common/platform/api/quiche_reference_counted.h"

namespace quic {

class CryptoHandshakeMessage;
class ProofSource;
class QuicClock;
class QuicServerConfigProtobuf;
struct QuicSignedServerConfig;

// ClientHelloInfo contains information about a client hello message that is
// only kept for as long as it's being processed.
struct QUICHE_EXPORT ClientHelloInfo {
  ClientHelloInfo(const QuicIpAddress& in_client_ip, QuicWallTime in_now);
  ClientHelloInfo(const ClientHelloInfo& other);
  ~ClientHelloInfo();

  // Inputs to EvaluateClientHello.
  const QuicIpAddress client_ip;
  const QuicWallTime now;

  // Outputs from EvaluateClientHello.
  bool valid_source_address_token;
  absl::string_view sni;
  absl::string_view client_nonce;
  absl::string_view server_nonce;
  absl::string_view user_agent_id;
  SourceAddressTokens source_address_tokens;

  // Errors from EvaluateClientHello.
  std::vector<uint32_t> reject_reasons;
  static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync");
};

namespace test {
class QuicCryptoServerConfigPeer;
}  // namespace test

// Hook that allows application code to subscribe to primary config changes.
class QUICHE_EXPORT PrimaryConfigChangedCallback {
 public:
  PrimaryConfigChangedCallback();
  PrimaryConfigChangedCallback(const PrimaryConfigChangedCallback&) = delete;
  PrimaryConfigChangedCallback& operator=(const PrimaryConfigChangedCallback&) =
      delete;
  virtual ~PrimaryConfigChangedCallback();
  virtual void Run(const std::string& scid) = 0;
};

// Callback used to accept the result of the |client_hello| validation step.
class QUICHE_EXPORT ValidateClientHelloResultCallback {
 public:
  // Opaque token that holds information about the client_hello and
  // its validity.  Can be interpreted by calling ProcessClientHello.
  struct QUICHE_EXPORT Result : public quiche::QuicheReferenceCounted {
    Result(const CryptoHandshakeMessage& in_client_hello,
           QuicIpAddress in_client_ip, QuicWallTime in_now);

    CryptoHandshakeMessage client_hello;
    ClientHelloInfo info;
    QuicErrorCode error_code;
    std::string error_details;

    // Populated if the CHLO STK contained a CachedNetworkParameters proto.
    CachedNetworkParameters cached_network_params;

   protected:
    ~Result() override;
  };

  ValidateClientHelloResultCallback();
  ValidateClientHelloResultCallback(const ValidateClientHelloResultCallback&) =
      delete;
  ValidateClientHelloResultCallback& operator=(
      const ValidateClientHelloResultCallback&) = delete;
  virtual ~ValidateClientHelloResultCallback();
  virtual void Run(quiche::QuicheReferenceCountedPointer<Result> result,
                   std::unique_ptr<ProofSource::Details> details) = 0;
};

// Callback used to accept the result of the ProcessClientHello method.
class QUICHE_EXPORT ProcessClientHelloResultCallback {
 public:
  ProcessClientHelloResultCallback();
  ProcessClientHelloResultCallback(const ProcessClientHelloResultCallback&) =
      delete;
  ProcessClientHelloResultCallback& operator=(
      const ProcessClientHelloResultCallback&) = delete;
  virtual ~ProcessClientHelloResultCallback();
  virtual void Run(QuicErrorCode error, const std::string& error_details,
                   std::unique_ptr<CryptoHandshakeMessage> message,
                   std::unique_ptr<DiversificationNonce> diversification_nonce,
                   std::unique_ptr<ProofSource::Details> details) = 0;
};

// Callback used to receive the results of a call to
// BuildServerConfigUpdateMessage.
class QUICHE_EXPORT BuildServerConfigUpdateMessageResultCallback {
 public:
  BuildServerConfigUpdateMessageResultCallback() = default;
  virtual ~BuildServerConfigUpdateMessageResultCallback() {}
  BuildServerConfigUpdateMessageResultCallback(
      const BuildServerConfigUpdateMessageResultCallback&) = delete;
  BuildServerConfigUpdateMessageResultCallback& operator=(
      const BuildServerConfigUpdateMessageResultCallback&) = delete;
  virtual void Run(bool ok, const CryptoHandshakeMessage& message) = 0;
};

// Object that is interested in built rejections (which include REJ, SREJ and
// cheap SREJ).
class QUICHE_EXPORT RejectionObserver {
 public:
  RejectionObserver() = default;
  virtual ~RejectionObserver() {}
  RejectionObserver(const RejectionObserver&) = delete;
  RejectionObserver& operator=(const RejectionObserver&) = delete;
  // Called after a rejection is built.
  virtual void OnRejectionBuilt(const std::vector<uint32_t>& reasons,
                                CryptoHandshakeMessage* out) const = 0;
};

// Factory for creating KeyExchange objects.
class QUICHE_EXPORT KeyExchangeSource {
 public:
  virtual ~KeyExchangeSource() = default;

  // Returns the default KeyExchangeSource.
  static std::unique_ptr<KeyExchangeSource> Default();

  // Create a new KeyExchange using the curve specified by |type| using the
  // specified private key.  |private_key| may be empty for key-exchange
  // mechanisms which do not hold the private key in-process.  If |is_fallback|
  // is set, |private_key| is required to be set, and a local key-exchange
  // object should be returned.
  virtual std::unique_ptr<AsynchronousKeyExchange> Create(
      std::string server_config_id, bool is_fallback, QuicTag type,
      absl::string_view private_key) = 0;
};

// QuicCryptoServerConfig contains the crypto configuration of a QUIC server.
// Unlike a client, a QUIC server can have multiple configurations active in
// order to support clients resuming with a previous configuration.
// TODO(agl): when adding configurations at runtime is added, this object will
// need to consider locking.
class QUICHE_EXPORT QuicCryptoServerConfig {
 public:
  // ConfigOptions contains options for generating server configs.
  struct QUICHE_EXPORT ConfigOptions {
    ConfigOptions();
    ConfigOptions(const ConfigOptions& other);
    ~ConfigOptions();

    // expiry_time is the time, in UNIX seconds, when the server config will
    // expire. If unset, it defaults to the current time plus six months.
    QuicWallTime expiry_time;
    // channel_id_enabled controls whether the server config will indicate
    // support for ChannelIDs.
    bool channel_id_enabled;
    // id contains the server config id for the resulting config. If empty, a
    // random id is generated.
    std::string id;
    // orbit contains the kOrbitSize bytes of the orbit value for the server
    // config. If |orbit| is empty then a random orbit is generated.
    std::string orbit;
    // p256 determines whether a P-256 public key will be included in the
    // server config. Note that this breaks deterministic server-config
    // generation since P-256 key generation doesn't use the QuicRandom given
    // to GenerateConfig().
    bool p256;
  };

  // |source_address_token_secret|: secret key material used for encrypting and
  //     decrypting source address tokens. It can be of any length as it is fed
  //     into a KDF before use. In tests, use TESTING.
  // |server_nonce_entropy|: an entropy source used to generate the orbit and
  //     key for server nonces, which are always local to a given instance of a
  //     server. Not owned.
  // |proof_source|: provides certificate chains and signatures.
  // |key_exchange_source|: provides key-exchange functionality.
  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);
  QuicCryptoServerConfig(const QuicCryptoServerConfig&) = delete;
  QuicCryptoServerConfig& operator=(const QuicCryptoServerConfig&) = delete;
  ~QuicCryptoServerConfig();

  // TESTING is a magic parameter for passing to the constructor in tests.
  static const char TESTING[];

  // Generates a QuicServerConfigProtobuf protobuf suitable for
  // AddConfig and SetConfigs.
  static QuicServerConfigProtobuf GenerateConfig(QuicRandom* rand,
                                                 const QuicClock* clock,
                                                 const ConfigOptions& options);

  // AddConfig adds a QuicServerConfigProtobuf to the available configurations.
  // It returns the SCFG message from the config if successful. |now| is used in
  // conjunction with |protobuf->primary_time()| to determine whether the
  // config should be made primary.
  std::unique_ptr<CryptoHandshakeMessage> AddConfig(
      const QuicServerConfigProtobuf& protobuf, QuicWallTime now);

  // AddDefaultConfig calls GenerateConfig to create a config and then calls
  // AddConfig to add it. See the comment for |GenerateConfig| for details of
  // the arguments.
  std::unique_ptr<CryptoHandshakeMessage> AddDefaultConfig(
      QuicRandom* rand, const QuicClock* clock, const ConfigOptions& options);

  // SetConfigs takes a vector of config protobufs and the current time.
  // Configs are assumed to be uniquely identified by their server config ID.
  // Previously unknown configs are added and possibly made the primary config
  // depending on their |primary_time| and the value of |now|. Configs that are
  // known, but are missing from the protobufs are deleted, unless they are
  // currently the primary config. SetConfigs returns false if any errors were
  // encountered and no changes to the QuicCryptoServerConfig will occur.
  bool SetConfigs(const std::vector<QuicServerConfigProtobuf>& protobufs,
                  const QuicServerConfigProtobuf* fallback_protobuf,
                  QuicWallTime now);

  // SetSourceAddressTokenKeys sets the keys to be tried, in order, when
  // decrypting a source address token.  Note that these keys are used *without*
  // passing them through a KDF, in contradistinction to the
  // |source_address_token_secret| argument to the constructor.
  void SetSourceAddressTokenKeys(const std::vector<std::string>& keys);

  // Get the server config ids for all known configs.
  std::vector<std::string> GetConfigIds() const;

  // Checks |client_hello| for gross errors and determines whether it can be
  // shown to be fresh (i.e. not a replay).  The result of the validation step
  // must be interpreted by calling QuicCryptoServerConfig::ProcessClientHello
  // from the done_cb.
  //
  // ValidateClientHello may invoke the done_cb before unrolling the
  // stack if it is able to assess the validity of the client_nonce
  // without asynchronous operations.
  //
  // client_hello: the incoming client hello message.
  // client_ip: the IP address of the client, which is used to generate and
  //     validate source-address tokens.
  // server_address: the IP address and port of the server. The IP address and
  //     port may be used for certificate selection.
  // version: protocol version used for this connection.
  // clock: used to validate client nonces and ephemeral keys.
  // signed_config: in/out parameter to which will be written the crypto proof
  //     used in reply to a proof demand.  The pointed-to-object must live until
  //     the callback is invoked.
  // done_cb: single-use callback that accepts an opaque
  //     ValidatedClientHelloMsg token that holds information about
  //     the client hello.  The callback will always be called exactly
  //     once, either under the current call stack, or after the
  //     completion of an asynchronous operation.
  void 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;

  // ProcessClientHello processes |client_hello| and decides whether to accept
  // or reject the connection. If the connection is to be accepted, |done_cb| is
  // invoked with the contents of the ServerHello and QUIC_NO_ERROR. Otherwise
  // |done_cb| is called with a REJ or SREJ message and QUIC_NO_ERROR.
  //
  // validate_chlo_result: Output from the asynchronous call to
  //     ValidateClientHello.  Contains the client hello message and
  //     information about it.
  // reject_only: Only generate rejections, not server hello messages.
  // connection_id: the ConnectionId for the connection, which is used in key
  //     derivation.
  // server_ip: the IP address of the server. The IP address may be used for
  //     certificate selection.
  // client_address: the IP address and port of the client. The IP address is
  //     used to generate and validate source-address tokens.
  // version: version of the QUIC protocol in use for this connection
  // supported_versions: versions of the QUIC protocol that this server
  //     supports.
  // clock: used to validate client nonces and ephemeral keys.
  // rand: an entropy source
  // compressed_certs_cache: the cache that caches a set of most recently used
  //     certs. Owned by QuicDispatcher.
  // params: the state of the handshake. This may be updated with a server
  //     nonce when we send a rejection.
  // signed_config: output structure containing the crypto proof used in reply
  //     to a proof demand.
  // total_framing_overhead: the total per-packet overhead for a stream frame
  // chlo_packet_size: the size, in bytes, of the CHLO packet
  // done_cb: the callback invoked on completion
  void 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;

  // BuildServerConfigUpdateMessage invokes |cb| with a SCUP message containing
  // the current primary config, an up to date source-address token, and cert
  // chain and proof in the case of secure QUIC. Passes true to |cb| if the
  // message was generated successfully, and false otherwise.  This method
  // assumes ownership of |cb|.
  //
  // |cached_network_params| is optional, and can be nullptr.
  void 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;

  // set_replay_protection controls whether replay protection is enabled. If
  // replay protection is disabled then no strike registers are needed and
  // frontends can share an orbit value without a shared strike-register.
  // However, an attacker can duplicate a handshake and cause a client's
  // request to be processed twice.
  void set_replay_protection(bool on);

  // set_chlo_multiplier specifies 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.
  void set_chlo_multiplier(size_t multiplier);

  // When sender is allowed to not pad client hello (not standards compliant),
  // we need to disable the client hello check.
  void set_validate_chlo_size(bool new_value) {
    validate_chlo_size_ = new_value;
  }

  // Returns whether the sender is allowed to not pad the client hello.
  bool validate_chlo_size() const { return validate_chlo_size_; }

  // When QUIC is tunneled through some other mechanism, source token validation
  // may be disabled. Do not disable it if you are not providing other
  // protection. (|true| protects against UDP amplification attack.).
  void set_validate_source_address_token(bool new_value) {
    validate_source_address_token_ = new_value;
  }

  // set_source_address_token_future_secs sets the number of seconds into the
  // future that source-address tokens will be accepted from. Since
  // source-address tokens are authenticated, this should only happen if
  // another, valid server has clock-skew.
  void set_source_address_token_future_secs(uint32_t future_secs);

  // set_source_address_token_lifetime_secs sets the number of seconds that a
  // source-address token will be valid for.
  void set_source_address_token_lifetime_secs(uint32_t lifetime_secs);

  // set_enable_serving_sct enables or disables serving signed cert timestamp
  // (RFC6962) in server hello.
  void set_enable_serving_sct(bool enable_serving_sct);

  // Set and take ownership of the callback to invoke on primary config changes.
  void AcquirePrimaryConfigChangedCb(
      std::unique_ptr<PrimaryConfigChangedCallback> cb);

  // Returns the number of configs this object owns.
  int NumberOfConfigs() const;

  // NewSourceAddressToken returns a fresh source address token for the given
  // IP address. |previous_tokens| is the received tokens, and can be empty.
  // |cached_network_params| is optional, and can be nullptr.
  std::string NewSourceAddressToken(
      const CryptoSecretBoxer& crypto_secret_boxer,
      const SourceAddressTokens& previous_tokens, const QuicIpAddress& ip,
      QuicRandom* rand, QuicWallTime now,
      const CachedNetworkParameters* cached_network_params) const;

  // ParseSourceAddressToken parses the source address tokens contained in
  // the encrypted |token|, and populates |tokens| with the parsed tokens.
  // Returns HANDSHAKE_OK if |token| could be parsed, or the reason for the
  // failure.
  HandshakeFailureReason ParseSourceAddressToken(
      const CryptoSecretBoxer& crypto_secret_boxer, absl::string_view token,
      SourceAddressTokens& tokens) const;

  // ValidateSourceAddressTokens returns HANDSHAKE_OK if the source address
  // tokens in |tokens| contain a valid and timely token for the IP address
  // |ip| given that the current time is |now|. Otherwise it returns the
  // reason for failure. |cached_network_params| is populated if the valid
  // token contains a CachedNetworkParameters proto.
  HandshakeFailureReason ValidateSourceAddressTokens(
      const SourceAddressTokens& tokens, const QuicIpAddress& ip,
      QuicWallTime now, CachedNetworkParameters* cached_network_params) const;

  // Callers retain the ownership of |rejection_observer| which must outlive the
  // config.
  void set_rejection_observer(RejectionObserver* rejection_observer) {
    rejection_observer_ = rejection_observer;
  }

  ProofSource* proof_source() const;

  SSL_CTX* ssl_ctx() const;

  // The groups to use for key exchange in the TLS handshake;
  const std::vector<uint16_t>& preferred_groups() const {
    return preferred_groups_;
  }

  // Sets the preferred groups that will be used in the TLS handshake. Values
  // in the |preferred_groups| vector are NamedGroup enum codepoints from
  // https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7.
  void set_preferred_groups(const std::vector<uint16_t>& preferred_groups) {
    preferred_groups_ = preferred_groups;
  }

  // Pre-shared key used during the handshake.
  const std::string& pre_shared_key() const { return pre_shared_key_; }
  void set_pre_shared_key(absl::string_view psk) {
    pre_shared_key_ = std::string(psk);
  }

  bool pad_rej() const { return pad_rej_; }
  void set_pad_rej(bool new_value) { pad_rej_ = new_value; }

  bool pad_shlo() const { return pad_shlo_; }
  void set_pad_shlo(bool new_value) { pad_shlo_ = new_value; }

  const CryptoSecretBoxer& source_address_token_boxer() const {
    return source_address_token_boxer_;
  }

 private:
  friend class test::QuicCryptoServerConfigPeer;
  friend struct QuicSignedServerConfig;

  // Config represents a server config: a collection of preferences and
  // Diffie-Hellman public values.
  class QUICHE_EXPORT Config : public QuicCryptoConfig,
                               public quiche::QuicheReferenceCounted {
   public:
    Config();
    Config(const Config&) = delete;
    Config& operator=(const Config&) = delete;

    // TODO(rtenneti): since this is a class, we should probably do
    // getters/setters here.
    // |serialized| contains the bytes of this server config, suitable for
    // sending on the wire.
    std::string serialized;
    // id contains the SCID of this server config.
    std::string id;
    // orbit contains the orbit value for this config: an opaque identifier
    // used to identify clusters of server frontends.
    unsigned char orbit[kOrbitSize];

    // key_exchanges contains key exchange objects. The values correspond,
    // one-to-one, with the tags in |kexs| from the parent class.
    std::vector<std::unique_ptr<AsynchronousKeyExchange>> key_exchanges;

    // channel_id_enabled is true if the config in |serialized| specifies that
    // ChannelIDs are supported.
    bool channel_id_enabled;

    // is_primary is true if this config is the one that we'll give out to
    // clients as the current one.
    bool is_primary;

    // primary_time contains the timestamp when this config should become the
    // primary config. A value of QuicWallTime::Zero() means that this config
    // will not be promoted at a specific time.
    QuicWallTime primary_time;

    // expiry_time contains the timestamp when this config expires.
    QuicWallTime expiry_time;

    // Secondary sort key for use when selecting primary configs and
    // there are multiple configs with the same primary time.
    // Smaller numbers mean higher priority.
    uint64_t priority;

    // source_address_token_boxer_ is used to protect the
    // source-address tokens that are given to clients.
    // Points to either source_address_token_boxer_storage or the
    // default boxer provided by QuicCryptoServerConfig.
    const CryptoSecretBoxer* source_address_token_boxer;

    // Holds the override source_address_token_boxer instance if the
    // Config is not using the default source address token boxer
    // instance provided by QuicCryptoServerConfig.
    std::unique_ptr<CryptoSecretBoxer> source_address_token_boxer_storage;

   private:
    ~Config() override;
  };

  using ConfigMap =
      std::map<ServerConfigID, quiche::QuicheReferenceCountedPointer<Config>>;

  // Get a ref to the config with a given server config id.
  quiche::QuicheReferenceCountedPointer<Config> GetConfigWithScid(
      absl::string_view requested_scid) const
      QUIC_SHARED_LOCKS_REQUIRED(configs_lock_);

  // A snapshot of the configs associated with an in-progress handshake.
  struct QUICHE_EXPORT Configs {
    quiche::QuicheReferenceCountedPointer<Config> requested;
    quiche::QuicheReferenceCountedPointer<Config> primary;
    quiche::QuicheReferenceCountedPointer<Config> fallback;
  };

  // Get a snapshot of the current configs associated with a handshake.  If this
  // method was called earlier in this handshake |old_primary_config| should be
  // set to the primary config returned from that invocation, otherwise nullptr.
  //
  // Returns true if any configs are loaded.  If false is returned, |configs| is
  // not modified.
  bool GetCurrentConfigs(
      const QuicWallTime& now, absl::string_view requested_scid,
      quiche::QuicheReferenceCountedPointer<Config> old_primary_config,
      Configs* configs) const;

  // ConfigPrimaryTimeLessThan returns true if a->primary_time <
  // b->primary_time.
  static bool ConfigPrimaryTimeLessThan(
      const quiche::QuicheReferenceCountedPointer<Config>& a,
      const quiche::QuicheReferenceCountedPointer<Config>& b);

  // SelectNewPrimaryConfig reevaluates the primary config based on the
  // "primary_time" deadlines contained in each.
  void SelectNewPrimaryConfig(QuicWallTime now) const
      QUIC_EXCLUSIVE_LOCKS_REQUIRED(configs_lock_);

  // EvaluateClientHello checks |client_hello_state->client_hello| for gross
  // errors and determines whether it is fresh (i.e. not a replay). The results
  // are written to |client_hello_state->info|.
  void 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;

  // Convenience class which carries the arguments passed to
  // |ProcessClientHellp| along.
  class QUICHE_EXPORT ProcessClientHelloContext {
   public:
    ProcessClientHelloContext(
        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)
        : validate_chlo_result_(validate_chlo_result),
          reject_only_(reject_only),
          connection_id_(connection_id),
          server_address_(server_address),
          client_address_(client_address),
          version_(version),
          supported_versions_(supported_versions),
          clock_(clock),
          rand_(rand),
          compressed_certs_cache_(compressed_certs_cache),
          params_(params),
          signed_config_(signed_config),
          total_framing_overhead_(total_framing_overhead),
          chlo_packet_size_(chlo_packet_size),
          done_cb_(std::move(done_cb)) {}

    ~ProcessClientHelloContext();

    // Invoke |done_cb_| with an error status
    void Fail(QuicErrorCode error, const std::string& error_details);

    // Invoke |done_cb_| with a success status
    void Succeed(std::unique_ptr<CryptoHandshakeMessage> message,
                 std::unique_ptr<DiversificationNonce> diversification_nonce,
                 std::unique_ptr<ProofSource::Details> proof_source_details);

    // Member accessors
    quiche::QuicheReferenceCountedPointer<
        ValidateClientHelloResultCallback::Result>
    validate_chlo_result() const {
      return validate_chlo_result_;
    }
    bool reject_only() const { return reject_only_; }
    QuicConnectionId connection_id() const { return connection_id_; }
    QuicSocketAddress server_address() const { return server_address_; }
    QuicSocketAddress client_address() const { return client_address_; }
    ParsedQuicVersion version() const { return version_; }
    ParsedQuicVersionVector supported_versions() const {
      return supported_versions_;
    }
    const QuicClock* clock() const { return clock_; }
    QuicRandom* rand() const { return rand_; }  // NOLINT
    QuicCompressedCertsCache* compressed_certs_cache() const {
      return compressed_certs_cache_;
    }
    quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
    params() const {
      return params_;
    }
    quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
    signed_config() const {
      return signed_config_;
    }
    QuicByteCount total_framing_overhead() const {
      return total_framing_overhead_;
    }
    QuicByteCount chlo_packet_size() const { return chlo_packet_size_; }

    // Derived value accessors
    const CryptoHandshakeMessage& client_hello() const {
      return validate_chlo_result()->client_hello;
    }
    const ClientHelloInfo& info() const { return validate_chlo_result()->info; }
    QuicTransportVersion transport_version() const {
      return version().transport_version;
    }

   private:
    const quiche::QuicheReferenceCountedPointer<
        ValidateClientHelloResultCallback::Result>
        validate_chlo_result_;
    const bool reject_only_;
    const QuicConnectionId connection_id_;
    const QuicSocketAddress server_address_;
    const QuicSocketAddress client_address_;
    const ParsedQuicVersion version_;
    const ParsedQuicVersionVector supported_versions_;
    const QuicClock* const clock_;
    QuicRandom* const rand_;
    QuicCompressedCertsCache* const compressed_certs_cache_;
    const quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters>
        params_;
    const quiche::QuicheReferenceCountedPointer<QuicSignedServerConfig>
        signed_config_;
    const QuicByteCount total_framing_overhead_;
    const QuicByteCount chlo_packet_size_;
    std::shared_ptr<ProcessClientHelloResultCallback> done_cb_;
  };

  // Callback class for bridging between ProcessClientHello and
  // ProcessClientHelloAfterGetProof.
  class ProcessClientHelloCallback;
  friend class ProcessClientHelloCallback;

  // Portion of ProcessClientHello which executes after GetProof.
  void ProcessClientHelloAfterGetProof(
      bool found_error,
      std::unique_ptr<ProofSource::Details> proof_source_details,
      std::unique_ptr<ProcessClientHelloContext> context,
      const Configs& configs) const;

  // Callback class for bridging between ProcessClientHelloAfterGetProof and
  // ProcessClientHelloAfterCalculateSharedKeys.
  class ProcessClientHelloAfterGetProofCallback;
  friend class ProcessClientHelloAfterGetProofCallback;

  // Portion of ProcessClientHello which executes after CalculateSharedKeys.
  void 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;

  // Send a REJ which contains a different ServerConfig than the one the client
  // originally used.  This is necessary in cases where we discover in the
  // middle of the handshake that the private key for the ServerConfig the
  // client used is not accessible.
  void SendRejectWithFallbackConfig(
      std::unique_ptr<ProcessClientHelloContext> context,
      quiche::QuicheReferenceCountedPointer<Config> fallback_config) const;

  // Callback class for bridging between SendRejectWithFallbackConfig and
  // SendRejectWithFallbackConfigAfterGetProof.
  class SendRejectWithFallbackConfigCallback;
  friend class SendRejectWithFallbackConfigCallback;

  // Portion of ProcessClientHello which executes after GetProof in the case
  // where we have received a CHLO but need to reject it due to the ServerConfig
  // private keys being inaccessible.
  void SendRejectWithFallbackConfigAfterGetProof(
      bool found_error,
      std::unique_ptr<ProofSource::Details> proof_source_details,
      std::unique_ptr<ProcessClientHelloContext> context,
      quiche::QuicheReferenceCountedPointer<Config> fallback_config) const;

  // BuildRejectionAndRecordStats calls |BuildRejection| below and also informs
  // the RejectionObserver.
  void BuildRejectionAndRecordStats(const ProcessClientHelloContext& context,
                                    const Config& config,
                                    const std::vector<uint32_t>& reject_reasons,
                                    CryptoHandshakeMessage* out) const;

  // BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
  void BuildRejection(const ProcessClientHelloContext& context,
                      const Config& config,
                      const std::vector<uint32_t>& reject_reasons,
                      CryptoHandshakeMessage* out) const;

  // CompressChain compresses the certificates in |chain->certs| and returns a
  // compressed representation. |client_cached_cert_hashes| contains
  // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
  static std::string CompressChain(
      QuicCompressedCertsCache* compressed_certs_cache,
      const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
      const std::string& client_cached_cert_hashes);

  // ParseConfigProtobuf parses the given config protobuf and returns a
  // quiche::QuicheReferenceCountedPointer<Config> if successful. The caller
  // adopts the reference to the Config. On error, ParseConfigProtobuf returns
  // nullptr.
  quiche::QuicheReferenceCountedPointer<Config> ParseConfigProtobuf(
      const QuicServerConfigProtobuf& protobuf, bool is_fallback) const;

  // ValidateSingleSourceAddressToken returns HANDSHAKE_OK if the source
  // address token in |token| is a timely token for the IP address |ip|
  // given that the current time is |now|. Otherwise it returns the reason
  // for failure.
  HandshakeFailureReason ValidateSingleSourceAddressToken(
      const SourceAddressToken& token, const QuicIpAddress& ip,
      QuicWallTime now) const;

  // Returns HANDSHAKE_OK if the source address token in |token| is a timely
  // token given that the current time is |now|. Otherwise it returns the
  // reason for failure.
  HandshakeFailureReason ValidateSourceAddressTokenTimestamp(
      const SourceAddressToken& token, QuicWallTime now) const;

  // NewServerNonce generates and encrypts a random nonce.
  std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const;

  // ValidateExpectedLeafCertificate checks the |client_hello| to see if it has
  // an XLCT tag, and if so, verifies that its value matches the hash of the
  // server's leaf certificate. |certs| is used to compare against the XLCT
  // value.  This method returns true if the XLCT tag is not present, or if the
  // XLCT tag is present and valid. It returns false otherwise.
  bool ValidateExpectedLeafCertificate(
      const CryptoHandshakeMessage& client_hello,
      const std::vector<std::string>& certs) const;

  // Callback to receive the results of ProofSource::GetProof.  Note: this
  // callback has no cancellation support, since the lifetime of the ProofSource
  // is controlled by this object via unique ownership.  If that ownership
  // stricture changes, this decision may need to be revisited.
  class BuildServerConfigUpdateMessageProofSourceCallback
      : public ProofSource::Callback {
   public:
    BuildServerConfigUpdateMessageProofSourceCallback(
        const BuildServerConfigUpdateMessageProofSourceCallback&) = delete;
    ~BuildServerConfigUpdateMessageProofSourceCallback() override;
    void operator=(const BuildServerConfigUpdateMessageProofSourceCallback&) =
        delete;
    BuildServerConfigUpdateMessageProofSourceCallback(
        const QuicCryptoServerConfig* config,
        QuicCompressedCertsCache* compressed_certs_cache,
        const QuicCryptoNegotiatedParameters& params,
        CryptoHandshakeMessage message,
        std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb);

    void Run(
        bool ok,
        const quiche::QuicheReferenceCountedPointer<ProofSource::Chain>& chain,
        const QuicCryptoProof& proof,
        std::unique_ptr<ProofSource::Details> details) override;

   private:
    const QuicCryptoServerConfig* config_;
    QuicCompressedCertsCache* compressed_certs_cache_;
    const std::string client_cached_cert_hashes_;
    const bool sct_supported_by_client_;
    const std::string sni_;
    CryptoHandshakeMessage message_;
    std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb_;
  };

  // Invoked by BuildServerConfigUpdateMessageProofSourceCallback::Run once
  // the proof has been acquired.  Finishes building the server config update
  // message and invokes |cb|.
  void 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;

  // Returns true if the next config promotion should happen now.
  bool IsNextConfigReady(QuicWallTime now) const
      QUIC_SHARED_LOCKS_REQUIRED(configs_lock_);

  // replay_protection_ controls whether the server enforces that handshakes
  // aren't replays.
  bool replay_protection_;

  // 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.
  size_t chlo_multiplier_;

  // configs_ satisfies the following invariants:
  //   1) configs_.empty() <-> primary_config_ == nullptr
  //   2) primary_config_ != nullptr -> primary_config_->is_primary
  //   3) ∀ c∈configs_, c->is_primary <-> c == primary_config_
  mutable QuicMutex configs_lock_;

  // configs_ contains all active server configs. It's expected that there are
  // about half-a-dozen configs active at any one time.
  ConfigMap configs_ QUIC_GUARDED_BY(configs_lock_);

  // primary_config_ points to a Config (which is also in |configs_|) which is
  // the primary config - i.e. the one that we'll give out to new clients.
  mutable quiche::QuicheReferenceCountedPointer<Config> primary_config_
      QUIC_GUARDED_BY(configs_lock_);

  // fallback_config_ points to a Config (which is also in |configs_|) which is
  // the fallback config, which will be used if the other configs are unuseable
  // for some reason.
  //
  // TODO(b/112548056): This is currently always nullptr.
  quiche::QuicheReferenceCountedPointer<Config> fallback_config_
      QUIC_GUARDED_BY(configs_lock_);

  // next_config_promotion_time_ contains the nearest, future time when an
  // active config will be promoted to primary.
  mutable QuicWallTime next_config_promotion_time_
      QUIC_GUARDED_BY(configs_lock_);

  // Callback to invoke when the primary config changes.
  std::unique_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_
      QUIC_GUARDED_BY(configs_lock_);

  // Used to protect the source-address tokens that are given to clients.
  CryptoSecretBoxer source_address_token_boxer_;

  // server_nonce_boxer_ is used to encrypt and validate suggested server
  // nonces.
  CryptoSecretBoxer server_nonce_boxer_;

  // server_nonce_orbit_ contains the random, per-server orbit values that this
  // server will use to generate server nonces (the moral equivalent of a SYN
  // cookies).
  uint8_t server_nonce_orbit_[8];

  // proof_source_ contains an object that can provide certificate chains and
  // signatures.
  std::unique_ptr<ProofSource> proof_source_;

  // key_exchange_source_ contains an object that can provide key exchange
  // objects.
  std::unique_ptr<KeyExchangeSource> key_exchange_source_;

  // ssl_ctx_ contains the server configuration for doing TLS handshakes.
  bssl::UniquePtr<SSL_CTX> ssl_ctx_;

  // The groups to use for key exchange in the TLS handshake;
  std::vector<uint16_t> preferred_groups_;

  // These fields store configuration values. See the comments for their
  // respective setter functions.
  uint32_t source_address_token_future_secs_;
  uint32_t source_address_token_lifetime_secs_;

  // Enable serving SCT or not.
  bool enable_serving_sct_;

  // Does not own this observer.
  RejectionObserver* rejection_observer_;

  // If non-empty, the server will operate in the pre-shared key mode by
  // incorporating |pre_shared_key_| into the key schedule.
  std::string pre_shared_key_;

  // Whether REJ message should be padded to max packet size.
  bool pad_rej_;

  // Whether SHLO message should be padded to max packet size.
  bool pad_shlo_;

  // If client is allowed to send a small client hello (by disabling padding),
  // server MUST not check for the client hello size.
  // DO NOT disable this unless you have some other way of validating client.
  // (e.g. in realtime scenarios, where quic is tunneled through ICE, ICE will
  // do its own peer validation using STUN pings with ufrag/upass).
  bool validate_chlo_size_;

  // When source address is validated by some other means (e.g. when using ICE),
  // source address token validation may be disabled.
  bool validate_source_address_token_;
};

struct QUICHE_EXPORT QuicSignedServerConfig
    : public quiche::QuicheReferenceCounted {
  QuicSignedServerConfig();

  QuicCryptoProof proof;
  quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain;
  // The server config that is used for this proof (and the rest of the
  // request).
  quiche::QuicheReferenceCountedPointer<QuicCryptoServerConfig::Config> config;
  std::string primary_scid;

 protected:
  ~QuicSignedServerConfig() override;
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
