Add ProofSource::GetCertChains() to lay groundwork for Trust Anchor IDs

PiperOrigin-RevId: 818821498
diff --git a/quiche/common/platform/api/quiche_reference_counted.h b/quiche/common/platform/api/quiche_reference_counted.h
index dc16166..0aa4965 100644
--- a/quiche/common/platform/api/quiche_reference_counted.h
+++ b/quiche/common/platform/api/quiche_reference_counted.h
@@ -54,6 +54,9 @@
 template <class T>
 class QUICHE_NO_EXPORT QuicheReferenceCountedPointer {
  public:
+  // For compatibility with googletest's `Pointee` matcher.
+  using element_type = T;
+
   QuicheReferenceCountedPointer() = default;
 
   // Constructor from raw pointer |p|. This guarantees that the reference count
diff --git a/quiche/quic/core/crypto/proof_source.cc b/quiche/quic/core/crypto/proof_source.cc
index ff489c1..ee3970b 100644
--- a/quiche/quic/core/crypto/proof_source.cc
+++ b/quiche/quic/core/crypto/proof_source.cc
@@ -4,11 +4,19 @@
 
 #include "quiche/quic/core/crypto/proof_source.h"
 
+#include <cstddef>
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <vector>
 
+#include "openssl/base.h"
+#include "openssl/pool.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/certificate_view.h"
 #include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_socket_address.h"
+#include "quiche/common/platform/api/quiche_reference_counted.h"
 
 namespace quic {
 
@@ -19,8 +27,10 @@
 }
 
 ProofSource::Chain::Chain(const std::vector<std::string>& certs,
-                          const std::string& trust_anchor_id)
-    : certs(certs), trust_anchor_id(trust_anchor_id) {}
+                          const std::string& trust_anchor_id, bool matches_sni)
+    : certs(certs),
+      trust_anchor_id(trust_anchor_id),
+      matches_sni(matches_sni) {}
 
 ProofSource::Chain::~Chain() {}
 
@@ -61,4 +71,18 @@
 
 void ProofSource::OnNewSslCtx(SSL_CTX*) {}
 
+std::vector<quiche::QuicheReferenceCountedPointer<ProofSource::Chain>>
+ProofSource::GetCertChains(const QuicSocketAddress& server_address,
+                           const QuicSocketAddress& client_address,
+                           const std::string& hostname) {
+  bool cert_matched_sni;
+  quiche::QuicheReferenceCountedPointer<Chain> chain =
+      GetCertChain(server_address, client_address, hostname, &cert_matched_sni);
+  if (chain == nullptr) {
+    return {};
+  }
+  chain->matches_sni = cert_matched_sni;
+  return {chain};
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/crypto/proof_source.h b/quiche/quic/core/crypto/proof_source.h
index 21276ed..2fc5694 100644
--- a/quiche/quic/core/crypto/proof_source.h
+++ b/quiche/quic/core/crypto/proof_source.h
@@ -7,7 +7,6 @@
 
 #include <cstddef>
 #include <cstdint>
-#include <functional>
 #include <memory>
 #include <optional>
 #include <string>
@@ -54,7 +53,7 @@
   // certificates.
   struct QUICHE_EXPORT Chain : public quiche::QuicheReferenceCounted {
     Chain(const std::vector<std::string>& certs,
-          const std::string& trust_anchor_id = "");
+          const std::string& trust_anchor_id = "", bool matches_sni = false);
     Chain(const Chain&) = delete;
     Chain& operator=(const Chain&) = delete;
 
@@ -65,6 +64,10 @@
     // trust anchor ID will be set.
     const std::string trust_anchor_id;
 
+    // Indicates whether the leaf certificate matches the SNI hostname. Note
+    // that this field is NOT set by `ProofSource::GetCertChain()`.
+    bool matches_sni;
+
    protected:
     ~Chain() override;
   };
@@ -170,11 +173,30 @@
   //
   // Sets *cert_matched_sni to true if the certificate matched the given
   // hostname, false if a default cert not matching the hostname was used.
+  //
+  // Does not set `Chain::matches_sni` on the returned chain.
+  //
+  // TODO: b/450539617 - Update all implementations to set `Chain::matches_sni`
+  // on the returned chain.
   virtual quiche::QuicheReferenceCountedPointer<Chain> GetCertChain(
       const QuicSocketAddress& server_address,
       const QuicSocketAddress& client_address, const std::string& hostname,
       bool* cert_matched_sni) = 0;
 
+  // Returns zero or more certificate chains for `hostname`. Chains are returned
+  // in decreasing order of preference, so earlier chains are preferred over
+  // later chains. Within each chain, certificates are in leaf-first order.
+  // Each chain's `Chain::matches_sni` field is set to true iff the leaf
+  // certificate matches `hostname`. None of the returned chains are nullptr.
+  //
+  // The default implementation returns a vector of zero or one elements based
+  // on the result of `GetCertChain()`. If `GetCertChain()` returns nullptr,
+  // this method returns the empty vector.
+  virtual std::vector<quiche::QuicheReferenceCountedPointer<Chain>>
+  GetCertChains(const QuicSocketAddress& server_address,
+                const QuicSocketAddress& client_address,
+                const std::string& hostname);
+
   // Computes a signature using the private key of the certificate for
   // |hostname|. The value in |in| is signed using the algorithm specified by
   // |signature_algorithm|, which is an |SSL_SIGN_*| value (as defined in TLS
