Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Unified Diff: net/http/transport_security_state_unittest.cc

Issue 1266723003: Process Public-Key-Pin-Report-Only headers (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: davidben comments Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/http/transport_security_state.cc ('k') | net/url_request/url_request_http_job.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « net/http/transport_security_state.cc ('k') | net/url_request/url_request_http_job.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698