Move cert verification from TlsClientHandshaker to TlsHandshaker

Protected by not protected.

PiperOrigin-RevId: 340749853
Change-Id: If973adbd9d4edbbf9b1d06654f9d4067adfca38f
diff --git a/quic/core/crypto/tls_client_connection.cc b/quic/core/crypto/tls_client_connection.cc
index 7908847..0591571 100644
--- a/quic/core/crypto/tls_client_connection.cc
+++ b/quic/core/crypto/tls_client_connection.cc
@@ -13,9 +13,9 @@
 // static
 bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx(
     bool enable_early_data) {
-  bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx();
+  bssl::UniquePtr<SSL_CTX> ssl_ctx =
+      TlsConnection::CreateSslCtx(SSL_VERIFY_PEER);
   // Configure certificate verification.
-  SSL_CTX_set_custom_verify(ssl_ctx.get(), SSL_VERIFY_PEER, &VerifyCallback);
   int reverify_on_resume_enabled = 1;
   SSL_CTX_set_reverify_on_resume(ssl_ctx.get(), reverify_on_resume_enabled);
 
@@ -29,14 +29,6 @@
 }
 
 // static
-enum ssl_verify_result_t TlsClientConnection::VerifyCallback(
-    SSL* ssl,
-    uint8_t* out_alert) {
-  return static_cast<TlsClientConnection*>(ConnectionFromSsl(ssl))
-      ->delegate_->VerifyCert(out_alert);
-}
-
-// static
 int TlsClientConnection::NewSessionCallback(SSL* ssl, SSL_SESSION* session) {
   static_cast<TlsClientConnection*>(ConnectionFromSsl(ssl))
       ->delegate_->InsertSession(bssl::UniquePtr<SSL_SESSION>(session));
diff --git a/quic/core/crypto/tls_client_connection.h b/quic/core/crypto/tls_client_connection.h
index a7ef209..6bea641 100644
--- a/quic/core/crypto/tls_client_connection.h
+++ b/quic/core/crypto/tls_client_connection.h
@@ -20,12 +20,6 @@
     virtual ~Delegate() {}
 
    protected:
-    // Verifies the peer's certificate chain. It may use
-    // SSL_get0_peer_certificates to get the cert chain. This method returns
-    // ssl_verify_ok if the cert is valid, ssl_verify_invalid if it is invalid,
-    // or ssl_verify_retry if verification is happening asynchronously.
-    virtual enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) = 0;
-
     // Called when a NewSessionTicket is received from the server.
     virtual void InsertSession(bssl::UniquePtr<SSL_SESSION> session) = 0;
 
@@ -42,10 +36,6 @@
   static bssl::UniquePtr<SSL_CTX> CreateSslCtx(bool enable_early_data);
 
  private:
-  // Registered as the callback for SSL_CTX_set_custom_verify. The
-  // implementation is delegated to Delegate::VerifyCert.
-  static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert);
-
   // Registered as the callback for SSL_CTX_sess_set_new_cb, which calls
   // Delegate::InsertSession.
   static int NewSessionCallback(SSL* ssl, SSL_SESSION* session);
diff --git a/quic/core/crypto/tls_connection.cc b/quic/core/crypto/tls_connection.cc
index 427474c..be274e2 100644
--- a/quic/core/crypto/tls_connection.cc
+++ b/quic/core/crypto/tls_connection.cc
@@ -93,12 +93,15 @@
       this);
 }
 // static
-bssl::UniquePtr<SSL_CTX> TlsConnection::CreateSslCtx() {
+bssl::UniquePtr<SSL_CTX> TlsConnection::CreateSslCtx(int cert_verify_mode) {
   CRYPTO_library_init();
   bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_with_buffers_method()));
   SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
   SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
   SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod);
+  if (cert_verify_mode != SSL_VERIFY_NONE) {
+    SSL_CTX_set_custom_verify(ssl_ctx.get(), cert_verify_mode, &VerifyCallback);
+  }
   return ssl_ctx;
 }
 
@@ -108,6 +111,12 @@
       ssl, SslIndexSingleton::GetInstance()->ssl_ex_data_index_connection()));
 }
 