diff --git a/quiche/quic/core/crypto/proof_source_x509_test.cc b/quiche/quic/core/crypto/proof_source_x509_test.cc
index c3bf9a5..ac1d2fc 100644
--- a/quiche/quic/core/crypto/proof_source_x509_test.cc
+++ b/quiche/quic/core/crypto/proof_source_x509_test.cc
@@ -24,6 +24,13 @@
 namespace test {
 namespace {
 
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+using ::testing::Pointee;
+
 quiche::QuicheReferenceCountedPointer<ProofSource::Chain> MakeChain(
     absl::string_view cert) {
   return quiche::QuicheReferenceCountedPointer<ProofSource::Chain>(
@@ -109,6 +116,14 @@
                   ->certs,
               ::testing::ElementsAre(kTestCertificate));
   EXPECT_FALSE(cert_matched_sni);
+
+  EXPECT_THAT(
+      proof_source_->GetCertChains(QuicSocketAddress(), QuicSocketAddress(),
+                                   "unknown.test"),
+      ElementsAre(Pointee(AllOf(
+          Field(&ProofSource::Chain::certs, ElementsAre(kTestCertificate)),
+          Field(&ProofSource::Chain::trust_anchor_id, IsEmpty()),
+          Field(&ProofSource::Chain::matches_sni, IsFalse())))));
 }
 
 // mail.example.org is explicitly a SubjectAltName in `kTestCertificate`.
@@ -120,6 +135,14 @@
                   ->certs,
               ::testing::ElementsAre(kTestCertificate));
   EXPECT_TRUE(cert_matched_sni);
+
+  EXPECT_THAT(
+      proof_source_->GetCertChains(QuicSocketAddress(), QuicSocketAddress(),
+                                   "mail.example.org"),
+      ElementsAre(Pointee(AllOf(
+          Field(&ProofSource::Chain::certs, ElementsAre(kTestCertificate)),
+          Field(&ProofSource::Chain::trust_anchor_id, IsEmpty()),
+          Field(&ProofSource::Chain::matches_sni, IsTrue())))));
 }
 
 // www.foo.test is in `kWildcardCertificate`.
@@ -131,6 +154,14 @@
                   ->certs,
               ::testing::ElementsAre(kWildcardCertificate));
   EXPECT_TRUE(cert_matched_sni);
+
+  EXPECT_THAT(
+      proof_source_->GetCertChains(QuicSocketAddress(), QuicSocketAddress(),
+                                   "www.foo.test"),
+      ElementsAre(Pointee(AllOf(
+          Field(&ProofSource::Chain::certs, ElementsAre(kWildcardCertificate)),
+          Field(&ProofSource::Chain::trust_anchor_id, IsEmpty()),
+          Field(&ProofSource::Chain::matches_sni, IsTrue())))));
 }
 
 // *.wildcard.test is in `kWildcardCertificate`.
@@ -144,12 +175,28 @@
               ::testing::ElementsAre(kWildcardCertificate));
   EXPECT_TRUE(cert_matched_sni);
 
+  EXPECT_THAT(
+      proof_source_->GetCertChains(QuicSocketAddress(), QuicSocketAddress(),
+                                   "www.wildcard.test"),
+      ElementsAre(Pointee(AllOf(
+          Field(&ProofSource::Chain::certs, ElementsAre(kWildcardCertificate)),
+          Field(&ProofSource::Chain::trust_anchor_id, IsEmpty()),
+          Field(&ProofSource::Chain::matches_sni, IsTrue())))));
+
   EXPECT_THAT(proof_source_
                   ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(),
                                  "etc.wildcard.test", &cert_matched_sni)
                   ->certs,
               ::testing::ElementsAre(kWildcardCertificate));
   EXPECT_TRUE(cert_matched_sni);
+
+  EXPECT_THAT(
+      proof_source_->GetCertChains(QuicSocketAddress(), QuicSocketAddress(),
+                                   "etc.wildcard.test"),
+      ElementsAre(Pointee(AllOf(
+          Field(&ProofSource::Chain::certs, ElementsAre(kWildcardCertificate)),
+          Field(&ProofSource::Chain::trust_anchor_id, IsEmpty()),
+          Field(&ProofSource::Chain::matches_sni, IsTrue())))));
 }
 
 // wildcard.test itself is not in `kWildcardCertificate`.
@@ -161,6 +208,14 @@
                   ->certs,
               ::testing::ElementsAre(kTestCertificate));
   EXPECT_FALSE(cert_matched_sni);
+
+  EXPECT_THAT(
+      proof_source_->GetCertChains(QuicSocketAddress(), QuicSocketAddress(),
+                                   "wildcard.test"),
+      ElementsAre(Pointee(AllOf(
+          Field(&ProofSource::Chain::certs, ElementsAre(kTestCertificate)),
+          Field(&ProofSource::Chain::trust_anchor_id, IsEmpty()),
+          Field(&ProofSource::Chain::matches_sni, IsFalse())))));
 }
 
 }  // namespace