Add mTLS support for IETF QUIC.

In QUICHE, this CL adds the code that allows embeders to enable TLS client certificates. In GFE, this CL adds client cert support for Google domains in two types of client cert configurations:
- REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY
- REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY
Other config types are not supported and will cause QUIC_HANDSHAKE_FAILED if such request is received.

Protected by FLAGS_quic_restart_flag_quic_tls_server_support_client_cert.

PiperOrigin-RevId: 407133294
diff --git a/quic/core/crypto/proof_source.h b/quic/core/crypto/proof_source.h
index fcc6cce..2eb755a 100644
--- a/quic/core/crypto/proof_source.h
+++ b/quic/core/crypto/proof_source.h
@@ -253,14 +253,15 @@
   //      TLS resumption tickets.
   // |cert_matched_sni| is true if the certificate matched the SNI hostname,
   //      false if a non-matching default cert was used.
+  // |delayed_ssl_config| contains SSL configs to be applied on the SSL object.
   //
   // When called asynchronously(is_sync=false), this method will be responsible
   // to continue the handshake from where it left off.
-  virtual void OnSelectCertificateDone(bool ok, bool is_sync,
-                                       const ProofSource::Chain* chain,
-                                       absl::string_view handshake_hints,
-                                       absl::string_view ticket_encryption_key,
-                                       bool cert_matched_sni) = 0;
+  virtual void OnSelectCertificateDone(
+      bool ok, bool is_sync, const ProofSource::Chain* chain,
+      absl::string_view handshake_hints,
+      absl::string_view ticket_encryption_key, bool cert_matched_sni,
+      QuicDelayedSSLConfig delayed_ssl_config) = 0;
 
   // Called when a ProofSourceHandle::ComputeSignature operation completes.
   virtual void OnComputeSignatureDone(
diff --git a/quic/core/crypto/tls_connection.h b/quic/core/crypto/tls_connection.h
index c8d377e..a7a869a 100644
--- a/quic/core/crypto/tls_connection.h
+++ b/quic/core/crypto/tls_connection.h
@@ -120,6 +120,8 @@
   // implementation is delegated to Delegate::VerifyCert.
   static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert);
 
+  QuicSSLConfig& mutable_ssl_config() { return ssl_config_; }
+
  private:
   // TlsConnection implements SSL_QUIC_METHOD, which provides the interface
   // between BoringSSL's TLS stack and a QUIC implementation.
@@ -147,7 +149,7 @@
 
   Delegate* delegate_;
   bssl::UniquePtr<SSL> ssl_;
-  const QuicSSLConfig ssl_config_;
+  QuicSSLConfig ssl_config_;
 };
 
 }  // namespace quic
diff --git a/quic/core/crypto/tls_server_connection.cc b/quic/core/crypto/tls_server_connection.cc
index 0da9bbc..007bdbb 100644
--- a/quic/core/crypto/tls_server_connection.cc
+++ b/quic/core/crypto/tls_server_connection.cc
@@ -7,18 +7,23 @@
 #include "absl/strings/string_view.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "quic/core/crypto/proof_source.h"