+// static
+enum ssl_verify_result_t TlsConnection::VerifyCallback(SSL* ssl,
+                                                       uint8_t* out_alert) {
+  return ConnectionFromSsl(ssl)->delegate_->VerifyCert(out_alert);
+}
+
 const SSL_QUIC_METHOD TlsConnection::kSslQuicMethod{
     TlsConnection::SetReadSecretCallback, TlsConnection::SetWriteSecretCallback,
     TlsConnection::WriteMessageCallback, TlsConnection::FlushFlightCallback,
diff --git a/quic/core/crypto/tls_connection.h b/quic/core/crypto/tls_connection.h
index ef5ca58..037e4b0 100644
--- a/quic/core/crypto/tls_connection.h
+++ b/quic/core/crypto/tls_connection.h
@@ -31,6 +31,16 @@
     virtual ~Delegate() {}
 
    protected:
+    // Certificate management functions:
+
+    // Verifies the peer's certificate chain. It may use
+    // SSL_get0_peer_certificates to get the cert chain. This method returns
+    // ssl_verify_ok if the cert is valid, ssl_verify_invalid if it is invalid,
+    // or ssl_verify_retry if verification is happening asynchronously.
+    virtual enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) = 0;
+
+    // QUIC-TLS interface functions:
+
     // SetWriteSecret provides the encryption secret used to encrypt messages at
     // encryption level |level|. The secret provided here is the one from the
     // TLS 1.3 key schedule (RFC 8446 section 7.1), in particular the handshake
@@ -87,7 +97,12 @@
   // Creates an SSL_CTX and configures it with the options that are appropriate
   // for both client and server. The caller is responsible for ownership of the
   // newly created struct.
-  static bssl::UniquePtr<SSL_CTX> CreateSslCtx();
+  //
+  // The provided |cert_verify_mode| is passed in as the |mode| argument for
+  // |SSL_CTX_set_verify|. See
+  // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_VERIFY_NONE
+  // for a description of possible values.
+  static bssl::UniquePtr<SSL_CTX> CreateSslCtx(int cert_verify_mode);
 
   // From a given SSL* |ssl|, returns a pointer to the TlsConnection that it
   // belongs to. This helper method allows the callbacks set in BoringSSL to be
@@ -96,6 +111,10 @@
   static TlsConnection* ConnectionFromSsl(const SSL* ssl);
 
  private:
+  // Registered as the callback for SSL_CTX_set_custom_verify. The
+  // implementation is delegated to Delegate::VerifyCert.
+  static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert);
+
   // TlsConnection implements SSL_QUIC_METHOD, which provides the interface
   // between BoringSSL's TLS stack and a QUIC implementation.
   static const SSL_QUIC_METHOD kSslQuicMethod;
