| Index: net/socket/ssl_client_socket_unittest.cc
|
| diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
|
| index 6067721f84f3dc30e3b8125a3729ad2b3227341e..1b62a269ac43268470a903b9216162bfb66216d3 100644
|
| --- a/net/socket/ssl_client_socket_unittest.cc
|
| +++ b/net/socket/ssl_client_socket_unittest.cc
|
| @@ -805,6 +805,50 @@ class AsyncFailingChannelIDStore : public ChannelIDStore {
|
| bool IsEphemeral() override { return true; }
|
| };
|
|
|
| +// A mock ExpectCTReporter that remembers the latest violation that was
|
| +// reported and the number of violations reported.
|
| +class MockExpectCTReporter : public TransportSecurityState::ExpectCTReporter {
|
| + public:
|
| + MockExpectCTReporter() : num_failures_(0) {}
|
| + ~MockExpectCTReporter() override {}
|
| +
|
| + void OnExpectCTFailed(const HostPortPair& host_port_pair,
|
| + const GURL& report_uri,
|
| + const X509Certificate* validated_certificate_chain,
|
| + const X509Certificate* served_certificate_chain,
|
| + const SignedCertificateTimestampAndStatusList&
|
| + signed_certificate_timestamps) override {
|
| + num_failures_++;
|
| + host_port_pair_ = host_port_pair;
|
| + report_uri_ = report_uri;
|
| + served_certificate_chain_ = served_certificate_chain;
|
| + validated_certificate_chain_ = validated_certificate_chain;
|
| + signed_certificate_timestamps_ = signed_certificate_timestamps;
|
| + }
|
| +
|
| + const HostPortPair& host_port_pair() { return host_port_pair_; }
|
| + const GURL& report_uri() { return report_uri_; }
|
| + uint32_t num_failures() { return num_failures_; }
|
| + const X509Certificate* served_certificate_chain() {
|
| + return served_certificate_chain_;
|
| + }
|
| + const X509Certificate* validated_certificate_chain() {
|
| + return validated_certificate_chain_;
|
| + }
|
| + const SignedCertificateTimestampAndStatusList&
|
| + signed_certificate_timestamps() {
|
| + return signed_certificate_timestamps_;
|
| + }
|
| +
|
| + private:
|
| + HostPortPair host_port_pair_;
|
| + GURL report_uri_;
|
| + uint32_t num_failures_;
|
| + const X509Certificate* served_certificate_chain_;
|
| + const X509Certificate* validated_certificate_chain_;
|
| + SignedCertificateTimestampAndStatusList signed_certificate_timestamps_;
|
| +};
|
| +
|
| // A mock CTVerifier that records every call to Verify but doesn't verify
|
| // anything.
|
| class MockCTVerifier : public CTVerifier {
|
| @@ -3478,6 +3522,105 @@ TEST_F(SSLClientSocketTest, CTIsRequired) {
|
| EXPECT_TRUE(sock_->IsConnected());
|
| }
|
|
|
| +// Test that when CT is required (in this case, by an Expect-CT opt-in), the
|
| +// absence of CT information is a socket error.
|
| +TEST_F(SSLClientSocketTest, CTIsRequiredByExpectCT) {
|
| + base::test::ScopedFeatureList feature_list;
|
| + feature_list.InitAndEnableFeature(
|
| + TransportSecurityState::kDynamicExpectCTFeature);
|
| +
|
| + SpawnedTestServer::SSLOptions ssl_options;
|
| + ASSERT_TRUE(StartTestServer(ssl_options));
|
| + scoped_refptr<X509Certificate> server_cert =
|
| + spawned_test_server()->GetCertificate();
|
| +
|
| + // Certificate is trusted and chains to a public root.
|
| + CertVerifyResult verify_result;
|
| + verify_result.is_issued_by_known_root = true;
|
| + verify_result.verified_cert = server_cert;
|
| + verify_result.public_key_hashes = MakeHashValueVector(0);
|
| + cert_verifier_->AddResultForCert(server_cert.get(), verify_result, OK);
|
| +
|
| + // Set up the Expect-CT opt-in.
|
| + const base::Time current_time(base::Time::Now());
|
| + const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
|
| + transport_security_state_->AddExpectCT(
|
| + spawned_test_server()->host_port_pair().host(), expiry,
|
| + true /* enforce */, GURL("https://example-report.test"));
|
| + MockExpectCTReporter reporter;
|
| + transport_security_state_->SetExpectCTReporter(&reporter);
|
| +
|
| + EXPECT_CALL(*ct_policy_enforcer_,
|
| + DoesConformToCertPolicy(server_cert.get(), _, _))
|
| + .WillRepeatedly(
|
| + Return(ct::CertPolicyCompliance::CERT_POLICY_NOT_ENOUGH_SCTS));
|
| +
|
| + SSLConfig ssl_config;
|
| + int rv;
|
| + ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
|
| + SSLInfo ssl_info;
|
| + ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
|
| +
|
| + EXPECT_THAT(rv, IsError(ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
|
| + EXPECT_TRUE(ssl_info.cert_status &
|
| + CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED);
|
| + EXPECT_TRUE(sock_->IsConnected());
|
| +
|
| + EXPECT_EQ(1u, reporter.num_failures());
|
| + EXPECT_EQ(GURL("https://example-report.test"), reporter.report_uri());
|
| + EXPECT_EQ(ssl_info.unverified_cert.get(),
|
| + reporter.served_certificate_chain());
|
| + EXPECT_EQ(ssl_info.cert.get(), reporter.validated_certificate_chain());
|
| + EXPECT_EQ(0u, reporter.signed_certificate_timestamps().size());
|
| +
|
| + EXPECT_CALL(*ct_policy_enforcer_,
|
| + DoesConformToCertPolicy(server_cert.get(), _, _))
|
| + .WillRepeatedly(
|
| + Return(ct::CertPolicyCompliance::CERT_POLICY_NOT_DIVERSE_SCTS));
|
| + ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
|
| + ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
|
| +
|
| + EXPECT_THAT(rv, IsError(ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
|
| + EXPECT_TRUE(ssl_info.cert_status &
|
| + CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED);
|
| + EXPECT_TRUE(sock_->IsConnected());
|
| +
|
| + EXPECT_EQ(2u, reporter.num_failures());
|
| + EXPECT_EQ(GURL("https://example-report.test"), reporter.report_uri());
|
| + EXPECT_EQ(ssl_info.unverified_cert.get(),
|
| + reporter.served_certificate_chain());
|
| + EXPECT_EQ(ssl_info.cert.get(), reporter.validated_certificate_chain());
|
| + EXPECT_EQ(0u, reporter.signed_certificate_timestamps().size());
|
| +
|
| + // If the connection is CT compliant, then there should be no socket error nor
|
| + // a report.
|
| + EXPECT_CALL(*ct_policy_enforcer_,
|
| + DoesConformToCertPolicy(server_cert.get(), _, _))
|
| + .WillRepeatedly(
|
| + Return(ct::CertPolicyCompliance::CERT_POLICY_COMPLIES_VIA_SCTS));
|
| + ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
|
| + ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
|
| +
|
| + EXPECT_EQ(net::OK, rv);
|
| + EXPECT_FALSE(ssl_info.cert_status &
|
| + CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED);
|
| + EXPECT_TRUE(sock_->IsConnected());
|
| + EXPECT_EQ(2u, reporter.num_failures());
|
| +
|
| + EXPECT_CALL(*ct_policy_enforcer_,
|
| + DoesConformToCertPolicy(server_cert.get(), _, _))
|
| + .WillRepeatedly(
|
| + Return(ct::CertPolicyCompliance::CERT_POLICY_BUILD_NOT_TIMELY));
|
| + ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
|
| + ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
|
| +
|
| + EXPECT_EQ(net::OK, rv);
|
| + EXPECT_FALSE(ssl_info.cert_status &
|
| + CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED);
|
| + EXPECT_TRUE(sock_->IsConnected());
|
| + EXPECT_EQ(2u, reporter.num_failures());
|
| +}
|
| +
|
| // When both HPKP and CT are required for a host, and both fail, the more
|
| // serious error is that the HPKP pin validation failed.
|
| TEST_F(SSLClientSocketTest, PKPMoreImportantThanCT) {
|
|
|