+#include "quic/core/quic_types.h"
 #include "quic/platform/api/quic_flag_utils.h"
 #include "quic/platform/api/quic_flags.h"
 
 namespace quic {
 
-TlsServerConnection::TlsServerConnection(SSL_CTX* ssl_ctx,
-                                         Delegate* delegate,
+TlsServerConnection::TlsServerConnection(SSL_CTX* ssl_ctx, Delegate* delegate,
                                          QuicSSLConfig ssl_config)
-    : TlsConnection(ssl_ctx,
-                    delegate->ConnectionDelegate(),
+    : TlsConnection(ssl_ctx, delegate->ConnectionDelegate(),
                     std::move(ssl_config)),
-      delegate_(delegate) {}
+      delegate_(delegate) {
+  // By default, cert verify callback is not installed on ssl(), so only need to
+  // UpdateCertVerifyCallback() if client_cert_mode is not kNone.
+  if (TlsConnection::ssl_config().client_cert_mode != ClientCertMode::kNone) {
+    UpdateCertVerifyCallback();
+  }
+}
 
 // static
 bssl::UniquePtr<SSL_CTX> TlsServerConnection::CreateSslCtx(
@@ -56,6 +61,31 @@
                         &TlsServerConnection::kPrivateKeyMethod);
 }
 
+void TlsServerConnection::SetClientCertMode(ClientCertMode client_cert_mode) {
+  if (ssl_config().client_cert_mode == client_cert_mode) {
+    return;
+  }
+
+  mutable_ssl_config().client_cert_mode = client_cert_mode;
+  UpdateCertVerifyCallback();
+}
+
+void TlsServerConnection::UpdateCertVerifyCallback() {
+  const ClientCertMode client_cert_mode = ssl_config().client_cert_mode;
+  if (client_cert_mode == ClientCertMode::kNone) {
+    SSL_set_custom_verify(ssl(), SSL_VERIFY_NONE, nullptr);
+    return;
+  }
+
+  int mode = SSL_VERIFY_PEER;
+  if (client_cert_mode == ClientCertMode::kRequire) {
+    mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+  } else {
+    QUICHE_DCHECK_EQ(client_cert_mode, ClientCertMode::kRequest);
+  }
+  SSL_set_custom_verify(ssl(), mode, &VerifyCallback);
+}
+
 const SSL_PRIVATE_KEY_METHOD TlsServerConnection::kPrivateKeyMethod{
     &TlsServerConnection::PrivateKeySign,
     nullptr,  // decrypt
diff --git a/quic/core/crypto/tls_server_connection.h b/quic/core/crypto/tls_server_connection.h
index 6c775b8..976620d 100644
--- a/quic/core/crypto/tls_server_connection.h
+++ b/quic/core/crypto/tls_server_connection.h
@@ -129,6 +129,11 @@
 
   void SetCertChain(const std::vector<CRYPTO_BUFFER*>& cert_chain);
 
+  // Set the client cert mode to be used on this connection. This should be
+  // called right after cert selection at the latest, otherwise it is too late
+  // to has an effect.
+  void SetClientCertMode(ClientCertMode client_cert_mode);
+
  private:
   // Specialization of TlsConnection::ConnectionFromSsl.
   static TlsServerConnection* ConnectionFromSsl(SSL* ssl);
@@ -183,6 +188,10 @@
                                                          const uint8_t* in,
                                                          size_t in_len);
 
+  // Install custom verify callback on ssl() if |ssl_config().client_cert_mode|
+  // is not ClientCertMode::kNone. Uninstall otherwise.
+  void UpdateCertVerifyCallback();
+
   Delegate* delegate_;
 };
 
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 23be7f9..73a6927 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -27,6 +27,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_deprecate_tlpr, true)
 // If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so.
 QUIC_FLAG(FLAGS_quic_restart_flag_quic_support_release_time_for_gso, false)
+// If true, TlsServerHandshaker will be able to 1) request client cert, and 2) verify the client cert in the virtual method TlsServerHandshaker::VerifyCertChain.
+QUIC_FLAG(FLAGS_quic_restart_flag_quic_tls_server_support_client_cert, true)
 // If true, abort async QPACK header decompression in QuicSpdyStream::Reset() and in QuicSpdyStream::OnStreamReset().
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true)
 // If true, accept empty crypto frame.
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 35e0bf3..56ec3b2 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -618,6 +618,9 @@
 
   virtual QuicSSLConfig GetSSLConfig() const { return QuicSSLConfig(); }
 
+  // Latched value of flag --quic_tls_server_support_client_cert.
+  bool support_client_cert() const { return support_client_cert_; }
+
   // Try converting all pending streams to normal streams.
   void ProcessAllPendingStreams();
 
@@ -980,6 +983,9 @@
 
   // Whether BoringSSL randomizes the order of TLS extensions.
   bool permutes_tls_extensions_ = true;
+
+  const bool support_client_cert_ =
+      GetQuicRestartFlag(quic_tls_server_support_client_cert);
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index 8c155c4..6bd58e9 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -326,6 +326,25 @@
   return os;
 }
 
