Index: net/http/transport_security_state_unittest.cc |
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc |
index 31c4062892d488d25378759b777b9d788f8f9833..4c63c06e446901e95e93d7e12331cb1ca0e83da6 100644 |
--- a/net/http/transport_security_state_unittest.cc |
+++ b/net/http/transport_security_state_unittest.cc |
@@ -42,8 +42,28 @@ namespace net { |
namespace { |
+const char kHost[] = "example.test"; |
+const char kSubdomain[] = "foo.example.test"; |
+const uint16_t kPort = 443; |
const char kReportUri[] = "http://example.test/test"; |
+// kGoodPath is blog.torproject.org. |
+const char* const kGoodPath[] = { |
+ "sha1/m9lHYJYke9k0GtVZ+bXSQYE8nDI=", "sha1/o5OZxATDsgmwgcIfIWIneMJ0jkw=", |
+ "sha1/wHqYaI2J+6sFZAwRfap9ZbjKzE4=", NULL, |
+}; |
+ |
+const char kGoodPin1[] = "m9lHYJYke9k0GtVZ+bXSQYE8nDI="; |
+const char kGoodPin2[] = "o5OZxATDsgmwgcIfIWIneMJ0jkw="; |
+const char kGoodPin3[] = "wHqYaI2J+6sFZAwRfap9ZbjKzE4="; |
+ |
+// kBadPath is plus.google.com via Trustcenter, which is utterly wrong for |
+// torproject.org. |
+const char* const kBadPath[] = { |
+ "sha1/4BjDjn8v2lWeUFQnqSs0BgbIcrU=", "sha1/gzuEEAB/bkqdQS3EIjk2by7lW+k=", |
+ "sha1/SOZo+SvSspXXR9gjIBBPM5iQn9Q=", NULL, |
+}; |
+ |
// A mock ReportSender that just remembers the latest report |
// URI and report to be sent. |
class MockCertificateReportSender |
@@ -57,6 +77,11 @@ class MockCertificateReportSender |
latest_report_ = report; |
} |
+ void Clear() { |
+ latest_report_uri_ = GURL(); |
+ latest_report_ = std::string(); |
+ } |
+ |
const GURL& latest_report_uri() { return latest_report_uri_; } |
const std::string& latest_report() { return latest_report_; } |
@@ -83,14 +108,11 @@ void CompareCertificateChainWithList( |
void CheckHPKPReport( |
const std::string& report, |
const HostPortPair& host_port_pair, |
- const base::Time& expiry, |
bool include_subdomains, |
const std::string& noted_hostname, |
const scoped_refptr<X509Certificate>& served_certificate_chain, |
const scoped_refptr<X509Certificate>& validated_certificate_chain, |
const HashValueVector& known_pins) { |
- // TODO(estark): check time in RFC3339 format. |
- |
scoped_ptr<base::Value> value(base::JSONReader::Read(report)); |
ASSERT_TRUE(value); |
ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); |
@@ -115,6 +137,17 @@ void CheckHPKPReport( |
EXPECT_TRUE(report_dict->GetString("noted-hostname", &report_noted_hostname)); |
EXPECT_EQ(noted_hostname, report_noted_hostname); |
+ // TODO(estark): check times in RFC3339 format. |
+ |
+ std::string report_expiration; |
+ EXPECT_TRUE( |
+ report_dict->GetString("effective-expiration-date", &report_expiration)); |
+ EXPECT_FALSE(report_expiration.empty()); |
+ |
+ std::string report_date; |
+ EXPECT_TRUE(report_dict->GetString("date-time", &report_date)); |
+ EXPECT_FALSE(report_date.empty()); |
+ |
base::ListValue* report_served_certificate_chain; |
EXPECT_TRUE(report_dict->GetList("served-certificate-chain", |
&report_served_certificate_chain)); |
@@ -1057,23 +1090,6 @@ static bool AddHash(const std::string& type_and_base64, |
} |
TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { |
- // kGoodPath is blog.torproject.org. |
- static const char* const kGoodPath[] = { |
- "sha1/m9lHYJYke9k0GtVZ+bXSQYE8nDI=", |
- "sha1/o5OZxATDsgmwgcIfIWIneMJ0jkw=", |
- "sha1/wHqYaI2J+6sFZAwRfap9ZbjKzE4=", |
- NULL, |
- }; |
- |
- // kBadPath is plus.google.com via Trustcenter, which is utterly wrong for |
- // torproject.org. |
- static const char* const kBadPath[] = { |
- "sha1/4BjDjn8v2lWeUFQnqSs0BgbIcrU=", |
- "sha1/gzuEEAB/bkqdQS3EIjk2by7lW+k=", |
- "sha1/SOZo+SvSspXXR9gjIBBPM5iQn9Q=", |
- NULL, |
- }; |
- |
HashValueVector good_hashes, bad_hashes; |
for (size_t i = 0; kGoodPath[i]; i++) { |
@@ -1200,12 +1216,9 @@ TEST_F(TransportSecurityStateTest, GooglePinnedProperties) { |
} |
TEST_F(TransportSecurityStateTest, HPKPReporting) { |
- const char kHost[] = "example.test"; |
- const char kSubdomain[] = "foo.example.test"; |
- static const uint16_t kPort = 443; |
HostPortPair host_port_pair(kHost, kPort); |
HostPortPair subdomain_host_port_pair(kSubdomain, kPort); |
- GURL report_uri("http://www.example.test/report"); |
+ GURL report_uri(kReportUri); |
// Two dummy certs to use as the server-sent and validated chains. The |
// contents don't matter. |
scoped_refptr<X509Certificate> cert1 = |
@@ -1215,19 +1228,6 @@ TEST_F(TransportSecurityStateTest, HPKPReporting) { |
ASSERT_TRUE(cert1); |
ASSERT_TRUE(cert2); |
- // kGoodPath is blog.torproject.org. |
- static const char* const kGoodPath[] = { |
- "sha1/m9lHYJYke9k0GtVZ+bXSQYE8nDI=", "sha1/o5OZxATDsgmwgcIfIWIneMJ0jkw=", |
- "sha1/wHqYaI2J+6sFZAwRfap9ZbjKzE4=", NULL, |
- }; |
- |
- // kBadPath is plus.google.com via Trustcenter, which is utterly wrong for |
- // torproject.org. |
- static const char* const kBadPath[] = { |
- "sha1/4BjDjn8v2lWeUFQnqSs0BgbIcrU=", "sha1/gzuEEAB/bkqdQS3EIjk2by7lW+k=", |
- "sha1/SOZo+SvSspXXR9gjIBBPM5iQn9Q=", NULL, |
- }; |
- |
HashValueVector good_hashes, bad_hashes; |
for (size_t i = 0; kGoodPath[i]; i++) |
@@ -1273,10 +1273,10 @@ TEST_F(TransportSecurityStateTest, HPKPReporting) { |
EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
std::string report = mock_report_sender.latest_report(); |
ASSERT_FALSE(report.empty()); |
- ASSERT_NO_FATAL_FAILURE(CheckHPKPReport(report, host_port_pair, expiry, true, |
- kHost, cert1.get(), cert2.get(), |
+ ASSERT_NO_FATAL_FAILURE(CheckHPKPReport(report, host_port_pair, true, kHost, |
+ cert1.get(), cert2.get(), |
good_hashes)); |
- |
+ mock_report_sender.Clear(); |
EXPECT_FALSE(state.CheckPublicKeyPins( |
subdomain_host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), |
TransportSecurityState::ENABLE_PIN_REPORTS, &failure_log)); |
@@ -1287,8 +1287,131 @@ TEST_F(TransportSecurityStateTest, HPKPReporting) { |
report = mock_report_sender.latest_report(); |
ASSERT_FALSE(report.empty()); |
ASSERT_NO_FATAL_FAILURE(CheckHPKPReport(report, subdomain_host_port_pair, |
- expiry, true, kHost, cert1.get(), |
- cert2.get(), good_hashes)); |
+ true, kHost, cert1.get(), cert2.get(), |
+ good_hashes)); |
+} |
+ |
+TEST_F(TransportSecurityStateTest, HPKPReportOnly) { |
+ HostPortPair host_port_pair(kHost, kPort); |
+ GURL report_uri(kReportUri); |
+ // Two dummy certs to use as the server-sent and validated chains. The |
+ // contents don't matter. |
+ scoped_refptr<X509Certificate> cert1 = |
+ ImportCertFromFile(GetTestCertsDirectory(), "test_mail_google_com.pem"); |
+ scoped_refptr<X509Certificate> cert2 = |
+ ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); |
+ ASSERT_TRUE(cert1); |
+ ASSERT_TRUE(cert2); |
+ |
+ TransportSecurityState state; |
+ MockCertificateReportSender mock_report_sender; |
+ state.SetReportSender(&mock_report_sender); |
+ |
+ // Check that a report is not sent for a Report-Only header with no |
+ // violation. |
+ std::string header = |
+ "pin-sha1=\"" + std::string(kGoodPin1) + "\";pin-sha1=\"" + |
+ std::string(kGoodPin2) + "\";pin-sha1=\"" + std::string(kGoodPin3) + |
+ "\";report-uri=\"" + report_uri.spec() + "\";includeSubdomains"; |
+ SSLInfo ssl_info; |
+ ssl_info.is_issued_by_known_root = true; |
+ ssl_info.unverified_cert = cert1; |
+ ssl_info.cert = cert2; |
+ for (size_t i = 0; kGoodPath[i]; i++) |
+ EXPECT_TRUE(AddHash(kGoodPath[i], &ssl_info.public_key_hashes)); |
+ |
+ EXPECT_TRUE( |
+ state.ProcessHPKPReportOnlyHeader(header, host_port_pair, ssl_info)); |
+ EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); |
+ EXPECT_EQ(std::string(), mock_report_sender.latest_report()); |
+ |
+ // Check that a report is sent for a Report-Only header with a |
+ // violation. |
+ ssl_info.public_key_hashes.clear(); |
+ for (size_t i = 0; kBadPath[i]; i++) |
+ EXPECT_TRUE(AddHash(kBadPath[i], &ssl_info.public_key_hashes)); |
+ |
+ EXPECT_TRUE( |
+ state.ProcessHPKPReportOnlyHeader(header, host_port_pair, ssl_info)); |
+ EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
+ std::string report = mock_report_sender.latest_report(); |
+ ASSERT_FALSE(report.empty()); |
+ ASSERT_NO_FATAL_FAILURE(CheckHPKPReport(report, host_port_pair, true, kHost, |
+ cert1.get(), cert2.get(), |
+ ssl_info.public_key_hashes)); |
+} |
+ |
+// Test that Report-Only reports are not sent on certs that chain to |
+// local roots. |
+TEST_F(TransportSecurityStateTest, HPKPReportOnlyOnLocalRoot) { |
+ HostPortPair host_port_pair(kHost, kPort); |
+ GURL report_uri(kReportUri); |
+ // Two dummy certs to use as the server-sent and validated chains. The |
+ // contents don't matter. |
+ scoped_refptr<X509Certificate> cert1 = |
+ ImportCertFromFile(GetTestCertsDirectory(), "test_mail_google_com.pem"); |
+ scoped_refptr<X509Certificate> cert2 = |
+ ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); |
+ ASSERT_TRUE(cert1); |
+ ASSERT_TRUE(cert2); |
+ |
+ std::string header = |
+ "pin-sha1=\"" + std::string(kGoodPin1) + "\";pin-sha1=\"" + |
+ std::string(kGoodPin2) + "\";pin-sha1=\"" + std::string(kGoodPin3) + |
+ "\";report-uri=\"" + report_uri.spec() + "\";includeSubdomains"; |
+ |
+ TransportSecurityState state; |
+ MockCertificateReportSender mock_report_sender; |
+ state.SetReportSender(&mock_report_sender); |
+ |
+ SSLInfo ssl_info; |
+ ssl_info.is_issued_by_known_root = true; |
+ ssl_info.unverified_cert = cert1; |
+ ssl_info.cert = cert2; |
+ for (size_t i = 0; kGoodPath[i]; i++) |
+ EXPECT_TRUE(AddHash(kGoodPath[i], &ssl_info.public_key_hashes)); |
+ ssl_info.is_issued_by_known_root = false; |
+ |
+ EXPECT_TRUE( |
+ state.ProcessHPKPReportOnlyHeader(header, host_port_pair, ssl_info)); |
+ EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); |
+ EXPECT_EQ(std::string(), mock_report_sender.latest_report()); |
+} |
+ |
+// Test that ProcessHPKPReportOnlyHeader() returns false if a report-uri |
+// wasn't specified or if the header fails to parse. |
+TEST_F(TransportSecurityStateTest, HPKPReportOnlyParseErrors) { |
+ HostPortPair host_port_pair(kHost, kPort); |
+ GURL report_uri(kReportUri); |
+ // Two dummy certs to use as the server-sent and validated chains. The |
+ // contents don't matter. |
+ scoped_refptr<X509Certificate> cert1 = |
+ ImportCertFromFile(GetTestCertsDirectory(), "test_mail_google_com.pem"); |
+ scoped_refptr<X509Certificate> cert2 = |
+ ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); |
+ ASSERT_TRUE(cert1); |
+ ASSERT_TRUE(cert2); |
+ |
+ std::string header = "pin-sha1=\"" + std::string(kGoodPin1) + |
+ "\";pin-sha1=\"" + std::string(kGoodPin2) + |
+ "\";pin-sha1=\"" + std::string(kGoodPin3) + "\""; |
+ |
+ TransportSecurityState state; |
+ MockCertificateReportSender mock_report_sender; |
+ state.SetReportSender(&mock_report_sender); |
+ |
+ SSLInfo ssl_info; |
+ ssl_info.is_issued_by_known_root = true; |
+ ssl_info.unverified_cert = cert1; |
+ ssl_info.cert = cert2; |
+ for (size_t i = 0; kGoodPath[i]; i++) |
+ EXPECT_TRUE(AddHash(kGoodPath[i], &ssl_info.public_key_hashes)); |
+ |
+ EXPECT_FALSE( |
+ state.ProcessHPKPReportOnlyHeader(header, host_port_pair, ssl_info)); |
+ header += ";report-uri=\""; |
+ EXPECT_FALSE( |
+ state.ProcessHPKPReportOnlyHeader(header, host_port_pair, ssl_info)); |
} |
} // namespace net |