For mTLS, change QuicCryptoClientConfig to include a quic::ClientProofSource instead of a quic::ProofSource. Also set the cert and key in quic::TlsClientHandshaker's constructor, both will be used if server requests it.
Split from cl/402574728.
PiperOrigin-RevId: 404341798
diff --git a/quic/core/crypto/certificate_view.cc b/quic/core/crypto/certificate_view.cc
index 4024a78..f0b1c18 100644
--- a/quic/core/crypto/certificate_view.cc
+++ b/quic/core/crypto/certificate_view.cc
@@ -586,7 +586,7 @@
}
std::string CertificatePrivateKey::Sign(absl::string_view input,
- uint16_t signature_algorithm) {
+ uint16_t signature_algorithm) const {
if (!ValidForSignatureAlgorithm(signature_algorithm)) {
QUIC_BUG(quic_bug_10640_2)
<< "Mismatch between the requested signature algorithm and the "
@@ -626,12 +626,13 @@
return output;
}
-bool CertificatePrivateKey::MatchesPublicKey(const CertificateView& view) {
+bool CertificatePrivateKey::MatchesPublicKey(
+ const CertificateView& view) const {
return EVP_PKEY_cmp(view.public_key(), private_key_.get()) == 1;
}
bool CertificatePrivateKey::ValidForSignatureAlgorithm(
- uint16_t signature_algorithm) {
+ uint16_t signature_algorithm) const {
return PublicKeyTypeFromSignatureAlgorithm(signature_algorithm) ==
PublicKeyTypeFromKey(private_key_.get());
}
diff --git a/quic/core/crypto/certificate_view.h b/quic/core/crypto/certificate_view.h
index 0f88165..f2826cd 100644
--- a/quic/core/crypto/certificate_view.h
+++ b/quic/core/crypto/certificate_view.h
@@ -105,17 +105,17 @@
std::istream* input);
// |signature_algorithm| is a TLS signature algorithm ID.
- std::string Sign(absl::string_view input, uint16_t signature_algorithm);
+ std::string Sign(absl::string_view input, uint16_t signature_algorithm) const;
// Verifies that the private key in question matches the public key of the
// certificate |view|.
- bool MatchesPublicKey(const CertificateView& view);
+ bool MatchesPublicKey(const CertificateView& view) const;
// Verifies that the private key can be used with the specified TLS signature
// algorithm.
- bool ValidForSignatureAlgorithm(uint16_t signature_algorithm);
+ bool ValidForSignatureAlgorithm(uint16_t signature_algorithm) const;
- EVP_PKEY* private_key() { return private_key_.get(); }
+ EVP_PKEY* private_key() const { return private_key_.get(); }
private:
CertificatePrivateKey() = default;
diff --git a/quic/core/crypto/client_proof_source.cc b/quic/core/crypto/client_proof_source.cc
new file mode 100644
index 0000000..4e4d442
--- /dev/null
+++ b/quic/core/crypto/client_proof_source.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2021 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.
+
+#include "quic/core/crypto/client_proof_source.h"
+
+#include "absl/strings/match.h"
+
+namespace quic {
+
+bool DefaultClientProofSource::AddCertAndKey(
+ std::vector<std::string> server_hostnames,
+ QuicReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey private_key) {
+ if (!ValidateCertAndKey(chain, private_key)) {
+ return false;
+ }
+
+ auto cert_and_key =
+ std::make_shared<CertAndKey>(std::move(chain), std::move(private_key));
+ for (const std::string& domain : server_hostnames) {
+ cert_and_keys_[domain] = cert_and_key;
+ }
+ return true;
+}
+
+const ClientProofSource::CertAndKey* DefaultClientProofSource::GetCertAndKey(
+ absl::string_view hostname) const {
+ const CertAndKey* result = LookupExact(hostname);
+ if (result != nullptr || hostname == "*") {
+ return result;
+ }
+
+ // Either a full or a wildcard domain lookup failed. In the former case,
+ // derive the wildcard domain and look it up.
+ if (hostname.size() > 1 && !absl::StartsWith(hostname, "*.")) {
+ auto dot_pos = hostname.find('.');
+ if (dot_pos != std::string::npos) {
+ std::string wildcard = absl::StrCat("*", hostname.substr(dot_pos));
+ const CertAndKey* result = LookupExact(wildcard);
+ if (result != nullptr) {
+ return result;
+ }
+ }
+ }
+
+ // Return default cert, if any.
+ return LookupExact("*");
+}
+
+const ClientProofSource::CertAndKey* DefaultClientProofSource::LookupExact(
+ absl::string_view map_key) const {
+ const auto it = cert_and_keys_.find(map_key);
+ QUIC_DVLOG(1) << "LookupExact(" << map_key
+ << ") found:" << (it != cert_and_keys_.end());
+ if (it != cert_and_keys_.end()) {
+ return it->second.get();
+ }
+ return nullptr;
+}
+
+} // namespace quic
diff --git a/quic/core/crypto/client_proof_source.h b/quic/core/crypto/client_proof_source.h
new file mode 100644
index 0000000..d1ab26c
--- /dev/null
+++ b/quic/core/crypto/client_proof_source.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2021 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_CLIENT_PROOF_SOURCE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CLIENT_PROOF_SOURCE_H_
+
+#include <memory>
+
+#include "absl/container/flat_hash_map.h"
+#include "quic/core/crypto/certificate_view.h"
+#include "quic/core/crypto/proof_source.h"
+
+namespace quic {
+
+// ClientProofSource is the interface for a QUIC client to provide client certs
+// and keys based on server hostname. It is only used by TLS handshakes.
+class QUIC_EXPORT_PRIVATE ClientProofSource {
+ public:
+ using Chain = ProofSource::Chain;
+
+ virtual ~ClientProofSource() {}
+
+ struct QUIC_EXPORT_PRIVATE CertAndKey {
+ CertAndKey(QuicReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey private_key)
+ : chain(std::move(chain)), private_key(std::move(private_key)) {}
+
+ QuicReferenceCountedPointer<Chain> chain;
+ CertificatePrivateKey private_key;
+ };
+
+ // Get the client certificate to be sent to the server with |server_hostname|
+ // and its corresponding private key. It returns nullptr if the cert and key
+ // can not be found.
+ //
+ // |server_hostname| is typically a full domain name(www.foo.com), but it
+ // could also be a wildcard domain(*.foo.com), or a "*" which will return the
+ // default cert.
+ virtual const CertAndKey* GetCertAndKey(
+ absl::string_view server_hostname) const = 0;
+};
+
+// DefaultClientProofSource is an implementation that simply keeps an in memory
+// map of server hostnames to certs.
+class QUIC_EXPORT_PRIVATE DefaultClientProofSource : public ClientProofSource {
+ public:
+ ~DefaultClientProofSource() override {}
+
+ // Associate all hostnames in |server_hostnames| with {|chain|,|private_key|}.
+ // Elements of |server_hostnames| can be full domain names(www.foo.com),
+ // wildcard domains(*.foo.com), or "*" which means the given cert chain is the
+ // default one.
+ // If any element of |server_hostnames| is already associated with a cert
+ // chain, it will be updated to be associated with the new cert chain.
+ bool AddCertAndKey(std::vector<std::string> server_hostnames,
+ QuicReferenceCountedPointer<Chain> chain,
+ CertificatePrivateKey private_key);
+
+ // ClientProofSource implementation
+ const CertAndKey* GetCertAndKey(absl::string_view hostname) const override;
+
+ private:
+ const CertAndKey* LookupExact(absl::string_view map_key) const;
+ absl::flat_hash_map<std::string, std::shared_ptr<CertAndKey>> cert_and_keys_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CLIENT_PROOF_SOURCE_H_
diff --git a/quic/core/crypto/client_proof_source_test.cc b/quic/core/crypto/client_proof_source_test.cc
new file mode 100644
index 0000000..08f5b76
--- /dev/null
+++ b/quic/core/crypto/client_proof_source_test.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2021 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.
+
+#include "quic/core/crypto/client_proof_source.h"
+
+#include "quic/platform/api/quic_expect_bug.h"
+#include "quic/platform/api/quic_test.h"
+#include "quic/test_tools/test_certificates.h"
+
+namespace quic {
+namespace test {
+
+QuicReferenceCountedPointer<ClientProofSource::Chain> TestCertChain() {
+ return QuicReferenceCountedPointer<ClientProofSource::Chain>(
+ new ClientProofSource::Chain({std::string(kTestCertificate)}));
+}
+
+CertificatePrivateKey TestPrivateKey() {
+ 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)));
+}
+
+const ClientProofSource::CertAndKey* TestCertAndKey() {
+ static const ClientProofSource::CertAndKey cert_and_key(TestCertChain(),
+ TestPrivateKey());
+ return &cert_and_key;
+}
+
+QuicReferenceCountedPointer<ClientProofSource::Chain> NullCertChain() {
+ return QuicReferenceCountedPointer<ClientProofSource::Chain>();
+}
+
+QuicReferenceCountedPointer<ClientProofSource::Chain> EmptyCertChain() {
+ return QuicReferenceCountedPointer<ClientProofSource::Chain>(
+ new ClientProofSource::Chain(std::vector<std::string>()));
+}
+
+QuicReferenceCountedPointer<ClientProofSource::Chain> BadCertChain() {
+ return QuicReferenceCountedPointer<ClientProofSource::Chain>(
+ new ClientProofSource::Chain({"This is the content of a bad cert."}));
+}
+
+CertificatePrivateKey EmptyPrivateKey() {
+ return CertificatePrivateKey(bssl::UniquePtr<EVP_PKEY>(EVP_PKEY_new()));
+}
+
+#define VERIFY_CERT_AND_KEY_MATCHES(lhs, rhs) \
+ do { \
+ SCOPED_TRACE(testing::Message()); \
+ VerifyCertAndKeyMatches(lhs, rhs); \
+ } while (0)
+
+void VerifyCertAndKeyMatches(const ClientProofSource::CertAndKey* lhs,
+ const ClientProofSource::CertAndKey* rhs) {
+ if (lhs == rhs) {
+ return;
+ }
+
+ if (lhs == nullptr) {
+ ADD_FAILURE() << "lhs is nullptr, but rhs is not";
+ return;
+ }
+
+ if (rhs == nullptr) {
+ ADD_FAILURE() << "rhs is nullptr, but lhs is not";
+ return;
+ }
+
+ if (1 != EVP_PKEY_cmp(lhs->private_key.private_key(),
+ rhs->private_key.private_key())) {
+ ADD_FAILURE() << "Private keys mismatch";
+ return;
+ }
+
+ const ClientProofSource::Chain* lhs_chain = lhs->chain.get();
+ const ClientProofSource::Chain* rhs_chain = rhs->chain.get();
+
+ if (lhs_chain == rhs_chain) {
+ return;
+ }
+
+ if (lhs_chain == nullptr) {
+ ADD_FAILURE() << "lhs->chain is nullptr, but rhs->chain is not";
+ return;
+ }
+
+ if (rhs_chain == nullptr) {
+ ADD_FAILURE() << "rhs->chain is nullptr, but lhs->chain is not";
+ return;
+ }
+
+ if (lhs_chain->certs.size() != rhs_chain->certs.size()) {
+ ADD_FAILURE() << "Cert chain length differ. lhs:" << lhs_chain->certs.size()
+ << ", rhs:" << rhs_chain->certs.size();
+ return;
+ }
+
+ for (size_t i = 0; i < lhs_chain->certs.size(); ++i) {
+ if (lhs_chain->certs[i] != rhs_chain->certs[i]) {
+ ADD_FAILURE() << "The " << i << "-th certs differ.";
+ return;
+ }
+ }
+
+ // All good.
+}
+
+TEST(DefaultClientProofSource, FullDomain) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(proof_source.AddCertAndKey({"www.google.com"}, TestCertChain(),
+ TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ EXPECT_EQ(proof_source.GetCertAndKey("*.google.com"), nullptr);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, WildcardDomain) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(proof_source.AddCertAndKey({"*.google.com"}, TestCertChain(),
+ TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*.google.com"),
+ TestCertAndKey());
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, DefaultDomain) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(
+ proof_source.AddCertAndKey({"*"}, TestCertChain(), TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*"),
+ TestCertAndKey());
+}
+
+TEST(DefaultClientProofSource, FullAndWildcard) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(proof_source.AddCertAndKey({"www.google.com", "*.google.com"},
+ TestCertChain(), TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("foo.google.com"),
+ TestCertAndKey());
+ EXPECT_EQ(proof_source.GetCertAndKey("www.example.com"), nullptr);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, FullWildcardAndDefault) {
+ DefaultClientProofSource proof_source;
+ ASSERT_TRUE(
+ proof_source.AddCertAndKey({"www.google.com", "*.google.com", "*"},
+ TestCertChain(), TestPrivateKey()));
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("foo.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("www.example.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*.google.com"),
+ TestCertAndKey());
+ VERIFY_CERT_AND_KEY_MATCHES(proof_source.GetCertAndKey("*"),
+ TestCertAndKey());
+}
+
+TEST(DefaultClientProofSource, EmptyCerts) {
+ DefaultClientProofSource proof_source;
+ bool ok;
+ EXPECT_QUIC_BUG(
+ ok = proof_source.AddCertAndKey({"*"}, NullCertChain(), TestPrivateKey()),
+ "Certificate chain is empty");
+ ASSERT_FALSE(ok);
+
+ EXPECT_QUIC_BUG(ok = proof_source.AddCertAndKey({"*"}, EmptyCertChain(),
+ TestPrivateKey()),
+ "Certificate chain is empty");
+ ASSERT_FALSE(ok);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, BadCerts) {
+ DefaultClientProofSource proof_source;
+ bool ok;
+ EXPECT_QUIC_BUG(
+ ok = proof_source.AddCertAndKey({"*"}, BadCertChain(), TestPrivateKey()),
+ "Unabled to parse leaf certificate");
+ ASSERT_FALSE(ok);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+TEST(DefaultClientProofSource, KeyMismatch) {
+ DefaultClientProofSource proof_source;
+ bool ok;
+ EXPECT_QUIC_BUG(ok = proof_source.AddCertAndKey(
+ {"www.google.com"}, TestCertChain(), EmptyPrivateKey()),
+ "Private key does not match the leaf certificate");
+ ASSERT_FALSE(ok);
+ EXPECT_EQ(proof_source.GetCertAndKey("*"), nullptr);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/quic/core/crypto/proof_source.cc b/quic/core/crypto/proof_source.cc
index bb3a795..9cb85c2 100644
--- a/quic/core/crypto/proof_source.cc
+++ b/quic/core/crypto/proof_source.cc
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "quic/core/crypto/proof_source.h"
+
#include <string>
-#include "quic/core/crypto/proof_source.h"
+#include "quic/platform/api/quic_bug_tracker.h"
namespace quic {
@@ -30,4 +32,28 @@
return crypto_buffers;
}
+bool ValidateCertAndKey(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const CertificatePrivateKey& key) {
+ if (chain.get() == nullptr || chain->certs.empty()) {
+ QUIC_BUG(quic_proof_source_empty_chain) << "Certificate chain is empty";
+ return false;
+ }
+
+ std::unique_ptr<CertificateView> leaf =
+ CertificateView::ParseSingleCertificate(chain->certs[0]);
+ if (leaf == nullptr) {
+ QUIC_BUG(quic_proof_source_unparsable_leaf_cert)
+ << "Unabled to parse leaf certificate";
+ return false;
+ }
+
+ if (!key.MatchesPublicKey(*leaf)) {
+ QUIC_BUG(quic_proof_source_key_mismatch)
+ << "Private key does not match the leaf certificate";
+ return false;
+ }
+ return true;
+}
+
} // namespace quic
diff --git a/quic/core/crypto/proof_source.h b/quic/core/crypto/proof_source.h
index c4a1f2c..fcc6cce 100644
--- a/quic/core/crypto/proof_source.h
+++ b/quic/core/crypto/proof_source.h
@@ -11,6 +11,7 @@
#include "absl/strings/string_view.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "quic/core/crypto/certificate_view.h"
#include "quic/core/crypto/quic_crypto_proof.h"
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_export.h"
@@ -342,6 +343,12 @@
friend class test::FakeProofSourceHandle;
};
+// Returns true if |chain| contains a parsable DER-encoded X.509 leaf cert and
+// it matches with |key|.
+QUIC_EXPORT_PRIVATE bool ValidateCertAndKey(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const CertificatePrivateKey& key);
+
} // namespace quic
#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
diff --git a/quic/core/crypto/quic_crypto_client_config.cc b/quic/core/crypto/quic_crypto_client_config.cc
index 99f9279..6ecf0e8 100644
--- a/quic/core/crypto/quic_crypto_client_config.cc
+++ b/quic/core/crypto/quic_crypto_client_config.cc
@@ -801,12 +801,12 @@
return session_cache_.get();
}
-ProofSource* QuicCryptoClientConfig::proof_source() const {
+ClientProofSource* QuicCryptoClientConfig::proof_source() const {
return proof_source_.get();
}
void QuicCryptoClientConfig::set_proof_source(
- std::unique_ptr<ProofSource> proof_source) {
+ std::unique_ptr<ClientProofSource> proof_source) {
proof_source_ = std::move(proof_source);
}
diff --git a/quic/core/crypto/quic_crypto_client_config.h b/quic/core/crypto/quic_crypto_client_config.h
index 847fc96..e316f8f 100644
--- a/quic/core/crypto/quic_crypto_client_config.h
+++ b/quic/core/crypto/quic_crypto_client_config.h
@@ -14,9 +14,9 @@
#include "absl/strings/string_view.h"
#include "third_party/boringssl/src/include/openssl/base.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "quic/core/crypto/client_proof_source.h"
#include "quic/core/crypto/crypto_handshake.h"
#include "quic/core/crypto/crypto_protocol.h"
-#include "quic/core/crypto/proof_source.h"
#include "quic/core/crypto/transport_parameters.h"
#include "quic/core/quic_packets.h"
#include "quic/core/quic_server_id.h"
@@ -336,8 +336,8 @@
ProofVerifier* proof_verifier() const;
SessionCache* session_cache() const;
- ProofSource* proof_source() const;
- void set_proof_source(std::unique_ptr<ProofSource> proof_source);
+ ClientProofSource* proof_source() const;
+ void set_proof_source(std::unique_ptr<ClientProofSource> proof_source);
SSL_CTX* ssl_ctx() const;
// Initialize the CachedState from |canonical_crypto_config| for the
@@ -429,7 +429,7 @@
std::unique_ptr<ProofVerifier> proof_verifier_;
std::unique_ptr<SessionCache> session_cache_;
- std::unique_ptr<ProofSource> proof_source_;
+ std::unique_ptr<ClientProofSource> proof_source_;
bssl::UniquePtr<SSL_CTX> ssl_ctx_;
diff --git a/quic/core/crypto/tls_client_connection.cc b/quic/core/crypto/tls_client_connection.cc
index bfd20b2..8e31bba 100644
--- a/quic/core/crypto/tls_client_connection.cc
+++ b/quic/core/crypto/tls_client_connection.cc
@@ -34,6 +34,12 @@
return ssl_ctx;
}
+void TlsClientConnection::SetCertChain(
+ const std::vector<CRYPTO_BUFFER*>& cert_chain, EVP_PKEY* privkey) {
+ SSL_set_chain_and_key(ssl(), cert_chain.data(), cert_chain.size(), privkey,
+ /*privkey_method=*/nullptr);
+}
+
// static
int TlsClientConnection::NewSessionCallback(SSL* ssl, SSL_SESSION* session) {
static_cast<TlsClientConnection*>(ConnectionFromSsl(ssl))
diff --git a/quic/core/crypto/tls_client_connection.h b/quic/core/crypto/tls_client_connection.h
index ce4b948..574441e 100644
--- a/quic/core/crypto/tls_client_connection.h
+++ b/quic/core/crypto/tls_client_connection.h
@@ -37,6 +37,11 @@
// Creates and configures an SSL_CTX that is appropriate for clients to use.
static bssl::UniquePtr<SSL_CTX> CreateSslCtx(bool enable_early_data);
+ // Set the client cert and private key to be used on this connection, if
+ // requested by the server.
+ void SetCertChain(const std::vector<CRYPTO_BUFFER*>& cert_chain,
+ EVP_PKEY* privkey);
+
private:
// Registered as the callback for SSL_CTX_sess_set_new_cb, which calls
// Delegate::InsertSession.
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 50ba814..2717db0 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -51,6 +51,15 @@
SSL_set1_sigalgs_list(ssl(),
crypto_config->tls_signature_algorithms()->c_str());
}
+ if (crypto_config->proof_source() != nullptr) {
+ const ClientProofSource::CertAndKey* cert_and_key =
+ crypto_config->proof_source()->GetCertAndKey(server_id.host());
+ if (cert_and_key != nullptr) {
+ QUIC_DVLOG(1) << "Setting client cert and key for " << server_id.host();
+ tls_connection_.SetCertChain(cert_and_key->chain->ToCryptoBuffers().value,
+ cert_and_key->private_key.private_key());
+ }
+ }
}
TlsClientHandshaker::~TlsClientHandshaker() {}