+absl::string_view ClientCertModeToString(ClientCertMode mode) {
+#define RETURN_REASON_LITERAL(x) \
+  case ClientCertMode::x:        \
+    return #x
+  switch (mode) {
+    RETURN_REASON_LITERAL(kNone);
+    RETURN_REASON_LITERAL(kRequest);
+    RETURN_REASON_LITERAL(kRequire);
+    default:
+      return "<invalid>";
+  }
+#undef RETURN_REASON_LITERAL
+}
+
+std::ostream& operator<<(std::ostream& os, ClientCertMode mode) {
+  os << ClientCertModeToString(mode);
+  return os;
+}
+
 std::string QuicConnectionCloseTypeString(QuicConnectionCloseType type) {
   switch (type) {
     RETURN_STRING_LITERAL(GOOGLE_QUIC_CONNECTION_CLOSE);
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index b0f32cd..b048121 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -486,12 +486,18 @@
 // Enumeration of whether a server endpoint will request a client certificate,
 // and whether that endpoint requires a valid client certificate to establish a
 // connection.
-enum class ClientCertMode {
+enum class ClientCertMode : uint8_t {
   kNone,     // Do not request a client certificate.  Default server behavior.
   kRequest,  // Request a certificate, but allow unauthenticated connections.
   kRequire,  // Require clients to provide a valid certificate.
 };
 
+QUIC_EXPORT_PRIVATE absl::string_view ClientCertModeToString(
+    ClientCertMode mode);
+
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+                                             ClientCertMode mode);
+
 enum AddressChangeType : uint8_t {
   // IP address and port remain unchanged.
   NO_CHANGE,
@@ -846,6 +852,17 @@
   // If set, used to configure the SSL object with
   // SSL_set_signing_algorithm_prefs.
   absl::optional<absl::InlinedVector<uint16_t, 8>> signing_algorithm_prefs;
+  // Client certificate mode for mTLS support. Only used at server side.
+  ClientCertMode client_cert_mode = ClientCertMode::kNone;
+};
+
+// QuicDelayedSSLConfig contains a subset of SSL config that can be applied
+// after BoringSSL's early select certificate callback. This overwrites all SSL
+// configs applied before cert selection.
+struct QUIC_NO_EXPORT QuicDelayedSSLConfig {
+  // Client certificate mode for mTLS support. Only used at server side.
+  // absl::nullopt means do not change client certificate mode.
+  absl::optional<ClientCertMode> client_cert_mode;
 };
 
 // ParsedClientHello contains client hello information extracted from a fully
diff --git a/quic/core/tls_handshaker.cc b/quic/core/tls_handshaker.cc
index dee634b..ac959f2 100644
--- a/quic/core/tls_handshaker.cc
+++ b/quic/core/tls_handshaker.cc
@@ -207,6 +207,7 @@
         std::string(reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert)),
                     CRYPTO_BUFFER_len(cert)));
   }
+  QUIC_DVLOG(1) << "VerifyCert: peer cert_chain length: " << certs.size();
 
   ProofVerifierCallbackImpl* proof_verify_callback =
       new ProofVerifierCallbackImpl(this);
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index a763875..637435c 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -85,7 +85,8 @@
   handshaker_->OnSelectCertificateDone(
       /*ok=*/true, /*is_sync=*/true, chain.get(),
       /*handshake_hints=*/absl::string_view(),
-      /*ticket_encryption_key=*/absl::string_view(), cert_matched_sni);
+      /*ticket_encryption_key=*/absl::string_view(), cert_matched_sni,
+      QuicDelayedSSLConfig());
   if (!handshaker_->select_cert_status().has_value()) {
     QUIC_BUG(quic_bug_12423_1)
         << "select_cert_status() has no value after a synchronous select cert";
@@ -184,8 +185,7 @@
 }
 
 TlsServerHandshaker::TlsServerHandshaker(
-    QuicSession* session,
-    const QuicCryptoServerConfig* crypto_config)
+    QuicSession* session, const QuicCryptoServerConfig* crypto_config)
     : TlsHandshaker(this, session),
       QuicCryptoServerStreamBase(session),
       proof_source_(crypto_config->proof_source()),