diff --git a/quic/core/crypto/tls_server_connection.cc b/quic/core/crypto/tls_server_connection.cc
index 9619ee7..f47e5ad 100644
--- a/quic/core/crypto/tls_server_connection.cc
+++ b/quic/core/crypto/tls_server_connection.cc
@@ -18,7 +18,8 @@
 // static
 bssl::UniquePtr<SSL_CTX> TlsServerConnection::CreateSslCtx(
     ProofSource* proof_source) {
-  bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx();
+  bssl::UniquePtr<SSL_CTX> ssl_ctx =
+      TlsConnection::CreateSslCtx(SSL_VERIFY_NONE);
   SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(),
                                          &SelectCertificateCallback);
   SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), &SelectAlpnCallback, nullptr);
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 16864bf..fc69ad6 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -20,35 +20,6 @@
 
 namespace quic {
 
-TlsClientHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
-    TlsClientHandshaker* parent)
-    : parent_(parent) {}
-
-TlsClientHandshaker::ProofVerifierCallbackImpl::~ProofVerifierCallbackImpl() {}
-
-void TlsClientHandshaker::ProofVerifierCallbackImpl::Run(
-    bool ok,
-    const std::string& /*error_details*/,
-    std::unique_ptr<ProofVerifyDetails>* details) {
-  if (parent_ == nullptr) {
-    return;
-  }
-
-  parent_->verify_details_ = std::move(*details);
-  parent_->verify_result_ = ok ? ssl_verify_ok : ssl_verify_invalid;
-  parent_->set_expected_ssl_error(SSL_ERROR_WANT_READ);
-  parent_->proof_verify_callback_ = nullptr;
-  if (parent_->verify_details_) {
-    parent_->proof_handler_->OnProofVerifyDetailsAvailable(
-        *parent_->verify_details_);
-  }
-  parent_->AdvanceHandshake();
-}
-
-void TlsClientHandshaker::ProofVerifierCallbackImpl::Cancel() {
-  parent_ = nullptr;
-}
-
 TlsClientHandshaker::TlsClientHandshaker(
     const QuicServerId& server_id,
     QuicCryptoStream* stream,
@@ -70,11 +41,7 @@
       has_application_state_(has_application_state),
       tls_connection_(crypto_config->ssl_ctx(), this) {}
 
-TlsClientHandshaker::~TlsClientHandshaker() {
-  if (proof_verify_callback_) {
-    proof_verify_callback_->Cancel();
-  }
-}
+TlsClientHandshaker::~TlsClientHandshaker() {}
 
 bool TlsClientHandshaker::CryptoConnect() {
   if (!pre_shared_key_.empty()) {
@@ -405,6 +372,32 @@
   handshaker_delegate()->DiscardOldDecryptionKey(ENCRYPTION_HANDSHAKE);
 }
 
+QuicAsyncStatus TlsClientHandshaker::VerifyCertChain(
+    const std::vector<std::string>& certs,
+    std::string* error_details,
+    std::unique_ptr<ProofVerifyDetails>* details,
+    std::unique_ptr<ProofVerifierCallback> callback) {
+  const uint8_t* ocsp_response_raw;
+  size_t ocsp_response_len;
+  SSL_get0_ocsp_response(ssl(), &ocsp_response_raw, &ocsp_response_len);
+  std::string ocsp_response(reinterpret_cast<const char*>(ocsp_response_raw),
+                            ocsp_response_len);
+  const uint8_t* sct_list_raw;
+  size_t sct_list_len;
+  SSL_get0_signed_cert_timestamp_list(ssl(), &sct_list_raw, &sct_list_len);
+  std::string sct_list(reinterpret_cast<const char*>(sct_list_raw),
+                       sct_list_len);
+
+  return proof_verifier_->VerifyCertChain(
+      server_id_.host(), server_id_.port(), certs, ocsp_response, sct_list,
+      verify_context_.get(), error_details, details, std::move(callback));
+}
+
+void TlsClientHandshaker::OnProofVerifyDetailsAvailable(
+    const ProofVerifyDetails& verify_details) {
+  proof_handler_->OnProofVerifyDetailsAvailable(verify_details);
+}
+
 void TlsClientHandshaker::FinishHandshake() {
   // Fill crypto_negotiated_params_:
   const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
@@ -493,61 +486,6 @@
   AdvanceHandshake();
 }
 
-enum ssl_verify_result_t TlsClientHandshaker::VerifyCert(uint8_t* out_alert) {
-  if (verify_result_ != ssl_verify_retry ||
-      expected_ssl_error() == SSL_ERROR_WANT_CERTIFICATE_VERIFY) {
-    enum ssl_verify_result_t result = verify_result_;
-    verify_result_ = ssl_verify_retry;
-    return result;
-  }
-  const STACK_OF(CRYPTO_BUFFER)* cert_chain = SSL_get0_peer_certificates(ssl());
-  if (cert_chain == nullptr) {
-    *out_alert = SSL_AD_INTERNAL_ERROR;
-    return ssl_verify_invalid;
-  }
-  // TODO(nharper): Pass the CRYPTO_BUFFERs into the QUIC stack to avoid copies.
-  std::vector<std::string> certs;
-  for (CRYPTO_BUFFER* cert : cert_chain) {
-    certs.push_back(
-        std::string(reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert)),
-                    CRYPTO_BUFFER_len(cert)));
-  }
-  const uint8_t* ocsp_response_raw;
-  size_t ocsp_response_len;
-  SSL_get0_ocsp_response(ssl(), &ocsp_response_raw, &ocsp_response_len);
-  std::string ocsp_response(reinterpret_cast<const char*>(ocsp_response_raw),
-                            ocsp_response_len);
-  const uint8_t* sct_list_raw;
-  size_t sct_list_len;
-  SSL_get0_signed_cert_timestamp_list(ssl(), &sct_list_raw, &sct_list_len);
-  std::string sct_list(reinterpret_cast<const char*>(sct_list_raw),
-                       sct_list_len);
-
-  ProofVerifierCallbackImpl* proof_verify_callback =
-      new ProofVerifierCallbackImpl(this);
-
-  QuicAsyncStatus verify_result = proof_verifier_->VerifyCertChain(
-      server_id_.host(), server_id_.port(), certs, ocsp_response, sct_list,
-      verify_context_.get(), &cert_verify_error_details_, &verify_details_,
-      std::unique_ptr<ProofVerifierCallback>(proof_verify_callback));
-  switch (verify_result) {
-    case QUIC_SUCCESS:
-      if (verify_details_) {
-        proof_handler_->OnProofVerifyDetailsAvailable(*verify_details_);
-      }
-      return ssl_verify_ok;
-    case QUIC_PENDING:
-      proof_verify_callback_ = proof_verify_callback;
-      set_expected_ssl_error(SSL_ERROR_WANT_CERTIFICATE_VERIFY);
-      return ssl_verify_retry;
-    case QUIC_FAILURE:
-    default:
-      QUIC_LOG(INFO) << "Cert chain verification failed: "
-                     << cert_verify_error_details_;
-      return ssl_verify_invalid;
-  }
-}
-
 void TlsClientHandshaker::InsertSession(bssl::UniquePtr<SSL_SESSION> session) {
   if (!received_transport_params_) {
     QUIC_BUG << "Transport parameters isn't received";
diff --git a/quic/core/tls_client_handshaker.h b/quic/core/tls_client_handshaker.h
index 2f16620..c2707c7 100644
--- a/quic/core/tls_client_handshaker.h
+++ b/quic/core/tls_client_handshaker.h
@@ -11,7 +11,6 @@
 
 #include "absl/strings/string_view.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
-#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
 #include "net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h"
 #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
@@ -89,32 +88,18 @@
   void FinishHandshake() override;
   void ProcessPostHandshakeMessage() override;
   bool ShouldCloseConnectionOnUnexpectedError(int ssl_error) override;
+  QuicAsyncStatus VerifyCertChain(
+      const std::vector<std::string>& certs,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details,
+      std::unique_ptr<ProofVerifierCallback> callback) override;
+  void OnProofVerifyDetailsAvailable(
+      const ProofVerifyDetails& verify_details) override;
 
   // TlsClientConnection::Delegate implementation:
-  enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) override;
   TlsConnection::Delegate* ConnectionDelegate() override { return this; }
 
  private:
-  // ProofVerifierCallbackImpl handles the result of an asynchronous certificate
-  // verification operation.
-  class QUIC_EXPORT_PRIVATE ProofVerifierCallbackImpl
-      : public ProofVerifierCallback {
-   public:
-    explicit ProofVerifierCallbackImpl(TlsClientHandshaker* parent);
-    ~ProofVerifierCallbackImpl() override;
-
-    // ProofVerifierCallback interface.
-    void Run(bool ok,
-             const std::string& error_details,
-             std::unique_ptr<ProofVerifyDetails>* details) override;
-
-    // If called, Cancel causes the pending callback to be a no-op.
-    void Cancel();
-
-   private:
-    TlsClientHandshaker* parent_;
-  };
-
   bool SetAlpn();
   bool SetTransportParameters();
   bool ProcessTransportParameters(std::string* error_details);
@@ -134,10 +119,10 @@
   QuicServerId server_id_;
 
   // Objects used for verifying the server's certificate chain.
-  // |proof_verifier_| is owned by the caller of TlsClientHandshaker's
-  // constructor.
+  // |proof_verifier_| is owned by the caller of TlsHandshaker's constructor.
   ProofVerifier* proof_verifier_;
   std::unique_ptr<ProofVerifyContext> verify_context_;
+
   // Unowned pointer to the proof handler which has the
   // OnProofVerifyDetailsAvailable callback to use for notifying the result of
   // certificate verification.
@@ -152,13 +137,6 @@
   // Pre-shared key used during the handshake.
   std::string pre_shared_key_;
 
-  // ProofVerifierCallback used for async certificate verification. This object
-  // is owned by |proof_verifier_|.
-  ProofVerifierCallbackImpl* proof_verify_callback_ = nullptr;
-  std::unique_ptr<ProofVerifyDetails> verify_details_;
-  enum ssl_verify_result_t verify_result_ = ssl_verify_retry;
-  std::string cert_verify_error_details_;
-
   HandshakeState state_ = HANDSHAKE_START;
   bool encryption_established_ = false;
   bool initial_keys_dropped_ = false;
diff --git a/quic/core/tls_handshaker.cc b/quic/core/tls_handshaker.cc
index 6c45f84..1f20f32 100644
--- a/quic/core/tls_handshaker.cc
+++ b/quic/core/tls_handshaker.cc
@@ -15,10 +15,42 @@
 
 namespace quic {
 
+TlsHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
+    TlsHandshaker* parent)
+    : parent_(parent) {}
+
+TlsHandshaker::ProofVerifierCallbackImpl::~ProofVerifierCallbackImpl() {}
+
+void TlsHandshaker::ProofVerifierCallbackImpl::Run(
+    bool ok,
+    const std::string& /*error_details*/,
+    std::unique_ptr<ProofVerifyDetails>* details) {
+  if (parent_ == nullptr) {
+    return;
+  }
+
+  parent_->verify_details_ = std::move(*details);
+  parent_->verify_result_ = ok ? ssl_verify_ok : ssl_verify_invalid;
+  parent_->set_expected_ssl_error(SSL_ERROR_WANT_READ);
+  parent_->proof_verify_callback_ = nullptr;
+  if (parent_->verify_details_) {
+    parent_->OnProofVerifyDetailsAvailable(*parent_->verify_details_);
+  }
+  parent_->AdvanceHandshake();
+}
+
+void TlsHandshaker::ProofVerifierCallbackImpl::Cancel() {
+  parent_ = nullptr;
+}
+
 TlsHandshaker::TlsHandshaker(QuicCryptoStream* stream, QuicSession* session)
     : stream_(stream), handshaker_delegate_(session) {}
 
-TlsHandshaker::~TlsHandshaker() {}
+TlsHandshaker::~TlsHandshaker() {
+  if (proof_verify_callback_) {
+    proof_verify_callback_->Cancel();
+  }
+}
 
 bool TlsHandshaker::ProcessInput(absl::string_view input,
                                  EncryptionLevel level) {
@@ -109,6 +141,50 @@
   return EVP_get_digestbynid(SSL_CIPHER_get_prf_nid(cipher));
 }
 
+enum ssl_verify_result_t TlsHandshaker::VerifyCert(uint8_t* out_alert) {
+  if (verify_result_ != ssl_verify_retry ||
+      expected_ssl_error() == SSL_ERROR_WANT_CERTIFICATE_VERIFY) {
+    enum ssl_verify_result_t result = verify_result_;
+    verify_result_ = ssl_verify_retry;
+    return result;
+  }
+  const STACK_OF(CRYPTO_BUFFER)* cert_chain = SSL_get0_peer_certificates(ssl());
+  if (cert_chain == nullptr) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return ssl_verify_invalid;
+  }
+  // TODO(nharper): Pass the CRYPTO_BUFFERs into the QUIC stack to avoid copies.
+  std::vector<std::string> certs;
+  for (CRYPTO_BUFFER* cert : cert_chain) {
+    certs.push_back(
+        std::string(reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert)),
+                    CRYPTO_BUFFER_len(cert)));
+  }
+
+  ProofVerifierCallbackImpl* proof_verify_callback =
+      new ProofVerifierCallbackImpl(this);
+
+  QuicAsyncStatus verify_result = VerifyCertChain(
+      certs, &cert_verify_error_details_, &verify_details_,
+      std::unique_ptr<ProofVerifierCallback>(proof_verify_callback));
+  switch (verify_result) {
+    case QUIC_SUCCESS:
+      if (verify_details_) {
+        OnProofVerifyDetailsAvailable(*verify_details_);
+      }
+      return ssl_verify_ok;
+    case QUIC_PENDING:
+      proof_verify_callback_ = proof_verify_callback;
+      set_expected_ssl_error(SSL_ERROR_WANT_CERTIFICATE_VERIFY);
+      return ssl_verify_retry;
+    case QUIC_FAILURE:
+    default:
+      QUIC_LOG(INFO) << "Cert chain verification failed: "
+                     << cert_verify_error_details_;
+      return ssl_verify_invalid;
+  }
+}
+
 void TlsHandshaker::SetWriteSecret(EncryptionLevel level,
                                    const SSL_CIPHER* cipher,
                                    const std::vector<uint8_t>& write_secret) {
diff --git a/quic/core/tls_handshaker.h b/quic/core/tls_handshaker.h
index 9288592..657c9c7 100644
--- a/quic/core/tls_handshaker.h
+++ b/quic/core/tls_handshaker.h
@@ -10,6 +10,7 @@
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/tls_connection.h"
@@ -83,6 +84,21 @@
   }
   int expected_ssl_error() const { return expected_ssl_error_; }
 
