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