@@ -193,6 +193,10 @@
       crypto_negotiated_params_(new QuicCryptoNegotiatedParameters),
       tls_connection_(crypto_config->ssl_ctx(), this, session->GetSSLConfig()),
       crypto_config_(crypto_config) {
+  QUIC_DVLOG(1) << "TlsServerHandshaker: support_client_cert:"
+                << session->support_client_cert()
+                << ", client_cert_mode initial value: " << client_cert_mode();
+
   QUICHE_DCHECK_EQ(PROTOCOL_TLS1_3,
                    session->connection()->version().handshake_protocol);
 
@@ -616,9 +620,18 @@
     std::unique_ptr<ProofVerifyDetails>* /*details*/,
     uint8_t* /*out_alert*/,
     std::unique_ptr<ProofVerifierCallback> /*callback*/) {
-  QUIC_BUG(quic_bug_10341_5)
-      << "Client certificates are not yet supported on the server";
-  return QUIC_FAILURE;
+  if (!session()->support_client_cert()) {
+    QUIC_BUG(quic_bug_10341_5)
+        << "Client certificates are not yet supported on the server";
+    return QUIC_FAILURE;
+  }
+
+  QUIC_RESTART_FLAG_COUNT_N(quic_tls_server_support_client_cert, 2, 2);
+  QUIC_DVLOG(1) << "VerifyCertChain returning success";
+
+  // No real verification here. A subclass can override this function to verify
+  // the client cert if needed.
+  return QUIC_SUCCESS;
 }
 
 void TlsServerHandshaker::OnProofVerifyDetailsAvailable(
@@ -963,7 +976,7 @@
 void TlsServerHandshaker::OnSelectCertificateDone(
     bool ok, bool is_sync, const ProofSource::Chain* chain,
     absl::string_view handshake_hints, absl::string_view ticket_encryption_key,
-    bool cert_matched_sni) {
+    bool cert_matched_sni, QuicDelayedSSLConfig delayed_ssl_config) {
   QUIC_DVLOG(1) << "OnSelectCertificateDone. ok:" << ok
                 << ", is_sync:" << is_sync
                 << ", len(handshake_hints):" << handshake_hints.size()
@@ -986,6 +999,13 @@
   ticket_encryption_key_ = std::string(ticket_encryption_key);
   select_cert_status_ = QUIC_FAILURE;
   cert_matched_sni_ = cert_matched_sni;
+  if (session()->support_client_cert()) {
+    if (delayed_ssl_config.client_cert_mode.has_value()) {
+      tls_connection_.SetClientCertMode(*delayed_ssl_config.client_cert_mode);
+      QUIC_DVLOG(1) << "client_cert_mode after cert selection: "
+                    << client_cert_mode();
+    }
+  }
   if (ok) {
     if (chain && !chain->certs.empty()) {
       tls_connection_.SetCertChain(chain->ToCryptoBuffers().value);
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index b7179a7..6a7f358 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -92,6 +92,11 @@
   virtual std::string GetAcceptChValueForHostname(
       const std::string& hostname) const;
 
+  // Get the ClientCertMode that is currently in effect on this handshaker.
+  ClientCertMode client_cert_mode() const {
+    return tls_connection_.ssl_config().client_cert_mode;
+  }
+
  protected:
   // Override for tracing.
   void InfoCallback(int type, int value) override;
@@ -173,11 +178,11 @@
   bool HasValidSignature(size_t max_signature_size) const;
 
   // ProofSourceHandleCallback implementation:
-  void OnSelectCertificateDone(bool ok, bool is_sync,
-                               const ProofSource::Chain* chain,
-                               absl::string_view handshake_hints,
-                               absl::string_view ticket_encryption_key,
-                               bool cert_matched_sni) override;
+  void OnSelectCertificateDone(
+      bool ok, bool is_sync, const ProofSource::Chain* chain,
+      absl::string_view handshake_hints,
+      absl::string_view ticket_encryption_key, bool cert_matched_sni,
+      QuicDelayedSSLConfig delayed_ssl_config) override;
 
   void OnComputeSignatureDone(
       bool ok,
diff --git a/quic/core/tls_server_handshaker_test.cc b/quic/core/tls_server_handshaker_test.cc
index bf68c85..3e707b2 100644
--- a/quic/core/tls_server_handshaker_test.cc
+++ b/quic/core/tls_server_handshaker_test.cc
@@ -2,20 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "quic/core/tls_server_handshaker.h"
+
 #include <memory>
 #include <utility>
 #include <vector>
 
 #include "absl/base/macros.h"
 #include "absl/strings/string_view.h"
+#include "quic/core/crypto/client_proof_source.h"
 #include "quic/core/crypto/proof_source.h"
 #include "quic/core/crypto/quic_random.h"
 #include "quic/core/quic_crypto_client_stream.h"
 #include "quic/core/quic_session.h"
+#include "quic/core/quic_types.h"
 #include "quic/core/quic_utils.h"
 #include "quic/core/quic_versions.h"
 #include "quic/core/tls_client_handshaker.h"
-#include "quic/core/tls_server_handshaker.h"
 #include "quic/platform/api/quic_flags.h"
 #include "quic/platform/api/quic_logging.h"
 #include "quic/platform/api/quic_test.h"
@@ -25,6 +28,7 @@
 #include "quic/test_tools/fake_proof_source_handle.h"
 #include "quic/test_tools/quic_test_utils.h"
 #include "quic/test_tools/simple_session_cache.h"
+#include "quic/test_tools/test_certificates.h"
 #include "quic/test_tools/test_ticket_crypter.h"
 
 namespace quic {
@@ -44,6 +48,21 @@
 const char kServerHostname[] = "test.example.com";
 const uint16_t kServerPort = 443;
 
+QuicReferenceCountedPointer<ClientProofSource::Chain> TestClientCertChain() {
+  return QuicReferenceCountedPointer<ClientProofSource::Chain>(
+      new ClientProofSource::Chain({std::string(kTestCertificate)}));
+}
+
+CertificatePrivateKey TestClientCertPrivateKey() {
+  CBS private_key_cbs;
+  CBS_init(&private_key_cbs,
+           reinterpret_cast<const uint8_t*>(kTestCertificatePrivateKey.data()),
+           kTestCertificatePrivateKey.size());
+
+  return CertificatePrivateKey(
+      bssl::UniquePtr<EVP_PKEY>(EVP_parse_private_key(&private_key_cbs)));
+}
+
 struct TestParams {
   ParsedQuicVersion version;
   bool disable_resumption;
@@ -85,13 +104,15 @@
 
   void SetupProofSourceHandle(
       FakeProofSourceHandle::Action select_cert_action,
-      FakeProofSourceHandle::Action compute_signature_action) {
+      FakeProofSourceHandle::Action compute_signature_action,
+      QuicDelayedSSLConfig dealyed_ssl_config = QuicDelayedSSLConfig()) {
     EXPECT_CALL(*this, MaybeCreateProofSourceHandle())
-        .WillOnce(testing::Invoke(
-            [this, select_cert_action, compute_signature_action]() {
+        .WillOnce(
+            testing::Invoke([this, select_cert_action, compute_signature_action,
+                             dealyed_ssl_config]() {
               auto handle = std::make_unique<FakeProofSourceHandle>(
                   proof_source_, this, select_cert_action,
-                  compute_signature_action);
+                  compute_signature_action, dealyed_ssl_config);
               fake_proof_source_handle_ = handle.get();
               return handle;
             }));
@@ -101,9 +122,21 @@
     return fake_proof_source_handle_;
   }
 
+  bool received_client_cert() const { return received_client_cert_; }
+
   using TlsServerHandshaker::AdvanceHandshake;
   using TlsServerHandshaker::expected_ssl_error;
 
+ protected:
+  QuicAsyncStatus VerifyCertChain(
+      const std::vector<std::string>& certs, std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details, uint8_t* out_alert,
+      std::unique_ptr<ProofVerifierCallback> callback) override {
+    received_client_cert_ = true;
+    return TlsServerHandshaker::VerifyCertChain(certs, error_details, details,
+                                                out_alert, std::move(callback));
+  }
+
  private:
   std::unique_ptr<ProofSourceHandle> RealMaybeCreateProofSourceHandle() {
     return TlsServerHandshaker::MaybeCreateProofSourceHandle();
@@ -112,6 +145,7 @@
   // Owned by TlsServerHandshaker.
   FakeProofSourceHandle* fake_proof_source_handle_ = nullptr;
   ProofSource* proof_source_ = nullptr;
+  bool received_client_cert_ = false;
 };
 
 class TlsServerHandshakerTestSession : public TestQuicSpdyServerSession {
@@ -185,6 +219,7 @@
         new TlsServerHandshakerTestSession(
             server_connection_, DefaultQuicConfig(), supported_versions_,
             server_crypto_config_.get(), &server_compressed_certs_cache_);
+    server_session->set_client_cert_mode(initial_client_cert_mode_);
     server_session->Initialize();
 
     // We advance the clock initially because the default time is zero and the
@@ -369,6 +404,7 @@
   std::unique_ptr<QuicCryptoServerConfig> server_crypto_config_;
   QuicCompressedCertsCache server_compressed_certs_cache_;
   QuicServerId server_id_;
+  ClientCertMode initial_client_cert_mode_ = ClientCertMode::kNone;
 
   // Client state.
   PacketSavingConnection* client_connection_;
@@ -852,6 +888,141 @@
   EXPECT_FALSE(server_stream()->IsZeroRtt());
 }
 
+TEST_P(TlsServerHandshakerTest, RequestClientCert) {
+  auto client_proof_source = std::make_unique<DefaultClientProofSource>();
+  ASSERT_TRUE(client_proof_source->AddCertAndKey({"*"}, TestClientCertChain(),
+                                                 TestClientCertPrivateKey()));
+  client_crypto_config_->set_proof_source(std::move(client_proof_source));
+  InitializeFakeClient();
+
+  initial_client_cert_mode_ = ClientCertMode::kRequest;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::
+          DELEGATE_SYNC);
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  if (GetQuicRestartFlag(quic_tls_server_support_client_cert)) {
+    EXPECT_TRUE(server_handshaker_->received_client_cert());
+  } else {
+    EXPECT_FALSE(server_handshaker_->received_client_cert());
+  }
+}
+
+TEST_P(TlsServerHandshakerTest, RequestClientCertByDelayedSslConfig) {
+  auto client_proof_source = std::make_unique<DefaultClientProofSource>();
+  ASSERT_TRUE(client_proof_source->AddCertAndKey({"*"}, TestClientCertChain(),
+                                                 TestClientCertPrivateKey()));
+  client_crypto_config_->set_proof_source(std::move(client_proof_source));
+  InitializeFakeClient();
+
+  QuicDelayedSSLConfig delayed_ssl_config;
+  delayed_ssl_config.client_cert_mode = ClientCertMode::kRequest;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      delayed_ssl_config);
+
+  AdvanceHandshakeWithFakeClient();
+  ASSERT_TRUE(
+      server_handshaker_->fake_proof_source_handle()->HasPendingOperation());
+  server_handshaker_->fake_proof_source_handle()->CompletePendingOperation();
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  if (GetQuicRestartFlag(quic_tls_server_support_client_cert)) {
+    EXPECT_TRUE(server_handshaker_->received_client_cert());
+  } else {
+    EXPECT_FALSE(server_handshaker_->received_client_cert());
+  }
+}
+
+TEST_P(TlsServerHandshakerTest, RequestClientCert_NoCert) {
+  initial_client_cert_mode_ = ClientCertMode::kRequest;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::
+          DELEGATE_SYNC);
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_FALSE(server_handshaker_->received_client_cert());
+}
+
+TEST_P(TlsServerHandshakerTest, RequestAndRequireClientCert) {
+  auto client_proof_source = std::make_unique<DefaultClientProofSource>();
+  ASSERT_TRUE(client_proof_source->AddCertAndKey({"*"}, TestClientCertChain(),
+                                                 TestClientCertPrivateKey()));
+  client_crypto_config_->set_proof_source(std::move(client_proof_source));
+  InitializeFakeClient();
+
+  initial_client_cert_mode_ = ClientCertMode::kRequire;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::
+          DELEGATE_SYNC);
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+
+  if (GetQuicRestartFlag(quic_tls_server_support_client_cert)) {
+    EXPECT_TRUE(server_handshaker_->received_client_cert());
+  } else {
+    EXPECT_FALSE(server_handshaker_->received_client_cert());
+  }
+}
+
+TEST_P(TlsServerHandshakerTest, RequestAndRequireClientCertByDelayedSslConfig) {
+  auto client_proof_source = std::make_unique<DefaultClientProofSource>();
+  ASSERT_TRUE(client_proof_source->AddCertAndKey({"*"}, TestClientCertChain(),
+                                                 TestClientCertPrivateKey()));
+  client_crypto_config_->set_proof_source(std::move(client_proof_source));
+  InitializeFakeClient();
+
+  QuicDelayedSSLConfig delayed_ssl_config;
+  delayed_ssl_config.client_cert_mode = ClientCertMode::kRequire;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      delayed_ssl_config);
+
+  AdvanceHandshakeWithFakeClient();
+  ASSERT_TRUE(
+      server_handshaker_->fake_proof_source_handle()->HasPendingOperation());
+  server_handshaker_->fake_proof_source_handle()->CompletePendingOperation();
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  if (GetQuicRestartFlag(quic_tls_server_support_client_cert)) {
+    EXPECT_TRUE(server_handshaker_->received_client_cert());
+  } else {
+    EXPECT_FALSE(server_handshaker_->received_client_cert());
+  }
+}
+
+TEST_P(TlsServerHandshakerTest, RequestAndRequireClientCert_NoCert) {
+  initial_client_cert_mode_ = ClientCertMode::kRequire;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::
+          DELEGATE_SYNC);
+
+  if (GetQuicRestartFlag(quic_tls_server_support_client_cert)) {
+    EXPECT_CALL(*server_connection_,
+                CloseConnection(QUIC_TLS_CERTIFICATE_REQUIRED, _, _, _));
+  }
+  AdvanceHandshakeWithFakeClient();
+  AdvanceHandshakeWithFakeClient();
+  EXPECT_FALSE(server_handshaker_->received_client_cert());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/fake_proof_source_handle.cc b/quic/test_tools/fake_proof_source_handle.cc
index 8c1e7f8..46a1d09 100644
--- a/quic/test_tools/fake_proof_source_handle.cc
+++ b/quic/test_tools/fake_proof_source_handle.cc
@@ -54,14 +54,14 @@
 }  // namespace
 
 FakeProofSourceHandle::FakeProofSourceHandle(
-    ProofSource* delegate,
-    ProofSourceHandleCallback* callback,
-    Action select_cert_action,
-    Action compute_signature_action)
+    ProofSource* delegate, ProofSourceHandleCallback* callback,
+    Action select_cert_action, Action compute_signature_action,
+    QuicDelayedSSLConfig dealyed_ssl_config)
     : delegate_(delegate),
       callback_(callback),
       select_cert_action_(select_cert_action),
-      compute_signature_action_(compute_signature_action) {}
+      compute_signature_action_(compute_signature_action),
+      dealyed_ssl_config_(dealyed_ssl_config) {}
 
 void FakeProofSourceHandle::CloseHandle() {
   select_cert_op_.reset();
@@ -88,14 +88,14 @@
   if (select_cert_action_ == Action::DELEGATE_ASYNC ||
       select_cert_action_ == Action::FAIL_ASYNC) {
     select_cert_op_.emplace(delegate_, callback_, select_cert_action_,
-                            all_select_cert_args_.back());
+                            all_select_cert_args_.back(), dealyed_ssl_config_);
     return QUIC_PENDING;
   } else if (select_cert_action_ == Action::FAIL_SYNC) {
     callback()->OnSelectCertificateDone(
         /*ok=*/false,
         /*is_sync=*/true, nullptr, /*handshake_hints=*/absl::string_view(),
         /*ticket_encryption_key=*/absl::string_view(),
-        /*cert_matched_sni=*/false);
+        /*cert_matched_sni=*/false, dealyed_ssl_config_);
     return QUIC_FAILURE;
   }
 
@@ -110,7 +110,7 @@
       ok, /*is_sync=*/true, chain.get(),
       /*handshake_hints=*/absl::string_view(),
       /*ticket_encryption_key=*/absl::string_view(),
-      /*cert_matched_sni=*/cert_matched_sni);
+      /*cert_matched_sni=*/cert_matched_sni, dealyed_ssl_config_);
   return ok ? QUIC_SUCCESS : QUIC_FAILURE;
 }
 