+  // Called to verify a cert chain. This is a simple wrapper around
+  // ProofVerifier or ServerProofVerifier, which optionally gathers additional
+  // arguments to pass into their VerifyCertChain method. This class retains a
+  // non-owning pointer to |callback|; the callback must live until this
+  // function returns QUIC_SUCCESS or QUIC_FAILURE, or until the callback is
+  // run.
+  virtual QuicAsyncStatus VerifyCertChain(
+      const std::vector<std::string>& certs,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details,
+      std::unique_ptr<ProofVerifierCallback> callback) = 0;
+  // Called when certificate verification is completed.
+  virtual void OnProofVerifyDetailsAvailable(
+      const ProofVerifyDetails& verify_details) = 0;
+
   // Returns the PRF used by the cipher suite negotiated in the TLS handshake.
   const EVP_MD* Prf(const SSL_CIPHER* cipher);
 
@@ -95,6 +111,8 @@
     return handshaker_delegate_;
   }
 
+  enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) override;
+
   // SetWriteSecret provides the encryption secret used to encrypt messages at
   // encryption level |level|. The secret provided here is the one from the TLS
   // 1.3 key schedule (RFC 8446 section 7.1), in particular the handshake
@@ -127,6 +145,36 @@
   void SendAlert(EncryptionLevel level, uint8_t desc) override;
 
  private:
+  // ProofVerifierCallbackImpl handles the result of an asynchronous certificate
+  // verification operation.
+  class QUIC_EXPORT_PRIVATE ProofVerifierCallbackImpl
+      : public ProofVerifierCallback {
+   public:
+    explicit ProofVerifierCallbackImpl(TlsHandshaker* parent);
+    ~ProofVerifierCallbackImpl() override;
+
+    // ProofVerifierCallback interface.
+    void Run(bool ok,
+             const std::string& error_details,
+             std::unique_ptr<ProofVerifyDetails>* details) override;
+
+    // If called, Cancel causes the pending callback to be a no-op.
+    void Cancel();
+
+   private:
+    // Non-owning pointer to the TlsHandshaker responsible for this callback.
+    // |parent_| must be valid for the life of this callback or until |Cancel|
+    // is called.
+    TlsHandshaker* parent_;
+  };
+
+  // ProofVerifierCallback used for async certificate verification. Ownership of
+  // this object is transferred to |VerifyCertChain|;
+  ProofVerifierCallbackImpl* proof_verify_callback_ = nullptr;
+  std::unique_ptr<ProofVerifyDetails> verify_details_;
+  enum ssl_verify_result_t verify_result_ = ssl_verify_retry;
+  std::string cert_verify_error_details_;
+
   int expected_ssl_error_ = SSL_ERROR_WANT_READ;
   bool is_connection_closed_ = false;
 
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index c14deb0..a935741 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -389,6 +389,18 @@
   handshaker_delegate()->DiscardOldDecryptionKey(ENCRYPTION_ZERO_RTT);
 }
 
+QuicAsyncStatus TlsServerHandshaker::VerifyCertChain(
+    const std::vector<std::string>& /*certs*/,
+    std::string* /*error_details*/,
+    std::unique_ptr<ProofVerifyDetails>* /*details*/,
+    std::unique_ptr<ProofVerifierCallback> /*callback*/) {
+  QUIC_BUG << "Client certificates are not yet supported on the server";
+  return QUIC_FAILURE;
+}
+
+void TlsServerHandshaker::OnProofVerifyDetailsAvailable(
+    const ProofVerifyDetails& /*verify_details*/) {}
+
 ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign(
     uint8_t* out,
     size_t* out_len,
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index 41870f9..2fba611 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -93,6 +93,13 @@
   // TlsHandshaker implementation:
   void FinishHandshake() override;
   void ProcessPostHandshakeMessage() override {}
+  QuicAsyncStatus VerifyCertChain(
+      const std::vector<std::string>& certs,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details,
+      std::unique_ptr<ProofVerifierCallback> callback) override;
+  void OnProofVerifyDetailsAvailable(
+      const ProofVerifyDetails& verify_details) override;
 
   // TlsServerConnection::Delegate implementation:
   int SelectCertificate(int* out_alert) override;