@@ -174,11 +174,11 @@
 }
 
 FakeProofSourceHandle::SelectCertOperation::SelectCertOperation(
-    ProofSource* delegate,
-    ProofSourceHandleCallback* callback,
-    Action action,
-    SelectCertArgs args)
-    : PendingOperation(delegate, callback, action), args_(std::move(args)) {}
+    ProofSource* delegate, ProofSourceHandleCallback* callback, Action action,
+    SelectCertArgs args, QuicDelayedSSLConfig dealyed_ssl_config)
+    : PendingOperation(delegate, callback, action),
+      args_(std::move(args)),
+      dealyed_ssl_config_(dealyed_ssl_config) {}
 
 void FakeProofSourceHandle::SelectCertOperation::Run() {
   if (action_ == Action::FAIL_ASYNC) {
@@ -187,7 +187,7 @@
         /*is_sync=*/false, nullptr,
         /*handshake_hints=*/absl::string_view(),
         /*ticket_encryption_key=*/absl::string_view(),
-        /*cert_matched_sni=*/false);
+        /*cert_matched_sni=*/false, dealyed_ssl_config_);
   } else if (action_ == Action::DELEGATE_ASYNC) {
     bool cert_matched_sni;
     QuicReferenceCountedPointer<ProofSource::Chain> chain =
@@ -198,7 +198,7 @@
         ok, /*is_sync=*/false, chain.get(),
         /*handshake_hints=*/absl::string_view(),
         /*ticket_encryption_key=*/absl::string_view(),
-        /*cert_matched_sni=*/cert_matched_sni);
+        /*cert_matched_sni=*/cert_matched_sni, dealyed_ssl_config_);
   } else {
     QUIC_BUG(quic_bug_10139_1)
         << "Unexpected action: " << static_cast<int>(action_);
diff --git a/quic/test_tools/fake_proof_source_handle.h b/quic/test_tools/fake_proof_source_handle.h
index 3d038a4..b4203e8 100644
--- a/quic/test_tools/fake_proof_source_handle.h
+++ b/quic/test_tools/fake_proof_source_handle.h
@@ -28,10 +28,11 @@
   };
 
   // |delegate| must do cert selection and signature synchronously.
-  FakeProofSourceHandle(ProofSource* delegate,
-                        ProofSourceHandleCallback* callback,
-                        Action select_cert_action,
-                        Action compute_signature_action);
+  // |dealyed_ssl_config| is the config passed to OnSelectCertificateDone.
+  FakeProofSourceHandle(
+      ProofSource* delegate, ProofSourceHandleCallback* callback,
+      Action select_cert_action, Action compute_signature_action,
+      QuicDelayedSSLConfig dealyed_ssl_config = QuicDelayedSSLConfig());
 
   ~FakeProofSourceHandle() override = default;
 
@@ -145,9 +146,9 @@
   class SelectCertOperation : public PendingOperation {
    public:
     SelectCertOperation(ProofSource* delegate,
-                        ProofSourceHandleCallback* callback,
-                        Action action,
-                        SelectCertArgs args);
+                        ProofSourceHandleCallback* callback, Action action,
+                        SelectCertArgs args,
+                        QuicDelayedSSLConfig dealyed_ssl_config);
 
     ~SelectCertOperation() override = default;
 
@@ -155,6 +156,7 @@
 
    private:
     const SelectCertArgs args_;
+    const QuicDelayedSSLConfig dealyed_ssl_config_;
   };
 
   class ComputeSignatureOperation : public PendingOperation {
@@ -182,6 +184,7 @@
   Action select_cert_action_ = Action::DELEGATE_SYNC;
   // Action for the next compute signature operation.
   Action compute_signature_action_ = Action::DELEGATE_SYNC;
+  const QuicDelayedSSLConfig dealyed_ssl_config_;
   absl::optional<SelectCertOperation> select_cert_op_;
   absl::optional<ComputeSignatureOperation> compute_signature_op_;
 
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index e059f31..77b7cbb 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -1028,17 +1028,30 @@
     if (early_data_enabled_.has_value()) {
       ssl_config.early_data_enabled = *early_data_enabled_;
     }
+    if (client_cert_mode_.has_value()) {
+      ssl_config.client_cert_mode = *client_cert_mode_;
+    }
+
     return ssl_config;
   }
 
   void set_early_data_enabled(bool enabled) { early_data_enabled_ = enabled; }
 
+  void set_client_cert_mode(ClientCertMode mode) {
+    if (support_client_cert()) {
+      client_cert_mode_ = mode;
+    }
+  }
+
  private:
   MockQuicSessionVisitor visitor_;
   MockQuicCryptoServerStreamHelper helper_;
   // If not nullopt, override the early_data_enabled value from base class'
   // ssl_config.
   absl::optional<bool> early_data_enabled_;
+  // If not nullopt, override the client_cert_mode value from base class'
+  // ssl_config.
+  absl::optional<ClientCertMode> client_cert_mode_;
 };
 
 // A test implementation of QuicClientPushPromiseIndex::Delegate.